├── Makefile ├── README.md ├── libconcurrency.sln └── libconcurrency ├── coro.c ├── coro.h ├── coro_fibers.c ├── ctxt.h ├── libconcurrency.vcproj ├── stdint.h └── tls.h /Makefile: -------------------------------------------------------------------------------- 1 | LIBNAME=libconcurrency 2 | 3 | # library name and version 4 | SONAME=$(LIBNAME).so.0.8 5 | 6 | # compiler and linker flags 7 | #CFLAGS=-W -Wall -c -g -O2 -fPIC -fomit-frame-pointer -I./ 8 | CFLAGS=-ansi -pedantic -O2 -fPIC -I./ 9 | LFLAGS=-shared -Wl,-soname,$(SONAME) -o $(SONAME) $< 10 | 11 | # compiler 12 | CC=gcc 13 | 14 | default: libconcurrency/coro.o 15 | $(CC) $(LFLAGS) 16 | 17 | clean: 18 | rm $(LIBNAME)/*.o *.so.* 19 | 20 | %.o: %.c 21 | $(CC) $(CFLAGS) -c $< -o $@ 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Concurrency in C 2 | 3 | A lightweight concurrency library for C, featuring symmetric [coroutines](http://en.wikipedia.org/wiki/Coroutine) 4 | as the main control flow abstraction. The library is similar to [State Threads](http://state-threads.sourceforge.net/), 5 | but using coroutines instead of green threads. This simplifies inter-procedural calls and largely eliminates 6 | the need for mutexes and semaphores for signaling. 7 | 8 | Eventually, coroutine calls will also be able to safely migrate between kernel threads, 9 | so the achievable scalability is consequently much higher than State Threads, which is purposely single-threaded. 10 | 11 | This library was inspired by Douglas W. Jones' ["minimal user-level thread package"](http://www.cs.uiowa.edu/~jones/opsys/threads/). 12 | The pseudo-platform-neutral probing algorithm on the svn trunk is derived from his code. 13 | 14 | There is also a [safer, more portable coroutine implementation based on stack copying](http://github.com/smokku/libconcurrency/tree/copying-cache-stacks), 15 | which was inspired by [sigfpe's page on portable continuations in C](http://homepage.mac.com/sigfpe/Computing/continuations.html). 16 | Copying is more portable and flexible than stack switching, and [making copying competitive with switching is being researched](http://higherlogics.blogspot.com/2008/07/coroutines-in-c-redux.html). 17 | 18 | ## Coroutines 19 | 20 | Coroutine calls consist of only one operation: coro_call. 21 | This operation suspends the currently executing coroutine, and resumes the target coroutine passing it a given value. 22 | Passing a value in the other direction is exactly the same. Coroutines which use only one operation like this 23 | are called _symmetric_ coroutines. 24 | 25 | _Asymmetric_ coroutines use two different operations, like call and yield, implying that one caller is subordinate 26 | to another. Lua supports asymmetric coroutines. 27 | 28 | In many ways, such coroutines closely resemble the Inter-Process Communication (IPC) facilities 29 | of microkernel operating systems like [EROS](http://eros-os.org/), and they are also closely related to actors. 30 | 31 | [Revisiting Coroutines](http://lambda-the-ultimate.org/node/2868) is a detailed paper explaining 32 | the advantages of coroutines as a control flow abstraction. While the authors prefer asymmetric coroutines, 33 | and use a coroutine label to avoid capture problems, I think it's simpler to simply use 34 | the coroutine reference itself in place of a label. 35 | 36 | ## Comparison to Existing Alternatives 37 | 38 | Features libconcurrency provides that are not found in [libcoro](http://software.schmorp.de/pkg/libcoro.html), 39 | [libCoroutine](http://www.dekorte.com/projects/opensource/libCoroutine/docs/), 40 | [libpcl](http://www.xmailserver.org/libpcl.html), [coro](http://www.goron.de/~froese/coro/): 41 | 42 | * **Cloning a coroutine:** this enables a straightforward implementation of multishot continuations. Without the ability to clone, only one-shot continuations can be implemented in terms of coroutines. Note, cloning is not available on Windows since Windows uses the Fibers API. This was necessary to handle [some unfortunate problems](http://higherlogics.blogspot.com/2008/07/coroutines-in-c-redux.html). The [more portable stack copying implementation does support cloning](http://github.com/smokku/libconcurrency/tree/copying-cache-stacks). 43 | * **Portable Speed:** libconcurrency is based on a portable technique that modifies jmp_buf and uses setjmp/longjmp to save and restore contexts. This is fast because setjmp/longjmp do not save and restore signals, whereas most other libraries are based on ucontext, which is very portable but inefficient, or assembler which is efficient but not portable. The library consists of entirely C99 compliant C and no exotic platform or OS features are used. 44 | * **Actual coroutine API:** most of the listed libraries implement green threads, not coroutines. You'll note that no values are passed between coroutines in these libraries. Passing values between coroutines is one of the primary reasons why they are more general than threads. 45 | * **[Simpler API](libconcurrency/coro.h):** the API consists of only 6 orthogonal functions: coro_init, coro_call, coro_clone, coro_poll, coro_free. 46 | * **Works on Windows:** libconcurrency is primarily developed on Windows, and uses Windows Fibers for coroutines. 47 | * **Resources are managed:** the coro_poll function must be called periodically to ensure a coroutine has enough resources to continue executing. This function can shrink or grow the stack using a hysteresis algorithm inspired by the description of one-shot continuations in [Representing Control in the Presence of One-Shot Continuations](http://citeseer.ist.psu.edu/bruggeman96representing.html). 48 | 49 | ## Status 50 | 51 | This library is functional, but not nearly as well developed as something like [State Threads](http://state-threads.sourceforge.net/). 52 | If you're willing to do a little work, libconcurrency can provide a good foundation on which to build, 53 | but if you're looking for something that provides 95% of what you'll need out of the box, 54 | State Threads is by far the more mature project. 55 | 56 | ## Example 57 | 58 | Here's a basic reader/writer that simply echoes characters read to the screen: 59 | 60 | ```c 61 | #include 62 | #include 63 | 64 | coro cr; 65 | coro cw; 66 | 67 | void reader(cvalue r) { 68 | while (1) { 69 | printf("> "); 70 | r.c = getchar(); 71 | if (r.c != '\n') { 72 | coro_call(cw, r); 73 | } 74 | } 75 | } 76 | 77 | void writer(cvalue w) { 78 | while(1) { 79 | printf(" echo: %c\n", w.c); 80 | w = coro_call(cr, cnone); 81 | } 82 | } 83 | 84 | int main(int argc, char **argv) { 85 | coro _main = coro_init(); 86 | printf("Simple reader/writer echo...\n"); 87 | cr = coro_new(reader); 88 | cw = coro_new(writer); 89 | coro_call(cw, cnone); 90 | return 0; 91 | } 92 | ``` 93 | -------------------------------------------------------------------------------- /libconcurrency.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 9.00 3 | # Visual Studio 2005 4 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libconcurrency", "libconcurrency\libconcurrency.vcproj", "{1BB1E95C-A4EE-469C-8546-CA3710738E16}" 5 | EndProject 6 | Global 7 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 8 | Debug|Win32 = Debug|Win32 9 | Release|Win32 = Release|Win32 10 | EndGlobalSection 11 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 12 | {1BB1E95C-A4EE-469C-8546-CA3710738E16}.Debug|Win32.ActiveCfg = Debug|Win32 13 | {1BB1E95C-A4EE-469C-8546-CA3710738E16}.Debug|Win32.Build.0 = Debug|Win32 14 | {1BB1E95C-A4EE-469C-8546-CA3710738E16}.Release|Win32.ActiveCfg = Release|Win32 15 | {1BB1E95C-A4EE-469C-8546-CA3710738E16}.Release|Win32.Build.0 = Release|Win32 16 | EndGlobalSection 17 | GlobalSection(SolutionProperties) = preSolution 18 | HideSolutionNode = FALSE 19 | EndGlobalSection 20 | EndGlobal 21 | -------------------------------------------------------------------------------- /libconcurrency/coro.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Co-routines in C 3 | * 4 | * Possible implementations: 5 | * 1. stack switching - need to constantly check stack usage (I use this). 6 | * 2. stack copying - essentially continuations. 7 | * 8 | * Notes: 9 | * * termination of a coroutine without explicit control transfer returns control 10 | * to the coroutine which initialized the coro library. 11 | * 12 | * Todo: 13 | * 1. Co-routines must be integrated with any VProc/kernel-thread interface, since 14 | * an invoked co-routine might be running on another cpu. A coro invoker must 15 | * check that the target vproc is the same as the current vproc; if not, queue the 16 | * invoker on the target vproc using an atomic op. 17 | * 2. VCpu should implement work-stealing, ie. when its run queue is exhausted, it 18 | * should contact another VCpu and steal a few of its coros, after checking its 19 | * migration queues of course. The rate of stealing should be tuned: 20 | * http://www.cs.cmu.edu/~acw/15740/proposal.html 21 | * 3. Provide an interface to register a coroutine for any errors generated. This is 22 | * a type of general Keeper, or exception handling. 23 | */ 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include "ctxt.h" 33 | 34 | /* 35 | * These are thresholds used to grow and shrink the stack. They are scaled by 36 | * the size of various platform constants. STACK_TGROW is sized to allow 37 | * approximately 200 nested calls. On a 32-bit machine assuming 4 words of 38 | * overhead per call, 256 calls = 1024. If stack allocation is performed, 39 | * this will need to be increased. 40 | */ 41 | #define STACK_TGROW 1024 42 | #define STACK_DEFAULT sizeof(intptr_t) * STACK_TGROW 43 | #define STACK_TSHRINK 2 * STACK_DEFAULT 44 | #define STACK_ADJ STACK_DEFAULT 45 | 46 | /* the coroutine structure */ 47 | struct _coro { 48 | _ctxt ctxt; 49 | _entry start; 50 | intptr_t stack_base; 51 | size_t stack_size; 52 | }; 53 | static cvalue cnone = { NULL }; 54 | 55 | /* 56 | * Each of these are local to the kernel thread. Volatile storage is necessary 57 | * otherwise _value is often cached as a local when switching contexts, so 58 | * a sequence of calls will always return the first value! 59 | */ 60 | THREAD_LOCAL volatile coro _cur; 61 | THREAD_LOCAL volatile cvalue _value; 62 | THREAD_LOCAL struct _coro _on_exit; 63 | 64 | /* 65 | * We probe the current machine and extract the data needed to modify the 66 | * machine context. The current thread is then initialized as the currently 67 | * executing coroutine. 68 | */ 69 | EXPORT 70 | coro coro_init() 71 | { 72 | _probe_arch(); 73 | _cur = &_on_exit; 74 | return _cur; 75 | } 76 | 77 | /*EXPORT 78 | coro coro_error() 79 | { 80 | coro c = (coro)malloc(sizeof(struct _coro)); 81 | c->stack_base = NULL; 82 | c->stack_size = 0; 83 | c->start = NULL; 84 | if (!_save_and_resumed(c->ctxt)) 85 | { 86 | _cur = c; 87 | } 88 | return _cur; 89 | }*/ 90 | 91 | /* copy the old stack frame to the new stack frame */ 92 | void _coro_cpframe(intptr_t local_sp, intptr_t new_sp) 93 | { 94 | intptr_t src = local_sp - (_stack_grows_up ? _frame_offset : 0); 95 | intptr_t dst = new_sp - (_stack_grows_up ? _frame_offset : 0); 96 | /* copy local stack frame to the new stack */ 97 | memcpy((void *)dst, (void *)src, _frame_offset); 98 | } 99 | 100 | /* rebase any values in saved state to the new stack */ 101 | void _coro_rebase(coro c, intptr_t local_sp, intptr_t new_sp) 102 | { 103 | intptr_t * s = (intptr_t *)c->ctxt; 104 | ptrdiff_t diff = new_sp - local_sp; /* subtract old base, and add new base */ 105 | int i; 106 | for (i = 0; i < _offsets_len; ++i) 107 | { 108 | s[_offsets[i]] += diff; 109 | } 110 | } 111 | 112 | /* 113 | * This function invokes the start function of the coroutine when the 114 | * coroutine is first called. If it was called from coro_new, then it sets 115 | * up the stack and initializes the saved context. 116 | */ 117 | void _coro_enter(coro c) 118 | { 119 | if (_save_and_resumed(c->ctxt)) 120 | { /* start the coroutine; stack is empty at this point. */ 121 | cvalue _return; 122 | _return.p = _cur; 123 | _cur->start(_value); 124 | /* return the exited coroutine to the exit handler */ 125 | coro_call(&_on_exit, _return); 126 | } 127 | /* this code executes when _coro_enter is called from coro_new */ 128 | INIT_CTXT: 129 | { 130 | /* local and new stack pointers at identical relative positions on the stack */ 131 | intptr_t local_sp = (intptr_t)&local_sp; 132 | /* I don't know what the addition "- sizeof(void *)" is for when 133 | the stack grows downards */ 134 | intptr_t new_sp = c->stack_base + 135 | (_stack_grows_up 136 | ? _frame_offset 137 | : c->stack_size - _frame_offset - sizeof(void *)); 138 | 139 | /* copy local stack frame to the new stack */ 140 | _coro_cpframe(local_sp, new_sp); 141 | 142 | /* reset any locals in the saved state to point to the new stack */ 143 | _coro_rebase(c, local_sp, new_sp); 144 | } 145 | } 146 | 147 | EXPORT 148 | coro coro_new(_entry fn) 149 | { 150 | /* FIXME: should not malloc directly? */ 151 | coro c = (coro)malloc(sizeof(struct _coro)); 152 | c->stack_size = STACK_DEFAULT; 153 | c->stack_base = (intptr_t)malloc(c->stack_size); 154 | c->start = fn; 155 | _coro_enter(c); 156 | return c; 157 | } 158 | 159 | /* 160 | * First, set the value in the volatile global. If _value were not volatile, this value 161 | * would be cached on the stack, and hence saved and restored on every call. We then 162 | * save the context for the current coroutine, set the target coroutine as the current 163 | * one running, then restore it. The target is now running, and it simply returns _value. 164 | */ 165 | EXPORT 166 | cvalue coro_call(coro target, cvalue value) 167 | { 168 | /* FIXME: ensure target is on the same proc as cur, else, migrate cur to target->proc */ 169 | 170 | _value = value; /* pass value to 'target' */ 171 | if (!_save_and_resumed(_cur->ctxt)) 172 | { 173 | /* we are calling someone else, so we set up the environment, and jump to target */ 174 | _cur = target; 175 | _rstr_and_jmp(_cur->ctxt); 176 | } 177 | /* when someone called us, just return the value */ 178 | return _value; 179 | } 180 | 181 | EXPORT 182 | coro coro_clone(coro c) 183 | { 184 | coro cnew = (coro)malloc(sizeof(struct _coro)); 185 | size_t stack_sz = c->stack_size; 186 | intptr_t stack_base = (intptr_t)malloc(stack_sz); 187 | /* copy the context then the stack data */ 188 | memcpy(cnew, c, sizeof(struct _coro)); 189 | memcpy((void *)stack_base, (void *)c->stack_base, stack_sz); 190 | cnew->stack_base = stack_base; 191 | cnew->stack_size = stack_sz; 192 | /* ensure new context references new stack */ 193 | _coro_rebase(cnew, c->stack_base, stack_base); 194 | return cnew; 195 | } 196 | 197 | EXPORT 198 | void coro_free(coro c) 199 | { 200 | free((void *)c->stack_base); 201 | free(c); 202 | } 203 | 204 | /* 205 | * Resume execution with a new stack: 206 | * 1. allocate a new stack 207 | * 2. copy all relevant data 208 | * 3. mark save point 209 | * 4. rebase the context using the new stack 210 | * 5. restore the context with the new stack 211 | */ 212 | static void _coro_resume_with(size_t sz) 213 | { 214 | /* allocate bigger stack */ 215 | intptr_t old_sp = _cur->stack_base; 216 | void * new_sp = malloc(sz); 217 | memcpy(new_sp, (void *)old_sp, _cur->stack_size); 218 | _cur->stack_base = (intptr_t)new_sp; 219 | _cur->stack_size = sz; 220 | /* save the current context; execution resumes here with new stack */ 221 | if (!_save_and_resumed(_cur->ctxt)) 222 | { 223 | /* rebase jmp_buf using new stack */ 224 | _coro_rebase(_cur, old_sp, (intptr_t)new_sp); 225 | _rstr_and_jmp(_cur->ctxt); 226 | } 227 | free((void *)old_sp); 228 | } 229 | 230 | /* 231 | * The stack poll uses some hysteresis to avoid thrashing. We grow the stack if 232 | * there's less than STACK_TGROW bytes left in the current stack, and we only shrink 233 | * if there's more than STACK_TSHRINK empty. 234 | */ 235 | EXPORT 236 | void coro_poll() 237 | { 238 | /* check the current stack pointer */ 239 | size_t stack_size = _cur->stack_size; 240 | size_t empty = (_stack_grows_up 241 | ? stack_size - ((uintptr_t)&empty - _cur->stack_base) 242 | : (uintptr_t)&empty - _cur->stack_base); 243 | 244 | if (empty < STACK_TGROW) 245 | { /* grow stack */ 246 | _coro_resume_with(stack_size + STACK_ADJ); 247 | } 248 | else if (empty > STACK_TSHRINK) 249 | { /* shrink stack */ 250 | _coro_resume_with(stack_size - STACK_ADJ); 251 | } 252 | } 253 | -------------------------------------------------------------------------------- /libconcurrency/coro.h: -------------------------------------------------------------------------------- 1 | #ifndef __CORO_H__ 2 | #define __CORO_H__ 3 | 4 | /* 5 | * Portable coroutines for C. Caveats: 6 | * 7 | * 1. You should not take the address of a stack variable, since stack management 8 | * could reallocate the stack, the new stack would reference a variable in the 9 | * old stack. Also, cloning a coroutine would cause the cloned coroutine to 10 | * reference a variable in the other stack. 11 | * 2. You must call coro_init for each kernel thread, since there are thread-local 12 | * data structures. This will eventually be exploited to scale coroutines across 13 | * CPUs. 14 | * 3. If setjmp/longjmp inspect the jmp_buf structure before executing a jump, this 15 | * library probably will not work. 16 | * 17 | * Refs: 18 | * http://www.yl.is.s.u-tokyo.ac.jp/sthreads/ 19 | */ 20 | 21 | #include 22 | #include 23 | 24 | /* a coroutine handle */ 25 | typedef struct _coro *coro; 26 | 27 | /* the type of value passed between coroutines */ 28 | typedef union _value { 29 | void *p; 30 | unsigned u; 31 | int i; 32 | char c; 33 | } cvalue; 34 | 35 | /* the type of entry function */ 36 | typedef void (*_entry)(cvalue v); 37 | 38 | static cvalue cnone; 39 | 40 | /* 41 | * Initialize the coroutine library, returning a coroutine for the thread that called init. 42 | */ 43 | EXPORT 44 | extern coro coro_init(); 45 | 46 | /* 47 | * Create a new coroutine from the given function, and with the 48 | * given stack. 49 | */ 50 | EXPORT 51 | extern coro coro_new(_entry fn); 52 | 53 | /* 54 | * Invoke a coroutine passing the given value. 55 | */ 56 | EXPORT 57 | extern cvalue coro_call(coro target, cvalue value); 58 | 59 | /* 60 | * Clone a given coroutine. This can be used to implement multishot continuations. 61 | */ 62 | /*EXPORT 63 | coro coro_clone(coro c);*/ 64 | 65 | /* 66 | * Free the coroutine and return the space for the stack. 67 | */ 68 | EXPORT 69 | extern void coro_free(coro c); 70 | 71 | /* 72 | * Poll the current coroutine to ensure sufficient resources are allocated. This 73 | * should be called periodically to ensure a coroutine doesn't segfault. 74 | */ 75 | EXPORT 76 | extern void coro_poll(); 77 | 78 | #endif /* __CORO_H__ */ 79 | -------------------------------------------------------------------------------- /libconcurrency/coro_fibers.c: -------------------------------------------------------------------------------- 1 | /* A coroutine implementation based on fibers */ 2 | 3 | /* minimum Windows version */ 4 | #define _WIN32_WINNT 0x0400 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | /* 15 | * The default stack size for new fibers 16 | */ 17 | #define STACK_DEFAULT sizeof(intptr_t) * 4096 18 | 19 | /* the coroutine structure */ 20 | struct _coro { 21 | void * fiber; 22 | _entry start; 23 | }; 24 | 25 | /* 26 | * Each of these are local to the kernel thread. Volatile storage is necessary 27 | * otherwise _value is often cached as a local when switching contexts, so 28 | * a sequence of calls will always return the first value! 29 | */ 30 | THREAD_LOCAL volatile coro _cur; 31 | THREAD_LOCAL volatile cvalue _value; 32 | THREAD_LOCAL struct _coro _on_exit; 33 | 34 | EXPORT 35 | coro coro_init() 36 | { 37 | _on_exit.fiber = ConvertThreadToFiber(NULL); 38 | _cur = &_on_exit; 39 | return _cur; 40 | } 41 | 42 | static void __stdcall _coro_enter(void * p) 43 | { 44 | cvalue _return; 45 | _return.p = _cur; 46 | _cur->start(_value); 47 | coro_call(&_on_exit, _return); 48 | } 49 | 50 | EXPORT 51 | coro coro_new(_entry start) 52 | { 53 | coro c = (coro)malloc(sizeof(struct _coro)); 54 | c->fiber = CreateFiber(STACK_DEFAULT, &_coro_enter, NULL); 55 | c->start = start; 56 | return c; 57 | } 58 | 59 | EXPORT 60 | cvalue coro_call(coro target, cvalue value) 61 | { 62 | _value = value; 63 | _cur = target; 64 | SwitchToFiber(target->fiber); 65 | return _value; 66 | } 67 | 68 | EXPORT 69 | void coro_free(coro c) 70 | { 71 | DeleteFiber(c->fiber); 72 | free(c); 73 | } 74 | 75 | EXPORT 76 | void coro_poll() 77 | { 78 | /* no-op with fibers */ 79 | } 80 | -------------------------------------------------------------------------------- /libconcurrency/ctxt.h: -------------------------------------------------------------------------------- 1 | #ifndef __CTXT_H__ 2 | #define __CTXT_H__ 3 | 4 | #include 5 | #include 6 | 7 | /* context management definitions */ 8 | typedef jmp_buf _ctxt; 9 | 10 | #ifndef _setjmp 11 | #define _setjmp setjmp 12 | #endif 13 | 14 | #ifndef _longjmp 15 | #define _longjmp longjmp 16 | #endif 17 | 18 | #define _save_and_resumed(c) _setjmp(c) 19 | #define _rstr_and_jmp(c) _longjmp(c, 1) 20 | 21 | #define SP_ALIGN(sp) sp 22 | /*#define SP_ALIGN(sp) (sp + sp % 16)*/ 23 | 24 | /* the list of offsets in jmp_buf to be adjusted */ 25 | /* # of offsets cannot be greater than jmp_buf */ 26 | static int _offsets[sizeof(jmp_buf) / sizeof(int)]; 27 | static int _offsets_len; 28 | 29 | /* true if stack grows up, false if down */ 30 | static int _stack_grows_up; 31 | 32 | /* the offset of the beginning of the stack frame in a function */ 33 | static size_t _frame_offset; 34 | 35 | /* This probing code is derived from Douglas Jones' user thread library */ 36 | struct _probe_data { 37 | intptr_t low_bound; /* below probe on stack */ 38 | intptr_t probe_local; /* local to probe on stack */ 39 | intptr_t high_bound; /* above probe on stack */ 40 | intptr_t prior_local; /* value of probe_local from earlier call */ 41 | 42 | jmp_buf probe_env; /* saved environment of probe */ 43 | jmp_buf probe_sameAR; /* second environment saved by same call */ 44 | jmp_buf probe_samePC; /* environment saved on previous call */ 45 | 46 | jmp_buf * ref_probe; /* switches between probes */ 47 | }; 48 | 49 | void boundhigh(struct _probe_data *p) 50 | { 51 | int c; 52 | p->high_bound = (intptr_t)&c; 53 | } 54 | 55 | void probe(struct _probe_data *p) 56 | { 57 | int c; 58 | p->prior_local = p->probe_local; 59 | p->probe_local = (intptr_t)&c; 60 | _setjmp( *(p->ref_probe) ); 61 | p->ref_probe = &p->probe_env; 62 | _setjmp( p->probe_sameAR ); 63 | boundhigh(p); 64 | } 65 | 66 | void boundlow(struct _probe_data *p) 67 | { 68 | int c; 69 | p->low_bound = (intptr_t)&c; 70 | probe(p); 71 | } 72 | 73 | void fill(struct _probe_data *p) 74 | { 75 | boundlow(p); 76 | } 77 | 78 | static void _infer_jmpbuf_offsets(struct _probe_data *pb) 79 | { 80 | /* following line views jump buffer as array of long intptr_t */ 81 | unsigned i; 82 | intptr_t * p = (intptr_t *)pb->probe_env; 83 | intptr_t * sameAR = (intptr_t *)pb->probe_sameAR; 84 | intptr_t * samePC = (intptr_t *)pb->probe_samePC; 85 | intptr_t prior_diff = pb->probe_local - pb->prior_local; 86 | intptr_t min_frame = pb->probe_local; 87 | 88 | for (i = 0; i < sizeof(jmp_buf) / sizeof(intptr_t); ++i) { 89 | intptr_t pi = p[i], samePCi = samePC[i]; 90 | if (pi != samePCi) { 91 | if (pi != sameAR[i]) { 92 | perror("No Thread Launch\n" ); 93 | exit(-1); 94 | } 95 | if ((pi - samePCi) == prior_diff) { 96 | /* the i'th pointer field in jmp_buf needs to be save/restored */ 97 | _offsets[_offsets_len++] = i; 98 | if ((_stack_grows_up && min_frame > pi) || (!_stack_grows_up && min_frame < pi)) { 99 | min_frame = pi; 100 | } 101 | } 102 | } 103 | } 104 | 105 | _frame_offset = (_stack_grows_up 106 | ? pb->probe_local - min_frame 107 | : min_frame - pb->probe_local); 108 | } 109 | 110 | static void _infer_direction_from(int *first_addr) 111 | { 112 | int second; 113 | _stack_grows_up = (first_addr < &second); 114 | } 115 | 116 | static void _infer_stack_direction() 117 | { 118 | int first; 119 | _infer_direction_from(&first); 120 | } 121 | 122 | static void _probe_arch() 123 | { 124 | struct _probe_data p; 125 | p.ref_probe = &p.probe_samePC; 126 | 127 | _infer_stack_direction(); 128 | 129 | /* do a probe with filler on stack */ 130 | fill(&p); 131 | /* do a probe without filler */ 132 | boundlow(&p); 133 | _infer_jmpbuf_offsets(&p); 134 | } 135 | 136 | #endif /*__CTXT_H__*/ 137 | -------------------------------------------------------------------------------- /libconcurrency/libconcurrency.vcproj: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 14 | 15 | 16 | 17 | 18 | 26 | 29 | 32 | 35 | 38 | 43 | 56 | 59 | 65 | 68 | 76 | 79 | 82 | 85 | 88 | 91 | 94 | 97 | 100 | 101 | 110 | 113 | 116 | 119 | 122 | 127 | 136 | 139 | 145 | 148 | 158 | 161 | 164 | 167 | 170 | 173 | 176 | 179 | 182 | 183 | 184 | 185 | 186 | 187 | 192 | 195 | 196 | 197 | 202 | 205 | 206 | 209 | 210 | 213 | 214 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | -------------------------------------------------------------------------------- /libconcurrency/stdint.h: -------------------------------------------------------------------------------- 1 | /* 2 | * C99 header that Windows neglects. I might eventually replace this with 3 | * one of the following: 4 | * http://www.azillionmonkeys.com/qed/pstdint.h 5 | * http://msinttypes.googlecode.com/svn/trunk/stdint.h 6 | */ 7 | 8 | #ifndef __STDINT_H__ 9 | #define __STDINT_H__ 10 | 11 | #include 12 | 13 | #endif /*__STDINT_H__*/ 14 | -------------------------------------------------------------------------------- /libconcurrency/tls.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Compiler-provided Thread-Local Storage 3 | * TLS declarations for various compilers: 4 | * http://en.wikipedia.org/wiki/Thread-local_storage 5 | */ 6 | 7 | #ifndef __TLS_H__ 8 | #define __TLS_H__ 9 | 10 | /* Each #ifdef must define 11 | * THREAD_LOCAL 12 | * EXPORT 13 | */ 14 | 15 | #ifdef _MSC_VER 16 | 17 | #define WIN32_LEAN_AND_MEAN 18 | #include 19 | #define THREAD_LOCAL __declspec(thread) 20 | #define EXPORT __declspec(dllexport) 21 | 22 | #else 23 | /* assume gcc or compatible compiler */ 24 | #define THREAD_LOCAL __thread 25 | #define EXPORT 26 | 27 | #endif /* _MSC_VER */ 28 | 29 | #endif /*__TLS_H__*/ 30 | --------------------------------------------------------------------------------