Title: StorageAccess.java ConcurrentModificationException · Issue #506 · ResearchStack/ResearchStack · GitHub
Open Graph Title: StorageAccess.java ConcurrentModificationException · Issue #506 · ResearchStack/ResearchStack
X Title: StorageAccess.java ConcurrentModificationException · Issue #506 · ResearchStack/ResearchStack
Description: In StorageAccess.java, listeners is initialized using Collections.synchronizedList. private List
Open Graph Description: In StorageAccess.java, listeners is initialized using Collections.synchronizedList. private List
X Description: In StorageAccess.java, listeners is initialized using Collections.synchronizedList. private List<StorageAccessListener> listeners = Collections.synchronizedList(new ArrayList<>()); Per ...
Opengraph URL: https://github.com/ResearchStack/ResearchStack/issues/506
X: @github
Domain: patch-diff.githubusercontent.com
{"@context":"https://schema.org","@type":"DiscussionForumPosting","headline":"StorageAccess.java ConcurrentModificationException","articleBody":"In StorageAccess.java, listeners is initialized using Collections.synchronizedList. \r\n` private List\u003cStorageAccessListener\u003e listeners = Collections.synchronizedList(new ArrayList\u003c\u003e());\r\n`\r\n\r\nPer the oracle Javadoc for syncrhonizedList, \"It is imperative that the user manually synchronize on the returned list when iterating over it\"\r\n\r\nThe notifyListenersReady method iterates over listeners, but does not synchronize on it. If one thread is running notifyListenersReady while a different thread adds or removes an element (i.e. registers / unregisters a listener) a ConcurrentModificationException will be thrown.\r\n\r\n` java.util.ConcurrentModificationException\r\n at java.util.ArrayList$Itr.next(ArrayList.java:831)\r\n at org.researchstack.backbone.StorageAccess.notifyListenersReady(StorageAccess.java:222)\r\n at org.researchstack.backbone.StorageAccess.access$lambda$0(StorageAccess.java)\r\n at org.researchstack.backbone.StorageAccess$$Lambda$1.run(Unknown Source)\r\n at android.os.Handler.handleCallback(Handler.java:836)\r\n at android.os.Handler.dispatchMessage(Handler.java:103)\r\n at android.os.Looper.loop(Looper.java:203)\r\n at android.app.ActivityThread.main(ActivityThread.java:6251)\r\n at java.lang.reflect.Method.invoke(Native Method)\r\n at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1063)\r\n at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:924)`\r\n\r\n\r\nJavadoc: https://docs.oracle.com/javase/7/docs/api/java/util/Collections.html#synchronizedList(java.util.List)\r\n\r\n\r\nThe race condition is triggered under the following conditions:\r\n\r\n1. We launch a task from activity A (activity A extends PinCodeActivity). This task is required - meaning the participant will be prompted to complete it if he/she cancels out of the task.\r\n2. While the task is running, the participant presses the back button\r\n3. Activity A's onActivityResult method checks for the Activity.RESULT_CANCELED result code. It finds it and launches a TaskNeededActivity - an activity with a view saying \"Task Required\". TaskNeededActitivty also extends PinCodeActivity.\r\n\r\nIn step 2, when the participant presses the back button, Activity A's onStart() and onResume() get called after onActivityResult. \r\n\r\nMeanwhile, in step 3, TaskNeededActivity launches and its onResume() method is called. The onResume() calls the superclass PinCodeActivity's onResume(). PinCodeActivity's onResume() calls StorageAccess.requestStorageAccess(). This method calls notifyReady() which then calls notifyListenersReady() which iterates through the listeners list in an unsynchronized manner. \r\n\r\nWhile TaskNeededActivity's onResume() is causing the program to iterate through the listeners list, Activity A's onDataReady() is called. It calls super() and PinCodeActivity's onDataReady() method calls storageAccessUnregister() which calls StorageAccess.unregister() which removes a listener from the listeners list. \r\n\r\nWhen the iterator from TaskNeededActivity calls next(), it raises a ConcurrentModificationException upon discovering that a listener was removed. \r\n\r\n","author":{"url":"https://github.com/ronykrell","@type":"Person","name":"ronykrell"},"datePublished":"2019-05-14T04:21:42.000Z","interactionStatistic":{"@type":"InteractionCounter","interactionType":"https://schema.org/CommentAction","userInteractionCount":0},"url":"https://github.com/506/ResearchStack/issues/506"}
| route-pattern | /_view_fragments/issues/show/:user_id/:repository/:id/issue_layout(.:format) |
| route-controller | voltron_issues_fragments |
| route-action | issue_layout |
| fetch-nonce | v2:1c578f67-1e57-8a15-2d26-8c422c566d53 |
| current-catalog-service-hash | 81bb79d38c15960b92d99bca9288a9108c7a47b18f2423d0f6438c5b7bcd2114 |
| request-id | D6A8:29F505:16F080D:1DEA78A:69816F19 |
| html-safe-nonce | 819a035f14d58e5b395ca822355d31b8975492bfaddf1c04714b957e31015453 |
| visitor-payload | eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiJENkE4OjI5RjUwNToxNkYwODBEOjFERUE3OEE6Njk4MTZGMTkiLCJ2aXNpdG9yX2lkIjoiNjIzMDQ2MTkxMjc3NDE3NjUzNyIsInJlZ2lvbl9lZGdlIjoiaWFkIiwicmVnaW9uX3JlbmRlciI6ImlhZCJ9 |
| visitor-hmac | a94f0d913ae811edbe16602e817c8429c5aff885e456367cdd01e53b97823f90 |
| hovercard-subject-tag | issue:443703322 |
| github-keyboard-shortcuts | repository,issues,copilot |
| google-site-verification | Apib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I |
| octolytics-url | https://collector.github.com/github/collect |
| analytics-location | / |
| fb:app_id | 1401488693436528 |
| apple-itunes-app | app-id=1477376905, app-argument=https://github.com/_view_fragments/issues/show/ResearchStack/ResearchStack/506/issue_layout |
| twitter:image | https://opengraph.githubassets.com/8aeea25853c8c1582c5d811095fbac63b90ef20a2c2e73bde1b112efd1d63d99/ResearchStack/ResearchStack/issues/506 |
| twitter:card | summary_large_image |
| og:image | https://opengraph.githubassets.com/8aeea25853c8c1582c5d811095fbac63b90ef20a2c2e73bde1b112efd1d63d99/ResearchStack/ResearchStack/issues/506 |
| og:image:alt | In StorageAccess.java, listeners is initialized using Collections.synchronizedList. private List |
| og:image:width | 1200 |
| og:image:height | 600 |
| og:site_name | GitHub |
| og:type | object |
| og:author:username | ronykrell |
| hostname | github.com |
| expected-hostname | github.com |
| None | e137814e266030874fd2c86863529d0622b13889eeda04148c57654b6ea84ad6 |
| turbo-cache-control | no-preview |
| go-import | github.com/ResearchStack/ResearchStack git https://github.com/ResearchStack/ResearchStack.git |
| octolytics-dimension-user_id | 15782830 |
| octolytics-dimension-user_login | ResearchStack |
| octolytics-dimension-repository_id | 43707061 |
| octolytics-dimension-repository_nwo | ResearchStack/ResearchStack |
| octolytics-dimension-repository_public | true |
| octolytics-dimension-repository_is_fork | false |
| octolytics-dimension-repository_network_root_id | 43707061 |
| octolytics-dimension-repository_network_root_nwo | ResearchStack/ResearchStack |
| turbo-body-classes | logged-out env-production page-responsive |
| disable-turbo | false |
| browser-stats-url | https://api.github.com/_private/browser/stats |
| browser-errors-url | https://api.github.com/_private/browser/errors |
| release | dd58d68a7813bbec9c91422c8c35f4af33832d70 |
| ui-target | full |
| theme-color | #1e2327 |
| color-scheme | light dark |
Links:
Viewport: width=device-width