Skip to content

Commit

Permalink
Merge cfcfba5 into 9aa3962
Browse files Browse the repository at this point in the history
  • Loading branch information
AndreLouisCaron committed Dec 29, 2015
2 parents 9aa3962 + cfcfba5 commit 64b8628
Show file tree
Hide file tree
Showing 8 changed files with 553 additions and 0 deletions.
19 changes: 19 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,29 @@ project(green)

option(GREEN_TESTS "Compile the test suite." ON)

# Configure stuff.
try_run(HAVE_UCONTEXT_RESULT HAVE_UCONTEXT
"${CMAKE_CURRENT_BINARY_DIR}/have-ucontext"
"${CMAKE_CURRENT_SOURCE_DIR}/configure/have-ucontext.c"
COMPILE_OUTPUT_VARIABLE have-ucontext-compile
RUN_OUTPUT_VARIABLE have-ucontext-output
)
if (NOT HAVE_UCONTEXT)
message(FATAL_ERROR "ucontext not available:\n${have-ucontext-compile}")
endif()
if (NOT (HAVE_UCONTEXT_RESULT EQUAL 0))
message(FATAL_ERROR "ucontext doesn't work: exit status is ${HAVE_UCONTEXT_RESULT}\n${have-ucontext-output}")
endif()

# Relative paths make __FILE__ more readable.
set(CMAKE_USE_RELATIVE_PATHS TRUE)

include_directories("${PROJECT_SOURCE_DIR}/include")

if(HAVE_UCONTEXT)
add_definitions(-D_XOPEN_SOURCE=500)
endif()

add_library(green
"src/green.c"
)
Expand All @@ -27,4 +45,5 @@ if(GREEN_TESTS)
enable_testing()
green_add_test(test-version "tests/test-version.c")
green_add_test(test-init-term "tests/test-init-term.c")
green_add_test(test-coroutine "tests/test-coroutine.c")
endif()
143 changes: 143 additions & 0 deletions configure/have-ucontext.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
////////////////////////////////////////////////////////////////////////
// Copyright(c) libgreen contributors. See LICENSE file for details. //
////////////////////////////////////////////////////////////////////////

// Required for using ucontext.
#define _XOPEN_SOURCE 500

#include <stdio.h>
#include <stdlib.h>
#include <ucontext.h>

ucontext_t hub;
ucontext_t coro;
int coro_status = 0;
void * P = NULL;
int stack_size = 64 * 1024; // SIGSTKSZ is too small on linux.

// NOTE: according to the ucontext specification, we can only pass int
// arguments to the coroutine. Use a pair of ints in case `sizeof(void*)
// != sizeof(int)`.
typedef union {
int i[2];
void * p;
} coroutine_args;

void coroutine(int i0, int i1)
{
// Decode pointer argument.
coroutine_args args;
args.i[0] = i0;
args.i[1] = i1;
void * p = args.p;

int i = 0;

fprintf(stderr, "coro: enter.\n");
if (p != P) {
fprintf(stderr, "coro: p != P\n");
coro_status = 1;
return;
}

fprintf(stderr, "coro: yield.\n");
i++;
if (swapcontext(&coro, &hub) < 0) {
fprintf(stderr, "coro: swapcontext()\n");
coro_status = 1;
return;
}

fprintf(stderr, "coro: check.\n");
if (i != 1) {
fprintf(stderr, "coro: stack corruption detected.\n");
coro_status = 1;
return;
}

fprintf(stderr, "coro: leave.\n");
}

