30 #define LELY_EV_LOOP_INLINE extern inline 43 #ifndef LELY_EV_LOOP_CTX_MAX_UNUSED 44 #define LELY_EV_LOOP_CTX_MAX_UNUSED 16 88 static void ev_loop_ctx_task_func(
struct ev_task *
task);
91 static void ev_loop_ctx_free(
struct ev_loop_ctx *ctx);
93 static void ev_loop_ctx_release(
struct ev_loop_ctx *ctx);
97 static void ev_loop_ctx_destroy(
struct ev_loop_ctx *ctx);
101 static size_t ev_loop_ctx_wait_one_until(
struct ev_loop_ctx **pctx,
103 const struct timespec *abs_time);
105 static int ev_loop_ctx_kill(
struct ev_loop_ctx *ctx,
int stop);
118 static _Thread_local struct ev_loop_thrd ev_loop_thrd = { 0, NULL };
121 static void ev_loop_std_exec_impl_on_task_init(ev_std_exec_impl_t *impl);
122 static void ev_loop_std_exec_impl_on_task_fini(ev_std_exec_impl_t *impl);
123 static void ev_loop_std_exec_impl_post(
125 static size_t ev_loop_std_exec_impl_abort(
126 ev_std_exec_impl_t *impl,
struct ev_task *task);
130 &ev_loop_std_exec_impl_on_task_init,
131 &ev_loop_std_exec_impl_on_task_fini,
132 &ev_loop_std_exec_impl_post,
133 &ev_loop_std_exec_impl_abort
167 #if LELY_NO_THREADS || (LELY_NO_ATOMICS && (!_WIN32 || defined(__MINGW32__))) 169 #elif _WIN64 && !defined(__MINGW32__) 170 volatile LONGLONG ntasks;
171 #elif _WIN32 && !defined(__MINGW32__) 172 volatile LONG ntasks;
195 static inline ev_loop_t *ev_loop_from_impl(
const ev_std_exec_impl_t *impl);
214 ev_loop_free(
void *ptr)
227 loop->
impl_vptr = &ev_loop_std_exec_impl_vtbl;
238 if (loop->
poll && poll_task)
241 #if LELY_NO_THREADS || LELY_NO_ATOMICS || (_WIN32 && !defined(__MINGW32__)) 244 atomic_init(&loop->
ntasks, 0);
269 ev_loop_ctx_free(ctx);
296 ev_loop_t *tmp = ev_loop_init(loop, poll, npoll, poll_task);
334 return &loop->
exec.exec_vptr;
345 ev_loop_do_stop(loop);
385 while (ev_loop_ctx_wait_one(&ctx, loop, future))
387 ev_loop_ctx_destroy(ctx);
393 const struct timespec *abs_time)
397 while (ev_loop_ctx_wait_one_until(&ctx, loop, future, abs_time))
399 ev_loop_ctx_destroy(ctx);
407 size_t n = ev_loop_ctx_wait_one(&ctx, loop, future);
408 ev_loop_ctx_destroy(ctx);
414 const struct timespec *abs_time)
417 size_t n = ev_loop_ctx_wait_one_until(&ctx, loop, future, abs_time);
418 ev_loop_ctx_destroy(ctx);
425 return &ev_loop_thrd;
436 struct ev_loop_thrd *
thr = thr_;
446 if ((result = ev_loop_ctx_kill(thr->
ctx, 1)) == -1)
460 ev_loop_ctx_task_func(
struct ev_task *task)
475 ev_loop_ctx_kill(ctx, 0);
480 ev_loop_ctx_release(ctx);
484 ev_loop_ctx_alloc(
void)
491 goto error_malloc_ctx;
499 ctx->
task = (
struct ev_task)EV_TASK_INIT(NULL, &ev_loop_ctx_task_func);
505 goto error_init_cond;
574 ev_loop_ctx_free(ctx);
598 ctx = ev_loop_ctx_alloc();
610 ev_loop_get_exec(loop), &ev_loop_ctx_task_func);
624 ev_loop_thrd.
ctx = ctx;
641 assert(ev_loop_thrd.
ctx == ctx);
644 ev_loop_ctx_release(ctx);
649 ev_loop_ctx_wait_one(
655 assert(!ctx || ctx->
loop == loop);
667 if (ev_loop_empty(loop) && !ev_loop_ntasks(loop)
668 && (!future || ctx)) {
669 ev_loop_do_stop(loop);
674 if (task && task == &loop->
task) {
699 ctx = *pctx = ev_loop_ctx_create(loop, future);
735 #else // !LELY_NO_THREADS 744 #endif // !LELY_NO_THREADS 754 ev_loop_kill_any(loop, 1);
757 ev_loop_kill_any(loop, 0);
772 ev_future_t *future,
const struct timespec *abs_time)
777 assert(!ctx || ctx->
loop == loop);
778 #if LELY_NO_THREADS && LELY_NO_TIMEOUT 792 if (ev_loop_empty(loop) && !ev_loop_ntasks(loop)
793 && (!future || ctx)) {
794 ev_loop_do_stop(loop);
799 if (task && task == &loop->
task) {
824 ctx = *pctx = ev_loop_ctx_create(loop, future);
851 if (empty && abs_time) {
852 struct timespec now = { 0, 0 };
863 else if (msec > INT_MAX)
866 #endif // !LELY_NO_TIMEOUT 881 #if !LELY_NO_THREADS && !LELY_NO_TIMEOUT 882 }
else if (abs_time) {
887 &ctx->
cond, &loop->
mtx, abs_time);
895 #endif // !LELY_NO_THREADS 907 ev_loop_kill_any(loop, 1);
910 ev_loop_kill_any(loop, 0);
922 ev_loop_ctx_kill(
struct ev_loop_ctx *ctx,
int stop)
944 ev_loop_std_exec_impl_on_task_init(ev_std_exec_impl_t *impl)
946 ev_loop_t *loop = ev_loop_from_impl(impl);
948 #if LELY_NO_THREADS || (LELY_NO_ATOMICS && (!_WIN32 || defined(__MINGW32__))) 950 #elif _WIN64 && !defined(__MINGW32__) 951 InterlockedIncrementNoFence64(&loop->
ntasks);
952 #elif _WIN32 && !defined(__MINGW32__) 953 InterlockedIncrementNoFence(&loop->
ntasks);
955 atomic_fetch_add_explicit(&loop->
ntasks, 1, memory_order_relaxed);
960 ev_loop_std_exec_impl_on_task_fini(ev_std_exec_impl_t *impl)
962 ev_loop_t *loop = ev_loop_from_impl(impl);
964 #if LELY_NO_THREADS || (LELY_NO_ATOMICS && (!_WIN32 || defined(__MINGW32__))) 966 #elif _WIN64 && !defined(__MINGW32__) 967 if (!InterlockedDecrementRelease64(&loop->
ntasks)) {
969 #elif _WIN32 && !defined(__MINGW32__) 970 if (!InterlockedDecrementRelease(&loop->
ntasks)) {
973 if (atomic_fetch_sub_explicit(&loop->
ntasks, 1, memory_order_release)
975 atomic_thread_fence(memory_order_acquire);
980 if (ev_loop_empty(loop))
981 ev_loop_do_stop(loop);
989 ev_loop_std_exec_impl_post(ev_std_exec_impl_t *impl,
struct ev_task *task)
991 ev_loop_t *loop = ev_loop_from_impl(impl);
1000 ev_loop_kill_any(loop, 1);
1001 #if !LELY_NO_THREADS 1007 ev_loop_std_exec_impl_abort(ev_std_exec_impl_t *impl,
struct ev_task *task)
1009 ev_loop_t *loop = ev_loop_from_impl(impl);
1014 #if !LELY_NO_THREADS 1031 #if !LELY_NO_THREADS 1042 ev_loop_from_impl(
const ev_std_exec_impl_t *impl)
1058 return node == &loop->
task._node && node->
next == NULL;
1066 #if LELY_NO_THREADS || LELY_NO_ATOMICS || (_WIN32 && !defined(__MINGW32__)) 1069 return atomic_load_explicit(
1070 (atomic_size_t *)&loop->
ntasks, memory_order_relaxed);
1081 #if !LELY_NO_THREADS 1085 ev_loop_ctx_kill(ctx, 1);
1091 ev_loop_ctx_kill(ctx, 1);
1102 #if !LELY_NO_THREADS 1106 return ev_loop_ctx_kill(ctx, 0);
1112 return ev_loop_ctx_kill(ctx, 0);
void * ev_loop_self(void)
Returns the identifier of the calling thread.
int ev_loop_stopped(const ev_loop_t *loop)
Returns 1 if the event loop is stopped, and 0 if not.
struct ev_loop_ctx * ctx
A pointer to the event loop context for this thread.
const struct ev_exec_vtbl *const ev_exec_t
An abstract task executor.
void * thr
The thread identifier of the polling instance.
This header file is part of the event library; it contains the standard executor declarations.
#define EV_TASK_INIT(exec, func)
The static initializer for ev_task.
ev_loop_t * loop
A pointer to the event loop managing this context.
void ev_loop_restart(ev_loop_t *loop)
Restarts an event loop.
#define dllist_foreach(list, node)
Iterates in order over each node in a doubly-linked list.
size_t refcnt
The number of references to this context.
size_t ev_loop_wait_one(ev_loop_t *loop, ev_future_t *future)
If the event loop has pending tasks, runs a single task.
void mtx_destroy(mtx_t *mtx)
Releases any resources used by the mutex at mtx.
int * pstopped
The address of the stopped flag of the thread.
int cnd_signal(cnd_t *cond)
Unblocks one of the threads that are blocked on the condition variable at cond at the time of the cal...
int ev_loop_kill(ev_loop_t *loop, void *thr_)
Interrupts an event loop running on the specified thread.
Indicates that the time specified in the call was reached without acquiring the requested resource...
struct ev_loop_ctx * next
A pointer to the next context in the list of running or unused contexts.
void set_errnum(errnum_t errnum)
Sets the current (thread-specific) platform-independent error number to errnum.
Indicates that the requested operation succeeded.
size_t npoll
The number of threads allowed to poll simultaneously.
ev_poll_t * ev_loop_get_poll(const ev_loop_t *loop)
Returns a pointer to the polling instance used by the event loop, or NULL if the loop does not poll...
struct ev_task task
The task to be executed once the future is ready.
int timespec_get(struct timespec *ts, int base)
Sets the interval at ts to hold the current calendar time based on the specified time base...
unsigned waiting
A flag indicating if a thread is waiting on cond.
unsigned polling
A flag indicating if a thread is polling.
A node in a singly-linked list.
struct slnode * sllist_pop_front(struct sllist *list)
Pops a node from the front of a singly-linked list.
struct slnode * sllist_first(const struct sllist *list)
Returns a pointer to the first node in a singly-linked list.
size_t ev_future_cancel(ev_future_t *future, struct ev_task *task)
Cancels the specified task submitted with ev_future_submit(), if it has not yet been scheduled for ex...
struct slnode * sllist_remove(struct sllist *list, struct slnode *node)
Removes a node from a singly-linked list.
A mutex type that supports neither timeout nor test and return.
int mtx_unlock(mtx_t *mtx)
Unlocks the mutex at mtx.
void set_errc(int errc)
Sets the current (thread-specific) native error code to errc.
ev_poll_t * poll
A pointer to the interface used to poll for events (can be NULL).
This header file is part of the event library; it contains the polling event loop declarations...
int ev_poll_wait(ev_poll_t *poll, int timeout)
Waits for at most timeout milliseconds while polling for new events.
void sllist_push_back(struct sllist *list, struct slnode *node)
Pushes a node to the back of a singly-linked list.
const struct ev_std_exec_impl_vtbl * impl_vptr
A pointer to the virtual table containing the interface used by the standard executor (exec)...
void ev_future_release(ev_future_t *future)
Releases a reference to a future.
This header file is part of the C11 and POSIX compatibility library; it includes <threads.h>, if it exists, and defines any missing functionality.
int mtx_lock(mtx_t *mtx)
Blocks until it locks the mutex at mtx.
struct dllist polling
The list of polling contexts.
pthread_cond_t cnd_t
A complete object type that holds an identifier for a condition variable.
pthread_mutex_t mtx_t
A complete object type that holds an identifier for a mutex.
int dllist_empty(const struct dllist *list)
Returns 1 if the doubly-linked list is empty, and 0 if not.
This header file is part of the utilities library; it contains the native and platform-independent er...
ev_exec_t * exec
A pointer to the executor to which the task is (to be) submitted.
This header file is part of the utilities library; it contains the time function declarations.
ev_future_t * ev_future_acquire(ev_future_t *future)
Acquires a reference to a future.
This header file is part of the C11 and POSIX compatibility library; it includes <stdatomic.h>, if it exists, and defines any missing functionality.
int cnd_init(cnd_t *cond)
Creates a condition variable.
struct dlnode node
The node of this context in the list of waiting or polling contexts.
ev_loop_t * ev_loop_create(ev_poll_t *poll, size_t npoll, int poll_task)
Creates a new polling event loop.
int stopped
A flag specifying whether the event loop is stopped.
void ev_loop_destroy(ev_loop_t *loop)
Destroys a polling event loop.
int get_errc(void)
Returns the last (thread-specific) native error code set by a system call or library function...
struct ev_task task
The task used to trigger polling.
int cnd_timedwait(cnd_t *cond, mtx_t *mtx, const struct timespec *ts)
Atomically unlocks the mutex at mtx and endeavors to block until the condition variable at cond is si...
const struct ev_poll_vtbl *const ev_poll_t
The abstract polling interface.
size_t ev_loop_wait_one_until(ev_loop_t *loop, ev_future_t *future, const struct timespec *abs_time)
If the event loop has pending tasks, runs a single task.
struct slnode * next
A pointer to the next node in the list.
This header file is part of the utilities library; it contains the doubly-linked list declarations...
size_t npolling
The number of polling contexts.
int errno2c(int errnum)
Transforms a standard C error number to a native error code.
struct dllist waiting
The list of waiting contexts.
int cnd_wait(cnd_t *cond, mtx_t *mtx)
Atomically unlocks the mutex at mtx and endeavors to block until the condition variable at cond is si...
int timespec_cmp(const void *p1, const void *p2)
Compares two times.
A node in a doubly-linked list.
void dlnode_init(struct dlnode *node)
Initializes a node in a doubly-linked list.
This header file is part of the C11 and POSIX compatibility library; it includes <stdint.h> and defines any missing functionality.
This header file is part of the event library; it contains the task declarations. ...
int_least64_t timespec_diff_msec(const struct timespec *t1, const struct timespec *t2)
Returns the time difference (in milliseconds) between *t1 and *t2.
This header file is part of the C11 and POSIX compatibility library; it includes <stdio.h> and defines any missing functionality.
int sllist_empty(const struct sllist *list)
Returns 1 if the singly-linked list is empty, and 0 if not.
int ev_poll_kill(ev_poll_t *poll, void *thr)
Interrupts a polling wait on the specified thread.
#define structof(ptr, type, member)
Obtains the address of a structure from the address of one of its members.
This header file is part of the event library; it contains the abstract task executor interface...
unsigned ready
A flag indicating if future is ready.
#define _Thread_local
An object whose identifier is declared with the storage-class specifier _Thread_local has thread stor...
void dllist_push_front(struct dllist *list, struct dlnode *node)
Pushes a node to the front of a doubly-linked list.
size_t nunused
The number of unused contexts.
void cnd_destroy(cnd_t *cond)
Releases all resources used by the condition variable at cond.
struct ev_task * ev_task_from_node(struct slnode *node)
Converts a pointer to a node in a queue to the address of the task containing the node...
struct sllist queue
The queue of pending tasks.
#define LELY_EV_LOOP_CTX_MAX_UNUSED
The maximum number of unused contexts per event loop.
struct ev_loop_ctx * unused
The list of unused contexts.
This header file is part of the C11 and POSIX compatibility library; it includes <stdlib.h> and defines any missing functionality.
atomic_size_t ntasks
The number of pending tasks.
void sllist_init(struct sllist *list)
Initializes a singly-linked list.
This is the internal header file of the event library.
size_t ev_loop_wait(ev_loop_t *loop, ev_future_t *future)
Equivalent to size_t n = 0; while (ev_loop_wait_one(loop, future)) n += n < SIZE_MAX; return ...
size_t ev_loop_wait_until(ev_loop_t *loop, ev_future_t *future, const struct timespec *abs_time)
Equivalent to size_t n = 0; while (ev_loop_wait_one_until(loop, future, abs_time)) n += n < S...
void dllist_init(struct dllist *list)
Initializes a doubly-linked list.
struct ev_std_exec exec
The executor corresponding to the event loop.
void ev_exec_run(ev_exec_t *exec, struct ev_task *task)
Invokes the task function in *task as if the task is being executed by *exec.
mtx_t mtx
The mutex protecting the task queue.
struct dlnode * dllist_first(const struct dllist *list)
Returns a pointer to the first node in a doubly-linked list.
cnd_t cond
The condition variable used by threads to wait for a task to be submitted to the event loop or for th...
int stopped
A flag used to interrupt the event loop on this thread.
This is the public header file of the utilities library.
void ev_loop_stop(ev_loop_t *loop)
Stops the event loop.
void * ev_poll_self(const ev_poll_t *poll)
Returns the identifier of the calling thread.
ev_exec_t * ev_loop_get_exec(const ev_loop_t *loop)
Returns a pointer to the executor corresponding to the event loop.
void ev_future_submit(ev_future_t *future, struct ev_task *task)
Submits a task to be executed once the specified future is ready.
void dllist_remove(struct dllist *list, struct dlnode *node)
Removes a node from a doubly-linked list.
int mtx_init(mtx_t *mtx, int type)
Creates a mutex object with properties indicated by type, which must have one of the four values: ...
ev_future_t * future
The future on which the loop is waiting.