├── README.md ├── green_threads.cpp ├── green_threads.h ├── green_threads.vcxproj └── green_threads_test.cpp /README.md: -------------------------------------------------------------------------------- 1 | green_threads 2 | ============= 3 | 4 | C++ Green threads and coroutines library 5 | 6 | **Motivation:** 7 | * Write faster multithreaded programs by utilizing reduced context switch time. 8 | * Write shorter and more elegant programs by using the yield functionality that is modeled after the python yield keyword and co-routines. 9 | 10 | **Getting started:** 11 | look at the very short [api](https://github.com/yigalirani/green_threads/blob/master/green_threads.h) and [example program](https://github.com/yigalirani/green_threads/blob/master/green_threads_test.cpp) 12 | 13 | **Future development:** 14 | * Integrate the library with IO functionality like sockets and files 15 | * Further improve speed . 16 | * Add dynamic number of green threads. 17 | * Port to non msvc/windows compilers and operating systems. 18 | * Port to non-x86 processors such as arm. 19 | 20 | **How does it work:** 21 | 22 | The library explicitly allocate runtime stack for each green thread. The gt_yield function switches the current green thread by manipulating, using assembler, the esp and ebp registers who point the current runtime stack. 23 | -------------------------------------------------------------------------------- /green_threads.cpp: -------------------------------------------------------------------------------- 1 | #include "green_threads.h" 2 | #define STACK_SIZE (1024*64) 3 | #define MAX_GT (16) 4 | #pragma runtime_checks( "[runtime_checks]", off ) 5 | enum GtStatus{ 6 | gts_zomby, 7 | gts_ready, 8 | gts_posted, 9 | gts_new, 10 | }; 11 | class GreenThread{ 12 | public: 13 | char stack[STACK_SIZE]; 14 | int stack_end;//stack grows back from here 15 | void *data; 16 | GtCallback cb; 17 | GtStatus status; 18 | bool posted; 19 | unsigned esp; 20 | unsigned ebp; 21 | unsigned tid; 22 | }; 23 | unsigned gs_num_threads; 24 | unsigned gs_cur_thread; 25 | GreenThread *gs_threads[MAX_GT]; 26 | GreenThread *gs_next_thread; 27 | 28 | GreenThread *add_thread(){ 29 | auto ans=new GreenThread; 30 | ans->data=0; 31 | ans->cb=0; 32 | ans->status=gts_new; 33 | ans->posted=false; 34 | ans->esp=0; 35 | ans->tid=gs_num_threads; 36 | gs_threads[gs_num_threads]=ans; 37 | gs_num_threads++; 38 | return ans; 39 | } 40 | int gt_start(GtCallback cb,void *data){ 41 | auto gt=add_thread(); 42 | gt->cb=cb; 43 | gt->data=data; 44 | gt->esp=(unsigned)&(gt->stack_end); 45 | gt->ebp=0; 46 | return gt->tid; 47 | } 48 | void gt_init(){ 49 | auto gt=add_thread(); 50 | gt->status=gts_ready; 51 | gs_cur_thread=0; 52 | } 53 | void run_new_thread(){ 54 | gs_next_thread->status=gts_ready; 55 | gs_next_thread->cb(gs_next_thread->data); 56 | gs_next_thread->status=gts_zomby; 57 | gt_yield(false);//thats pure gold right there 58 | } 59 | bool gt_next(int tid){ 60 | auto gt = gs_threads[tid]; 61 | while(true){ 62 | switch(gt->status){ 63 | case gts_new: 64 | case gts_ready: 65 | gt_yield(false); 66 | continue; 67 | case gts_posted: 68 | gt->status=gts_ready; 69 | return true; 70 | case gts_zomby: 71 | return false; 72 | } 73 | } 74 | return true; 75 | } 76 | unsigned get_next_thread_id(){ 77 | for (unsigned i=gs_cur_thread+1;i != gs_cur_thread;i++){ 78 | if (i>=gs_num_threads) 79 | i=0; 80 | auto the_status=gs_threads[i]->status; 81 | if (the_status==gts_ready || the_status==gts_new) 82 | return i; 83 | } 84 | return gs_cur_thread; 85 | } 86 | __declspec(noinline) void gt_yield(bool posted){ 87 | auto gt = gs_threads[gs_cur_thread]; 88 | if (posted) 89 | gt->status=gts_posted; 90 | unsigned next_tid=get_next_thread_id(); 91 | if (next_tid==gs_cur_thread) 92 | return; 93 | gs_next_thread=gs_threads[next_tid]; 94 | gs_cur_thread=next_tid; 95 | unsigned cur_esp; 96 | unsigned cur_ebp; 97 | _asm{ 98 | mov cur_esp,esp 99 | mov cur_ebp,ebp 100 | } 101 | gt->esp=cur_esp; 102 | gt->ebp=cur_ebp; 103 | int next_esp=gs_next_thread->esp; 104 | int next_ebp=gs_next_thread->ebp; 105 | __asm{ 106 | mov esp,next_esp 107 | mov ebp,next_ebp 108 | } 109 | if (gs_next_thread->status==gts_new) 110 | run_new_thread(); 111 | } 112 | -------------------------------------------------------------------------------- /green_threads.h: -------------------------------------------------------------------------------- 1 | typedef void (__cdecl*GtCallback)(void *data); //implement grean thread by implementing this callback 2 | void gt_init(); //initialize the library 3 | int gt_start(GtCallback cb,void *data); //start a new green thread. the data variable is passed the callback 4 | bool gt_next(int gtid); //used it coroutines to fetch the next yielded value 5 | void gt_yield(bool posted); //give up execution of the current thread. optionaly post a value to 6 | -------------------------------------------------------------------------------- /green_threads.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | 14 | {BDE7DA7A-D4BA-44A4-80D4-C9FE043A0619} 15 | Win32Proj 16 | green_threads 17 | 18 | 19 | 20 | Application 21 | true 22 | v110 23 | Unicode 24 | 25 | 26 | Application 27 | false 28 | v110 29 | true 30 | Unicode 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | true 44 | 45 | 46 | false 47 | 48 | 49 | 50 | 51 | 52 | Level3 53 | Disabled 54 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 55 | 56 | 57 | Console 58 | true 59 | 60 | 61 | 62 | 63 | Level3 64 | 65 | 66 | MaxSpeed 67 | true 68 | true 69 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 70 | AnySuitable 71 | true 72 | 73 | 74 | Console 75 | true 76 | true 77 | true 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /green_threads_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "green_threads.h" 3 | struct Enumerate{ 4 | unsigned start; 5 | unsigned end; 6 | unsigned cur; 7 | }; 8 | void enumerate_integers(void *data){ 9 | auto e = (Enumerate *)data; 10 | unsigned end=e->end; 11 | for (e->cur=e->start;e->curcur++) 12 | gt_yield(true); 13 | } 14 | void print_integrers(int start,int end){ 15 | Enumerate e; 16 | e.start=start; 17 | e.end=end; 18 | int tid=gt_start(enumerate_integers,&e); 19 | while(gt_next(tid)) 20 | if (e.cur%10000000==0) 21 | printf("%d\n",e.cur); 22 | } 23 | //this test program create one green thread. It enumerate though a range integers and post each using the gt_yield 24 | void main(){ 25 | gt_init(); 26 | print_integrers(10,100000000); 27 | } 28 | --------------------------------------------------------------------------------