int main()
{
int i = 0;

fprintf(stderr, "main: enter.\n");

// Store the pointer we pass as an argument to the coroutine to make sure
// the coroutine can check that it is passed safely.
P = &hub;

// Encode pointer argument.
coroutine_args args;
args.p = &hub;

// Spawn the coroutine (delayed start).
fprintf(stderr, "main: spawn.\n");
if (getcontext(&coro) < 0) {
fprintf(stderr, "main: getcontext()\n");
return EXIT_FAILURE;
}
void * stack = malloc(stack_size);
if (stack == NULL) {
fprintf(stderr, "main: malloc()\n");
return EXIT_FAILURE;
}
coro.uc_link = &hub;
coro.uc_stack.ss_sp = stack;
coro.uc_stack.ss_size = stack_size;
makecontext(&coro, (void(*)())coroutine, 2, args.i[0], args.i[1]);

fprintf(stderr, "main: stack @ %p.\n", coro.uc_stack.ss_sp);

// Yield to the coroutine until it yields back.
i++;
fprintf(stderr, "main: yield (1).\n");
if (swapcontext(&hub, &coro) < 0) {
fprintf(stderr, "main: getcontext()\n");
return EXIT_FAILURE;
}
if (coro_status != 0) {
fprintf(stderr, "main: coroutine error\n");
return EXIT_FAILURE;
}

// Check for stack corruption.
fprintf(stderr, "main: check.\n");
if (i != 1) {
fprintf(stderr, "main: stack corruption detected.\n");
return EXIT_FAILURE;
}

// Yield back to the coroutine to let it finish.
i++;
fprintf(stderr, "main: yield (2).\n");
if (swapcontext(&hub, &coro) < 0) {
fprintf(stderr, "main: getcontext()\n");
return EXIT_FAILURE;
}
if (coro_status != 0) {
fprintf(stderr, "main: coroutine error\n");
return EXIT_FAILURE;
}

// Check for stack corruption.
fprintf(stderr, "main: check.\n");
if (i != 2) {
fprintf(stderr, "main: stack corruption detected.\n");
return EXIT_FAILURE;
}

// Release the coroutine's stack.
//
// NOTE: we can't rely on `coro.uc_stack.ss_sp` because the ucontext
// implementation is free to manipulate this at will and there is no
// guarantee that this will be the pointer we allocated.
fprintf(stderr, "main: cleanup.\n");
fprintf(stderr, "main: stack @ %p.\n", coro.uc_stack.ss_sp);
free(stack);
coro.uc_stack.ss_sp = NULL;

fprintf(stderr, "main: leave.\n");
return EXIT_SUCCESS;
}
76 changes: 76 additions & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,82 @@ Library setup
:return: Zero if the function succeeds.
.. _hub:
Hub
~~~
.. c:type:: green_hub_t
This is an opaque pointer type to a reference-counted object.
.. c:function:: green_hub_t green_hub_init()
.. c:function:: int green_hub_acquire(green_hub_t hub)
Increase the reference count.
:return: Zero if the function succeeds.
.. c:function:: int green_hub_release(green_hub_t hub)
Decrease the reference count and destroy the object if necessary.
:return: Zero if the function succeeds.
.. _coroutine:
Coroutine
~~~~~~~~~
.. c:type:: green_coroutine_t
This is an opaque pointer type to a reference-counted object.
.. c:function:: green_coroutine_t green_coroutine_init(green_hub_t hub, int(*method)(void*,void*), void * object)
Call ``method(hub, object)`` in a new coroutine.
:arg hub: Hub that owns the current coroutine.
:arg method: Pointer to application callback that will be called inside a
new coroutine.
:arg object: Pointer to application data that will be passed uninterpreted
to ``method``.
:return: A new coroutine.
.. note:: This function is implemented as a macro.
.. c:function:: int green_yield(green_hub_t hub, green_coroutine_t coro)
Block until any other coroutine yields back.
:arg hub: Hub that owns the current coroutine (and ``coro``).
:arg coro: Coroutine to which control should be yielded. When ``NULL``,
control is returned to the hub.
:return: Zero if the function succeeds.
.. note:: This function is implemented as a macro.
.. c:function:: green_future_t green_coroutine_join(green_coroutine_t coro)
:arg coro: Coroutine whose completion you are interested in.
:return: A future that will be completed when ``coro`` returns.
.. note:: Cancelling this future does **not** cancel the coroutine.
.. c:function:: int green_coroutine_acquire(green_coroutine_t coro)
Increase the reference count.
:return: Zero if the function succeeds.
.. c:function:: int green_coroutine_release(green_coroutine_t coro)
Decrease the reference count and destroy the object if necessary.
:return: Zero if the function succeeds.
Indices and tables
==================
Expand Down
24 changes: 24 additions & 0 deletions include/green.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,28 @@ int _green_init(int major, int minor);
_green_init(GREEN_MAJOR, GREEN_MINOR)
int green_term();

// Hub setup and teardown.
typedef struct green_hub * green_hub_t;
green_hub_t green_hub_init();
int green_hub_acquire(green_hub_t hub);
int green_hub_release(green_hub_t hub);

// Coroutine methods.
typedef struct green_coroutine * green_coroutine_t;

green_coroutine_t _green_coroutine_init(
green_hub_t hub, int(*method)(void*,void*),
void * object, const char * source
);
#define green_coroutine_init(hub, method, object) \
_green_coroutine_init(hub, method, object, __FILE__ ":" GREEN_STRING(__LINE__))

int _green_yield(green_hub_t hub, green_coroutine_t coro, const char * source);
#define green_yield(hub, coro) \
_green_yield(hub, coro, __FILE__ ":" GREEN_STRING(__LINE__))

green_coroutine_t green_coroutine_join(green_coroutine_t coro);
int green_coroutine_acquire(green_coroutine_t coro);
int green_coroutine_release(green_coroutine_t coro);

#endif // _GREEN_H__
Loading

0 comments on commit 64b8628

Please sign in to comment.