Skip to content

Commit cc45d5c

Browse files
authored
Fix NoSuchElementException in CircularFifoQueue when cloning a Scope (#2328)
1 parent f809aac commit cc45d5c

File tree

4 files changed

+41
-1
lines changed

4 files changed

+41
-1
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
### Fixes
66

77
- Use correct set-cookie for the HTTP Client response object ([#2326](https://github.com/getsentry/sentry-java/pull/2326))
8+
- Fix NoSuchElementException in CircularFifoQueue when cloning a Scope ([#2328](https://github.com/getsentry/sentry-java/pull/2328))
89

910
### Features
1011

sentry/src/main/java/io/sentry/Scope.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ public Scope(final @NotNull SentryOptions options) {
9797
this.fingerprint = new ArrayList<>(scope.fingerprint);
9898
this.eventProcessors = new CopyOnWriteArrayList<>(scope.eventProcessors);
9999

100-
final Queue<Breadcrumb> breadcrumbsRef = scope.breadcrumbs;
100+
final Breadcrumb[] breadcrumbsRef = scope.breadcrumbs.toArray(new Breadcrumb[0]);
101101

102102
Queue<Breadcrumb> breadcrumbsClone = createBreadcrumbsList(scope.options.getMaxBreadcrumbs());
103103

sentry/src/main/java/io/sentry/SynchronizedQueue.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,4 +131,18 @@ public E remove() {
131131
return decorated().remove();
132132
}
133133
}
134+
135+
@Override
136+
public Object[] toArray() {
137+
synchronized (lock) {
138+
return decorated().toArray();
139+
}
140+
}
141+
142+
@Override
143+
public <T> T[] toArray(T[] object) {
144+
synchronized (lock) {
145+
return decorated().toArray(object);
146+
}
147+
}
134148
}

sentry/src/test/java/io/sentry/ScopeTest.kt

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,31 @@ class ScopeTest {
217217
assertTrue(clone.attachments is CopyOnWriteArrayList)
218218
}
219219

220+
@Test
221+
fun `copying scope won't crash if there are concurrent operations`() {
222+
val options = SentryOptions().apply {
223+
maxBreadcrumbs = 10000
224+
}
225+
val scope = Scope(options)
226+
for (i in 0 until options.maxBreadcrumbs) {
227+
scope.addBreadcrumb(Breadcrumb.info("item"))
228+
}
229+
230+
// remove one breadcrumb after the other on an extra thread
231+
Thread({
232+
while (scope.breadcrumbs.isNotEmpty()) {
233+
scope.breadcrumbs.remove()
234+
}
235+
}, "thread-breadcrumb-remover").start()
236+
237+
// clone in the meantime
238+
while (scope.breadcrumbs.isNotEmpty()) {
239+
Scope(scope)
240+
}
241+
242+
// expect no exception to be thrown ¯\_(ツ)_/¯
243+
}
244+
220245
@Test
221246
fun `clear scope resets scope to default state`() {
222247
val scope = Scope(SentryOptions())

0 commit comments

Comments
 (0)