Skip to content

Commit 1b7a6c4

Browse files
Copilotdrewnoakes
andauthored
Fix macOS hang: cap Socket.Select timeout to 500ms to prevent indefinite blocking
Agent-Logs-Url: https://github.com/zeromq/netmq/sessions/657e4fe7-212a-434e-a43f-b35f96202cb7 Co-authored-by: drewnoakes <[email protected]>
1 parent 03c96e9 commit 1b7a6c4

1 file changed

Lines changed: 17 additions & 5 deletions

File tree

src/NetMQ/Core/Utils/Poller.cs

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,15 @@ public void Stop()
252252
m_stopping = true;
253253
}
254254

255+
/// <summary>
256+
/// Maximum timeout (in microseconds) passed to Socket.Select.
257+
/// On macOS ARM64, <c>Socket.Select</c> can fail to wake up when a TCP
258+
/// loopback socket becomes readable. Capping the timeout ensures the loop
259+
/// re-evaluates <see cref="m_stopping"/> periodically so that
260+
/// <see cref="Stop"/> requests are never missed.
261+
/// </summary>
262+
private const int MaxSelectTimeoutMicroseconds = 500_000; // 500,000 µs = 500 ms
263+
255264
/// <summary>
256265
/// This method is the polling-loop that is invoked on a background thread when Start is called.
257266
/// As long as Stop hasn't been called: execute the timers, and invoke the handler-methods on each of the saved PollSets.
@@ -274,11 +283,14 @@ private void Loop()
274283
try
275284
{
276285
timeout = timeout != 0 ? timeout * 1000 : -1;
277-
// Pass null for the error list: every socket is already tracked in
278-
// m_checkRead (callers always invoke SetPollIn after AddHandle), so
279-
// socket errors surface as readable events too. Passing a non-null
280-
// error list alongside readList causes Socket.Select to hang
281-
// indefinitely on macOS .NET (https://github.com/dotnet/corefx/issues/39617).
286+
287+
// Cap the timeout so the loop wakes up periodically. This
288+
// prevents an indefinite hang on platforms where Socket.Select
289+
// does not reliably detect readability on TCP loopback pairs
290+
// (the Signaler mechanism used by the Mailbox).
291+
if (timeout < 0 || timeout > MaxSelectTimeoutMicroseconds)
292+
timeout = MaxSelectTimeoutMicroseconds;
293+
282294
Socket.Select(readList, null, null, timeout);
283295
}
284296
catch (SocketException)

0 commit comments

Comments
 (0)