├── 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 |
--------------------------------------------------------------------------------