aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/src/libraries/Runnr.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libraries/Runnr.c')
-rw-r--r--src/libraries/Runnr.c593
1 files changed, 559 insertions, 34 deletions
diff --git a/src/libraries/Runnr.c b/src/libraries/Runnr.c
index 72bc523..c905ce9 100644
--- a/src/libraries/Runnr.c
+++ b/src/libraries/Runnr.c
@@ -1,62 +1,139 @@
1/* Runnr - a library that deals with running Lua scripts. 1/* Runnr - a library that deals with running Lua scripts.
2
3*/ 2*/
4 3
5 4
5#include "LumbrJack.h"
6#include "Runnr.h" 6#include "Runnr.h"
7 7
8 8
9static Ecore_Idle_Enterer *enterer;
10static Eina_Clist scripts;
11
12static int _send(lua_State *L);
13static int _receive(lua_State *L);
14static void _cancel(void *data, Ecore_Thread *thread);
15
16
17static const struct luaL_reg runnrFunctions[] =
18{
19 { "send", _send },
20 { "receive", _receive },
21 { NULL, NULL }
22};
23
24
9void dumpStack(lua_State *L, int i) 25void dumpStack(lua_State *L, int i)
10{ 26{
11 int type = lua_type(L, i); 27 int type = lua_type(L, i);
28 const char *t = lua_typename(L, type);
12 29
30 printf("Stack %d is %s", i, t);
13 switch (type) 31 switch (type)
14 { 32 {
15 case LUA_TNONE : printf("Stack %d is empty\n", i); break; 33 case LUA_TNONE : break;
16 case LUA_TNIL : printf("Stack %d is a nil\n", i); break; 34 case LUA_TNIL : break;
17 case LUA_TBOOLEAN : printf("Stack %d is a boolean - %d\n", i, lua_toboolean(L, i)); break; 35 case LUA_TBOOLEAN : printf(" - %d", lua_toboolean(L, i)); break;
18 case LUA_TNUMBER : printf("Stack %d is a number\n - %f", i, lua_tonumber(L, i)); break; 36 case LUA_TNUMBER : printf(" - %f", lua_tonumber(L, i)); break;
19 case LUA_TSTRING : printf("Stack %d is a string - %s\n", i, lua_tostring(L, i)); break; 37 case LUA_TSTRING : printf(" - %s", lua_tostring(L, i)); break;
20 case LUA_TFUNCTION : printf("Stack %d is a function\n", i); break; 38 case LUA_TFUNCTION : break;
21 case LUA_TTHREAD : printf("Stack %d is a thread\n", i); break; 39 case LUA_TTHREAD : break;
22 case LUA_TTABLE : 40 case LUA_TTABLE :
23 { 41 {
24 int j; 42 int j;
25 43
26 printf("Stack %d is a table", i);
27 lua_getfield(L, i, "_NAME"); 44 lua_getfield(L, i, "_NAME");
28 j = lua_gettop(L); 45 j = lua_gettop(L);
29 if (lua_isstring(L, j)) 46 if (lua_isstring(L, j))
30 printf(" - %s", lua_tostring(L, j)); 47 printf(" - %s", lua_tostring(L, j));
31 lua_pop(L, 1); 48 lua_pop(L, 1);
32 printf("\n");
33 break; 49 break;
34 } 50 }
35 case LUA_TUSERDATA : printf("Stack %d is a userdata\n", i); break; 51 case LUA_TUSERDATA : break;
36 case LUA_TLIGHTUSERDATA : printf("Stack %d is a light userdata\n", i); break; 52 case LUA_TLIGHTUSERDATA : break;
37 default : printf("Stack %d is unknown\n", i); break; 53 default : printf("- unknown!"); break;
38 } 54 }
55 printf("\n");
39} 56}
40 57
41static int traceBack(lua_State *L) 58static int traceBack(lua_State *L)
42{ 59{
43 const char *msg = ""; 60 lua_Debug ar;
44 int top = lua_gettop(L); 61 int i, top = lua_gettop(L), b = 1;
45// int i;
46 62
47// printf("Stack is %d deep\n", top); 63// printf("Stack is %d deep\n", top);
48// for (i = 1; i <= top; i++) 64// for (i = 1; i <= top; i++)
49// dumpStack(L, i); 65// dumpStack(L, i);
50 66
51 if (top) 67 if (top)
52 msg = lua_tostring(L, 1); 68 printf("Lua error - %s\n", lua_tostring(L, 1));
53 lua_getglobal(L, "debug"); 69
54 push_lua(L, "@ ( )", lua_gettop(L), "traceback", 1); 70 i = 0;
55 lua_pushstring(L, "\n"); 71 while (lua_getstack(L, i++, &ar))
56 lua_pushstring(L, msg); 72 {
57 lua_concat(L, 3); 73 if (lua_getinfo(L, "nSlu", &ar))
58 74 {
59 return 1; 75 if (NULL == ar.name)
76 ar.name = "DUNNO";
77 printf(" Lua backtrace %d - %s %s %s @ %s : %d\n", i, ar.what, ar.namewhat, ar.name, ar.short_src, ar.currentline);
78 b = 0;
79 }
80 else
81 printf(" Failed to get trace line!\n");
82 }
83
84 if (b)
85 printf(" NO BACKTRACE!\n");
86
87 return 0;
88}
89
90static void printLuaError(int err, char *string, lua_State *L)
91{
92 const char *err_type;
93
94 switch (err)
95 {
96 case LUA_ERRRUN: err_type = "runtime"; break;
97 case LUA_ERRSYNTAX: err_type = "syntax"; break;
98 case LUA_ERRMEM: err_type = "memory allocation"; break;
99 case LUA_ERRERR: err_type = "error handler"; break;
100 default: err_type = "unknown"; break;
101 }
102 printf("Error running - %s, \n %s - %s\n", string, err_type, lua_tostring(L, -1));
103}
104
105static int panics = 0;
106static int _panic(lua_State *L) // Stack usage [-0, +0, m]
107{
108 // If we somehow manage to have multiple panics, it's likely due to being out
109 // of memory in the following lua_tostring() call.
110 panics++;
111 if (panics)
112 printf("Lua PANICS!!!!!");
113 else
114 printf("Lua PANIC!!!!!: %s", lua_tostring(L, -1)); // Stack usage [-0, +0, m]
115 // The docs say that this will cause an exit(EXIT_FAILURE) if we return,
116 // and that we we should long jump some where to avoid that. This is only
117 // called for things not called from a protected environment. We always
118 // use pcalls though, except for the library load calls. If we can't load
119 // the standard libraries, then perhaps a crash is the right thing.
120 //
121 // The above is not true when we deal with the threaded stuff, that uses lua_resume().
122 return 0;
123}
124
125void printScriptsStatus()
126{
127 int active, pending_total, pending_feedback, pending_short, available;
128
129 active = ecore_thread_active_get();
130 pending_total = ecore_thread_pending_total_get();
131 pending_feedback = ecore_thread_pending_feedback_get();
132 pending_short = ecore_thread_pending_get();
133 available = ecore_thread_available_get();
134
135 printf("Scripts - active %d available %d pending short jobs %d pending feedback jobs %d pending total %d\n",
136 active, available, pending_short, pending_feedback, pending_total);
60} 137}
61 138
62void doLuaString(lua_State *L, char *string, char *module) 139void doLuaString(lua_State *L, char *string, char *module)
@@ -87,23 +164,452 @@ void doLuaString(lua_State *L, char *string, char *module)
87 } 164 }
88 } 165 }
89 166
167//printf("doLuaString(%s)\n", string);
90 if ((err = lua_pcall(L, 0, LUA_MULTRET, _T))) 168 if ((err = lua_pcall(L, 0, LUA_MULTRET, _T)))
169 printLuaError(err, string, L);
170 }
171}
172
173static void _stopScript(script *s)
174{
175 scriptMessage *sm0, *sm1;
176
177//printf("^^^^^^^^^^^^^^^^^^^_stop(, %s)\n", s->name);
178 if (s->L) lua_close(s->L); s->L = NULL;
179 if (s->timer) ecore_timer_del(s->timer); s->timer = NULL;
180 EINA_CLIST_FOR_EACH_ENTRY_SAFE(sm0, sm1, &(s->messages), scriptMessage, node)
181 {
182 eina_clist_remove(&(sm0->node));
183 free(sm0);
184 }
185 s->status = RUNNR_NOT_STARTED;
186}
187
188static void _workerFunction(void *data, Ecore_Thread *thread)
189{
190 script *s = data;
191 scriptMessage *msg = NULL;
192 const char *message = NULL;
193
194 takeScript(s);
195// if (RUNNR_FINISHED == s->status)
196// {
197// releaseScript(s);
198// return;
199// }
200
201 // The documentation is not clear on which thread is which inside and out,
202 // but states that at least for some they are different.
203 // So store the internal one as well.
204#if THREADIT
205 s->me = thread;
206#endif
207
208 if (RUNNR_RESET == s->status)
209 _stopScript(s);
210
211 if (RUNNR_READY == s->status)
212 {
213//printf("_workerFunction() READY %s\n", s->name);
214 if ((msg = (scriptMessage *) eina_clist_head(&(s->messages))))
91 { 215 {
92 const char *err_type; 216 eina_clist_remove(&(msg->node));
217 message = msg->message;
218 if (s->L)
219 s->status = RUNNR_RUNNING;
220 else
221 s->status = RUNNR_NOT_STARTED;
222 }
223 }
224
225 if (RUNNR_NOT_STARTED == s->status)
226 {
227 int err;
93 228
94 switch (err) 229//printf("_workerFunction() STARTING %s\n", s->name);
230 s->status = RUNNR_RUNNING;
231 s->L = luaL_newstate(); // Sets a standard allocator and panic function.
232
233 lua_atpanic(s->L, _panic);
234 // TODO - Set our allocator here.
235 luaL_openlibs(s->L);
236 luaL_register(s->L, "Runnr", runnrFunctions);
237
238 // Store the script struct in its own Lua state,
239 lua_pushlightuserdata(s->L, s);
240 lua_setfield(s->L, LUA_REGISTRYINDEX, "_SELF");
241
242 err = luaL_loadfile(s->L, s->binName);
243 if (err != 0)
244 s->status = RUNNR_FINISHED;
245 gettimeofday(&s->startTime, NULL);
246 }
247
248 releaseScript(s);
249
250 if (RUNNR_RUNNING == s->status)
251 {
252 int stat;
253
254//printf("_workerFunction() RUNNING %s %s\n", s->name, message);
255 // Resume running the script.
256 // lua_resume() needs a Lua thread, and the initial Lua state is a thread.
257 // Other Lua threads have their own state, but share the environment with the initial state.
258 // In theory lua_resume() is a pcall.
259 if (message)
260 lua_pushstring(s->L, message);
261 stat = lua_resume(s->L, (message) ? 1 : 0);
262 free(msg);
263
264 takeScript(s);
265 // If the script finished.
266 if (stat == 0)
267 s->status = RUNNR_FINISHED;
268 else if (stat != LUA_YIELD)
269 {
270 printf("lua_resume error at %s\n", s->name);
271 printLuaError(stat, s->name, s->L);
272 s->status = RUNNR_FINISHED;
273 }
274 else
275 {
276 if (eina_clist_count(&s->messages) == 0)
277 s->status = RUNNR_WAIT;
278 else
279 s->status = RUNNR_READY;
280 }
281 releaseScript(s);
282 }
283
284 takeScript(s);
285 // Start again from the top when Ecore_Thread has a spare thread ready, unless the script finished.
286 if (RUNNR_FINISHED == s->status)
287 {
288//printf("_workerFunction() FINISHED %s\n", s->name);
289#if THREADIT
290 ecore_thread_cancel(thread);
291#else
292 _cancel(s, NULL);
293#endif
294 }
295 else if (RUNNR_WAIT == s->status)
296 {
297;//printf("_workerFunction() WAIT %s\n", s->name);
298 }
299#if THREADIT
300 else if (RUNNR_READY == s->status)
301 ecore_thread_reschedule(thread);
302#endif
303 releaseScript(s);
304}
305
306static void _notify(void *data, Ecore_Thread *thread, void *message)
307{
308 script *s = data;
309
310 if (s->send2server) s->send2server(s, message);
311 free(message);
312}
313
314static void _cancel(void *data, Ecore_Thread *thread)
315{
316 script *s = data;
317
318 takeScript(s);
319 _stopScript(s);
320 s->status = RUNNR_FINISHED;
321 eina_clist_remove(&(s->node));
322 if (s->SID[0]) ecore_thread_global_data_del(s->SID); s->SID[0] = 0;
323//printf("^^^^^^^^^^^^^^^^^^^_del(, %s)\n", s->name);
324 // TODO - Perhaps have our own deletion callback to pass back?
325 releaseScript(s);
326#if THREADIT
327 eina_lock_free(&s->mutex);
328#endif
329 free(s);
330}
331
332#if THREADIT
333static void _end(void *data, Ecore_Thread *thread)
334{
335}
336#endif
337
338static Eina_Bool _enterer(void *data)
339{
340 script *s, *s1;
341
342 EINA_CLIST_FOR_EACH_ENTRY_SAFE(s, s1, &(scripts), script, node)
343 {
344 takeScript(s);
345 if ((RUNNR_WAIT == s->status) && (eina_clist_count(&s->messages)))
346 {
347 s->status = RUNNR_READY;
348#if THREADIT
349 ecore_thread_feedback_run(_workerFunction, _notify, _end, _cancel, s, EINA_FALSE);
350#else
351 _workerFunction(s, NULL);
352#endif
353 }
354 if ((RUNNR_RESET == s->status) || (RUNNR_READY == s->status) || (RUNNR_RUNNING == s->status) || (RUNNR_NOT_STARTED == s->status))
355 {
356#if THREADIT
357 ecore_thread_feedback_run(_workerFunction, _notify, _end, _cancel, s, EINA_FALSE);
358#else
359 _workerFunction(s, NULL);
360#endif
361 }
362 releaseScript(s);
363 }
364
365 return ECORE_CALLBACK_RENEW;
366}
367
368script *scriptAdd(char *file, char *SID, RunnrServerCb send2server, void *data)
369{
370 script *result;
371
372 if (!enterer)
373 {
374 eina_clist_init(&(scripts));
375 enterer = ecore_idler_add(_enterer, NULL);
376 }
377
378 result = calloc(1, sizeof(script));
379 gettimeofday(&result->startTime, NULL);
380 strcpy(result->SID, SID);
381 result->status = RUNNR_NOT_STARTED;
382 result->data = data;
383 result->send2server = send2server;
384
385 strncpy(result->fileName, file, sizeof(result->fileName));
386 result->name = &result->fileName[strlen(prefix_data_get())];
387 sprintf(result->binName, "%s.lua.out", result->fileName);
388
389#if THREADIT
390 eina_lock_new(&result->mutex);
391#endif
392 eina_clist_init(&(result->messages));
393 ecore_thread_global_data_add(result->SID, result, NULL, EINA_FALSE);
394
395 eina_clist_add_tail(&(scripts), &(result->node));
396
397 return result;
398}
399
400static int luaWriter(lua_State *L, const void* p, size_t sz, void* ud)
401{
402 FILE *out = ud;
403 int result = 0;
404
405 if (sz != fwrite(p, 1, sz, out))
406 result = -1;
407 return result;
408}
409
410// TODO - This didn't help the compile time much, perhaps move the rest of the compiling stage into this thread as a callback?
411static void _compileNotify(void *data, Ecore_Thread *thread, void *message)
412{
413 LuaCompiler *compiler = data;
414
415 if (compiler->cb) compiler->cb(compiler);
416}
417
418static void _compileThread(void *data, Ecore_Thread *thread)
419{
420 LuaCompiler *compiler = data;
421 char name[PATH_MAX];
422 lua_State *L;
423 FILE *out;
424 int err;
425
426 strcpy(name, compiler->luaName);
427 if ((L = luaL_newstate()))
428 {
429 luaL_openlibs(L);
430 // This ends up pushing a function onto the stack. The function is the compiled code.
431 err = luaL_loadfile(L, name);
432 if (err)
433 {
434 compiler->bugCount++;
435 if (LUA_ERRSYNTAX == err)
436 printf("Lua syntax error in %s: %s\n", name, lua_tostring(L, -1));
437 else if (LUA_ERRFILE == err)
438 printf("Lua compile file error in %s: %s\n", name, lua_tostring(L, -1));
439 else if (LUA_ERRMEM == err)
440 printf("Lua compile memory allocation error in %s: %s\n", name, lua_tostring(L, -1));
441 }
442 else
443 {
444 // Write the compiled code to a file.
445 strcat(name, ".out");
446 out = fopen(name, "w");
447 if (out)
448 {
449 err = lua_dump(L, luaWriter, out);
450 if (err)
451 {
452 compiler->bugCount++;
453 printf("Lua compile file error writing to %s\n", name);
454 }
455 fclose(out);
456 }
457 else
458 {
459 compiler->bugCount++;
460 printf("CRITICAL! Unable to open file %s for writing!\n", name);
461 }
462 }
463 }
464 else
465 {
466 compiler->bugCount++;
467 printf("Can't create a new Lua state!\n");
468 }
469
470 ecore_thread_feedback(thread, compiler);
471}
472
473void compileScript(LuaCompiler *compiler)
474{
475 ecore_thread_feedback_run(_compileThread, _compileNotify, NULL, NULL, compiler, EINA_FALSE);
476}
477
478// Assumes the scripts mutex is taken already.
479void runScript(script *s)
480{
481#if THREADIT
482 if ((RUNNR_NOT_STARTED == s->status) || (RUNNR_FINISHED == s->status))
483#endif
484 {
485#if THREADIT
486 ecore_thread_feedback_run(_workerFunction, _notify, _end, _cancel, s, EINA_FALSE);
487#elsif
488 _workerFunction(s, NULL);
489#endif
490 }
491}
492
493void resetScript(script *s)
494{
495 takeScript(s);
496 s->status = RUNNR_RESET;
497 releaseScript(s);
498}
499
500script *getScript(char *SID)
501{
502 script *result = ecore_thread_global_data_find(SID);
503
504 if (result)
505 {
506 takeScript(result);
507 if (RUNNR_FINISHED == result->status)
508 {
509 releaseScript(result);
510 result = NULL;
511 }
512 }
513 return result;
514}
515
516void takeScript(script *s)
517{
518#if THREADIT
519 Eina_Lock_Result result = eina_lock_take(&s->mutex);
520 if (EINA_LOCK_DEADLOCK == result) printf("Script %s IS DEADLOCKED!\n", s->name);
521 if (EINA_LOCK_FAIL == result) printf("Script %s LOCK FAILED!\n", s->name);
522#endif
523}
524
525void releaseScript(script *s)
526{
527#if THREADIT
528 eina_lock_release(&s->mutex);
529#endif
530}
531
532void send2script(const char *SID, const char *message)
533{
534 if (SID)
535 {
536 script *s = ecore_thread_global_data_find(SID);
537
538 if (s)
539 {
540 takeScript(s);
541 if (RUNNR_FINISHED != s->status)
95 { 542 {
96 case LUA_ERRRUN: err_type = "runtime"; break; 543 scriptMessage *sm = NULL;
97 case LUA_ERRSYNTAX: err_type = "syntax"; break; 544
98 case LUA_ERRMEM: err_type = "memory allocation"; break; 545 if ((sm = malloc(sizeof(scriptMessage))))
99 case LUA_ERRERR: err_type = "error handler"; break; 546 {
100 default: err_type = "unknown"; break; 547 runnrStatus stat;
548
549 sm->s = s;
550 strcpy((char *) sm->message, message);
551 eina_clist_add_tail(&(s->messages), &(sm->node));
552
553 stat = s->status;
554 s->status = RUNNR_READY;
555 if (RUNNR_WAIT == stat)
556#if THREADIT
557 ecore_thread_feedback_run(_workerFunction, _notify, _end, _cancel, s, EINA_FALSE);
558#else
559 _workerFunction(s, NULL);
560#endif
561 }
101 } 562 }
102 printf("Error running - %s, \n%s - %s", string, err_type, lua_tostring(L, -1)); 563 releaseScript(s);
103 } 564 }
104 } 565 }
105} 566}
106 567
568static script *_getSelf(lua_State *L)
569{
570 script *s;
571
572 lua_getfield(L, LUA_REGISTRYINDEX, "_SELF");
573 s = (script *) lua_touserdata(L, -1);
574 lua_pop(L, 1);
575
576 return s;
577}
578
579static int _send(lua_State *L)
580{
581 script *self = _getSelf(L);
582 const char *SID = NULL, *message = luaL_checkstring(L, 2);
583
584 if (lua_isstring(L, 1))
585 SID = lua_tostring(L, 1);
586
587 if (SID)
588 send2script(SID, message);
589 else
590 {
591 takeScript(self);
592#if THREADIT
593 ecore_thread_feedback(self->me, strdup(message));
594#else
595 _notify(self, NULL, strdup(message));
596#endif
597 releaseScript(self);
598 }
599
600 return 0;
601}
602
603static int _receive(lua_State *L)
604{
605 script *self = _getSelf(L);
606
607 takeScript(self);
608 self->status = RUNNR_WAIT;
609 releaseScript(self);
610 return lua_yield(L, 0);
611}
612
107 613
108// These are what the various symbols are for each type - 614// These are what the various symbols are for each type -
109// int % 615// int %
@@ -282,10 +788,24 @@ int push_lua(lua_State *L, char *params, ...) // Stack usage [-0, +n, em]
282 va_list vl; 788 va_list vl;
283 char *f = strdup(params); 789 char *f = strdup(params);
284 char *p = f; 790 char *p = f;
285 int n = 0, table = 0, i = -1; 791 int n = 0, table = 0, i = -1, needTrace = 0, _T;
286 792
287 if (!f) return -1; 793 if (!f) return -1;
288 794
795 // Scan ahead looking for ), so we know to put the traceBack function on the stack first.
796 while (*p)
797 {
798 p++;
799 if ('0' == *p)
800 {
801 lua_pushcfunction(L, traceBack);
802 _T = lua_gettop(L);
803 needTrace = 1;
804 break;
805 }
806 }
807 p = f;
808
289 va_start(vl, params); 809 va_start(vl, params);
290 810
291 while (*p) 811 while (*p)
@@ -364,7 +884,10 @@ int push_lua(lua_State *L, char *params, ...) // Stack usage [-0, +n, em]
364 } 884 }
365 case ')': 885 case ')':
366 { 886 {
367 lua_call(L, n - 1, va_arg(vl, int)); 887 int err;
888
889 if ((err = lua_pcall(L, n - 1, va_arg(vl, int), _T)))
890 printLuaError(err, params, L);
368 n = 0; 891 n = 0;
369 set = EINA_FALSE; 892 set = EINA_FALSE;
370 break; 893 break;
@@ -401,6 +924,8 @@ int push_lua(lua_State *L, char *params, ...) // Stack usage [-0, +n, em]
401 } 924 }
402 925
403 va_end(vl); 926 va_end(vl);
927 if (needTrace)
928 lua_remove(L, _T);
404 free(f); 929 free(f);
405 return n; 930 return n;
406} 931}