Skip to content

Simple C library for asynchronous computation and event processing.

Notifications You must be signed in to change notification settings

ernacktob/asyncio

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

66 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

WARNING: UNDER CONSTRUCTION

A large amount of refactoring is undergoing, and the code as it is currently will not function. I am commiting these changes as a form of backup. When the last commit that doesn't break the code is done, I will remove this warning.

asyncio

Simple C library for asynchronous computation and event processing.

Overview

This library was written to abstract common inherently asynchronous operations such as waiting on non-blocking file descriptors and executing tasks asynchronously. Asyncio provides a simple API that hides the multithreading issues and allows the programmer not to worry about writing his or her own eventloops and threadpools.

The API currently allows the following functionalities:

  • Dispatch a function to be executed asynchronously by a threadpool.
  • Wait for events on a file descriptor, and call a user-provided callback when an event occurs.
  • Execute a function asynchronously after a specified amount of time.
  • Blocking until a previous asynchronous task has completed (joining).
  • Cancelling an asynchronous task.

For more details about the API, see the API section.

The library was designed to comply with POSIX and standard C, so it should hopefully compile and run on any POSIX system with pthreads and a decent C compiler. So far, it has only been tested on Ubuntu Linux 12.04 (64-bit) and OS X 10.8.5 (64-bit), so let me know if you have tried it on other systems.

Building

To build the library, simply type make.
To also build tests, type make tests.
To also build examples, type make examples.
To clean everything produced during make, type make clean.

Dependencies

The library uses POSIX pthreads and the poll() system call interface for file descriptor multiplexing.

API

asyncio_dispatch

int asyncio_dispatch(asyncio_dispatch_fn fn, void *arg, asyncio_flag_t flags, asyncio_handle_t *handle);

Dispatch a function to be executed asynchronously by a threadpool.

Parameter Purpose
fn Pointer to function that will be executed.
arg Argument passed to the function. Can be NULL.
flags Flags controlling specific options for the threadpool (see FLAGS).
handle Pointer to returned asyncio_handle.

asyncio_fdevent

int asyncio_fdevent(int fd, asyncio_fdevent_t events, asyncio_fdevent_cb cb, void *arg, asyncio_flag_t flags, asyncio_handle_t *handle);

Wait for events on a file descriptor and call the user-provided callback when an event occurs.
The event is only caught once; to wait for following events, use asyncio_continue or re-register with asyncio_fdevent.

Parameter Purpose
fd File descriptor to wait on.
events Events to wait for. Possible values are ASYNCIO_FDEVENT_READ, ASYNCIO_FDEVENT_WRITE and ASYNCIO_FDEVENT_ERROR.
cb Pointer to callback function called when event occurs.
arg Argument passed to the function. Can be NULL.
flags Flags controlling specific options for the threadpool (see FLAGS).
handle Pointer to returned asyncio_handle.

asyncio_timevent

int asyncio_timevent(asyncio_time_t timeout, asyncio_timevent_cb cb, void *arg, asyncio_flag_t flags, asyncio_handle_t *handle);

Execute a user-provided callback after a certain amount of time has elapsed.

Parameter Purpose
timeout Time to wait before calling the callback (in milliseconds).
cb Pointer to callback function called when timeout occurs.
arg Argument passed to the function. Can be NULL.
flags Flags controlling specific options for the threadpool (see FLAGS).
handle Pointer to returned asyncio_handle.

asyncio_join

int asyncio_join(asyncio_handle_t handle);

Block until the task referred to by the handle has completed.
This can be used on any handle which was returned from successful calls to asyncio_dispatch, asyncio_fdevent or asyncio_timevent. Completion means that the function/callback has completed execution or that the task was cancelled by a call to asyncio_cancel. For fdevents or timevents, if asyncio_continue is called within the callback then the task does not count as completed.

Parameter Purpose
handle Handle initialized by successful calls to asyncio_dispatch, asyncio_fdevent or asyncio_timevent.

asyncio_cancel

int asyncio_cancel(asyncio_handle_t handle);

Cancel task referred to by the handle.
Task cancellability is controlled using the flag parameter when creating the task. The ASYNCIO_FLAG_CANCELLABLE flag allows a task to be cancellable (default is not cancellable). The ASYNCIO_FLAG_ASYNCCANCEL flag allows the task to be asynchronously cancellable (default is deferred cancellation). A task without the ASYNCIO_FLAG_CANCELLABLE flag set will not actually be cancelled after this function is called.
The difference between asynchronous and deferred cancellation is that asynchronously cancellable threads can be cancelled any time, while deferred cancellable threads will be cancelled when they reach a cancellation point. See the pthread documentation for more information (man pthread_setcanceltype).
Care must be taken when allowing threads to be cancelled, because you might get memory leaks or deadlock if the thread allocated ressources or acquired a lock before getting cancelled, and did not have the time to release it.

