Skip to content

Commit

Permalink
Prevent ConcurrentModificationException in callbacks (#266)
Browse files Browse the repository at this point in the history
  • Loading branch information
kattrali committed Mar 9, 2018
2 parents dfc4c12 + 8ddfa14 commit a689a57
Show file tree
Hide file tree
Showing 2 changed files with 83 additions and 4 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package com.bugsnag.android;

import android.support.annotation.NonNull;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;

import java.util.Collection;

/**
* Ensures that if a callback is added or removed during iteration, a
* {@link java.util.ConcurrentModificationException} is not thrown
*/
@RunWith(AndroidJUnit4.class)
@SmallTest
public class ConcurrentCallbackTest {

private Client client;

@Before
public void setUp() throws Exception {
client = BugsnagTestUtils.generateClient();
}

@Test
public void testClientNotifyModification() throws Exception {
final Collection<BeforeNotify> beforeNotifyTasks = client.config.getBeforeNotifyTasks();
client.beforeNotify(new BeforeNotify() {
@Override
public boolean run(Error error) {
beforeNotifyTasks.add(new BeforeNotifySkeleton());
// modify the Set, when iterating to the next callback this should not crash
return true;
}
});
client.beforeNotify(new BeforeNotifySkeleton());
client.notify(new RuntimeException());
}

@Test
public void testClientBreadcrumbModification() throws Exception {
final Collection<BeforeRecordBreadcrumb> breadcrumbTasks = client.config.getBeforeRecordBreadcrumbTasks();

client.beforeRecordBreadcrumb(new BeforeRecordBreadcrumb() {
@Override
public boolean shouldRecord(@NonNull Breadcrumb breadcrumb) {
breadcrumbTasks.add(new BeforeRecordBreadcrumbSkeleton());
// modify the Set, when iterating to the next callback this should not crash
return true;
}
});
client.beforeRecordBreadcrumb(new BeforeRecordBreadcrumbSkeleton());
client.leaveBreadcrumb("Whoops");
client.notify(new RuntimeException());
}

static class BeforeNotifySkeleton implements BeforeNotify {
@Override
public boolean run(Error error) {
return true;
}
}

static class BeforeRecordBreadcrumbSkeleton implements BeforeRecordBreadcrumb {
@Override
public boolean shouldRecord(@NonNull Breadcrumb breadcrumb) {
return true;
}
}

}
13 changes: 9 additions & 4 deletions sdk/src/main/java/com/bugsnag/android/Configuration.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import java.util.Map;
import java.util.Observable;
import java.util.Observer;
import java.util.concurrent.ConcurrentLinkedQueue;

/**
* User-specified configuration storage object, contains information
Expand Down Expand Up @@ -50,9 +51,9 @@ public class Configuration extends Observable implements Observer {

@NonNull
private MetaData metaData;
private final Collection<BeforeNotify> beforeNotifyTasks = new LinkedHashSet<>();
private final Collection<BeforeNotify> beforeNotifyTasks = new ConcurrentLinkedQueue<>();
private final Collection<BeforeRecordBreadcrumb> beforeRecordBreadcrumbTasks
= new LinkedHashSet<>();
= new ConcurrentLinkedQueue<>();
private String codeBundleId;
private String notifierType;

Expand Down Expand Up @@ -547,7 +548,9 @@ protected boolean shouldIgnoreClass(String className) {
* @param beforeNotify the new before notify task
*/
protected void beforeNotify(BeforeNotify beforeNotify) {
this.beforeNotifyTasks.add(beforeNotify);
if (!beforeNotifyTasks.contains(beforeNotify)) {
beforeNotifyTasks.add(beforeNotify);
}
}

/**
Expand All @@ -556,7 +559,9 @@ protected void beforeNotify(BeforeNotify beforeNotify) {
* @param beforeRecordBreadcrumb the new before breadcrumb task
*/
protected void beforeRecordBreadcrumb(BeforeRecordBreadcrumb beforeRecordBreadcrumb) {
this.beforeRecordBreadcrumbTasks.add(beforeRecordBreadcrumb);
if (!beforeRecordBreadcrumbTasks.contains(beforeRecordBreadcrumb)) {
beforeRecordBreadcrumbTasks.add(beforeRecordBreadcrumb);
}
}

/**
Expand Down

0 comments on commit a689a57

Please sign in to comment.