Code refactoring.
[lua-llthreads2.git] / src / llthread.c
1 #if !defined(_WIN32) && !defined(USE_PTHREAD)
2 #  define USE_PTHREAD
3 #endif
4
5 #ifndef USE_PTHREAD
6 #  include <windows.h>
7 #  include <process.h>
8 #else
9 #  include <pthread.h>
10 #endif
11
12 #include <stdlib.h>
13 #include <stdio.h>
14 #include <memory.h>
15 #include <assert.h>
16 #include <errno.h>
17 #include <lualib.h>
18 #include "l52util.h"
19 #include "traceback.inc"
20 #include "copy.inc"
21
22 /*export*/
23 #ifdef _WIN32
24 #  define LLTHREADS_EXPORT_API __declspec(dllexport)
25 #else
26 #  define LLTHREADS_EXPORT_API LUALIB_API
27 #endif
28
29 /* wrap strerror_s(). */
30 #ifdef _WIN32
31 #  ifdef __GNUC__
32 #    ifndef strerror_r
33 #      define strerror_r(errno, buf, buflen) do { \
34          strncpy((buf), strerror(errno), (buflen)-1); \
35          (buf)[(buflen)-1] = '\0'; \
36        } while(0)
37 #    endif
38 #  else
39 #    ifndef strerror_r
40 #      define strerror_r(errno, buf, buflen) strerror_s((buf), (buflen), (errno))
41 #    endif
42 #  endif
43 #endif
44
45 #ifndef USE_PTHREAD
46 #  define OS_THREAD_RETURN      unsigned int __stdcall
47 #  define INVALID_THREAD        INVALID_HANDLE_VALUE
48 #  define INFINITE_JOIN_TIMEOUT INFINITE
49 #  define JOIN_OK               0
50 #  define JOIN_ETIMEDOUT        1
51 #  define JOIN_FAIL             2
52 typedef DWORD  join_timeout_t;
53 typedef HANDLE os_thread_t;
54 #else
55 #  define OS_THREAD_RETURN      void *
56 #  define INFINITE_JOIN_TIMEOUT -1
57 #  define JOIN_OK               0
58 #  define JOIN_ETIMEDOUT        ETIMEDOUT
59 typedef int       join_timeout_t;
60 typedef pthread_t os_thread_t;
61 #endif
62
63 #define ERROR_LEN 1024
64
65 #define flags_t unsigned char
66
67 #define FLAG_NONE      (flags_t)0
68 #define FLAG_STARTED   (flags_t)1<<0
69 #define FLAG_DETACHED  (flags_t)1<<1
70 #define FLAG_JOINED    (flags_t)1<<2
71 #define FLAG_JOINABLE  (flags_t)1<<3
72
73 /*At least one flag*/
74 #define FLAG_IS_SET(O, F) (O->flags & (flags_t)(F))
75 #define FLAG_SET(O, F) O->flags |= (flags_t)(F)
76 #define FLAG_UNSET(O, F) O->flags &= ~((flags_t)(F))
77 #define IS(O, F) FLAG_IS_SET(O, FLAG_##F)
78 #define SET(O, F) FLAG_SET(O, FLAG_##F)
79
80 #define ALLOC_STRUCT(S) (S*)calloc(1, sizeof(S))
81 #define FREE_STRUCT(O) free(O)
82
83 LLTHREADS_EXPORT_API int luaopen_llthreads(lua_State *L);
84
85 #define LLTHREAD_T_NAME "LLThread"
86 static const char *LLTHREAD_T = LLTHREAD_T_NAME;
87 static const char *LLTHREAD_LOGGER_HOLDER = LLTHREAD_T_NAME " logger holder";
88
89 typedef struct llthread_child_t {
90   lua_State  *L;
91   int        status;
92   flags_t    flags;
93 } llthread_child_t;
94
95 typedef struct llthread_t {
96   llthread_child_t *child;
97   os_thread_t       thread;
98   flags_t           flags;
99 } llthread_t;
100
101 static int fail(lua_State *L, const char *msg){
102   lua_pushnil(L);
103   lua_pushstring(L, msg);
104   return 2;
105 }
106
107 //{ logger interface
108 void llthread_log(lua_State *L, const char *hdr, const char *msg){
109   int top = lua_gettop(L);
110   lua_rawgetp(L, LUA_REGISTRYINDEX, LLTHREAD_LOGGER_HOLDER);
111   if(lua_isnil(L, -1)){
112     lua_pop(L, 1);
113     fputs(hdr,  stderr);
114     fputs(msg,  stderr);
115     fputc('\n', stderr);
116     fflush(stderr);
117     return;
118   }
119   lua_pushstring(L, hdr);
120   lua_pushstring(L, msg);
121   lua_concat(L, 2);
122   lua_pcall(L, 1, 0, 0);
123   lua_settop(L, top);
124 }
125 //}
126
127 //{ llthread_child
128
129 static void open_thread_libs(lua_State *L){
130 #define L_REGLIB(L, name) lua_pushcfunction(L, luaopen_##name); lua_setfield(L, -2)
131
132   int top = lua_gettop(L);
133
134 #ifndef LLTHREAD_REGISTER_STD_LIBRARY
135
136   luaL_openlibs(L);
137   lua_getglobal(L, "package"); lua_getfield(L, -1, "preload"); lua_remove(L, -2);
138
139 #else
140
141   lutil_require(L, "_G",      luaopen_base,    1);
142   lutil_require(L, "package", luaopen_package, 1);
143   lua_settop(L, top);
144
145   /* get package.preload */
146   lua_getglobal(L, "package"); lua_getfield(L, -1, "preload"); lua_remove(L, -2);
147   L_REGLIB(L, io,        1);
148   L_REGLIB(L, os,        1);
149   L_REGLIB(L, math,      1);
150   L_REGLIB(L, table,     1);
151   L_REGLIB(L, string,    1);
152
153 #ifdef LUA_DBLIBNAME
154   L_REGLIB(L, debug,     1);
155 #endif
156
157   /* @fixme find out luaopen_XXX at runtime */
158 #ifdef LUA_JITLIBNAME
159   L_REGLIB(L, bit,        1);
160   L_REGLIB(L, jit,        1);
161   L_REGLIB(L, ffi,        1);
162 #elif defined LUA_BITLIBNAME
163   L_REGLIB(L, bit32,      1);
164 #endif
165
166 #endif
167
168 #ifdef LLTHREAD_REGISTER_THREAD_LIBRARY
169   L_REGLIB(L, llthreads, 0);
170 #endif
171
172   lua_settop(L, top);
173
174 #undef L_REGLIB
175 }
176
177 static llthread_child_t *llthread_child_new() {
178   llthread_child_t *this = ALLOC_STRUCT(llthread_child_t);
179   if(!this) return NULL;
180
181   memset(this, 0, sizeof(llthread_child_t));
182
183   /* create new lua_State for the thread.             */
184   /* open standard libraries.                         */
185   this->L = luaL_newstate();
186   open_thread_libs(this->L);
187
188   return this;
189 }
190
191 static void llthread_child_destroy(llthread_child_t *this) {
192   lua_close(this->L);
193   FREE_STRUCT(this);
194 }
195
196 static OS_THREAD_RETURN llthread_child_thread_run(void *arg) {
197   llthread_child_t *this = (llthread_child_t *)arg;
198   lua_State *L = this->L;
199   int nargs = lua_gettop(L) - 1;
200
201   /* push traceback function as first value on stack. */
202   lua_pushcfunction(this->L, traceback); 
203   lua_insert(L, 1);
204
205   this->status = lua_pcall(L, nargs, LUA_MULTRET, 1);
206
207   /* alwasy print errors here, helps with debugging bad code. */
208   if(this->status != 0) {
209     llthread_log(L, "Error from thread: ", lua_tostring(L, -1));
210   }
211
212   if(IS(this, DETACHED) || !IS(this, JOINABLE)) {
213     /* thread is detached, so it must clean-up the child state. */
214     llthread_child_destroy(this);
215     this = NULL;
216   }
217
218 #ifndef USE_PTHREAD
219   if(this) {
220     /* attached thread, don't close thread handle. */
221     _endthreadex(0);
222   } else {
223     /* detached thread, close thread handle. */
224     _endthread();
225   }
226   return 0;
227 #else
228   return this;
229 #endif
230 }
231
232 //}
233
234 //{ llthread
235
236 static void llthread_validate(llthread_t *this){
237   /* describe valid state of llthread_t object 
238    * from after create and before destroy
239    */
240   if(!IS(this, STARTED)){
241     assert(!IS(this, DETACHED));
242     assert(!IS(this, JOINED));
243     assert(!IS(this, JOINABLE));
244     return;
245   }
246
247   if(IS(this, DETACHED)){
248     if(!IS(this, JOINABLE)) assert(this->child == NULL);
249     else assert(this->child != NULL);
250   }
251 }
252
253 static int llthread_detach(llthread_t *this);
254
255 static int llthread_join(llthread_t *this, join_timeout_t timeout);
256
257 static llthread_t *llthread_new() {
258   llthread_t *this = ALLOC_STRUCT(llthread_t);
259   if(!this) return NULL;
260
261   this->flags  = FLAG_NONE;
262 #ifndef USE_PTHREAD
263   this->thread = INVALID_THREAD;
264 #endif
265   this->child  = llthread_child_new();
266   if(!this->child){
267     FREE_STRUCT(this);
268     return NULL;
269   }
270
271   return this;
272 }
273
274 static void llthread_cleanup_child(llthread_t *this) {
275   if(this->child) {
276     llthread_child_destroy(this->child);
277     this->child = NULL;
278   }
279 }
280
281 static void llthread_destroy(llthread_t *this) {
282   do{
283     /* thread not started */
284     if(!IS(this, STARTED)){
285       llthread_cleanup_child(this);
286       break;
287     }
288
289     /* DETACHED */
290     if(IS(this, DETACHED)){
291       if(IS(this, JOINABLE)){
292         llthread_detach(this);
293       }
294       break;
295     }
296
297     /* ATACHED */
298     if(!IS(this, JOINED)){
299       llthread_join(this, INFINITE_JOIN_TIMEOUT);
300       if(!IS(this, JOINED)){
301         /* @todo use current lua state to logging */
302         /*
303           * char buf[ERROR_LEN];
304           * strerror_r(errno, buf, ERROR_LEN);
305           * llthread_log(L, "Error can not join thread on gc: ", buf);
306           */
307       }
308     }
309     if(IS(this, JOINABLE)){
310       llthread_cleanup_child(this);
311     }
312
313   }while(0);
314
315   FREE_STRUCT(this);
316 }
317
318 static int llthread_push_args(lua_State *L, llthread_child_t *child, int idx, int top) {
319   return llthread_copy_values(L, child->L, idx, top, 1 /* is_arg */);
320 }
321
322 static int llthread_push_results(lua_State *L, llthread_child_t *child, int idx, int top) {
323   return llthread_copy_values(child->L, L, idx, top, 0 /* is_arg */);
324 }
325
326 static int llthread_detach(llthread_t *this){
327   int rc = 0;
328
329   assert(IS(this, STARTED));
330   assert(this->child != NULL);
331
332   /*we can not detach joined thread*/
333   if(IS(this, JOINED))
334     return 0;
335
336   this->child = NULL;
337 #ifdef USE_PTHREAD
338   rc = pthread_detach(this->thread);
339 #else
340   assert(this->thread != INVALID_THREAD);
341   CloseHandle(this->thread);
342   this->thread = INVALID_THREAD;
343 #endif
344   return rc;
345 }
346
347 /*   | detached | joinable ||    join    | which thread  |  gc    | detach  |
348  *   |          |          ||   return   | destroy child | calls  |   on    |
349  *   ------------------------------------------------------------------------
350  *   |   false  |   falas  ||  <NONE>    |    child      |  join  | <NEVER> |
351  *  *|   false  |   true   || Lua values |    parent     |  join  | <NEVER> |
352  *  *|   true   |   false  ||  <ERROR>   |    child      | <NONE> |  start  |
353  *   |   true   |   true   ||  <NONE>    |    child      | detach |   gc    |
354  *   ------------------------------------------------------------------------
355  *   * llthread behavior.
356  */
357 static int llthread_start(llthread_t *this, int start_detached, int joinable) {
358   llthread_child_t *child = this->child;
359   int rc = 0;
360
361   llthread_validate(this);
362
363   if(joinable) SET(child, JOINABLE);
364   if(start_detached) SET(child, DETACHED);
365
366 #ifndef USE_PTHREAD
367   this->thread = (HANDLE)_beginthreadex(NULL, 0, llthread_child_thread_run, child, 0, NULL);
368   if(INVALID_THREAD == this->thread){
369     rc = -1;
370   }
371 #else
372   rc = pthread_create(&(this->thread), NULL, llthread_child_thread_run, child);
373 #endif
374
375   if(rc == 0) {
376     SET(this, STARTED);
377     if(joinable) SET(this, JOINABLE);
378     if(start_detached) SET(this, DETACHED);
379     if((start_detached)&&(!joinable)){
380       rc = llthread_detach(this);
381     }
382   }
383
384   llthread_validate(this);
385
386   return rc;
387 }
388
389 static int llthread_join(llthread_t *this, join_timeout_t timeout) {
390   llthread_validate(this);
391
392   if(IS(this, JOINED)){
393     return JOIN_OK;
394   } else{
395 #ifndef USE_PTHREAD
396   DWORD ret = 0;
397   if(INVALID_THREAD == this->thread) return JOIN_OK;
398   ret = WaitForSingleObject( this->thread, timeout );
399   if( ret == WAIT_OBJECT_0){ /* Destroy the thread object. */
400     CloseHandle( this->thread );
401     this->thread = INVALID_THREAD;
402     SET(this, JOINED);
403
404     llthread_validate(this);
405
406     return JOIN_OK;
407   }
408   else if( ret == WAIT_TIMEOUT ){
409     return JOIN_ETIMEDOUT;
410   }
411   return JOIN_FAIL;
412 #else
413   int rc;
414   if(timeout == 0){
415     rc = pthread_kill(this->thread, 0);
416     if(rc == 0){ /* still alive */
417       return JOIN_ETIMEDOUT;
418     }
419
420     if(rc != ESRCH){ 
421       /*@fixme what else it can be ?*/
422       return rc;
423     }
424
425     /*thread dead so we call join to free pthread_t struct */
426   }
427
428   /* @todo use pthread_tryjoin_np/pthread_timedjoin_np to support timeout */
429
430   /* then join the thread. */
431   rc = pthread_join(this->thread, NULL);
432   if((rc == 0) || (rc == ESRCH)) {
433     SET(this, JOINED);
434     rc = JOIN_OK;
435   }
436
437   llthread_validate(this);
438
439   return rc;
440 #endif
441   }
442 }
443
444 static llthread_t *llthread_create(lua_State *L, const char *code, size_t code_len) {
445   llthread_t *this        = llthread_new();
446   llthread_child_t *child = this->child;
447
448   /* load Lua code into child state. */
449   int rc = luaL_loadbuffer(child->L, code, code_len, code);
450   if(rc != 0) {
451     /* copy error message to parent state. */
452     size_t len; const char *str = lua_tolstring(child->L, -1, &len);
453     if(str != NULL) {
454       lua_pushlstring(L, str, len);
455     } else {
456       /* non-string error message. */
457       lua_pushfstring(L, "luaL_loadbuffer() failed to load Lua code: rc=%d", rc);
458     }
459     llthread_destroy(this);
460     lua_error(L);
461     return NULL;
462   }
463
464   /* copy extra args from main state to child state. */
465   /* Push all args after the Lua code. */
466   llthread_push_args(L, child, 3, lua_gettop(L));
467
468   llthread_validate(this);
469
470   return this;
471 }
472
473 //}
474
475 //{ Lua interface to llthread
476
477 static llthread_t *l_llthread_at (lua_State *L, int i) {
478   llthread_t **this = (llthread_t **)lutil_checkudatap (L, i, LLTHREAD_T);
479   luaL_argcheck (L,  this != NULL, i, "thread expected");
480   luaL_argcheck (L, *this != NULL, i, "thread expected");
481   // luaL_argcheck (L, !(counter->flags & FLAG_DESTROYED), 1, "PDH Counter is destroyed");
482   return *this;
483 }
484
485 static int l_llthread_delete(lua_State *L) {
486   llthread_t **pthis = (llthread_t **)lutil_checkudatap (L, 1, LLTHREAD_T);
487   luaL_argcheck (L, pthis != NULL, 1, "thread expected");
488   if(*pthis == NULL) return 0;
489   llthread_destroy(*pthis);
490   *pthis = NULL;
491
492   return 0;
493 }
494
495 static int l_llthread_start(lua_State *L) {
496   llthread_t *this = l_llthread_at(L, 1);
497   int start_detached = lua_toboolean(L, 2);
498   int joinable, rc;
499
500   if(!lua_isnone(L, 3)) joinable = lua_toboolean(L, 3);
501   else joinable = start_detached ? 0 : 1;
502
503   if(IS(this, STARTED)) {
504     return fail(L, "Thread already started.");
505   }
506
507   rc = llthread_start(this, start_detached, joinable);
508   if(rc != 0) {
509     char buf[ERROR_LEN];
510     strerror_r(errno, buf, ERROR_LEN);
511     return fail(L, buf);
512   }
513
514   lua_settop(L, 1); // return this
515   return 1;
516 }
517
518 static int l_llthread_join(lua_State *L) {
519   llthread_t *this = l_llthread_at(L, 1);
520   llthread_child_t *child = this->child;
521   int rc;
522
523   if(!IS(this, STARTED )) {
524     return fail(L, "Can't join a thread that hasn't be started.");
525   }
526   if( IS(this, DETACHED) && !IS(this, JOINABLE)) {
527     return fail(L, "Can't join a thread that has been detached.");
528   }
529   if( IS(this, JOINED  )) {
530     return fail(L, "Can't join a thread that has already been joined.");
531   }
532
533   /* join the thread. */
534   rc = llthread_join(this, luaL_optint(L, 2, INFINITE_JOIN_TIMEOUT));
535
536   if(child && IS(this, JOINED)) {
537     int top;
538
539     if(IS(this, DETACHED) || !IS(this, JOINABLE)){
540       /*child lua state has been destroyed by child thread*/
541       /*@todo return thread exit code*/
542       lua_pushboolean(L, 1);
543       lua_pushnumber(L, 0);
544       return 2;
545     }
546     
547     /* copy values from child lua state */
548     if(child->status != 0) {
549       const char *err_msg = lua_tostring(child->L, -1);
550       lua_pushboolean(L, 0);
551       lua_pushfstring(L, "Error from child thread: %s", err_msg);
552       top = 2;
553     } else {
554       lua_pushboolean(L, 1);
555       top = lua_gettop(child->L);
556       /* return results to parent thread. */
557       llthread_push_results(L, child, 2, top);
558     }
559
560     llthread_cleanup_child(this);
561     return top;
562   }
563
564   if( rc == JOIN_ETIMEDOUT ){
565     return fail(L, "timeout");
566   } 
567
568   {
569     char buf[ERROR_LEN];
570     strerror_r(errno, buf, ERROR_LEN);
571
572     /* llthread_cleanup_child(this); */
573
574     return fail(L, buf);
575   }
576
577 }
578
579 static int l_llthread_new(lua_State *L) {
580   size_t lua_code_len; const char *lua_code = luaL_checklstring(L, 1, &lua_code_len);
581   llthread_t **this = lutil_newudatap(L, llthread_t*, LLTHREAD_T);
582   lua_insert(L, 2); /*move self prior args*/
583   *this = llthread_create(L, lua_code, lua_code_len);
584
585   lua_settop(L, 2);
586   return 1;
587 }
588
589 static const struct luaL_Reg l_llthread_meth[] = {
590   {"start",         l_llthread_start         },
591   {"join",          l_llthread_join          },
592   {"__gc",          l_llthread_delete        },
593
594   {NULL, NULL}
595 };
596
597 //}
598
599 static int l_llthread_set_logger(lua_State *L){
600   lua_settop(L, 1);
601   luaL_argcheck(L, lua_isfunction(L, 1), 1, "function expected");
602   lua_rawsetp(L, LUA_REGISTRYINDEX, LLTHREAD_LOGGER_HOLDER);
603   return 0;
604 }
605
606 static const struct luaL_Reg l_llthreads_lib[] = {
607   {"new",           l_llthread_new           },
608   {"set_logger",    l_llthread_set_logger    },
609
610   {NULL, NULL}
611 };
612
613 LLTHREADS_EXPORT_API int luaopen_llthreads(lua_State *L) {
614   int top = lua_gettop(L);
615   lutil_createmetap(L, LLTHREAD_T,   l_llthread_meth,   0);
616   lua_settop(L, top);
617
618   lua_newtable(L);
619   luaL_setfuncs(L, l_llthreads_lib, 0);
620   return 1;
621 }