libgreen – Python-style asyncio in C¶
Introduction¶
libgreen is a coroutine-based library for friendly asynchronous I/O in C.
API¶
Library version¶
The library follows semantic versioning. The (GREEN_MINOR,
GREEN_MINOR) pair identifes the API contract version while
GREEN_PATCH is incremented for internal changes that fix bugs.
This library’s API offers several facilities for using the library version at compile-time (for conditional compilation) and at run-time (usally for diagnostics).
-
GREEN_MAJOR¶ The library’s API version. This value is increased with each breaking API change.
See
GREEN_VERSIONandGREEN_MAKE_VERSION()for version testing.
-
GREEN_MINOR¶ The library’s feature version. This value is increased when adding new features that do not break existing APIs.
See
GREEN_VERSIONandGREEN_MAKE_VERSION()for version testing.
-
GREEN_PATCH¶ The library’s patch version. This value is increased for releases that contain only internal changes, typically to fix bugs without changing the API.
Attention
This library treats a discrepancy between the documentation and the behavior as a bug in the code. If your application relies on a bug in the library, it may be surprised by an apparent change in behavior.
-
GREEN_VERSION¶ A single number that encodes
GREEN_MAJOR,GREEN_MINORandGREEN_PATCHfor compile-time version testing.This is typically used to compare against a call to
GREEN_MAKE_VERSION.Here is how to use this macro to handle multiple contract versions in the same library:
Compile-time workaround for API break¶#if GREEN_VERSION < GREEN_MAKE_VERSION(1, 0, 0) // Workaround for pre-1.0 versions. #endif
Here is how to use this macro to handle presence and absence of a specific feature:
Compile-time feature testing¶#define HAVE_GREEN_XYZ \ (GREEN_VERSION >= GREEN_MAKE_VERSION(1, 1, 0)) // ... #if HAVE_GREEN_XYZ // Use XYZ. #endif
See
green_version()to see which version your application is currently running against.
-
GREEN_MAKE_VERSION(major, minor, patch)¶ Build a value which can be used to compare against
GREEN_VERSIONor the result ofgreen_version().See
GREEN_VERSIONfor examples on how to use this macro.
-
int
green_version()¶ See
GREEN_VERSIONto see which version your application was compiled against.
-
GREEN_VERSION_STRING¶ Build a dotted version string that can be used for display.
See
green_version_string()to see which version your application is currently running against.
-
const char *
green_version_string()¶ Build a dotted version string that can be used for display.
See
GREEN_VERSION_STRING()to see which version your application was compiled against.
Library setup¶
-
int
green_init()¶ Returns: Zero if the function succeeds. Note
This function is implemented as a macro.
-
int
green_term()¶ Returns: Zero if the function succeeds.
Loop¶
The event loop is the central hub for coordination between a set of coroutine instances running in the same OS thread.
-
green_loop_t¶ This is an opaque pointer type to a reference-counted object.
-
green_loop_t
green_loop_init()¶ Create a new event loop.
Your application can create as many event loops as it wishes, but it make no sense to have more than one per thread at any given time since coroutines in different loops cannot resume each other.
Use
green_coroutine_init()to launch new coroutines in the new event loop.Returns: A new event loop.
-
int
green_loop_acquire(green_loop_t loop)¶ Increase the reference count.
Returns: Zero if the function succeeds.
-
int
green_loop_release(green_loop_t loop)¶ Decrease the reference count and destroy the object if necessary.
Returns: Zero if the function succeeds.
Coroutine¶
-
green_coroutine_t¶ This is an opaque pointer type to a reference-counted object.
-
green_coroutine_t
green_coroutine_init(green_loop_t loop, int(*method)(void*,void*), void * object, size_t stack_size)¶ Call
method(loop, object)in a new coroutine.Parameters: - loop – Loop that owns the current coroutine.
- method – Pointer to application callback that will be called inside a new coroutine.
- object – Pointer to application data that will be passed uninterpreted
to
method. - stack_size – Size of the stack in bytes. When zero, a default and possibly system-specific stack size is selected.
Returns: A new coroutine.
Note
This function is implemented as a macro.
-
int
green_yield(green_loop_t loop, green_coroutine_t coro)¶ Block until any other coroutine yields back.
Parameters: - loop – Loop that owns the current coroutine (and
coro). - coro – Coroutine to which control should be yielded. When
NULL, control is returned to the loop.
Returns: Zero if the function succeeds.
Note
This function is implemented as a macro.
- loop – Loop that owns the current coroutine (and
-
int
green_coroutine_acquire(green_coroutine_t coro)¶ Increase the reference count.
Returns: Zero if the function succeeds.
-
int
green_coroutine_release(green_coroutine_t coro)¶ Decrease the reference count and destroy the object if necessary.
Returns: Zero if the function succeeds.
Future¶
The future is the foundation of libgreen. It represents the promise of
completion of an asynchronous operation. Combined with the poller and
green_select(), it can be used to multiplex multiple asynchronous
operations in the same coroutine.
-
green_future_t¶ This is an opaque pointer type to a reference-counted object.
-
green_future_t
green_future_init(green_hub_t hub)¶ Create a custom future. When your asynchronous operation completes, call
green_future_set_result()to mark it as complete and unblock a coroutine if one is waiting on this future.Parameters: - hub – Hub to which the future will be attached. The coroutine that completes this future MUST be running from this hub.
Returns: A future that you can complete whenever you wish.
-
int
green_future_set_result(green_future_t future, void * p, int i)¶ Mark the future as completed. If any coroutine is currently blocking on
green_select()with a poller in which this future is registered, then that coroutine will be unblocked and resumed soon.Parameters: - future – Future to complete.
- p – Pointer result. Will be returned by
green_future_result(). - i – Integer result. Will be returned by
green_future_result().
Returns: Zero if the function succeeds.
-
int
green_future_done(green_future_t future)¶ Check if the future is completd or canceled.
Parameters: - future – Future to check for completion.
Returns: Non-zero if the future is completed or cancelled. Zero if the future is still pending.
-
int
green_future_canceled(green_future_t future)¶ Check if the future is canceled.
Parameters: - future – Future to check for cancellation.
Returns: Non-zero if the future is cancelled. Zero if the future is still pending or completed.
-
int
green_future_result(green_future_t future, void ** p, int * i)¶ Return the result that was stored when the future was completed.
Parameters: - future – Future to check for result after completion.
- p – Pointer into which the value passed to
green_future_set_result()will be stored. - p – Integer into which the value passed to
green_future_set_result()will be stored.
Returns: Zero if the function succeeds,
GREEN_EBUSYif the future is pending,GREEN_EBADFDif the future is canceled.
-
int
green_future_cancel(green_future_t future)¶ Since there is no reason to keep around a cancelled future, canceling a future automatically decrements the reference count and there is no need to call
green_future_release()after canceling the future.Attention
Cancellation is usually asynchronous and may not be natively supported by all asynchronous operations or by all platforms for a given asynchronous operation. The basic cancellation guarantee is that the future will never be returned by
green_select(), but the future may not be deleted until it is actually completed.Parameters: - future – The future to cancel.
Returns: Zero on success.
-
int
green_future_acquire(green_future_t future)¶ Increase the reference count.
Returns: Zero if the function succeeds.
-
int
green_future_release(green_future_t future)¶ Decrease the reference count and destroy the object if necessary.
Returns: Zero if the function succeeds.
Poller¶
The poller is a specialized container that stores a set of future refrences in way that allows very efficient dispatch of future completion and implicit unblocking of a coroutine currently waiting on a completed future.
When you initiate a new asynchronous operation, call green_poller_add()
to add the future to the poller. When your coroutine is ready, call
green_select() to block until one or more such futures complete.
The poller is similar to the fd_set that is used with select(), but
implemented in a way that allows O(1) dispatch.
Attention
Pollers MUST NOT be shared between coroutines. Any
modification of a poller on which another coroutine is blocking via
green_select() may result in undefined behavior.
-
green_poller_t¶ This is an opaque pointer type to a reference-counted object.
Use
green_poller_release()when you are done with such a pointer. If you need to grab extra references, callgreen_poller_acquire(). Make sure you callgreen_poller_release()once for each call togreen_poller_acquire().
-
green_poller_t
green_poller_init(green_hub_t hub, size_t size)¶ Create a new poller for use with
green_select().Parameters: - hub – Hub to which the current coroutine is attached.
- size – Maximum number of futures you intend on adding to this poller.
Returns: A new poller that can be passed to
green_select(). Callgreen_poller_release()when you are done with this poller.
-
size_t
green_poller_size(green_poller_t poller)¶ Get the total number of slots in the future.
Parameters: - poller – Poller to check for maximum size.
Returns: The maximum number of futures that can be stored in the poller.
-
size_t
green_poller_used(green_poller_t poller)¶ Get the number of slots currently used by futures (pending and completed).
Parameters: - poller – Poller to check for current size.
Returns: The current number of futures stored in the poller.
-
size_t
green_poller_done(green_poller_t poller)¶ Get the number of slots currently used by completed futures. This number indicates the number of times you can call
green_poller_pop()before it returnsNULL.Parameters: - poller – Poller to check for completed futures.
Returns: The number of completed futures currently stored in the poller.
-
int
green_poller_add(green_poller_t poller, green_future_t future)¶ Add a future to the set.
It is legal to add a completed future to a poller. In that case, the next call to
green_select()with that poller will not block. This is especially convenient to handle synchronous completion of some asynchronous operations as it removes the need for an alternate code path to handle synchronous completion – especially for operations that may be synchronous only on some platforms.Parameters: - poller – Poller to which the future should be added.
- future – Future that should be added to the poller.
Returns: Zero if the function succeeds.
-
int
green_poller_rem(green_poller_t poller, green_future_t future)¶ Remove a future from the poller without fulfilling or cancelling it.
Futures are automatically removed from the poller when fulfilled or cancelled. However, the implicit removal of a cancelled future may be asynchronous. If you cancel a future and then need the slot immediately to add a new future, you can call this to free the slot immediately.
Parameters: - poller – Poller from which the future should be removed.
- future – Future that should be removed from the poller.
Returns: Zero if the function succeeds.
-
green_future_t
green_poller_pop(green_poller_t poller)¶ Grab the next completed future from the poller. You can call
green_poller_done()to determine how many times you can call this function before it returnsNULL.Parameters: - poller – Poller from which a completed future should be removed.
Returns: A completed future. If
pollercontains no completed futures,NULLis returned.
-
int
green_poller_acquire(green_poller_t poller)¶ Increase the reference count.
Returns: Zero if the function succeeds.
-
int
green_poller_release(green_poller_t poller)¶ Decrease the reference count and destroy the object if necessary.
Returns: Zero if the function succeeds.
-
green_future_t
green_select(green_poller_t poller)¶ Block until any of the futures registered in
pollerare completed. If the poller contains any completed futures, the function returns immediately.Parameters: - poller – The poller in which all futures that can unblock the current coroutine are registered.
Returns: A completed future if the function succeeds, else
NULL.Note
This function is implemented as a macro.
Error codes¶
-
GREEN_SUCCESS¶ The call completed successfully. This value is guaranteed to be zero.
-
GREEN_EBUSY¶ The future is still pending.
-
GREEN_ENOMEM¶ Could not allocate enough memory.
-
GREEN_EBADFD¶ The future is in an invalid state.
-
GREEN_ECANCELED¶ Cannot complete the future because it is already canceled.
-
GREEN_EALREADY¶ Cannot add the future to the poller because the future is alredy in a poller.
-
GREEN_ENOENT¶ Cannot remove the future from the poller because it was not found inside the poller.
-
GREEN_ENFILE¶ Cannot add the future to the poller because the poller is already full.