Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/ft-async' into libuv
Browse files Browse the repository at this point in the history
  • Loading branch information
hargoniX committed Jan 2, 2025
2 parents af8a897 + 2a8f136 commit c7a5d6e
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 14 deletions.
32 changes: 23 additions & 9 deletions src/runtime/uv/event_loop.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,21 @@
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Sofia Rodrigues
Author: Sofia Rodrigues, Henrik Böving
*/
#include "runtime/uv/event_loop.h"


/*
This file builds a thread safe event loop on top of the thread unsafe libuv event loop.
We achieve this by always having a `uv_async_t` associated with the libuv event loop.
As `uv_async_t` are a thread safe primitive it is safe to send a notification to it from another
thread. Once this notification arrives the event loop suspends its own execution and unlocks a mutex
that protects it. This mutex can then be taken by another thread that wants to work with the event
loop. After that work is done it signals a condition variable that the event loop is waiting on
to continue its execution.
*/

namespace lean {
#ifndef LEAN_EMSCRIPTEN
using namespace std;
Expand All @@ -26,8 +37,8 @@ void async_callback(uv_async_t * handle) {
uv_stop(handle->loop);
}

// Awakes the event loop and stops it so it can receive future requests.
void event_loop_wake(event_loop_t * event_loop) {
// Interrupts the event loop and stops it so it can receive future requests.
void event_loop_interrupt(event_loop_t * event_loop) {
int result = uv_async_send(&event_loop->async);
(void)result;
lean_assert(result == 0);
Expand All @@ -46,32 +57,35 @@ void event_loop_init(event_loop_t * event_loop) {
void event_loop_lock(event_loop_t * event_loop) {
if (uv_mutex_trylock(&event_loop->mutex) != 0) {
event_loop->n_waiters++;
event_loop_wake(event_loop);
event_loop_interrupt(event_loop);
uv_mutex_lock(&event_loop->mutex);
event_loop->n_waiters--;
}
}

// Unlock event loop
void event_loop_unlock(event_loop_t * event_loop) {
uv_mutex_unlock(&event_loop->mutex);
if (event_loop->n_waiters == 0) {
uv_cond_signal(&event_loop->cond_var);
}
uv_mutex_unlock(&event_loop->mutex);
}

// Runs the loop and stops when it needs to register new requests.
void event_loop_run_loop(event_loop_t * event_loop) {
while (uv_loop_alive(event_loop->loop)) {
uv_mutex_lock(&event_loop->mutex);

if (event_loop->n_waiters != 0) {
while (event_loop->n_waiters != 0) {
uv_cond_wait(&event_loop->cond_var, &event_loop->mutex);
}

if (event_loop->n_waiters == 0) {
uv_run(event_loop->loop, UV_RUN_ONCE);
}
uv_run(event_loop->loop, UV_RUN_ONCE);
/*
* We leave `uv_run` only when `uv_stop` is called as there is always the `uv_async_t` so
* we can never run out of things to wait on. `uv_stop` is only called from `async_callback`
* when another thread wants to work with the event loop so we need to give up the mutex.
*/

uv_mutex_unlock(&event_loop->mutex);
}
Expand Down
9 changes: 4 additions & 5 deletions src/runtime/uv/event_loop.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ using namespace std;
// Event loop structure for managing asynchronous events and synchronization across multiple threads.
typedef struct {
uv_loop_t * loop; // The libuv event loop.
uv_mutex_t mutex; // Mutex for protecting shared resources.
uv_cond_t cond_var; // Condition variable for thread synchronization.
uv_async_t async; // Async handle to notify the main event loop.
_Atomic(int) n_waiters; // Atomic counter for managing waiters.
uv_mutex_t mutex; // Mutex for protecting `loop`.
uv_cond_t cond_var; // Condition variable for signaling that `loop` is free.
uv_async_t async; // Async handle to interrupt `loop`.
_Atomic(int) n_waiters; // Atomic counter for managing waiters for `loop`.
} event_loop_t;

// The multithreaded event loop object for all tasks in the task manager.
Expand All @@ -35,7 +35,6 @@ void event_loop_init(event_loop_t *event_loop);
void event_loop_cleanup(event_loop_t *event_loop);
void event_loop_lock(event_loop_t *event_loop);
void event_loop_unlock(event_loop_t *event_loop);
void event_loop_wake(event_loop_t *event_loop);
void event_loop_run_loop(event_loop_t *event_loop);

#endif
Expand Down

0 comments on commit c7a5d6e

Please sign in to comment.