Parameter Purpose
handle Handle initialized by successful calls to asyncio_dispatch, asyncio_fdevent or asyncio_timevent.

asyncio_acquire

int asyncio_acquire(asyncio_handle_t handle);

Acquire reference to handle before passing it to another thread.
This is not required when using a handle obtained from a successful call to asyncio_dispatch, asyncio_fdevent or asyncio_timevent because they acquire a reference by default. It is mostly only needed if the thread that got this handle decides to pass it to another thread. This prevents race conditions where one thread uses the handle after its ressources have been freed by the other thread. The ressources will be freed only once all references to the handle have been released.

Parameter Purpose
handle Handle initialized by successful calls to asyncio_dispatch, asyncio_fdevent or asyncio_timevent.

asyncio_release

void asyncio_release(asyncio_handle_t handle);

Release reference to handle.
After this function is called, the calling thread must not use the handle anymore and not be passed to any other API functions. It must be considered as garbage. Other threads may still hold references to the handle and use it normally, so its ressources will be actually freed only once all references have been released. The API functions that return a handle always acquire a reference for the caller, so the caller must release the handle once it decides not to use it anymore in order to free its allocated ressources.

Parameter Purpose
handle Handle initialized by successful calls to asyncio_dispatch, asyncio_fdevent or asyncio_timevent.

asyncio_continue

void asyncio_continue(asyncio_continue_t continued);

Re-register the handle to continue waiting on event or timeout.
This is implemented as a macro, and is called within an asyncio_fdevent_cb or asyncio_timevent_cb to indicate that the task has not completed yet (the callback will be called again next time the event or timeout occurs). The task is considered completed only when the executed code path in the callback does not call asyncio_continue anymore.

FLAGS
The following flags can be set independently in any of the asyncio_dispatch, asyncio_fdevent or asyncio_timevent functions:

  • ASYNCIO_FLAG_CONTRACTOR: Use a "contractor thread" instead of "worker thread" to run function/callback. The threadpool uses a fixed amount of worker threads executing tasks in a queue, but can also spawn additional threads called "contractors" if the user desires. This may be useful if the function will take a long time to complete, in order to avoid stalling the worker queue, but it does not lead to scalable code because eventually the operating system's thread limit will be reached.
  • ASYNCIO_FLAG_CANCELLABLE: Allow a task to be cancellable. See asyncio_cancel and the pthread documentation for details.
  • ASYNCIO_FLAG_ASYNCCANCEL: Make task asynchronously cancellable. See asyncio_cancel and the pthread documentation for details. Note that if this is set while ASYNCIO_FLAG_CANCELLABLE is not set, this will have no effect.

Return values
All functions that return int return 0 when successful, and -1 on failure.
Currently there isn't a very detailed error code mechanism, instead errors are logged through the ASYNCIO_SYSERROR and ASYNCIO_ERROR macros.

Tweaking

Certain #defines can be tweaked to get better performance on your system. They can usually be found at the top of the relevant source files.
Some examples that might be interesting:

  • MAX_WORKER_THREADS (threadpool.c): Controls number of worker threads used for threadpool. Ideally should be close to number of cores. The default is set to 5.
  • MAX_CONTRACTORS (threadpool.c): Controls maximum number of contractor threads that can be spawned. Defaults to 1024.
  • MAX_POLLFDS (fdevents.c): Controls maximum number of file descriptors handled at once by poll(). The default is set to 10000.
  • MAX_DEADLINES (timevents.c): Controls maximum number of deadlines that can be handled by the timevents module. Defaults to 10000.
  • MALLOC_IS_THREAD_SAFE (safe_malloc.c): If defined, assumes the standard C library implementation for malloc() is thread-safe. Defined by default in Makefile. The safe_malloc macro will expand to malloc or to malloc_locked, which is a wrapper around malloc that uses a mutex before the call.
  • DEBUG (logging.c): If defined, detailed debugging information will be displayed with the ASYNCIO_DEBUG_* macros. These macros have not been used throughout the whole library due to the clutter they create in the code. They mostly show up in the threadpool code, used during initial debugging stages. Note that the debug macros will affect timing, and certain bugs may be triggered or hidden by using these macros. This is disabled by default.

About

Simple C library for asynchronous computation and event processing.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published