Commit | Line | Data |
---|---|---|
3719602c PC |
1 | /* |
2 | libco.x86 (2009-10-12) | |
3 | author: byuu | |
4 | license: public domain | |
5 | */ | |
6 | ||
7 | #define LIBCO_C | |
8 | #include <libco.h> | |
9 | #include <assert.h> | |
10 | #include <stdlib.h> | |
11 | ||
12 | #ifdef __cplusplus | |
13 | extern "C" { | |
14 | #endif | |
15 | ||
16 | #if defined(_MSC_VER) | |
17 | #define fastcall __fastcall | |
18 | #elif defined(__GNUC__) | |
19 | #define fastcall __attribute__((fastcall)) | |
20 | #else | |
21 | #error "libco: please define fastcall macro" | |
22 | #endif | |
23 | ||
24 | static thread_local long co_active_buffer[64]; | |
25 | static thread_local cothread_t co_active_handle = 0; | |
26 | static void (fastcall *co_swap)(cothread_t, cothread_t) = 0; | |
27 | ||
28 | //ABI: fastcall | |
29 | static unsigned char co_swap_function[] = { | |
30 | 0x89, 0x22, /* mov [edx],esp */ | |
31 | 0x8b, 0x21, /* mov esp,[ecx] */ | |
32 | 0x58, /* pop eax */ | |
33 | 0x89, 0x6a, 0x04, /* mov [edx+0x04],ebp */ | |
34 | 0x89, 0x72, 0x08, /* mov [edx+0x08],esi */ | |
35 | 0x89, 0x7a, 0x0c, /* mov [edx+0x0c],edi */ | |
36 | 0x89, 0x5a, 0x10, /* mov [edx+0x10],ebx */ | |
37 | 0x8b, 0x69, 0x04, /* mov ebp,[ecx+0x04] */ | |
38 | 0x8b, 0x71, 0x08, /* mov esi,[ecx+0x08] */ | |
39 | 0x8b, 0x79, 0x0c, /* mov edi,[ecx+0x0c] */ | |
40 | 0x8b, 0x59, 0x10, /* mov ebx,[ecx+0x10] */ | |
41 | 0xff, 0xe0, /* jmp eax */ | |
42 | }; | |
43 | ||
44 | #ifdef _WIN32 | |
45 | #include <windows.h> | |
46 | ||
47 | static void co_init(void) | |
48 | { | |
49 | DWORD old_privileges; | |
50 | VirtualProtect(co_swap_function, | |
51 | sizeof co_swap_function, PAGE_EXECUTE_READWRITE, &old_privileges); | |
52 | } | |
53 | #else | |
54 | #include <unistd.h> | |
55 | #include <sys/mman.h> | |
56 | ||
57 | static void co_init(void) | |
58 | { | |
59 | unsigned long addr = (unsigned long)co_swap_function; | |
60 | unsigned long base = addr - (addr % sysconf(_SC_PAGESIZE)); | |
61 | unsigned long size = (addr - base) + sizeof co_swap_function; | |
62 | mprotect((void*)base, size, PROT_READ | PROT_WRITE | PROT_EXEC); | |
63 | } | |
64 | #endif | |
65 | ||
66 | static void crash(void) | |
67 | { | |
68 | assert(0); /* called only if cothread_t entrypoint returns */ | |
69 | } | |
70 | ||
71 | cothread_t co_active(void) | |
72 | { | |
73 | if (!co_active_handle) | |
74 | co_active_handle = &co_active_buffer; | |
75 | return co_active_handle; | |
76 | } | |
77 | ||
78 | cothread_t co_create(unsigned int size, void (*entrypoint)(void)) | |
79 | { | |
80 | cothread_t handle; | |
81 | if (!co_swap) | |
82 | { | |
83 | co_init(); | |
84 | co_swap = (void (fastcall*)(cothread_t, cothread_t))co_swap_function; | |
85 | } | |
86 | ||
87 | if (!co_active_handle) | |
88 | co_active_handle = &co_active_buffer; | |
89 | ||
90 | size += 256; /* allocate additional space for storage */ | |
91 | size &= ~15; /* align stack to 16-byte boundary */ | |
92 | ||
93 | if ((handle = (cothread_t)malloc(size))) | |
94 | { | |
95 | long *p = (long*)((char*)handle + size); /* seek to top of stack */ | |
96 | *--p = (long)crash; /* crash if entrypoint returns */ | |
97 | *--p = (long)entrypoint; /* start of function */ | |
98 | *(long*)handle = (long)p; /* stack pointer */ | |
99 | } | |
100 | ||
101 | return handle; | |
102 | } | |
103 | ||
104 | void co_delete(cothread_t handle) | |
105 | { | |
106 | free(handle); | |
107 | } | |
108 | ||
109 | void co_switch(cothread_t handle) | |
110 | { | |
111 | register cothread_t co_previous_handle = co_active_handle; | |
112 | co_swap(co_active_handle = handle, co_previous_handle); | |
113 | } | |
114 | ||
115 | #ifdef __cplusplus | |
116 | } | |
117 | #endif |