Skip to content

Commit

Permalink
Merge 32df0ff into 4d551ef
Browse files Browse the repository at this point in the history
  • Loading branch information
AndreLouisCaron committed Jan 2, 2016
2 parents 4d551ef + 32df0ff commit 2db7c6f
Show file tree
Hide file tree
Showing 5 changed files with 719 additions and 11 deletions.
7 changes: 6 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,13 @@ option(GREEN_VALGRIND "Run tests with memory leak checker." OFF)
if (GREEN_GCOV)
message(STATUS "Code coverage enabled.")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -O0 --coverage")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O0 --coverage")
set(CMAKE_STATIC_LINKER_FLAGS="--coverage")
set(CMAKE_MODULE_LINKER_FLAGS="--coverage")
set(CMAKE_EXE_LINKER_FLAGS="--coverage")
endif()

set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99")

# Configure stuff.
try_run(HAVE_UCONTEXT_RESULT HAVE_UCONTEXT
"${CMAKE_CURRENT_BINARY_DIR}/have-ucontext"
Expand Down Expand Up @@ -59,6 +60,9 @@ add_library(green
"src/green.c"
)

# libm is required for functions from <math.h>.
target_link_libraries(green m)

# This enables `ctest -T memcheck`.
if (GREEN_VALGRIND)
find_program(MEMORYCHECK_COMMAND "valgrind")
Expand All @@ -80,4 +84,5 @@ if(GREEN_TESTS)
green_add_test(test-init-term "tests/test-init-term.c")
green_add_test(test-loop "tests/test-loop.c")
green_add_test(test-coroutine "tests/test-coroutine.c")
green_add_test(test-poller "tests/test-poller.c")
endif()
161 changes: 161 additions & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,167 @@ Coroutine
:return: Zero if the function succeeds.
.. _future:
Future
~~~~~~
The future is the foundation of ``libgreen``. It represents the promise of
completion of an asynchronous operation. Combined with the poller_ and
:c:func:`green_select`, it can be used to multiplex multiple asynchronous
operations in the same coroutine_.
.. c:type:: green_future_t
This is an opaque pointer type to a reference-counted object.
.. c:function:: green_future_t green_future_init(green_hub_t hub)
Create a custom future. When your asynchronous operation completes, call
:c:func:`green_future_set_result` to mark it as complete and unblock a
coroutine if one is waiting on this future.
:arg hub: Hub to which the future will be attached. The coroutine that
completes this future **MUST** be running from this hub.
:return: A future that you can complete whenever you wish.
.. c:function:: 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
:c:func:`green_select` with a poller in which this future is registered,
then that coroutine will be unblocked and resumed soon.
:arg future: Future to complete.
:arg p: Pointer result. Will be returned by :c:func:`green_future_result`.
:arg i: Integer result. Will be returned by :c:func:`green_future_result`.
:return: Zero if the function succeeds.
.. c:function:: int green_future_done(green_future_t future)
.. c:function:: int green_future_result(green_future_t future, void ** p, int * i)
.. c:function:: 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 :c:func:`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 :c:func:`green_select`, but the future
may not be deleted until it is actually completed.
:arg future: The future to cancel.
:return: Zero on success.
.. c:function:: int green_future_acquire(green_future_t future)
Increase the reference count.
:return: Zero if the function succeeds.
.. c:function:: int green_future_release(green_future_t future)
Decrease the reference count and destroy the object if necessary.
:return: Zero if the function succeeds.
.. _poller:
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 :c:func:`green_poller_add`
to add the future_ to the poller. When your coroutine is ready, call
:c:func:`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
:c:func:`green_select` may result in undefined behavior.
.. c:type:: green_poller_t
This is an opaque pointer type to a reference-counted object.
Use :c:func:`green_poller_release` when you are done with such a pointer.
If you need to grab extra references, call :c:func:`green_poller_acquire`.
Make sure you call :c:func:`green_poller_release` once for each call to
:c:func:`green_poller_acquire`.
.. c:function:: green_poller_t green_poller_init(green_hub_t hub, size_t size)
Create a new poller for use with :c:func:`green_select`.
:arg hub: Hub to which the current coroutine is attached.
:arg size: Maximum number of futures you intend on adding to this poller.
:return: A new poller that can be passed to :c:func:`green_select`. Call
:c:func:`green_poller_release` when you are done with this poller.
.. c:function:: size_t green_poller_size(green_poller_t poller)
.. c:function:: size_t green_poller_used(green_poller_t poller)
.. c:function:: size_t green_poller_done(green_poller_t poller)
.. c:function:: 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 :c:func:`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.
:arg poller: Poller to which the future should be added.
:arg future: Future that should be added to the poller.
:return: Zero if the function succeeds.
.. c:function:: 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.
:arg poller: Poller from which the future should be removed.
:arg future: Future that should be removed from the poller.
:return: Zero if the function succeeds.
.. c:function:: green_future_t green_poller_pop(green_poller_t poller)
.. c:function:: green_future_t green_select(green_poller_t poller)
Block until any of the futures registered in ``poller`` are completed. If
the poller contains any completed futures, the function returns immediately.
:arg poller: The poller in which all futures that can unblock the current
coroutine are registered.
:return: A completed future if the function succeeds, else ``NULL``.
.. note:: This function is implemented as a macro.
.. c:function:: int green_poller_acquire(green_poller_t poller)
Increase the reference count.
:return: Zero if the function succeeds.
.. c:function:: int green_poller_release(green_poller_t poller)
Decrease the reference count and destroy the object if necessary.
:return: Zero if the function succeeds.
Indices and tables
==================
Expand Down
40 changes: 40 additions & 0 deletions include/green.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,17 @@
GREEN_STRING(GREEN_MINOR) "." \
GREEN_STRING(GREEN_PATCH)

// Error codes.
#define GREEN_SUCCESS 0
#define GREEN_EINVAL 1
#define GREEN_ENOMEM 2
#define GREEN_EBUSY 3
#define GREEN_ECANCELED 4
#define GREEN_EALREADY 5
#define GREEN_ENOENT 6
#define GREEN_ENFILE 7
#define GREEN_EBADFD 8

// Lib version.
int green_version();
const char * green_version_string();
Expand Down Expand Up @@ -64,4 +75,33 @@ int green_coroutine_result(green_coroutine_t coro);
int green_coroutine_acquire(green_coroutine_t coro);
int green_coroutine_release(green_coroutine_t coro);

// Future.
typedef struct green_future * green_future_t;
green_future_t green_future_init(green_loop_t loop);
int green_future_done(green_future_t future);
int green_future_canceled(green_future_t future);
int green_future_set_result(green_future_t future, void * p, int i);
int green_future_result(green_future_t future, void ** p, int * i);
int green_future_cancel(green_future_t future);

int green_future_acquire(green_future_t future);
int green_future_release(green_future_t future);

// Poller.
typedef struct green_poller * green_poller_t;
green_poller_t green_poller_init(green_loop_t loop, size_t size);
size_t green_poller_size(green_poller_t poller);
size_t green_poller_used(green_poller_t poller);
size_t green_poller_done(green_poller_t poller);
int green_poller_add(green_poller_t poller, green_future_t future);
int green_poller_rem(green_poller_t poller, green_future_t future);
green_future_t green_poller_pop(green_poller_t poller);

int green_poller_acquire(green_poller_t poller);
int green_poller_release(green_poller_t poller);

green_future_t _green_select(green_poller_t poller, const char * source);
#define green_select(poller) \
green_select_ex(poller, timeout, __FILE__ ":" GREEN_STRING(__LINE__))

#endif // _GREEN_H__
Loading

0 comments on commit 2db7c6f

Please sign in to comment.