Closed Bug 975838 Opened 11 years ago Closed 11 years ago

Log things getting touched off the UI Thread

Categories

(Firefox for Android Graveyard :: General, defect)

30 Branch
All
Android
defect
Not set
critical

Tracking

(firefox29 unaffected, firefox30 affected, fennec30+)

RESOLVED FIXED
Firefox 31
Tracking Status
firefox29 --- unaffected
firefox30 --- affected
fennec 30+ ---

People

(Reporter: rnewman, Assigned: rnewman)

References

Details

(4 keywords, Whiteboard: [native-crash])

Crash Data

Attachments

(4 files)

This bug was filed from the Socorro interface and is report bp-ef5d94fb-48d6-41ee-92c5-b95a42140222. ============================================================= https://crash-stats.mozilla.com/report/index/ef5d94fb-48d6-41ee-92c5-b95a42140222 java.util.ConcurrentModificationException at java.util.HashMap$HashIterator.nextEntry(HashMap.java:792) at java.util.HashMap$KeyIterator.next(HashMap.java:819) at org.mozilla.gecko.menu.GeckoMenu.refresh(GeckoMenu.java:647) at org.mozilla.gecko.GeckoApp.onPreparePanel(GeckoApp.java:429) at org.mozilla.gecko.GeckoApp.onCreatePanelView(GeckoApp.java:397) at com.android.internal.policy.impl.PhoneWindow.preparePanel(PhoneWindow.java:431) at com.android.internal.policy.impl.PhoneWindow.openPanel(PhoneWindow.java:626) at com.android.internal.policy.impl.PhoneWindow.openPanel(PhoneWindow.java:586) at android.app.Activity.openOptionsMenu(Activity.java:2891) at org.mozilla.gecko.BrowserApp.openOptionsMenu(BrowserApp.java:2042) Distinct from Bug 826064.
Cause: mSecondaryActionItems is being modified by code off the UI thread, and it's racing with openOptionsMenu. There are a fair few callers of this, so it'll take a little while to poke around in Eclipse and see. Adding an assert for being on the UI thread wherever those HashMaps are modified is one good debugging strategy, if someone can repro this. Another solution is to simply make those HashMaps into ConcurrentHashMaps, and stop worrying about which thread modifies them. That's a little bit cavalier, though.
There are 85 crashes with this signature in the last week, so nomming.
tracking-fennec: --- → ?
Possibly related: Adapter#notifyDataSetChanged() must be called on the UI thread, but we're apparently calling it off the UI thread. Assuming the comment at [1] is correct, note that we call notifyDataSetChanged immediately after that comment at [2]. [1] http://hg.mozilla.org/mozilla-central/file/31113754db3b/mobile/android/base/menu/GeckoMenu.java#l486 [2] http://hg.mozilla.org/mozilla-central/file/31113754db3b/mobile/android/base/menu/GeckoMenu.java#l506 Since the menu is UI and extends ListView, we should require that all GeckoMenu methods be called on the UI thread. Allowing these methods to be called off the UI thread and then posting them a la [1] is messy and error prone.
I tried to provoke an error by using the attached patch, but was unable to. It adds assertOnUiThread calls to most methods on GeckoMenu and its inner classes, and additionally replaces HashMap with a UIThreadHashMap implementation, which throws if accessed off the main thread. Oh well.
Assignee: nobody → rnewman
Status: NEW → ASSIGNED
Hi I too had this crash today. Report ID bp-b4c90446-618b-4e6e-891b-a66332140224
Updated my Nightly from the 19th to today's build on my Galaxy S4. Attempts to open the menu crash the browser.
Keywords: reproducible
Whiteboard: [native-crash]
Regression from bug 975525?
Keywords: regression
(In reply to Brian Nicholson (:bnicholson) from comment #3) > Possibly related: Adapter#notifyDataSetChanged() must be called on the UI > thread, but we're apparently calling it off the UI thread. Assuming the > comment at [1] is correct, note that we call notifyDataSetChanged > immediately after that comment at [2]. > > [1] > http://hg.mozilla.org/mozilla-central/file/31113754db3b/mobile/android/base/ > menu/GeckoMenu.java#l486 > [2] > http://hg.mozilla.org/mozilla-central/file/31113754db3b/mobile/android/base/ > menu/GeckoMenu.java#l506 > > Since the menu is UI and extends ListView, we should require that all > GeckoMenu methods be called on the UI thread. Allowing these methods to be > called off the UI thread and then posting them a la [1] is messy and error > prone. So, is this the right time to ask Java to help us? That is, can we annotate methods as needing to be called on the UI thread, and either error or post them to the UI thread if they are not? Seems like somebody would have built this, a la FindBugs [1]. A quick search turns up [2], which looks pretty messy, but points the way. At first, I expect this would just be "annotation to assert", but we could try to add static analysis to force callers of annotated code to make appropriate arrangements. [1] http://findbugs.sourceforge.net [2] https://github.com/excilys/androidannotations
bzexport auto-assigned this to me, but I ain't working on it.
Assignee: rnewman → nobody
Status: ASSIGNED → NEW
This has been by far the topcrash on Android Nightly in the last few days, accounting for 3/4 of all crashes yesterday. I see bug 975525 has been backed out, let's see it that helps.
Seems fixed for me on today's build. Shall we close this or do we still want the attached patch?
Actually I take it back, I can still crash on today's build. The back-out didn't hit m-c yet.
I had 12 crashes in a series on yesterday's build : Nightly 30.0a1 (2014-02-25) Launch Nightly Tap on Menu to open Firefox Menu 1. Crash! 2. Crash! 3. Crash! 4. Freezed 5. Crash! on launch It self. 6. Crash! 7. Browse for some times then try to open menu - Crash! 8. Crash! 10. Crash! 11. Crash! 12. Crash!
Ashish, yesterday's build of Nightly based off of mozilla-central did not contain the backout as mentioned in comment #14. Today's build, (02/26) should be better.
Hi Aaron , I am not seeing this issue on build of 26-02 seems fix has been landed now
tracking-fennec: ? → 30+
Assignee: nobody → rnewman
Summary: crash in java.util.ConcurrentModificationException: at java.util.HashMap$HashIterator.nextEntry(HashMap.java) → Log things getting touched off the UI Thread
The approach I'd like to take for this: augment assertOnUiThread to fail hard on dev builds (and Nightly?), to log otherwise, and spread it liberally around code that should only be called on the UI thread. If I can, I'll make that an Annotation, so just anno @UIThread or @NonUIThread. If we only log, we're relying on developers to read the logs, and we know not many of us do that. Brian, do you agree with that approach?
Flags: needinfo?(bnicholson)
(In reply to Richard Newman [:rnewman] from comment #20) > The approach I'd like to take for this: augment assertOnUiThread to fail > hard on dev builds (and Nightly?), to log otherwise, and spread it liberally > around code that should only be called on the UI thread. > > If I can, I'll make that an Annotation, so just anno @UIThread or > @NonUIThread. > > If we only log, we're relying on developers to read the logs, and we know > not many of us do that. > > Brian, do you agree with that approach? Sounds perfect. I'm glad we're finally untangling this mess!
Flags: needinfo?(bnicholson)
We pass in the argument, 'cos we don't want ThreadUtils to be coupled to our release definitions.
Attachment #8383315 - Flags: review?(wjohnston)
These are all of the call sites that touch the HashMaps in question.
Attachment #8383316 - Flags: review?(wjohnston)
Comment on attachment 8383319 [details] [diff] [review] Part 3: robocop test that a GeckoMenu call will fail on the wrong thread. v1 Review of attachment 8383319 [details] [diff] [review]: ----------------------------------------------------------------- ::: mobile/android/base/tests/testGeckoMenu.java @@ +61,5 @@ > + mAsserter.is(exception, null, "No exception was thrown: release build."); > + return; > + } > + > + mAsserter.is(exception != null, true, "An exception was thrown."); We have mAsserter.ok(condition, message, diag). (though I've never understood what diag is supposed to stand for...)
Attachment #8383319 - Flags: review?(wjohnston) → review+
Comment on attachment 8383316 [details] [diff] [review] Part 2: pepper GeckoMenu with thread affinities. v1 Review of attachment 8383316 [details] [diff] [review]: ----------------------------------------------------------------- A couple questions, but I don't hate doing this. Just want clarification. Are we moving things to the UI thread because they definately do UI things. If we get a call from Gecko to add a menu item, in most cases that code doesn't need to run on the UI thread at all. It updates a list that updates an adapter that doesn't do anything if the menu isn't currently showing. This forces a few of those code paths onto the UI thread (I think?). ::: mobile/android/base/menu/GeckoMenu.java @@ +160,5 @@ > return menuItem; > } > > private void addItem(GeckoMenuItem menuItem) { > + ThreadUtils.assertOnUiThread(!AppConstants.RELEASE_BUILD); Do we care that this is only called on the UI Thread? This method doesn't actually update UI, it just updates our list (which may update the UI....). Are we using this as a hefty synchronization method here? This does call adapter.addMenuItem, which would eventually call notifyDataSetChanged, which might update the UI. So I guess I'm curious here, would we rather force you onto the UI thread early or wait until we really know you need to be on it? @@ +359,5 @@ > } > > @Override > public boolean hasVisibleItems() { > + ThreadUtils.assertOnUiThread(!AppConstants.RELEASE_BUILD); This just queries the in memory data stores. It seems better to synchronize it on them if we're worried...
Attachment #8383316 - Flags: review?(wjohnston) → review+
(In reply to Richard Newman [:rnewman] from comment #22) > Created attachment 8383315 [details] [diff] [review] > Part 1: add methods to log or throw if called on the wrong thread. v1 > > We pass in the argument, 'cos we don't want ThreadUtils to be coupled to our > release definitions. Drive-by comment: you can also introduce a "strict mode" that, once set, throws on assertion failure instead of logs. This way you don't have to repeat |!AppConstants.RELEASE_BUILD| a bunch of times :)
(In reply to Wesley Johnston (:wesj) from comment #26) > doesn't need to run on the UI thread at all. It updates a list that updates > an adapter that doesn't do anything if the menu isn't currently showing. Without seeing the code in question, the "updates an adapter" part of this sounds dangerous. Adapter is not thread-safe, and updates must always happen on the UI thread.
Comment on attachment 8383315 [details] [diff] [review] Part 1: add methods to log or throw if called on the wrong thread. v1 Review of attachment 8383315 [details] [diff] [review]: ----------------------------------------------------------------- Whoops. I forgot to hit send on this. ::: mobile/android/base/GeckoEditable.java @@ +360,5 @@ > return mIcRunHandler.getLooper() == Looper.myLooper(); > } > > private void assertOnIcThread() { > + ThreadUtils.assertOnThread(mIcRunHandler.getLooper().getThread(), true); I hate boolean parameters. Can we pass an enum value?
Attachment #8383315 - Flags: review?(wjohnston) → review+
Switched to an enum, did a little tidying, landed in two parts. Thanks, chaps! https://hg.mozilla.org/integration/fx-team/rev/1e8fa32e8abd https://hg.mozilla.org/integration/fx-team/rev/02bceb824051
Status: NEW → ASSIGNED
Target Milestone: --- → Firefox 31
Target Milestone: Firefox 31 → ---
Status: ASSIGNED → RESOLVED
Closed: 11 years ago
Resolution: --- → FIXED
Target Milestone: --- → Firefox 31
Product: Firefox for Android → Firefox for Android Graveyard
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: