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