diff options
author | Benoit Germain <b n t DOT g e r m a i n AT g m a i l DOT c o m> | 2013-08-13 08:12:05 +0200 |
---|---|---|
committer | Benoit Germain <b n t DOT g e r m a i n AT g m a i l DOT c o m> | 2013-08-13 08:12:05 +0200 |
commit | 7f7b29063d2f19a8bc2b229ae9b0ec82ce447cab (patch) | |
tree | 296e407a77e7e7f94add52967fe06e09aa7d58f3 | |
parent | a6f76ba28d7e7e9d2d277a85dfd054cb70c02b69 (diff) | |
download | lanes-7f7b29063d2f19a8bc2b229ae9b0ec82ce447cab.tar.gz lanes-7f7b29063d2f19a8bc2b229ae9b0ec82ce447cab.tar.bz2 lanes-7f7b29063d2f19a8bc2b229ae9b0ec82ce447cab.zip |
version 3.6.3v3.6.3
* lane:cancel(<negative-timeout>) only causes cancel_test() to return
true but won't interrupt execution of the lane during linda operations
* more explicit errors when trying to transfer unknown source functions
(with new configure option verbose_errors)
* default options wrap allocator around a mutex when run by LuaJIT
-rw-r--r-- | CHANGES | 10 | ||||
-rw-r--r-- | docs/index.html | 35 | ||||
-rw-r--r-- | lanes-3.6.3-1.rockspec (renamed from lanes-3.6.2-1.rockspec) | 4 | ||||
-rw-r--r-- | src/lanes.c | 156 | ||||
-rw-r--r-- | src/lanes.lua | 11 | ||||
-rw-r--r-- | src/tools.c | 168 | ||||
-rw-r--r-- | src/tools.h | 3 | ||||
-rw-r--r-- | tests/fifo.lua | 7 | ||||
-rw-r--r-- | tests/linda_perf.lua | 5 | ||||
-rw-r--r-- | tests/require.lua | 7 |
10 files changed, 281 insertions, 125 deletions
@@ -1,5 +1,15 @@ | |||
1 | CHANGES: | 1 | CHANGES: |
2 | 2 | ||
3 | CHANGE 67: BGe 2-Aug-13 | ||
4 | * version 3.6.3 | ||
5 | * lane:cancel(<negative-timeout>) only causes cancel_test() to return true but won't interrupt execution of the lane during linda operations | ||
6 | |||
7 | CHANGE 66: BGe 31-Jul-13 | ||
8 | * more explicit errors when trying to transfer unknown source functions (with new configure option verbose_errors) | ||
9 | |||
10 | CHANGE 65: BGe 23-Jul-13 | ||
11 | * default options wrap allocator around a mutex when run by LuaJIT | ||
12 | |||
3 | CHANGE 64: BGe 20-Jul-13 | 13 | CHANGE 64: BGe 20-Jul-13 |
4 | * WIN32 builds against pre-Vista versions no longer use PulseEvent to fix occasional hangs when a wake event is missed | 14 | * WIN32 builds against pre-Vista versions no longer use PulseEvent to fix occasional hangs when a wake event is missed |
5 | 15 | ||
diff --git a/docs/index.html b/docs/index.html index 617403a..0e32504 100644 --- a/docs/index.html +++ b/docs/index.html | |||
@@ -70,7 +70,7 @@ | |||
70 | </p> | 70 | </p> |
71 | 71 | ||
72 | <p> | 72 | <p> |
73 | This document was revised on 20-May-13, and applies to version <tt>3.6.2</tt>. | 73 | This document was revised on 02-Aug-13, and applies to version <tt>3.6.3</tt>. |
74 | </p> | 74 | </p> |
75 | </font> | 75 | </font> |
76 | </center> | 76 | </center> |
@@ -294,6 +294,19 @@ | |||
294 | 294 | ||
295 | <tr valign=top> | 295 | <tr valign=top> |
296 | <td> | 296 | <td> |
297 | <code>.verbose_errors</code> | ||
298 | </td> | ||
299 | <td> | ||
300 | <tt>nil</tt>/<tt>false</tt>/<tt>true</tt> | ||
301 | </td> | ||
302 | <td> | ||
303 | (Since v3.6.3) If equal to <tt>true</tt>, Lanes will collect more information when transfering stuff across Lua states to help identify errors (with a cost). | ||
304 | Default is <tt>false</tt>. | ||
305 | </td> | ||
306 | </tr> | ||
307 | |||
308 | <tr valign=top> | ||
309 | <td> | ||
297 | <code>.protect_allocator</code> | 310 | <code>.protect_allocator</code> |
298 | </td> | 311 | </td> |
299 | <td> | 312 | <td> |
@@ -539,7 +552,7 @@ | |||
539 | </td> | 552 | </td> |
540 | <td>integer >= 1/<tt>true</tt></td> | 553 | <td>integer >= 1/<tt>true</tt></td> |
541 | <td> | 554 | <td> |
542 | By default, lanes are only cancellable when they <u>enter</u> a pending <tt>:receive()</tt> or <tt>:send()</tt> call. With this option, one can set cancellation check to occur every <tt>N</tt> Lua statements. The value <tt>true</tt> uses a default value (100). | 555 | By default, lanes are only cancellable when they <u>enter</u> a pending <tt>:receive()</tt> or <tt>:send()</tt> call. With this option, one can set <a href="#cancelling">cancellation</a> check to occur every <tt>N</tt> Lua statements. The value <tt>true</tt> uses a default value (100). |
543 | It is also possible to manually test for cancel requests with <tt>cancel_test()</tt>. | 556 | It is also possible to manually test for cancel requests with <tt>cancel_test()</tt>. |
544 | </td> | 557 | </td> |
545 | </tr> | 558 | </tr> |
@@ -705,7 +718,7 @@ | |||
705 | </td> | 718 | </td> |
706 | <td/> | 719 | <td/> |
707 | <td> | 720 | <td> |
708 | received cancellation and finished itself. | 721 | received <a href="#cancelling">cancellation</a> and finished itself. |
709 | </td> | 722 | </td> |
710 | </tr> | 723 | </tr> |
711 | <tr> | 724 | <tr> |
@@ -833,8 +846,10 @@ | |||
833 | </pre></td></tr></table> | 846 | </pre></td></tr></table> |
834 | 847 | ||
835 | <p> | 848 | <p> |
836 | <tt>cancel()</tt>sends a cancellation request to the lane. If <tt>timeout_secs</tt> is non-zero, waits for the request to be processed, or a timeout to occur. | 849 | <tt>cancel()</tt>sends a cancellation request to the lane.<br/> |
837 | Returns <tt>true</tt> if the lane was already done (in <tt>"done"</tt>, <tt>"error"</tt> or <tt>"cancelled"</tt> status) or if the cancellation was fruitful within timeout period. | 850 | If <tt>timeout_secs</tt> is positive (aka "hard cancel"), waits for the request to be processed, or a timeout to occur.<br/> |
851 | If <tt>timeout_secs</tt> is negative (aka "soft cancel"), starting with version 3.6.3, will only cause <tt>cancel_test()</tt> to return true, so that the lane can cleanup manually. You can't provide a second argument in that case.<br/> | ||
852 | Returns <tt>true</tt> if soft cancelling, or the lane was already done (in <tt>"done"</tt>, <tt>"error"</tt> or <tt>"cancelled"</tt> status), or the cancellation was fruitful within timeout period. | ||
838 | </p> | 853 | </p> |
839 | 854 | ||
840 | <p> | 855 | <p> |
@@ -887,7 +902,7 @@ | |||
887 | -- no special error: true error | 902 | -- no special error: true error |
888 | print( " error: "..tostring(err)) | 903 | print( " error: "..tostring(err)) |
889 | elseif type( err) == "userdata" then | 904 | elseif type( err) == "userdata" then |
890 | -- lane cancellation is performed by throwing a special userdata as error | 905 | -- lane <a href="#cancelling">cancellation</a> is performed by throwing a special userdata as error |
891 | print( "after cancel") | 906 | print( "after cancel") |
892 | else | 907 | else |
893 | -- no error: we just got finalized | 908 | -- no error: we just got finalized |
@@ -977,11 +992,15 @@ | |||
977 | </p> | 992 | </p> |
978 | 993 | ||
979 | <p> | 994 | <p> |
980 | <tt>send</tt> returns <tt>true</tt> if the sending succeeded, and <tt>false</tt> if the queue limit was met, and the queue did not empty enough during the given timeout, or the operation was <a href="#cancelling">cancelled</a>. | 995 | <a href="#cancelling">Hard cancellation</a> will cause pending linda operations to abort execution of the lane through a cancellation error. This means that you have to install a <a href="#finalizers">finalizer</a> in your lane if you want to run some code in that situation. |
996 | </p> | ||
997 | |||
998 | <p> | ||
999 | <tt>send</tt> returns <tt>true</tt> if the sending succeeded, and <tt>false</tt> if the queue limit was met, and the queue did not empty enough during the given timeout. | ||
981 | </p> | 1000 | </p> |
982 | 1001 | ||
983 | <p> | 1002 | <p> |
984 | Equally, <tt>receive</tt> returns a key and the value extracted from it, or nothing for timeout or <a href="#cancelling">cancellation</a>. Note that <tt>nil</tt>s can be sent and received; the <tt>key</tt> value will tell it apart from a timeout. | 1003 | Equally, <tt>receive</tt> returns a key and the value extracted from it, or nothing for timeout. Note that <tt>nil</tt>s can be sent and received; the <tt>key</tt> value will tell it apart from a timeout. |
985 | <br> | 1004 | <br> |
986 | Version 3.4.0 introduces an API change in the returned values: <tt>receive</tt> returns the key followed by the value(s), in that order, and not the other way around. | 1005 | Version 3.4.0 introduces an API change in the returned values: <tt>receive</tt> returns the key followed by the value(s), in that order, and not the other way around. |
987 | </p> | 1006 | </p> |
diff --git a/lanes-3.6.2-1.rockspec b/lanes-3.6.3-1.rockspec index 8679c08..18bae0a 100644 --- a/lanes-3.6.2-1.rockspec +++ b/lanes-3.6.3-1.rockspec | |||
@@ -7,11 +7,11 @@ | |||
7 | 7 | ||
8 | package = "Lanes" | 8 | package = "Lanes" |
9 | 9 | ||
10 | version = "3.6.2-1" | 10 | version = "3.6.3-1" |
11 | 11 | ||
12 | source= { | 12 | source= { |
13 | url= "git://github.com/LuaLanes/lanes.git", | 13 | url= "git://github.com/LuaLanes/lanes.git", |
14 | branch= "v3.6.2" | 14 | branch= "v3.6.3" |
15 | } | 15 | } |
16 | 16 | ||
17 | description = { | 17 | description = { |
diff --git a/src/lanes.c b/src/lanes.c index 08bdb9a..e36a073 100644 --- a/src/lanes.c +++ b/src/lanes.c | |||
@@ -52,7 +52,7 @@ | |||
52 | * ... | 52 | * ... |
53 | */ | 53 | */ |
54 | 54 | ||
55 | char const* VERSION = "3.6.2"; | 55 | char const* VERSION = "3.6.3"; |
56 | 56 | ||
57 | /* | 57 | /* |
58 | =============================================================================== | 58 | =============================================================================== |
@@ -115,10 +115,21 @@ THE SOFTWARE. | |||
115 | */ | 115 | */ |
116 | #define ERROR_FULL_STACK | 116 | #define ERROR_FULL_STACK |
117 | 117 | ||
118 | /* | ||
119 | * Lane cancellation request modes | ||
120 | */ | ||
121 | enum e_cancel_request | ||
122 | { | ||
123 | CANCEL_NONE, // no pending cancel request | ||
124 | CANCEL_SOFT, // user wants the lane to cancel itself manually on cancel_test() | ||
125 | CANCEL_HARD // user wants the lane to be interrupted (meaning code won't return from those functions) from inside linda:send/receive calls | ||
126 | }; | ||
127 | |||
118 | // NOTE: values to be changed by either thread, during execution, without | 128 | // NOTE: values to be changed by either thread, during execution, without |
119 | // locking, are marked "volatile" | 129 | // locking, are marked "volatile" |
120 | // | 130 | // |
121 | struct s_lane { | 131 | struct s_lane |
132 | { | ||
122 | THREAD_T thread; | 133 | THREAD_T thread; |
123 | // | 134 | // |
124 | // M: sub-thread OS thread | 135 | // M: sub-thread OS thread |
@@ -140,7 +151,7 @@ struct s_lane { | |||
140 | // | 151 | // |
141 | // When status is WAITING, points on the linda's signal the thread waits on, else NULL | 152 | // When status is WAITING, points on the linda's signal the thread waits on, else NULL |
142 | 153 | ||
143 | volatile bool_t cancel_request; | 154 | volatile enum e_cancel_request cancel_request; |
144 | // | 155 | // |
145 | // M: sets to FALSE, flags TRUE for cancel request | 156 | // M: sets to FALSE, flags TRUE for cancel request |
146 | // S: reads to see if cancel is requested | 157 | // S: reads to see if cancel is requested |
@@ -178,7 +189,7 @@ struct s_lane { | |||
178 | // For tracking only | 189 | // For tracking only |
179 | }; | 190 | }; |
180 | 191 | ||
181 | static bool_t cancel_test( lua_State*L ); | 192 | static enum e_cancel_request cancel_test( lua_State* L); |
182 | static void cancel_error( lua_State*L ); | 193 | static void cancel_error( lua_State*L ); |
183 | 194 | ||
184 | #define CANCEL_TEST_KEY ((void*)cancel_test) // used as registry key | 195 | #define CANCEL_TEST_KEY ((void*)cancel_test) // used as registry key |
@@ -372,7 +383,7 @@ LUAG_FUNC( linda_send) | |||
372 | { | 383 | { |
373 | struct s_Linda *linda = lua_toLinda( L, 1); | 384 | struct s_Linda *linda = lua_toLinda( L, 1); |
374 | bool_t ret; | 385 | bool_t ret; |
375 | bool_t cancel = FALSE; | 386 | enum e_cancel_request cancel = CANCEL_NONE; |
376 | int pushed; | 387 | int pushed; |
377 | time_d timeout= -1.0; | 388 | time_d timeout= -1.0; |
378 | uint_t key_i = 2; // index of first key, if timeout not there | 389 | uint_t key_i = 2; // index of first key, if timeout not there |
@@ -434,7 +445,7 @@ LUAG_FUNC( linda_send) | |||
434 | /* limit faced; push until timeout */ | 445 | /* limit faced; push until timeout */ |
435 | 446 | ||
436 | cancel = cancel_test( L); // testing here causes no delays | 447 | cancel = cancel_test( L); // testing here causes no delays |
437 | if (cancel) | 448 | if( cancel != CANCEL_NONE) // if user wants to cancel, the call returns without sending anything |
438 | { | 449 | { |
439 | break; | 450 | break; |
440 | } | 451 | } |
@@ -486,7 +497,8 @@ LUAG_FUNC( linda_send) | |||
486 | return luaL_error( L, "tried to copy unsupported types"); | 497 | return luaL_error( L, "tried to copy unsupported types"); |
487 | } | 498 | } |
488 | 499 | ||
489 | if( cancel) | 500 | // raise an error interrupting execution only in case of hard cancel |
501 | if( cancel == CANCEL_HARD) | ||
490 | cancel_error( L); | 502 | cancel_error( L); |
491 | 503 | ||
492 | lua_pushboolean( L, ret); | 504 | lua_pushboolean( L, ret); |
@@ -510,7 +522,7 @@ LUAG_FUNC( linda_receive) | |||
510 | { | 522 | { |
511 | struct s_Linda *linda = lua_toLinda( L, 1); | 523 | struct s_Linda *linda = lua_toLinda( L, 1); |
512 | int pushed, expected_pushed_min, expected_pushed_max; | 524 | int pushed, expected_pushed_min, expected_pushed_max; |
513 | bool_t cancel = FALSE; | 525 | enum e_cancel_request cancel = CANCEL_NONE; |
514 | keeper_api_t keeper_receive; | 526 | keeper_api_t keeper_receive; |
515 | 527 | ||
516 | time_d timeout = -1.0; | 528 | time_d timeout = -1.0; |
@@ -592,16 +604,16 @@ LUAG_FUNC( linda_receive) | |||
592 | /* nothing received; wait until timeout */ | 604 | /* nothing received; wait until timeout */ |
593 | 605 | ||
594 | cancel = cancel_test( L); // testing here causes no delays | 606 | cancel = cancel_test( L); // testing here causes no delays |
595 | if( cancel) | 607 | if( cancel != CANCEL_NONE) // if user wants to cancel, the call returns without providing anything |
596 | { | 608 | { |
597 | break; | 609 | break; |
598 | } | 610 | } |
599 | 611 | ||
600 | // change status of lane to "waiting" | 612 | // change status of lane to "waiting" |
601 | { | 613 | { |
602 | struct s_lane *s; | 614 | struct s_lane* s; |
603 | enum e_status prev_status = ERROR_ST; // prevent 'might be used uninitialized' warnings | 615 | enum e_status prev_status = ERROR_ST; // prevent 'might be used uninitialized' warnings |
604 | STACK_GROW(L,1); | 616 | STACK_GROW( L, 1); |
605 | 617 | ||
606 | STACK_CHECK( L); | 618 | STACK_CHECK( L); |
607 | lua_pushlightuserdata( L, CANCEL_TEST_KEY); | 619 | lua_pushlightuserdata( L, CANCEL_TEST_KEY); |
@@ -643,7 +655,8 @@ LUAG_FUNC( linda_receive) | |||
643 | return luaL_error( L, "tried to copy unsupported types"); | 655 | return luaL_error( L, "tried to copy unsupported types"); |
644 | } | 656 | } |
645 | 657 | ||
646 | if( cancel) | 658 | // raise an error interrupting execution only in case of hard cancel |
659 | if( cancel == CANCEL_HARD) | ||
647 | cancel_error( L); | 660 | cancel_error( L); |
648 | 661 | ||
649 | return pushed; | 662 | return pushed; |
@@ -1176,31 +1189,41 @@ static cancel_result thread_cancel( struct s_lane *s, double secs, bool_t force) | |||
1176 | } | 1189 | } |
1177 | else if( s->status < DONE) | 1190 | else if( s->status < DONE) |
1178 | { | 1191 | { |
1179 | s->cancel_request = TRUE; // it's now signaled to stop | ||
1180 | // signal the linda the wake up the thread so that it can react to the cancel query | 1192 | // signal the linda the wake up the thread so that it can react to the cancel query |
1181 | // let us hope we never land here with a pointer on a linda that has been destroyed... | 1193 | // let us hope we never land here with a pointer on a linda that has been destroyed... |
1194 | if( secs < 0.0) | ||
1195 | { | ||
1196 | s->cancel_request = CANCEL_SOFT; // it's now signaled to stop | ||
1197 | // negative timeout: we don't want to truly abort the lane, we just want it to react to cancel_test() on its own | ||
1198 | // say we succeeded though | ||
1199 | result = CR_Cancelled; | ||
1200 | } | ||
1201 | else | ||
1182 | { | 1202 | { |
1183 | SIGNAL_T *waiting_on = s->waiting_on; | 1203 | s->cancel_request = CANCEL_HARD; // it's now signaled to stop |
1184 | if( s->status == WAITING && waiting_on != NULL) | ||
1185 | { | 1204 | { |
1186 | SIGNAL_ALL( waiting_on); | 1205 | SIGNAL_T *waiting_on = s->waiting_on; |
1206 | if( s->status == WAITING && waiting_on != NULL) | ||
1207 | { | ||
1208 | SIGNAL_ALL( waiting_on); | ||
1209 | } | ||
1187 | } | 1210 | } |
1188 | } | ||
1189 | 1211 | ||
1190 | result = THREAD_WAIT( &s->thread, secs, &s->done_signal, &s->done_lock, &s->status) ? CR_Cancelled : CR_Timeout; | 1212 | result = THREAD_WAIT( &s->thread, secs, &s->done_signal, &s->done_lock, &s->status) ? CR_Cancelled : CR_Timeout; |
1191 | 1213 | ||
1192 | if( (result == CR_Timeout) && force) | 1214 | if( (result == CR_Timeout) && force) |
1193 | { | 1215 | { |
1194 | // Killing is asynchronous; we _will_ wait for it to be done at | 1216 | // Killing is asynchronous; we _will_ wait for it to be done at |
1195 | // GC, to make sure the data structure can be released (alternative | 1217 | // GC, to make sure the data structure can be released (alternative |
1196 | // would be use of "cancellation cleanup handlers" that at least | 1218 | // would be use of "cancellation cleanup handlers" that at least |
1197 | // PThread seems to have). | 1219 | // PThread seems to have). |
1198 | // | 1220 | // |
1199 | THREAD_KILL( &s->thread); | 1221 | THREAD_KILL( &s->thread); |
1200 | s->mstatus = KILLED; // mark 'gc' to wait for it | 1222 | s->mstatus = KILLED; // mark 'gc' to wait for it |
1201 | // note that s->status value must remain to whatever it was at the time of the kill | 1223 | // note that s->status value must remain to whatever it was at the time of the kill |
1202 | // because we need to know if we can lua_close() the Lua State or not. | 1224 | // because we need to know if we can lua_close() the Lua State or not. |
1203 | result = CR_Killed; | 1225 | result = CR_Killed; |
1226 | } | ||
1204 | } | 1227 | } |
1205 | } | 1228 | } |
1206 | else | 1229 | else |
@@ -1307,7 +1330,7 @@ static int selfdestruct_gc( lua_State* L) | |||
1307 | struct s_lane* s = selfdestruct_first; | 1330 | struct s_lane* s = selfdestruct_first; |
1308 | while( s != SELFDESTRUCT_END ) | 1331 | while( s != SELFDESTRUCT_END ) |
1309 | { | 1332 | { |
1310 | // attempt a regular unforced cancel with a small timeout | 1333 | // attempt a regular unforced hard cancel with a small timeout |
1311 | bool_t cancelled = THREAD_ISNULL( s->thread) || thread_cancel( s, 0.0001, FALSE); | 1334 | bool_t cancelled = THREAD_ISNULL( s->thread) || thread_cancel( s, 0.0001, FALSE); |
1312 | // if we failed, and we know the thread is waiting on a linda | 1335 | // if we failed, and we know the thread is waiting on a linda |
1313 | if( cancelled == FALSE && s->status == WAITING && s->waiting_on != NULL) | 1336 | if( cancelled == FALSE && s->status == WAITING && s->waiting_on != NULL) |
@@ -1353,7 +1376,7 @@ static int selfdestruct_gc( lua_State* L) | |||
1353 | struct s_lane* s = selfdestruct_first; | 1376 | struct s_lane* s = selfdestruct_first; |
1354 | while( s != SELFDESTRUCT_END) | 1377 | while( s != SELFDESTRUCT_END) |
1355 | { | 1378 | { |
1356 | if( s->cancel_request) | 1379 | if( s->cancel_request == CANCEL_HARD) |
1357 | ++ n; | 1380 | ++ n; |
1358 | s = s->selfdestruct_next; | 1381 | s = s->selfdestruct_next; |
1359 | } | 1382 | } |
@@ -1444,21 +1467,22 @@ static int selfdestruct_gc( lua_State* L) | |||
1444 | * Returns TRUE if any locks are to be exited, and 'cancel_error()' called, | 1467 | * Returns TRUE if any locks are to be exited, and 'cancel_error()' called, |
1445 | * to make execution of the lane end. | 1468 | * to make execution of the lane end. |
1446 | */ | 1469 | */ |
1447 | static bool_t cancel_test( lua_State*L ) { | 1470 | static enum e_cancel_request cancel_test( lua_State* L) |
1448 | struct s_lane *s; | 1471 | { |
1472 | struct s_lane* s; | ||
1449 | 1473 | ||
1450 | STACK_GROW(L,1); | 1474 | STACK_GROW( L, 1); |
1451 | 1475 | ||
1452 | STACK_CHECK( L); | 1476 | STACK_CHECK( L); |
1453 | lua_pushlightuserdata( L, CANCEL_TEST_KEY ); | 1477 | lua_pushlightuserdata( L, CANCEL_TEST_KEY); |
1454 | lua_rawget( L, LUA_REGISTRYINDEX ); | 1478 | lua_rawget( L, LUA_REGISTRYINDEX); |
1455 | s= lua_touserdata( L, -1 ); // lightuserdata (true 's_lane' pointer) / nil | 1479 | s = lua_touserdata( L, -1); // lightuserdata (true 's_lane' pointer) / nil |
1456 | lua_pop(L,1); | 1480 | lua_pop( L, 1); |
1457 | STACK_END( L, 0); | 1481 | STACK_END( L, 0); |
1458 | 1482 | ||
1459 | // 's' is NULL for the original main state (no-one can cancel that) | 1483 | // 's' is NULL for the original main state (no-one can cancel that) |
1460 | // | 1484 | // |
1461 | return s && s->cancel_request; | 1485 | return s ? s->cancel_request : CANCEL_NONE; |
1462 | } | 1486 | } |
1463 | 1487 | ||
1464 | static void cancel_error( lua_State*L ) { | 1488 | static void cancel_error( lua_State*L ) { |
@@ -1467,22 +1491,24 @@ static void cancel_error( lua_State*L ) { | |||
1467 | lua_error(L); // no return | 1491 | lua_error(L); // no return |
1468 | } | 1492 | } |
1469 | 1493 | ||
1470 | static void cancel_hook( lua_State*L, lua_Debug *ar ) { | 1494 | static void cancel_hook( lua_State*L, lua_Debug *ar ) |
1471 | (void)ar; | 1495 | { |
1472 | if (cancel_test(L)) cancel_error(L); | 1496 | (void)ar; |
1497 | if( cancel_test( L) != CANCEL_NONE) | ||
1498 | cancel_error( L); | ||
1473 | } | 1499 | } |
1474 | 1500 | ||
1475 | 1501 | ||
1476 | //--- | 1502 | //--- |
1477 | // bool= cancel_test() | 1503 | // bool = cancel_test() |
1478 | // | 1504 | // |
1479 | // Available inside the global namespace of lanes | 1505 | // Available inside the global namespace of lanes |
1480 | // returns a boolean saying if a cancel request is pending | 1506 | // returns a boolean saying if a cancel request is pending |
1481 | // | 1507 | // |
1482 | LUAG_FUNC( cancel_test) | 1508 | LUAG_FUNC( cancel_test) |
1483 | { | 1509 | { |
1484 | bool_t test = cancel_test( L); | 1510 | enum e_cancel_request test = cancel_test( L); |
1485 | lua_pushboolean( L, test); | 1511 | lua_pushboolean( L, test != CANCEL_NONE); |
1486 | return 1; | 1512 | return 1; |
1487 | } | 1513 | } |
1488 | 1514 | ||
@@ -1724,7 +1750,7 @@ static THREAD_RETURN_T THREAD_CALLCONV lane_main( void *vs) | |||
1724 | // Tie "cancel_test()" to the state | 1750 | // Tie "cancel_test()" to the state |
1725 | // | 1751 | // |
1726 | lua_pushcfunction( L, LG_cancel_test); | 1752 | lua_pushcfunction( L, LG_cancel_test); |
1727 | lua_setglobal( L, "cancel_test" ); | 1753 | lua_setglobal( L, "cancel_test"); |
1728 | 1754 | ||
1729 | #ifdef ERROR_FULL_STACK | 1755 | #ifdef ERROR_FULL_STACK |
1730 | // Tie "set_error_reporting()" to the state | 1756 | // Tie "set_error_reporting()" to the state |
@@ -2065,7 +2091,7 @@ LUAG_FUNC( thread_new) | |||
2065 | s->status= PENDING; | 2091 | s->status= PENDING; |
2066 | s->waiting_on = NULL; | 2092 | s->waiting_on = NULL; |
2067 | s->debug_name = NULL; | 2093 | s->debug_name = NULL; |
2068 | s->cancel_request= FALSE; | 2094 | s->cancel_request = CANCEL_NONE; |
2069 | 2095 | ||
2070 | #if THREADWAIT_METHOD == THREADWAIT_CONDVAR | 2096 | #if THREADWAIT_METHOD == THREADWAIT_CONDVAR |
2071 | MUTEX_INIT( &s->done_lock); | 2097 | MUTEX_INIT( &s->done_lock); |
@@ -2183,6 +2209,10 @@ LUAG_FUNC( thread_cancel) | |||
2183 | if( lua_isnumber( L, 2)) | 2209 | if( lua_isnumber( L, 2)) |
2184 | { | 2210 | { |
2185 | secs = lua_tonumber( L, 2); | 2211 | secs = lua_tonumber( L, 2); |
2212 | if( secs < 0.0 && lua_gettop( L) > 2) | ||
2213 | { | ||
2214 | return luaL_error( L, "can't force a soft cancel"); | ||
2215 | } | ||
2186 | ++ force_i; | 2216 | ++ force_i; |
2187 | } | 2217 | } |
2188 | else if( lua_isnil( L, 2)) | 2218 | else if( lua_isnil( L, 2)) |
@@ -2611,8 +2641,9 @@ void register_core_libfuncs_for_keeper( lua_State* L) | |||
2611 | /* | 2641 | /* |
2612 | ** One-time initializations | 2642 | ** One-time initializations |
2613 | */ | 2643 | */ |
2614 | static void init_once_LOCKED( lua_State* L, int const _on_state_create, int const nbKeepers, lua_Number _shutdown_timeout, bool_t _track_lanes) | 2644 | static void init_once_LOCKED( lua_State* L, int const _on_state_create, int const nbKeepers, lua_Number _shutdown_timeout, bool_t _track_lanes, bool_t verbose_errors) |
2615 | { | 2645 | { |
2646 | GVerboseErrors = verbose_errors; | ||
2616 | #if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) | 2647 | #if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) |
2617 | now_secs(); // initialize 'now_secs()' internal offset | 2648 | now_secs(); // initialize 'now_secs()' internal offset |
2618 | #endif | 2649 | #endif |
@@ -2724,10 +2755,12 @@ LUAG_FUNC( configure) | |||
2724 | char const* name = luaL_checkstring( L, lua_upvalueindex( 1)); | 2755 | char const* name = luaL_checkstring( L, lua_upvalueindex( 1)); |
2725 | // all parameter checks are done lua-side | 2756 | // all parameter checks are done lua-side |
2726 | int const nbKeepers = (int)lua_tointeger( L, 1); | 2757 | int const nbKeepers = (int)lua_tointeger( L, 1); |
2758 | // all these can be nil when lanes.core is required internally! (but are only processed at first init anyway) | ||
2727 | int const on_state_create = lua_isfunction( L, 2) ? 2 : 0; | 2759 | int const on_state_create = lua_isfunction( L, 2) ? 2 : 0; |
2728 | lua_Number shutdown_timeout = lua_tonumber( L, 3); | 2760 | lua_Number shutdown_timeout = lua_tonumber( L, 3); |
2729 | bool_t track_lanes = lua_toboolean( L, 4); | 2761 | bool_t track_lanes = lua_toboolean( L, 4); |
2730 | bool_t protect_allocator = lua_toboolean( L, 5); | 2762 | bool_t protect_allocator = lua_toboolean( L, 5); |
2763 | bool_t verbose_errors = lua_toboolean( L, 6); | ||
2731 | 2764 | ||
2732 | DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "%p: lanes.configure() BEGIN\n" INDENT_END, L)); | 2765 | DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "%p: lanes.configure() BEGIN\n" INDENT_END, L)); |
2733 | DEBUGSPEW_CODE( ++ debugspew_indent_depth); | 2766 | DEBUGSPEW_CODE( ++ debugspew_indent_depth); |
@@ -2738,11 +2771,14 @@ LUAG_FUNC( configure) | |||
2738 | { | 2771 | { |
2739 | void* ud; | 2772 | void* ud; |
2740 | lua_Alloc allocf = lua_getallocf( L, &ud); | 2773 | lua_Alloc allocf = lua_getallocf( L, &ud); |
2741 | struct ProtectedAllocator_s* s = (struct ProtectedAllocator_s*) allocf( ud, NULL, 0, sizeof( struct ProtectedAllocator_s)); | 2774 | if( allocf != protected_lua_Alloc) // just in case |
2742 | s->allocf = allocf; | 2775 | { |
2743 | s->ud = ud; | 2776 | struct ProtectedAllocator_s* s = (struct ProtectedAllocator_s*) allocf( ud, NULL, 0, sizeof( struct ProtectedAllocator_s)); |
2744 | MUTEX_INIT( &s->lock); | 2777 | s->allocf = allocf; |
2745 | lua_setallocf( L, protected_lua_Alloc, s); | 2778 | s->ud = ud; |
2779 | MUTEX_INIT( &s->lock); | ||
2780 | lua_setallocf( L, protected_lua_Alloc, s); | ||
2781 | } | ||
2746 | } | 2782 | } |
2747 | 2783 | ||
2748 | // Create main module interface table | 2784 | // Create main module interface table |
@@ -2818,7 +2854,7 @@ LUAG_FUNC( configure) | |||
2818 | static volatile int /*bool*/ go_ahead; // = 0 | 2854 | static volatile int /*bool*/ go_ahead; // = 0 |
2819 | if( InterlockedCompareExchange( &s_initCount, 1, 0) == 0) | 2855 | if( InterlockedCompareExchange( &s_initCount, 1, 0) == 0) |
2820 | { | 2856 | { |
2821 | init_once_LOCKED( L, on_state_create, nbKeepers, shutdown_timeout, track_lanes); | 2857 | init_once_LOCKED( L, on_state_create, nbKeepers, shutdown_timeout, track_lanes, verbose_errors); |
2822 | go_ahead = 1; // let others pass | 2858 | go_ahead = 1; // let others pass |
2823 | } | 2859 | } |
2824 | else | 2860 | else |
@@ -2836,7 +2872,7 @@ LUAG_FUNC( configure) | |||
2836 | // | 2872 | // |
2837 | if( s_initCount == 0) | 2873 | if( s_initCount == 0) |
2838 | { | 2874 | { |
2839 | init_once_LOCKED( L, on_state_create, nbKeepers, shutdown_timeout, track_lanes); | 2875 | init_once_LOCKED( L, on_state_create, nbKeepers, shutdown_timeout, track_lanes, verbose_errors); |
2840 | s_initCount = 1; | 2876 | s_initCount = 1; |
2841 | } | 2877 | } |
2842 | } | 2878 | } |
diff --git a/src/lanes.lua b/src/lanes.lua index ac3da26..175002a 100644 --- a/src/lanes.lua +++ b/src/lanes.lua | |||
@@ -69,6 +69,7 @@ lanes.configure = function( _params) | |||
69 | shutdown_timeout = 0.25, | 69 | shutdown_timeout = 0.25, |
70 | with_timers = true, | 70 | with_timers = true, |
71 | track_lanes = nil, | 71 | track_lanes = nil, |
72 | verbose_errors = false, | ||
72 | -- LuaJIT provides a thread-unsafe allocator by default, so we need to protect it when used in parallel lanes | 73 | -- LuaJIT provides a thread-unsafe allocator by default, so we need to protect it when used in parallel lanes |
73 | protect_allocator = (jit and jit.version) and true or false | 74 | protect_allocator = (jit and jit.version) and true or false |
74 | } | 75 | } |
@@ -105,6 +106,14 @@ lanes.configure = function( _params) | |||
105 | track_lanes = function( _val) | 106 | track_lanes = function( _val) |
106 | -- track_lanes may be nil or boolean | 107 | -- track_lanes may be nil or boolean |
107 | return _val and type( _val) == "boolean" or true | 108 | return _val and type( _val) == "boolean" or true |
109 | end, | ||
110 | verbose_errors = function( _val) | ||
111 | -- verbose_errors may be nil or boolean | ||
112 | if _val then | ||
113 | return type( _val) == "boolean" | ||
114 | else | ||
115 | return true -- _val is either false or nil | ||
116 | end | ||
108 | end | 117 | end |
109 | } | 118 | } |
110 | 119 | ||
@@ -138,7 +147,7 @@ lanes.configure = function( _params) | |||
138 | assert( type( core)=="table") | 147 | assert( type( core)=="table") |
139 | 148 | ||
140 | -- configure() is available only the first time lanes.core is required process-wide, and we *must* call it to have the other functions in the interface | 149 | -- configure() is available only the first time lanes.core is required process-wide, and we *must* call it to have the other functions in the interface |
141 | if core.configure then core.configure( _params.nb_keepers, _params.on_state_create, _params.shutdown_timeout, _params.track_lanes, _params.protect_allocator) end | 150 | if core.configure then core.configure( _params.nb_keepers, _params.on_state_create, _params.shutdown_timeout, _params.track_lanes, _params.protect_allocator, _params.verbose_errors) end |
142 | 151 | ||
143 | local thread_new = assert( core.thread_new) | 152 | local thread_new = assert( core.thread_new) |
144 | 153 | ||
diff --git a/src/tools.c b/src/tools.c index d2dfdf5..a3cc6b7 100644 --- a/src/tools.c +++ b/src/tools.c | |||
@@ -42,6 +42,10 @@ THE SOFTWARE. | |||
42 | #include <string.h> | 42 | #include <string.h> |
43 | #include <ctype.h> | 43 | #include <ctype.h> |
44 | #include <stdlib.h> | 44 | #include <stdlib.h> |
45 | #include <malloc.h> | ||
46 | |||
47 | // for verbose errors | ||
48 | bool_t GVerboseErrors = FALSE; | ||
45 | 49 | ||
46 | /* | 50 | /* |
47 | ** Copied from Lua 5.2 loadlib.c | 51 | ** Copied from Lua 5.2 loadlib.c |
@@ -1285,9 +1289,9 @@ static bool_t push_cached_table( lua_State *L2, uint_t L2_cache_i, lua_State *L, | |||
1285 | * | 1289 | * |
1286 | * Always pushes a function to 'L2'. | 1290 | * Always pushes a function to 'L2'. |
1287 | */ | 1291 | */ |
1288 | static void inter_copy_func( lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i); | 1292 | static void inter_copy_func( lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i, char const* upName_); |
1289 | 1293 | ||
1290 | static void push_cached_func( lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i) | 1294 | static void push_cached_func( lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i, char const* upName_) |
1291 | { | 1295 | { |
1292 | void* const aspointer = (void*)lua_topointer( L, i); | 1296 | void* const aspointer = (void*)lua_topointer( L, i); |
1293 | // TBD: Merge this and same code for tables | 1297 | // TBD: Merge this and same code for tables |
@@ -1319,7 +1323,7 @@ static void push_cached_func( lua_State* L2, uint_t L2_cache_i, lua_State* L, ui | |||
1319 | // via upvalues | 1323 | // via upvalues |
1320 | // | 1324 | // |
1321 | // pushes a copy of the func, stores a reference in the cache | 1325 | // pushes a copy of the func, stores a reference in the cache |
1322 | inter_copy_func( L2, L2_cache_i, L, i); // ... {cache} ... function | 1326 | inter_copy_func( L2, L2_cache_i, L, i, upName_); // ... {cache} ... function |
1323 | } | 1327 | } |
1324 | else // found function in the cache | 1328 | else // found function in the cache |
1325 | { | 1329 | { |
@@ -1363,16 +1367,19 @@ static int discover_object_name_recur( lua_State* L, int shortest_, int depth_) | |||
1363 | lua_pushinteger( L, 1); // o "r" {c} {fqn} ... {?} {?} 1 | 1367 | lua_pushinteger( L, 1); // o "r" {c} {fqn} ... {?} {?} 1 |
1364 | lua_rawset( L, cache); // o "r" {c} {fqn} ... {?} | 1368 | lua_rawset( L, cache); // o "r" {c} {fqn} ... {?} |
1365 | // scan table contents | 1369 | // scan table contents |
1370 | STACK_CHECK( L); | ||
1366 | lua_pushnil( L); // o "r" {c} {fqn} ... {?} nil | 1371 | lua_pushnil( L); // o "r" {c} {fqn} ... {?} nil |
1367 | while( lua_next( L, -2)) // o "r" {c} {fqn} ... {?} k v | 1372 | while( lua_next( L, -2)) // o "r" {c} {fqn} ... {?} k v |
1368 | { | 1373 | { |
1369 | //char const *const key = lua_tostring( L, -2); // only for debugging (BEWARE, IT MAY CHANGE THE VALUE IF IT IS CONVERTIBLE, AND WRECK THE LOOP ITERATION PROCESS!) | 1374 | //char const *const strKey = (lua_type( L, -2) == LUA_TSTRING) ? lua_tostring( L, -2) : NULL; // only for debugging |
1375 | //lua_Number const numKey = (lua_type( L, -2) == LUA_TNUMBER) ? lua_tonumber( L, -2) : -6666; // only for debugging | ||
1370 | // append key name to fqn stack | 1376 | // append key name to fqn stack |
1371 | ++ depth_; | 1377 | ++ depth_; |
1372 | lua_pushvalue( L, -2); // o "r" {c} {fqn} ... {?} k v k | 1378 | lua_pushvalue( L, -2); // o "r" {c} {fqn} ... {?} k v k |
1373 | lua_rawseti( L, fqn, depth_); // o "r" {c} {fqn} ... {?} k v | 1379 | lua_rawseti( L, fqn, depth_); // o "r" {c} {fqn} ... {?} k v |
1374 | if( lua_rawequal( L, -1, what)) // is it what we are looking for? | 1380 | if( lua_rawequal( L, -1, what)) // is it what we are looking for? |
1375 | { | 1381 | { |
1382 | STACK_MID( L, 2); | ||
1376 | // update shortest name | 1383 | // update shortest name |
1377 | if( depth_ < shortest_) | 1384 | if( depth_ < shortest_) |
1378 | { | 1385 | { |
@@ -1382,32 +1389,74 @@ static int discover_object_name_recur( lua_State* L, int shortest_, int depth_) | |||
1382 | } | 1389 | } |
1383 | // no need to search further at this level | 1390 | // no need to search further at this level |
1384 | lua_pop( L, 2); // o "r" {c} {fqn} ... {?} | 1391 | lua_pop( L, 2); // o "r" {c} {fqn} ... {?} |
1392 | STACK_MID( L, 0); | ||
1385 | break; | 1393 | break; |
1386 | } | 1394 | } |
1387 | else if( lua_istable( L, -1)) | 1395 | else if( lua_istable( L, -1)) // o "r" {c} {fqn} ... {?} k {} |
1388 | { | 1396 | { |
1397 | STACK_MID( L, 2); | ||
1389 | shortest_ = discover_object_name_recur( L, shortest_, depth_); | 1398 | shortest_ = discover_object_name_recur( L, shortest_, depth_); |
1390 | // search in the table's metatable too | 1399 | // search in the table's metatable too |
1391 | if( lua_getmetatable( L, -1)) | 1400 | if( lua_getmetatable( L, -1)) // o "r" {c} {fqn} ... {?} k {} {mt} |
1392 | { | 1401 | { |
1393 | if( lua_istable( L, -1)) | 1402 | if( lua_istable( L, -1)) |
1394 | { | 1403 | { |
1404 | ++ depth_; | ||
1405 | lua_pushliteral( L, "__metatable"); // o "r" {c} {fqn} ... {?} k {} {mt} "__metatable" | ||
1406 | lua_rawseti( L, fqn, depth_); // o "r" {c} {fqn} ... {?} k {} {mt} | ||
1395 | shortest_ = discover_object_name_recur( L, shortest_, depth_); | 1407 | shortest_ = discover_object_name_recur( L, shortest_, depth_); |
1408 | lua_pushnil( L); // o "r" {c} {fqn} ... {?} k {} {mt} nil | ||
1409 | lua_rawseti( L, fqn, depth_); // o "r" {c} {fqn} ... {?} k {} {mt} | ||
1410 | -- depth_; | ||
1396 | } | 1411 | } |
1397 | lua_pop( L, 1); | 1412 | lua_pop( L, 1); // o "r" {c} {fqn} ... {?} k {} |
1413 | STACK_MID( L, 2); | ||
1398 | } | 1414 | } |
1399 | } | 1415 | } |
1400 | else if( lua_isuserdata( L, -1)) | 1416 | else if( lua_isthread( L, -1)) // o "r" {c} {fqn} ... {?} k T |
1401 | { | 1417 | { |
1418 | // search in the object's uservalue if it is a table | ||
1419 | lua_getuservalue( L, -1); // o "r" {c} {fqn} ... {?} k T {u} | ||
1420 | if( lua_istable( L, -1)) | ||
1421 | { | ||
1422 | shortest_ = discover_object_name_recur( L, shortest_, depth_); | ||
1423 | } | ||
1424 | lua_pop( L, 1); // o "r" {c} {fqn} ... {?} k T | ||
1425 | STACK_MID( L, 2); | ||
1426 | } | ||
1427 | else if( lua_isuserdata( L, -1)) // o "r" {c} {fqn} ... {?} k U | ||
1428 | { | ||
1429 | STACK_MID( L, 2); | ||
1402 | // search in the object's metatable (some modules are built that way) | 1430 | // search in the object's metatable (some modules are built that way) |
1403 | if( lua_getmetatable( L, -1)) | 1431 | if( lua_getmetatable( L, -1)) // o "r" {c} {fqn} ... {?} k U {mt} |
1404 | { | 1432 | { |
1405 | if( lua_istable( L, -1)) | 1433 | if( lua_istable( L, -1)) |
1406 | { | 1434 | { |
1435 | ++ depth_; | ||
1436 | lua_pushliteral( L, "__metatable"); // o "r" {c} {fqn} ... {?} k U {mt} "__metatable" | ||
1437 | lua_rawseti( L, fqn, depth_); // o "r" {c} {fqn} ... {?} k U {mt} | ||
1407 | shortest_ = discover_object_name_recur( L, shortest_, depth_); | 1438 | shortest_ = discover_object_name_recur( L, shortest_, depth_); |
1439 | lua_pushnil( L); // o "r" {c} {fqn} ... {?} k U {mt} nil | ||
1440 | lua_rawseti( L, fqn, depth_); // o "r" {c} {fqn} ... {?} k U {mt} | ||
1441 | -- depth_; | ||
1408 | } | 1442 | } |
1409 | lua_pop( L, 1); | 1443 | lua_pop( L, 1); // o "r" {c} {fqn} ... {?} k U |
1444 | STACK_MID( L, 2); | ||
1445 | } | ||
1446 | // search in the object's uservalue if it is a table | ||
1447 | lua_getuservalue( L, -1); // o "r" {c} {fqn} ... {?} k U {u} | ||
1448 | if( lua_istable( L, -1)) | ||
1449 | { | ||
1450 | ++ depth_; | ||
1451 | lua_pushliteral( L, "uservalue"); // o "r" {c} {fqn} ... {?} k v {u} "uservalue" | ||
1452 | lua_rawseti( L, fqn, depth_); // o "r" {c} {fqn} ... {?} k v {u} | ||
1453 | shortest_ = discover_object_name_recur( L, shortest_, depth_); | ||
1454 | lua_pushnil( L); // o "r" {c} {fqn} ... {?} k v {u} nil | ||
1455 | lua_rawseti( L, fqn, depth_); // o "r" {c} {fqn} ... {?} k v {u} | ||
1456 | -- depth_; | ||
1410 | } | 1457 | } |
1458 | lua_pop( L, 1); // o "r" {c} {fqn} ... {?} k U | ||
1459 | STACK_MID( L, 2); | ||
1411 | } | 1460 | } |
1412 | // make ready for next iteration | 1461 | // make ready for next iteration |
1413 | lua_pop( L, 1); // o "r" {c} {fqn} ... {?} k | 1462 | lua_pop( L, 1); // o "r" {c} {fqn} ... {?} k |
@@ -1416,6 +1465,7 @@ static int discover_object_name_recur( lua_State* L, int shortest_, int depth_) | |||
1416 | lua_rawseti( L, fqn, depth_); // o "r" {c} {fqn} ... {?} k | 1465 | lua_rawseti( L, fqn, depth_); // o "r" {c} {fqn} ... {?} k |
1417 | -- depth_; | 1466 | -- depth_; |
1418 | } // o "r" {c} {fqn} ... {?} | 1467 | } // o "r" {c} {fqn} ... {?} |
1468 | STACK_END( L, 0); | ||
1419 | // remove the visited table from the cache, in case a shorter path to the searched object exists | 1469 | // remove the visited table from the cache, in case a shorter path to the searched object exists |
1420 | lua_pushvalue( L, -1); // o "r" {c} {fqn} ... {?} {?} | 1470 | lua_pushvalue( L, -1); // o "r" {c} {fqn} ... {?} {?} |
1421 | lua_pushnil( L); // o "r" {c} {fqn} ... {?} {?} nil | 1471 | lua_pushnil( L); // o "r" {c} {fqn} ... {?} {?} nil |
@@ -1442,6 +1492,7 @@ int luaG_nameof( lua_State* L) | |||
1442 | lua_insert( L, -2); // "type" o | 1492 | lua_insert( L, -2); // "type" o |
1443 | return 2; | 1493 | return 2; |
1444 | } | 1494 | } |
1495 | |||
1445 | STACK_GROW( L, 4); | 1496 | STACK_GROW( L, 4); |
1446 | // this slot will contain the shortest name we found when we are done | 1497 | // this slot will contain the shortest name we found when we are done |
1447 | lua_pushnil( L); // o nil | 1498 | lua_pushnil( L); // o nil |
@@ -1461,7 +1512,7 @@ int luaG_nameof( lua_State* L) | |||
1461 | /* | 1512 | /* |
1462 | * Push a looked-up native/LuaJIT function. | 1513 | * Push a looked-up native/LuaJIT function. |
1463 | */ | 1514 | */ |
1464 | static void lookup_native_func( lua_State* L2, lua_State* L, uint_t i) | 1515 | static void lookup_native_func( lua_State* L2, lua_State* L, uint_t i, char const* upName_) |
1465 | { | 1516 | { |
1466 | char const* fqn; // L // L2 | 1517 | char const* fqn; // L // L2 |
1467 | size_t len; | 1518 | size_t len; |
@@ -1478,14 +1529,28 @@ static void lookup_native_func( lua_State* L2, lua_State* L, uint_t i) | |||
1478 | lua_pop( L, 2); // ... f ... | 1529 | lua_pop( L, 2); // ... f ... |
1479 | if( !fqn) | 1530 | if( !fqn) |
1480 | { | 1531 | { |
1481 | char const* from; | 1532 | char const *from, *typewhat, *what, *gotchaA, *gotchaB; |
1482 | // try to discover the name of the function we want to send | 1533 | // try to discover the name of the function we want to send |
1483 | lua_pushcfunction( L, luaG_nameof); // ... f ...luaG_nameof | 1534 | lua_getglobal( L, "decoda_name"); // ... f ... decoda_name |
1484 | lua_pushvalue( L, i); // ... f ... luaG_nameof f | ||
1485 | lua_call( L, 1, 2); // ... f ... "type" "name" | ||
1486 | lua_getglobal( L, "decoda_name"); // ... f ... "type" "name" decoda_name | ||
1487 | from = lua_tostring( L, -1); | 1535 | from = lua_tostring( L, -1); |
1488 | (void) luaL_error( L, "%s '%s' not found in %s origin transfer database.", lua_tostring( L, -3), lua_tostring( L, -2), from ? from : "main"); | 1536 | lua_pushcfunction( L, luaG_nameof); // ... f ... decoda_name luaG_nameof |
1537 | lua_pushvalue( L, i); // ... f ... decoda_name luaG_nameof f | ||
1538 | lua_call( L, 1, 2); // ... f ... decoda_name "type" "name"|nil | ||
1539 | typewhat = (lua_type( L, -2) == LUA_TSTRING) ? lua_tostring( L, -2) : luaL_typename( L, -2); | ||
1540 | // second return value can be nil if the function was not found | ||
1541 | // probable reason: the function was removed from the source Lua state before Lanes was required. | ||
1542 | if( lua_isnil( L, -1)) | ||
1543 | { | ||
1544 | gotchaA = " referenced by"; | ||
1545 | gotchaB = "\n(did you remove it from the source Lua state before requiring Lanes?)"; | ||
1546 | what = upName_; | ||
1547 | } | ||
1548 | else | ||
1549 | { | ||
1550 | gotchaB = ""; | ||
1551 | what = (lua_type( L, -1) == LUA_TSTRING) ? lua_tostring( L, -1) : luaL_typename( L, -1); | ||
1552 | } | ||
1553 | (void) luaL_error( L, "%s%s '%s' not found in %s origin transfer database.%s", typewhat, gotchaA, what, from ? from : "main", gotchaB); | ||
1489 | return; | 1554 | return; |
1490 | } | 1555 | } |
1491 | STACK_END( L, 0); | 1556 | STACK_END( L, 0); |
@@ -1516,9 +1581,9 @@ static void lookup_native_func( lua_State* L2, lua_State* L, uint_t i) | |||
1516 | enum e_vt { | 1581 | enum e_vt { |
1517 | VT_NORMAL, VT_KEY, VT_METATABLE | 1582 | VT_NORMAL, VT_KEY, VT_METATABLE |
1518 | }; | 1583 | }; |
1519 | static bool_t inter_copy_one_( lua_State *L2, uint_t L2_cache_i, lua_State *L, uint_t i, enum e_vt value_type ); | 1584 | static bool_t inter_copy_one_( lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i, enum e_vt value_type, char const* upName_); |
1520 | 1585 | ||
1521 | static void inter_copy_func( lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i) | 1586 | static void inter_copy_func( lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i, char const* upName_) |
1522 | { | 1587 | { |
1523 | FuncSubType funcSubType; | 1588 | FuncSubType funcSubType; |
1524 | /*lua_CFunction cfunc =*/ luaG_tocfunction( L, i, &funcSubType); // NULL for LuaJIT-fast && bytecode functions | 1589 | /*lua_CFunction cfunc =*/ luaG_tocfunction( L, i, &funcSubType); // NULL for LuaJIT-fast && bytecode functions |
@@ -1612,14 +1677,14 @@ static void inter_copy_func( lua_State* L2, uint_t L2_cache_i, lua_State* L, uin | |||
1612 | * instead, the function shall have LUA_RIDX_GLOBALS taken in the destination state! | 1677 | * instead, the function shall have LUA_RIDX_GLOBALS taken in the destination state! |
1613 | */ | 1678 | */ |
1614 | { | 1679 | { |
1615 | DEBUGSPEW_CODE( char const* upname); | 1680 | char const* upname; |
1616 | #if LUA_VERSION_NUM == 502 | 1681 | #if LUA_VERSION_NUM == 502 |
1617 | // With Lua 5.2, each Lua function gets its environment as one of its upvalues (named LUA_ENV, aka "_ENV" by default) | 1682 | // With Lua 5.2, each Lua function gets its environment as one of its upvalues (named LUA_ENV, aka "_ENV" by default) |
1618 | // Generally this is LUA_RIDX_GLOBALS, which we don't want to copy from the source to the destination state... | 1683 | // Generally this is LUA_RIDX_GLOBALS, which we don't want to copy from the source to the destination state... |
1619 | // -> if we encounter an upvalue equal to the global table in the source, bind it to the destination's global table | 1684 | // -> if we encounter an upvalue equal to the global table in the source, bind it to the destination's global table |
1620 | lua_pushglobaltable( L); // ... _G | 1685 | lua_pushglobaltable( L); // ... _G |
1621 | #endif // LUA_VERSION_NUM | 1686 | #endif // LUA_VERSION_NUM |
1622 | for( n = 0; (DEBUGSPEW_CODE( upname =) lua_getupvalue( L, i, 1 + n)) != NULL; ++ n) | 1687 | for( n = 0; (upname = lua_getupvalue( L, i, 1 + n)) != NULL; ++ n) |
1623 | { // ... _G up[n] | 1688 | { // ... _G up[n] |
1624 | DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "UPNAME[%d]: %s\n" INDENT_END, n, upname)); | 1689 | DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "UPNAME[%d]: %s\n" INDENT_END, n, upname)); |
1625 | #if LUA_VERSION_NUM == 502 | 1690 | #if LUA_VERSION_NUM == 502 |
@@ -1630,8 +1695,10 @@ static void inter_copy_func( lua_State* L2, uint_t L2_cache_i, lua_State* L, uin | |||
1630 | else | 1695 | else |
1631 | #endif // LUA_VERSION_NUM | 1696 | #endif // LUA_VERSION_NUM |
1632 | { | 1697 | { |
1633 | if( !inter_copy_one_( L2, L2_cache_i, L, lua_gettop( L), VT_NORMAL)) // ... {cache} ... function <upvalues> | 1698 | if( !inter_copy_one_( L2, L2_cache_i, L, lua_gettop( L), VT_NORMAL, upname)) // ... {cache} ... function <upvalues> |
1699 | { | ||
1634 | luaL_error( L, "Cannot copy upvalue type '%s'", luaL_typename( L, -1)); | 1700 | luaL_error( L, "Cannot copy upvalue type '%s'", luaL_typename( L, -1)); |
1701 | } | ||
1635 | } | 1702 | } |
1636 | lua_pop( L, 1); // ... _G | 1703 | lua_pop( L, 1); // ... _G |
1637 | } | 1704 | } |
@@ -1664,7 +1731,7 @@ static void inter_copy_func( lua_State* L2, uint_t L2_cache_i, lua_State* L, uin | |||
1664 | { | 1731 | { |
1665 | lua_pop( L2, 1); // ... {cache} ... | 1732 | lua_pop( L2, 1); // ... {cache} ... |
1666 | // No need to transfer upvalues for C/JIT functions since they weren't actually copied, only looked up | 1733 | // No need to transfer upvalues for C/JIT functions since they weren't actually copied, only looked up |
1667 | lookup_native_func( L2, L, i); // ... {cache} ... function | 1734 | lookup_native_func( L2, L, i, upName_); // ... {cache} ... function |
1668 | } | 1735 | } |
1669 | STACK_END( L, 0); | 1736 | STACK_END( L, 0); |
1670 | } | 1737 | } |
@@ -1679,7 +1746,7 @@ static void inter_copy_func( lua_State* L2, uint_t L2_cache_i, lua_State* L, uin | |||
1679 | * | 1746 | * |
1680 | * Returns TRUE if value was pushed, FALSE if its type is non-supported. | 1747 | * Returns TRUE if value was pushed, FALSE if its type is non-supported. |
1681 | */ | 1748 | */ |
1682 | static bool_t inter_copy_one_( lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i, enum e_vt vt) | 1749 | static bool_t inter_copy_one_( lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i, enum e_vt vt, char const* upName_) |
1683 | { | 1750 | { |
1684 | bool_t ret = TRUE; | 1751 | bool_t ret = TRUE; |
1685 | 1752 | ||
@@ -1762,25 +1829,9 @@ static bool_t inter_copy_one_( lua_State* L2, uint_t L2_cache_i, lua_State* L, u | |||
1762 | break; | 1829 | break; |
1763 | } | 1830 | } |
1764 | { | 1831 | { |
1765 | /* | 1832 | DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "FUNCTION\n" INDENT_END)); |
1766 | * Passing C functions is risky; if they refer to LUA_ENVIRONINDEX | ||
1767 | * and/or LUA_REGISTRYINDEX they might work unintended (not work) | ||
1768 | * at the target. | ||
1769 | * | ||
1770 | * On the other hand, NOT copying them causes many self tests not | ||
1771 | * to work (timer, hangtest, ...) | ||
1772 | * | ||
1773 | * The trouble is, we cannot KNOW if the function at hand is safe | ||
1774 | * or not. We cannot study it's behaviour. We could trust the user, | ||
1775 | * but they might not even know they're sending lua_CFunction over | ||
1776 | * (as upvalues etc.). | ||
1777 | */ | ||
1778 | #if 0 | ||
1779 | if( lua_iscfunction( L, i)) | ||
1780 | luaL_error( L, "Copying lua_CFunction between Lua states is risky, and currently disabled." ); | ||
1781 | #endif | ||
1782 | STACK_CHECK( L2); | 1833 | STACK_CHECK( L2); |
1783 | push_cached_func( L2, L2_cache_i, L, i); | 1834 | push_cached_func( L2, L2_cache_i, L, i, upName_); |
1784 | STACK_END( L2, 1); | 1835 | STACK_END( L2, 1); |
1785 | } | 1836 | } |
1786 | break; | 1837 | break; |
@@ -1822,13 +1873,28 @@ static bool_t inter_copy_one_( lua_State* L2, uint_t L2_cache_i, lua_State* L, u | |||
1822 | 1873 | ||
1823 | /* Only basic key types are copied over; others ignored | 1874 | /* Only basic key types are copied over; others ignored |
1824 | */ | 1875 | */ |
1825 | if( inter_copy_one_( L2, 0 /*key*/, L, key_i, VT_KEY)) | 1876 | if( inter_copy_one_( L2, 0 /*key*/, L, key_i, VT_KEY, upName_)) |
1826 | { | 1877 | { |
1878 | char* valPath = (char*) upName_; | ||
1879 | if( GVerboseErrors) | ||
1880 | { | ||
1881 | // for debug purposes, let's try to build a useful name | ||
1882 | if( lua_type( L, key_i) == LUA_TSTRING) | ||
1883 | { | ||
1884 | valPath = (char*) alloca( strlen( upName_) + strlen( lua_tostring( L, key_i)) + 2); | ||
1885 | sprintf( valPath, "%s.%s", upName_, lua_tostring( L, key_i)); | ||
1886 | } | ||
1887 | else if( lua_type( L, key_i) == LUA_TNUMBER) | ||
1888 | { | ||
1889 | valPath = (char*) alloca( strlen( upName_) + 32 + 3); | ||
1890 | sprintf( valPath, "%s[" LUA_NUMBER_FMT "]", upName_, lua_tonumber( L, key_i)); | ||
1891 | } | ||
1892 | } | ||
1827 | /* | 1893 | /* |
1828 | * Contents of metatables are copied with cache checking; | 1894 | * Contents of metatables are copied with cache checking; |
1829 | * important to detect loops. | 1895 | * important to detect loops. |
1830 | */ | 1896 | */ |
1831 | if( inter_copy_one_( L2, L2_cache_i, L, val_i, VT_NORMAL)) | 1897 | if( inter_copy_one_( L2, L2_cache_i, L, val_i, VT_NORMAL, valPath)) |
1832 | { | 1898 | { |
1833 | ASSERT_L( lua_istable(L2,-3)); | 1899 | ASSERT_L( lua_istable(L2,-3)); |
1834 | lua_rawset( L2, -3); // add to table (pops key & val) | 1900 | lua_rawset( L2, -3); // add to table (pops key & val) |
@@ -1870,7 +1936,7 @@ static bool_t inter_copy_one_( lua_State* L2, uint_t L2_cache_i, lua_State* L, u | |||
1870 | lua_pop( L2, 1); | 1936 | lua_pop( L2, 1); |
1871 | STACK_MID( L2, 2); | 1937 | STACK_MID( L2, 2); |
1872 | ASSERT_L( lua_istable(L,-1)); | 1938 | ASSERT_L( lua_istable(L,-1)); |
1873 | if( inter_copy_one_( L2, L2_cache_i /*for function cacheing*/, L, lua_gettop(L) /*[-1]*/, VT_METATABLE)) | 1939 | if( inter_copy_one_( L2, L2_cache_i /*for function cacheing*/, L, lua_gettop(L) /*[-1]*/, VT_METATABLE, upName_)) |
1874 | { | 1940 | { |
1875 | // | 1941 | // |
1876 | // L2 ([-3]: copied table) | 1942 | // L2 ([-3]: copied table) |
@@ -1942,7 +2008,9 @@ int luaG_inter_copy( lua_State* L, lua_State* L2, uint_t n) | |||
1942 | { | 2008 | { |
1943 | uint_t top_L = lua_gettop( L); | 2009 | uint_t top_L = lua_gettop( L); |
1944 | uint_t top_L2 = lua_gettop( L2); | 2010 | uint_t top_L2 = lua_gettop( L2); |
1945 | uint_t i; | 2011 | uint_t i, j; |
2012 | char tmpBuf[16]; | ||
2013 | char* pBuf = GVerboseErrors ? tmpBuf : "?"; | ||
1946 | bool_t copyok = TRUE; | 2014 | bool_t copyok = TRUE; |
1947 | 2015 | ||
1948 | if( n > top_L) | 2016 | if( n > top_L) |
@@ -1960,9 +2028,13 @@ int luaG_inter_copy( lua_State* L, lua_State* L2, uint_t n) | |||
1960 | */ | 2028 | */ |
1961 | lua_newtable( L2); | 2029 | lua_newtable( L2); |
1962 | 2030 | ||
1963 | for( i = top_L - n + 1; i <= top_L; ++ i) | 2031 | for( i = top_L - n + 1, j = 1; i <= top_L; ++ i, ++ j) |
1964 | { | 2032 | { |
1965 | copyok = inter_copy_one_( L2, top_L2 + 1, L, i, VT_NORMAL); | 2033 | if( GVerboseErrors) |
2034 | { | ||
2035 | sprintf( tmpBuf, "arg_%d", j); | ||
2036 | } | ||
2037 | copyok = inter_copy_one_( L2, top_L2 + 1, L, i, VT_NORMAL, pBuf); | ||
1966 | if( !copyok) | 2038 | if( !copyok) |
1967 | { | 2039 | { |
1968 | break; | 2040 | break; |
diff --git a/src/tools.h b/src/tools.h index 2fe7259..93ed92c 100644 --- a/src/tools.h +++ b/src/tools.h | |||
@@ -107,5 +107,8 @@ void populate_func_lookup_table( lua_State* L, int _i, char const* _name); | |||
107 | void serialize_require( lua_State *L); | 107 | void serialize_require( lua_State *L); |
108 | extern MUTEX_T require_cs; | 108 | extern MUTEX_T require_cs; |
109 | 109 | ||
110 | // for verbose errors | ||
111 | extern bool_t GVerboseErrors; | ||
112 | |||
110 | #endif | 113 | #endif |
111 | // TOOLS_H | 114 | // TOOLS_H |
diff --git a/tests/fifo.lua b/tests/fifo.lua index b68d8a4..47db4c9 100644 --- a/tests/fifo.lua +++ b/tests/fifo.lua | |||
@@ -4,10 +4,9 @@ | |||
4 | -- Sample program for Lua Lanes | 4 | -- Sample program for Lua Lanes |
5 | -- | 5 | -- |
6 | 6 | ||
7 | local lanes = require "lanes" | 7 | local lanes = require "lanes".configure{shutdown_timeout=3,with_timers=true} |
8 | lanes.configure() | ||
9 | 8 | ||
10 | local linda= lanes.linda() | 9 | local linda= lanes.linda( "atom") |
11 | local atomic_inc= lanes.genatomic( linda, "FIFO_n" ) | 10 | local atomic_inc= lanes.genatomic( linda, "FIFO_n" ) |
12 | 11 | ||
13 | assert( atomic_inc()==1 ) | 12 | assert( atomic_inc()==1 ) |
@@ -46,3 +45,5 @@ print( B:receive( 2.0 ) ) | |||
46 | -- Note: A and B can be passed between threads, or used as upvalues | 45 | -- Note: A and B can be passed between threads, or used as upvalues |
47 | -- by multiple threads (other parts will be copied but the 'linda' | 46 | -- by multiple threads (other parts will be copied but the 'linda' |
48 | -- handle is shared userdata and will thus point to the single place) | 47 | -- handle is shared userdata and will thus point to the single place) |
48 | lanes.timer_lane:cancel() | ||
49 | lanes.timer_lane:join() \ No newline at end of file | ||
diff --git a/tests/linda_perf.lua b/tests/linda_perf.lua index be582ce..ebe9eac 100644 --- a/tests/linda_perf.lua +++ b/tests/linda_perf.lua | |||
@@ -84,8 +84,10 @@ local tests = | |||
84 | { 4000000, 0, 21}, | 84 | { 4000000, 0, 21}, |
85 | { 4000000, 0, 44},]] | 85 | { 4000000, 0, 44},]] |
86 | } | 86 | } |
87 | print "tests #1" | ||
87 | for k, v in pairs( tests) do | 88 | for k, v in pairs( tests) do |
88 | local pre, loop, batch = v[1], v[2], v[3] | 89 | local pre, loop, batch = v[1], v[2], v[3] |
90 | print( "testing", pre, loop, batch) | ||
89 | print( pre, loop, batch, "duration = " .. ziva( pre, loop, batch)) | 91 | print( pre, loop, batch, "duration = " .. ziva( pre, loop, batch)) |
90 | end | 92 | end |
91 | 93 | ||
@@ -188,8 +190,11 @@ local tests2 = | |||
188 | { 4000000, 0, 21}, | 190 | { 4000000, 0, 21}, |
189 | { 4000000, 0, 44}, | 191 | { 4000000, 0, 44}, |
190 | } | 192 | } |
193 | |||
194 | print "tests #2" | ||
191 | for k, v in pairs( tests2) do | 195 | for k, v in pairs( tests2) do |
192 | local pre, loop, batch = v[1], v[2], v[3] | 196 | local pre, loop, batch = v[1], v[2], v[3] |
197 | print( "testing", pre, loop, batch) | ||
193 | print( pre, loop, batch, "duration = " .. ziva2( pre, loop, batch)) | 198 | print( pre, loop, batch, "duration = " .. ziva2( pre, loop, batch)) |
194 | end | 199 | end |
195 | 200 | ||
diff --git a/tests/require.lua b/tests/require.lua index 1d081b4..656a7dd 100644 --- a/tests/require.lua +++ b/tests/require.lua | |||
@@ -4,9 +4,10 @@ | |||
4 | -- Test that 'require' works from sublanes | 4 | -- Test that 'require' works from sublanes |
5 | -- | 5 | -- |
6 | lanes = require "lanes" | 6 | lanes = require "lanes" |
7 | lanes.configure() | 7 | lanes.configure{with_timers = false} |
8 | 8 | ||
9 | local function a_lane() | 9 | local function a_lane() |
10 | print "IN A LANE" | ||
10 | -- To require 'math' we still actually need to have it initialized for | 11 | -- To require 'math' we still actually need to have it initialized for |
11 | -- the lane. | 12 | -- the lane. |
12 | -- | 13 | -- |
@@ -15,7 +16,7 @@ local function a_lane() | |||
15 | assert( math.sqrt(4)==2 ) | 16 | assert( math.sqrt(4)==2 ) |
16 | 17 | ||
17 | assert( lanes==nil ) | 18 | assert( lanes==nil ) |
18 | local lanes = require "lanes".configure() | 19 | local lanes = require "lanes".configure{with_timers = false} |
19 | assert( lanes and lanes.gen ) | 20 | assert( lanes and lanes.gen ) |
20 | 21 | ||
21 | local h= lanes.gen( function() return 42 end ) () | 22 | local h= lanes.gen( function() return 42 end ) () |
@@ -24,7 +25,7 @@ local function a_lane() | |||
24 | return v==42 | 25 | return v==42 |
25 | end | 26 | end |
26 | 27 | ||
27 | local gen= lanes.gen( "math,package,string,table", a_lane ) | 28 | local gen= lanes.gen( "math,package,string,table", {package={}},a_lane ) |
28 | 29 | ||
29 | local h= gen() | 30 | local h= gen() |
30 | local ret= h[1] | 31 | local ret= h[1] |