summaryrefslogtreecommitdiffstats
path: root/Utilities/cmlibuv/src/unix/async.c
diff options
context:
space:
mode:
Diffstat (limited to 'Utilities/cmlibuv/src/unix/async.c')
-rw-r--r--Utilities/cmlibuv/src/unix/async.c37
1 files changed, 33 insertions, 4 deletions
diff --git a/Utilities/cmlibuv/src/unix/async.c b/Utilities/cmlibuv/src/unix/async.c
index 0b450ae..a5c47bc 100644
--- a/Utilities/cmlibuv/src/unix/async.c
+++ b/Utilities/cmlibuv/src/unix/async.c
@@ -61,14 +61,43 @@ int uv_async_send(uv_async_t* handle) {
if (ACCESS_ONCE(int, handle->pending) != 0)
return 0;
- if (cmpxchgi(&handle->pending, 0, 1) == 0)
- uv__async_send(handle->loop);
+ /* Tell the other thread we're busy with the handle. */
+ if (cmpxchgi(&handle->pending, 0, 1) != 0)
+ return 0;
+
+ /* Wake up the other thread's event loop. */
+ uv__async_send(handle->loop);
+
+ /* Tell the other thread we're done. */
+ if (cmpxchgi(&handle->pending, 1, 2) != 1)
+ abort();
return 0;
}
+/* Only call this from the event loop thread. */
+static int uv__async_spin(uv_async_t* handle) {
+ int rc;
+
+ for (;;) {
+ /* rc=0 -- handle is not pending.
+ * rc=1 -- handle is pending, other thread is still working with it.
+ * rc=2 -- handle is pending, other thread is done.
+ */
+ rc = cmpxchgi(&handle->pending, 2, 0);
+
+ if (rc != 1)
+ return rc;
+
+ /* Other thread is busy with this handle, spin until it's done. */
+ cpu_relax();
+ }
+}
+
+
void uv__async_close(uv_async_t* handle) {
+ uv__async_spin(handle);
QUEUE_REMOVE(&handle->queue);
uv__handle_stop(handle);
}
@@ -109,8 +138,8 @@ static void uv__async_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) {
QUEUE_REMOVE(q);
QUEUE_INSERT_TAIL(&loop->async_handles, q);
- if (cmpxchgi(&h->pending, 1, 0) == 0)
- continue;
+ if (0 == uv__async_spin(h))
+ continue; /* Not pending. */
if (h->async_cb == NULL)
continue;