Skip to content

Commit b8c7c2e

Browse files
committed
ensures no modifications during iteration
since Processor/Subscription termination with `cancel` or `onError` leads to the following self-removal logic, it can happen that collection concurrent modification exception may appear. To avoid so we can copy all the entries and by doing so avoid any subsequent problems Signed-off-by: Oleh Dokuka <[email protected]> Signed-off-by: Oleh Dokuka <[email protected]>
1 parent 040278a commit b8c7c2e

File tree

2 files changed

+20
-7
lines changed

2 files changed

+20
-7
lines changed

Diff for: rsocket-core/src/main/java/io/rsocket/core/RSocketRequester.java

+13-5
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@
5050
import io.rsocket.keepalive.KeepAliveSupport;
5151
import io.rsocket.lease.RequesterLeaseHandler;
5252
import java.nio.channels.ClosedChannelException;
53+
import java.util.ArrayList;
54+
import java.util.Collection;
5355
import java.util.concurrent.atomic.AtomicBoolean;
5456
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
5557
import java.util.function.Consumer;
@@ -772,20 +774,26 @@ private void terminate(Throwable e) {
772774
leaseHandler.dispose();
773775

774776
// Iterate explicitly to handle collisions with concurrent removals
775-
for (IntObjectMap.PrimitiveEntry<Processor<Payload, Payload>> entry : receivers.entries()) {
777+
final IntObjectMap<Processor<Payload, Payload>> receivers = this.receivers;
778+
// copy to avoid collection modification from the foreach loop
779+
final Collection<Processor<Payload, Payload>> receiversCopy =
780+
new ArrayList<>(receivers.values());
781+
for (Processor<Payload, Payload> handler : receiversCopy) {
776782
try {
777-
entry.value().onError(e);
783+
handler.onError(e);
778784
} catch (Throwable ex) {
779785
if (LOGGER.isDebugEnabled()) {
780786
LOGGER.debug("Dropped exception", ex);
781787
}
782788
}
783789
}
784-
785790
// Iterate explicitly to handle collisions with concurrent removals
786-
for (IntObjectMap.PrimitiveEntry<Subscription> entry : senders.entries()) {
791+
final IntObjectMap<Subscription> senders = this.senders;
792+
// copy to avoid collection modification from the foreach loop
793+
final Collection<Subscription> sendersCopy = new ArrayList<>(senders.values());
794+
for (Subscription subscription : sendersCopy) {
787795
try {
788-
entry.value().cancel();
796+
subscription.cancel();
789797
} catch (Throwable ex) {
790798
if (LOGGER.isDebugEnabled()) {
791799
LOGGER.debug("Dropped exception", ex);

Diff for: rsocket-core/src/main/java/io/rsocket/core/RSocketResponder.java

+7-2
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@
4040
import io.rsocket.internal.UnboundedProcessor;
4141
import io.rsocket.lease.ResponderLeaseHandler;
4242
import java.nio.channels.ClosedChannelException;
43+
import java.util.ArrayList;
44+
import java.util.Collection;
4345
import java.util.concurrent.CancellationException;
4446
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
4547
import java.util.function.Consumer;
@@ -264,9 +266,12 @@ private void cleanup(Throwable e) {
264266

265267
private synchronized void cleanUpSendingSubscriptions() {
266268
// Iterate explicitly to handle collisions with concurrent removals
267-
for (IntObjectMap.PrimitiveEntry<Subscription> entry : sendingSubscriptions.entries()) {
269+
final IntObjectMap<Subscription> sendingSubscriptions = this.sendingSubscriptions;
270+
final Collection<Subscription> sendingSubscriptionsCopy =
271+
new ArrayList<>(sendingSubscriptions.values());
272+
for (Subscription subscription : sendingSubscriptionsCopy) {
268273
try {
269-
entry.value().cancel();
274+
subscription.cancel();
270275
} catch (Throwable ex) {
271276
if (LOGGER.isDebugEnabled()) {
272277
LOGGER.debug("Dropped exception", ex);

0 commit comments

Comments
 (0)