Skip to content

Commit f64d81e

Browse files
committed
fix: Wake the selector for every queued immediate task (14.1.2.0 cl 122127 --> 14.1.2.0 CE)
RunnableSelectionService used queue occupancy as a proxy for a durable selector wakeup. A producer could enqueue after the selector consumed the prior wakeup and entered a timed select, delaying TMB progression for approximately 2.5 seconds. Publish a wakeup for every immediate task; Selector coalesces redundant notifications. Add a regression test for the required enqueue-to-wakeup invariant. [git-p4: depot-paths = "//dev/coherence-ce/release/coherence-ce-v14.1.2.0/": change = 122152]
1 parent b286d7e commit f64d81e

2 files changed

Lines changed: 74 additions & 7 deletions

File tree

prj/coherence-core/src/main/java/com/oracle/coherence/common/internal/net/RunnableSelectionService.java

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
/*
2-
* Copyright (c) 2000, 2022, Oracle and/or its affiliates.
2+
* Copyright (c) 2000, 2026, Oracle and/or its affiliates.
33
*
44
* Licensed under the Universal Permissive License v 1.0 as shown at
5-
* http://oss.oracle.com/licenses/upl.
5+
* https://oss.oracle.com/licenses/upl.
66
*/
77
package com.oracle.coherence.common.internal.net;
88

@@ -81,12 +81,15 @@ public void invoke(final SelectableChannel chan, final Runnable runnable, long c
8181

8282
if (cMillis == 0)
8383
{
84-
boolean fEmpty = f_tasks.isEmpty();
8584
f_tasks.add(runnable);
86-
if (fEmpty)
87-
{
88-
wakeup();
89-
}
85+
86+
// A selector wakeup may be consumed by a selection that is already
87+
// returning. The queue can also appear non-empty while its consumer
88+
// marker is being drained, so queue non-emptiness is not proof that
89+
// a durable wakeup is pending for this task. Selector.wakeup()
90+
// coalesces redundant notifications, so publish one for every newly
91+
// queued immediate task.
92+
wakeup();
9093
}
9194
else
9295
{
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
* Copyright (c) 2026, Oracle and/or its affiliates.
3+
*
4+
* Licensed under the Universal Permissive License v 1.0 as shown at
5+
* https://oss.oracle.com/licenses/upl.
6+
*/
7+
package com.oracle.coherence.common.internal.net;
8+
9+
import org.junit.Test;
10+
11+
import java.io.IOException;
12+
13+
import java.nio.channels.Pipe;
14+
15+
import static org.hamcrest.CoreMatchers.is;
16+
import static org.hamcrest.MatcherAssert.assertThat;
17+
18+
/**
19+
* Unit tests for {@link RunnableSelectionService}.
20+
*
21+
* @author Aleks Seovic 2026.08.13
22+
* @since 26.07
23+
*/
24+
public class RunnableSelectionServiceTest
25+
{
26+
@Test
27+
public void shouldPublishWakeupForEveryImmediateTask()
28+
throws IOException
29+
{
30+
Pipe pipe = Pipe.open();
31+
CountingSelectionService service = new CountingSelectionService();
32+
33+
try
34+
{
35+
pipe.source().configureBlocking(false);
36+
37+
service.invoke(pipe.source(), () -> {}, 0);
38+
service.invoke(pipe.source(), () -> {}, 0);
39+
40+
assertThat(service.m_cWakeups, is(2));
41+
}
42+
finally
43+
{
44+
service.shutdown();
45+
pipe.source().close();
46+
pipe.sink().close();
47+
}
48+
}
49+
50+
/**
51+
* Selection service that counts published wakeups.
52+
*/
53+
private static class CountingSelectionService
54+
extends RunnableSelectionService
55+
{
56+
@Override
57+
protected void wakeup()
58+
{
59+
++m_cWakeups;
60+
}
61+
62+
private int m_cWakeups;
63+
}
64+
}

0 commit comments

Comments
 (0)