diff options
author | Benoit Germain <bnt.germain@gmail.com> | 2011-02-12 17:06:03 +0100 |
---|---|---|
committer | Benoit Germain <bnt.germain@gmail.com> | 2011-02-12 17:06:03 +0100 |
commit | a661736f7984292a41d71847de68590f6b8ca08a (patch) | |
tree | 43e0799c20e835f1cc2d8c5fa1324b64301763e0 | |
parent | c91a03bd956bf19848253bded8217687ccd2ad81 (diff) | |
download | lanes-a661736f7984292a41d71847de68590f6b8ca08a.tar.gz lanes-a661736f7984292a41d71847de68590f6b8ca08a.tar.bz2 lanes-a661736f7984292a41d71847de68590f6b8ca08a.zip |
Changed idfunc signature and contract to clarify that fact it is not lua-callable and to be able to require the module it was exported from in the target lanes.
-rw-r--r-- | CHANGES | 4 | ||||
-rw-r--r-- | dist.info | 2 | ||||
-rw-r--r-- | docs/index.html | 99 | ||||
-rw-r--r-- | src/lanes.c | 102 | ||||
-rw-r--r-- | src/lanes.lua | 12 | ||||
-rw-r--r-- | src/tools.c | 170 | ||||
-rw-r--r-- | src/tools.h | 8 | ||||
-rw-r--r-- | tests/basic.lua | 17 |
8 files changed, 226 insertions, 188 deletions
@@ -3,6 +3,10 @@ CHANGES: | |||
3 | 3 | ||
4 | CHANGE X: | 4 | CHANGE X: |
5 | 5 | ||
6 | CHANGE 25 BGe 12-Feb-2011: | ||
7 | Changed idfunc signature and contract to clarify that fact it is not lua-callable | ||
8 | and to be able to require the module it was exported from in the target lanes | ||
9 | |||
6 | CHANGE 24 DPtr 25-Jan-2011: | 10 | CHANGE 24 DPtr 25-Jan-2011: |
7 | Changed lanes.c to export functions as a module rather than writing them directly to the globals table. | 11 | Changed lanes.c to export functions as a module rather than writing them directly to the globals table. |
8 | 12 | ||
@@ -1,7 +1,7 @@ | |||
1 | --- This file is part of LuaDist project | 1 | --- This file is part of LuaDist project |
2 | 2 | ||
3 | name = "lanes" | 3 | name = "lanes" |
4 | version = "2.0.10" | 4 | version = "2.0.11" |
5 | 5 | ||
6 | desc = "Lanes is a lightweight, native, lazy evaluating multithreading library for Lua 5.1." | 6 | desc = "Lanes is a lightweight, native, lazy evaluating multithreading library for Lua 5.1." |
7 | author = "Asko Kauppi" | 7 | author = "Asko Kauppi" |
diff --git a/docs/index.html b/docs/index.html index fec2212..9d66510 100644 --- a/docs/index.html +++ b/docs/index.html | |||
@@ -21,11 +21,11 @@ | |||
21 | <tr> | 21 | <tr> |
22 | <td align="center"> | 22 | <td align="center"> |
23 | <a href="http://www.lua.org"> | 23 | <a href="http://www.lua.org"> |
24 | <img src="http://akauppi.googlepages.com/multi.png" alt="Lua" align="middle" border="0" height="120" width="128" /> | 24 | <img src="multi.png" alt="Lua" align="middle" border="0" height="120" width="128" /> |
25 | <img src="http://akauppi.googlepages.com/multi.png" alt="Lua" align="middle" border="0" height="120" width="128" /> | 25 | <img src="multi.png" alt="Lua" align="middle" border="0" height="120" width="128" /> |
26 | <img src="http://akauppi.googlepages.com/multi.png" alt="Lua" align="middle" border="0" height="120" width="128" /> | 26 | <img src="multi.png" alt="Lua" align="middle" border="0" height="120" width="128" /> |
27 | <img src="http://akauppi.googlepages.com/multi.png" alt="Lua" align="middle" border="0" height="120" width="128" /> | 27 | <img src="multi.png" alt="Lua" align="middle" border="0" height="120" width="128" /> |
28 | <img src="http://akauppi.googlepages.com/multi.png" alt="Lua" align="middle" border="0" height="120" width="128" /> | 28 | <img src="multi.png" alt="Lua" align="middle" border="0" height="120" width="128" /> |
29 | </a></td> | 29 | </a></td> |
30 | </tr> | 30 | </tr> |
31 | <tr> | 31 | <tr> |
@@ -56,7 +56,7 @@ | |||
56 | 56 | ||
57 | <p><br/><font size="-1"><i>Copyright © 2007-11 Asko Kauppi. All rights reserved.</i> | 57 | <p><br/><font size="-1"><i>Copyright © 2007-11 Asko Kauppi. All rights reserved.</i> |
58 | <br>Lua Lanes is published under the same <A HREF="http://en.wikipedia.org/wiki/MIT_License">MIT license</A> as Lua 5.1. | 58 | <br>Lua Lanes is published under the same <A HREF="http://en.wikipedia.org/wiki/MIT_License">MIT license</A> as Lua 5.1. |
59 | </p><p>This document was revised on 3-Jan-11, and applies to version 2.0.10. | 59 | </p><p>This document was revised on 12-Feb-11, and applies to version 2.0.11. |
60 | </font></p> | 60 | </font></p> |
61 | 61 | ||
62 | </center> | 62 | </center> |
@@ -124,15 +124,14 @@ | |||
124 | <li>Windows 2000/XP and later <font size="-1">(MinGW or Visual C++ 2005/2008)</font></li> | 124 | <li>Windows 2000/XP and later <font size="-1">(MinGW or Visual C++ 2005/2008)</font></li> |
125 | <!-- | 125 | <!-- |
126 | Other OS'es here once people help test them. (and the tester's name) | 126 | Other OS'es here once people help test them. (and the tester's name) |
127 | 127 | ||
128 | Win64, BSD, Linux x64, Linux embedded, QNX, Solaris, ... | 128 | Win64, BSD, Linux x64, Linux embedded, QNX, Solaris, ... |
129 | --> | 129 | --> |
130 | </ul> | 130 | </ul> |
131 | 131 | ||
132 | <p>The underlying threading code can be compiled either towards Win32 API | 132 | <p>The underlying threading code can be compiled either towards Win32 API |
133 | or <a TARGET="_blank" HREF="http://en.wikipedia.org/wiki/POSIX_Threads">Pthreads</a>. Unfortunately, thread prioritation under Pthreads is a JOKE, | 133 | or <a TARGET="_blank" HREF="http://en.wikipedia.org/wiki/POSIX_Threads">Pthreads</a>. Unfortunately, thread prioritization under Pthreads is a JOKE, |
134 | requiring OS specific tweaks and guessing undocumented behaviour. Other | 134 | requiring OS specific tweaks and guessing undocumented behaviour. Other features should be portable to any modern platform. |
135 | features should be portable to any modern platform. | ||
136 | </p> | 135 | </p> |
137 | </p> | 136 | </p> |
138 | 137 | ||
@@ -147,7 +146,7 @@ details and limitations. | |||
147 | </p> | 146 | </p> |
148 | 147 | ||
149 | <p>To install Lanes, all you need are the <tt>lanes.lua</tt> and <tt>lua51-lanes.so|dll</tt> | 148 | <p>To install Lanes, all you need are the <tt>lanes.lua</tt> and <tt>lua51-lanes.so|dll</tt> |
150 | files to be reachable by Lua (see LUA_PATH, LUA_CPATH). | 149 | files to be reachable by Lua (see LUA_PATH, LUA_CPATH). |
151 | 150 | ||
152 | Or use <A HREF="http://www.luarocks.org" TARGET="_blank">Lua Rocks</A> package management. | 151 | Or use <A HREF="http://www.luarocks.org" TARGET="_blank">Lua Rocks</A> package management. |
153 | </p> | 152 | </p> |
@@ -264,7 +263,7 @@ also in the new lanes. | |||
264 | by platform. Especially Linux kernel 2.6 is not supporting priorities in user mode. | 263 | by platform. Especially Linux kernel 2.6 is not supporting priorities in user mode. |
265 | </td></tr> | 264 | </td></tr> |
266 | </table> | 265 | </table> |
267 | 266 | ||
268 | </p> | 267 | </p> |
269 | 268 | ||
270 | <h3>Free running lanes</h3> | 269 | <h3>Free running lanes</h3> |
@@ -383,7 +382,7 @@ for the request to be processed, or a timeout to occur. | |||
383 | Returns <tt>true</tt> if the lane was already done (in <tt>"done"</tt>, <tt>"error"</tt> or <tt>"cancelled"</tt> status) | 382 | Returns <tt>true</tt> if the lane was already done (in <tt>"done"</tt>, <tt>"error"</tt> or <tt>"cancelled"</tt> status) |
384 | or if the cancellation was fruitful within timeout period. | 383 | or if the cancellation was fruitful within timeout period. |
385 | </p><p> | 384 | </p><p> |
386 | If the lane is still running and <tt>force_kill</tt> is <tt>true</tt>, the | 385 | If the lane is still running and <tt>force_kill</tt> is <tt>true</tt>, the |
387 | OS thread running the lane is forcefully killed. This means no GC, and should | 386 | OS thread running the lane is forcefully killed. This means no GC, and should |
388 | generally be the last resort. | 387 | generally be the last resort. |
389 | </p> | 388 | </p> |
@@ -404,16 +403,16 @@ or <tt>send</tt> call is currently not awakened, and may be a reason for a non-d | |||
404 | </table> | 403 | </table> |
405 | 404 | ||
406 | <p>The <tt>error</tt> call is used for throwing exceptions in Lua. What Lua | 405 | <p>The <tt>error</tt> call is used for throwing exceptions in Lua. What Lua |
407 | does not offer, however, is scoped <a href="http://en.wikipedia.org/wiki/Finalizer">finalizers</a> | 406 | does not offer, however, is scoped <a href="http://en.wikipedia.org/wiki/Finalizer">finalizers</a> |
408 | that would get called when a certain block of instructions gets exited, whether | 407 | that would get called when a certain block of instructions gets exited, whether |
409 | through peaceful return or abrupt <tt>error</tt>. | 408 | through peaceful return or abrupt <tt>error</tt>. |
410 | </p> | 409 | </p> |
411 | <p>Since 2.0.3, Lanes prepares a function <tt>set_finalizer</tt> for doing this. | 410 | <p>Since 2.0.3, Lanes prepares a function <tt>set_finalizer</tt> for doing this. |
412 | Any functions given to it will be called in the lane Lua state, just prior to | 411 | Any functions given to it will be called in the lane Lua state, just prior to |
413 | closing it. They are not called in any particular order. | 412 | closing it. They are not called in any particular order. |
414 | </p> | 413 | </p> |
415 | <p>An error in a finalizer itself overrides the state of the regular chunk | 414 | <p>An error in a finalizer itself overrides the state of the regular chunk |
416 | (in practise, it would be highly preferable <i>not</i> to have errors in finalizers). | 415 | (in practise, it would be highly preferable <i>not</i> to have errors in finalizers). |
417 | If one finalizer errors, the others may not get called. | 416 | If one finalizer errors, the others may not get called. |
418 | </p> | 417 | </p> |
419 | 418 | ||
@@ -424,7 +423,7 @@ If one finalizer errors, the others may not get called. | |||
424 | 423 | ||
425 | <p>Communications between lanes is completely detached from the lane handles | 424 | <p>Communications between lanes is completely detached from the lane handles |
426 | themselves. By itself, a lane can only provide return values once it's finished, | 425 | themselves. By itself, a lane can only provide return values once it's finished, |
427 | or throw an error. Needs to communicate during runtime are handled by <A HREF="http://en.wikipedia.org/wiki/Linda_%28coordination_language%29" TARGET="_blank">Linda objects</A>, which are | 426 | or throw an error. Needs to communicate during runtime are handled by <A HREF="http://en.wikipedia.org/wiki/Linda_%28coordination_language%29" TARGET="_blank">Linda objects</A>, which are |
428 | <A HREF="#deep_userdata">deep userdata</A> instances. They can be provided to a lane | 427 | <A HREF="#deep_userdata">deep userdata</A> instances. They can be provided to a lane |
429 | as startup parameters, upvalues or in some other Linda's message. | 428 | as startup parameters, upvalues or in some other Linda's message. |
430 | </p><p> | 429 | </p><p> |
@@ -445,7 +444,7 @@ level locking is required; each Linda operation is atomic. | |||
445 | linda:send( "x", i ) -- linda as upvalue | 444 | linda:send( "x", i ) -- linda as upvalue |
446 | end | 445 | end |
447 | end | 446 | end |
448 | 447 | ||
449 | a= lanes.gen("",loop)( 10000 ) | 448 | a= lanes.gen("",loop)( 10000 ) |
450 | 449 | ||
451 | while true do | 450 | while true do |
@@ -512,7 +511,7 @@ many producers, and many consumers. It's up to you. | |||
512 | if the queue limit was met, and the queue did not empty enough during the given | 511 | if the queue limit was met, and the queue did not empty enough during the given |
513 | timeout. | 512 | timeout. |
514 | </p><p> | 513 | </p><p> |
515 | Equally, <tt>receive</tt> returns a value and the key that provided the value, | 514 | Equally, <tt>receive</tt> returns a value and the key that provided the value, |
516 | or nothing for timeout. Note that <tt>nil</tt>s can be sent and received; | 515 | or nothing for timeout. Note that <tt>nil</tt>s can be sent and received; |
517 | the <tt>key</tt> value will tell it apart from a timeout. | 516 | the <tt>key</tt> value will tell it apart from a timeout. |
518 | </p><p> | 517 | </p><p> |
@@ -537,8 +536,8 @@ be used for making priority queues. | |||
537 | The table access methods are for accessing a slot without queuing or consuming. | 536 | The table access methods are for accessing a slot without queuing or consuming. |
538 | They can be used for making shared tables of storage among the lanes. | 537 | They can be used for making shared tables of storage among the lanes. |
539 | </p><p> | 538 | </p><p> |
540 | Writing to a slot overwrites existing value, and clears any possible queued | 539 | Writing to a slot overwrites existing value, and clears any possible queued |
541 | entries. Table access and <tt>send</tt>/<tt>receive</tt> can be used together; | 540 | entries. Table access and <tt>send</tt>/<tt>receive</tt> can be used together; |
542 | reading a slot essentially peeks the next outcoming value of a queue. | 541 | reading a slot essentially peeks the next outcoming value of a queue. |
543 | </p> | 542 | </p> |
544 | 543 | ||
@@ -554,7 +553,7 @@ discussing it is good for a preview of how deep userdata works. | |||
554 | Because proxy objects (<tt>linda_h</tt>) are just pointers to the real, deep | 553 | Because proxy objects (<tt>linda_h</tt>) are just pointers to the real, deep |
555 | userdata, they cannot be used to identify a certain Linda from the others. | 554 | userdata, they cannot be used to identify a certain Linda from the others. |
556 | The internal timer system needs to do this, and the <tt>:deep()</tt> method | 555 | The internal timer system needs to do this, and the <tt>:deep()</tt> method |
557 | has been added for its use. It returns a light userdata pointing to the | 556 | has been added for its use. It returns a light userdata pointing to the |
558 | <i>actual</i> deep object, and thus can be used for seeing, which proxies actually | 557 | <i>actual</i> deep object, and thus can be used for seeing, which proxies actually |
559 | mean the same underlying object. You might or might not need a similar system | 558 | mean the same underlying object. You might or might not need a similar system |
560 | with your own deep userdata. | 559 | with your own deep userdata. |
@@ -574,13 +573,13 @@ you want to use several? | |||
574 | for the other. This keeps your code clear and readable. You can pass | 573 | for the other. This keeps your code clear and readable. You can pass |
575 | multiple Linda handles to a lane with practically no added cost. | 574 | multiple Linda handles to a lane with practically no added cost. |
576 | </li> | 575 | </li> |
577 | 576 | ||
578 | <li>Namespace control. Linda keys have a "flat" namespace, so collisions | 577 | <li>Namespace control. Linda keys have a "flat" namespace, so collisions |
579 | are possible if you try to use the same Linda for too many separate uses. | 578 | are possible if you try to use the same Linda for too many separate uses. |
580 | </li> | 579 | </li> |
581 | 580 | ||
582 | <li>Performance. Changing any slot in a Linda causes all pending threads | 581 | <li>Performance. Changing any slot in a Linda causes all pending threads |
583 | for that Linda to be momentarily awakened (at least in the C level). | 582 | for that Linda to be momentarily awakened (at least in the C level). |
584 | This can degrade performance due to unnecessary OS level context switches. | 583 | This can degrade performance due to unnecessary OS level context switches. |
585 | </li> | 584 | </li> |
586 | </ul> | 585 | </ul> |
@@ -603,13 +602,13 @@ events to a common Linda, but... :).</font> | |||
603 | </table> | 602 | </table> |
604 | 603 | ||
605 | <p> | 604 | <p> |
606 | Timers can be run once, or in a reoccurring fashion (<tt>period_secs > 0</tt>). | 605 | Timers can be run once, or in a reoccurring fashion (<tt>period_secs > 0</tt>). |
607 | The first occurrence can be given either as a date or as a relative delay in seconds. | 606 | The first occurrence can be given either as a date or as a relative delay in seconds. |
608 | The <tt>date</tt> table is like what <tt>os.date("*t")</tt> returns, in the | 607 | The <tt>date</tt> table is like what <tt>os.date("*t")</tt> returns, in the |
609 | local time zone. | 608 | local time zone. |
610 | </p><p> | 609 | </p><p> |
611 | Once a timer expires, the <tt>key</tt> is set with the current time | 610 | Once a timer expires, the <tt>key</tt> is set with the current time |
612 | (in seconds, same offset as <tt>os.time()</tt> but with millisecond accuracy). | 611 | (in seconds, same offset as <tt>os.time()</tt> but with millisecond accuracy). |
613 | The key can be waited upon using the regular Linda <tt>:receive()</tt> | 612 | The key can be waited upon using the regular Linda <tt>:receive()</tt> |
614 | method. | 613 | method. |
615 | </p><p> | 614 | </p><p> |
@@ -632,17 +631,17 @@ A timer can be stopped simply by <tt>first_secs=0</tt> and no period. | |||
632 | t.sec= 0 | 631 | t.sec= 0 |
633 | 632 | ||
634 | lanes.timer( linda, "min", t, 60 ) -- reoccur every minute (sharp) | 633 | lanes.timer( linda, "min", t, 60 ) -- reoccur every minute (sharp) |
635 | 634 | ||
636 | while true do | 635 | while true do |
637 | local v,key= linda:receive( "sec", "min" ) | 636 | local v,key= linda:receive( "sec", "min" ) |
638 | print( "Timer "..key..": "..v ) | 637 | print( "Timer "..key..": "..v ) |
639 | end | 638 | end |
640 | </pre> | 639 | </pre> |
641 | </table> | 640 | </table> |
642 | 641 | ||
643 | </p><p> | 642 | </p><p> |
644 | NOTE: Timer keys are set, not queued, so missing a beat is possible especially | 643 | NOTE: Timer keys are set, not queued, so missing a beat is possible especially |
645 | if the timer cycle is extremely small. The key value can be used to know the | 644 | if the timer cycle is extremely small. The key value can be used to know the |
646 | actual time passed. | 645 | actual time passed. |
647 | </p><p> | 646 | </p><p> |
648 | <table> | 647 | <table> |
@@ -704,7 +703,7 @@ Similar sugar exists for atomic counters: | |||
704 | </table> | 703 | </table> |
705 | </p><p> | 704 | </p><p> |
706 | 705 | ||
707 | Each time called, the generated function will change <tt>linda[key]</tt> | 706 | Each time called, the generated function will change <tt>linda[key]</tt> |
708 | atomically, without other lanes being able to interfere. The new value is | 707 | atomically, without other lanes being able to interfere. The new value is |
709 | returned. You can use either <tt>diff 0.0</tt> or <tt>get</tt> to just read the current | 708 | returned. You can use either <tt>diff 0.0</tt> or <tt>get</tt> to just read the current |
710 | value. | 709 | value. |
@@ -725,7 +724,7 @@ Note that the generated functions can be passed on to other lanes. | |||
725 | <p><ul> | 724 | <p><ul> |
726 | <li>Booleans, numbers, strings, light userdata, Lua functions and tables of such can always be passed. | 725 | <li>Booleans, numbers, strings, light userdata, Lua functions and tables of such can always be passed. |
727 | </li> | 726 | </li> |
728 | <li>Cyclic tables and/or duplicate references are allowed and reproduced appropriately, | 727 | <li>Cyclic tables and/or duplicate references are allowed and reproduced appropriately, |
729 | but only <u>within the same transmission</u>. | 728 | but only <u>within the same transmission</u>. |
730 | <ul> | 729 | <ul> |
731 | <li>using the same source table in multiple Linda messages keeps no ties between the tables | 730 | <li>using the same source table in multiple Linda messages keeps no ties between the tables |
@@ -777,14 +776,14 @@ should be covered into a one-time-only construct such as below. | |||
777 | int luaopen_module( lua_State *L ) | 776 | int luaopen_module( lua_State *L ) |
778 | { | 777 | { |
779 | static char been_here; /* 0 by ANSI C */ | 778 | static char been_here; /* 0 by ANSI C */ |
780 | 779 | ||
781 | /* Calls to 'require' serialized by Lanes; this is safe. | 780 | /* Calls to 'require' serialized by Lanes; this is safe. |
782 | */ | 781 | */ |
783 | if (!been_here) { | 782 | if (!been_here) { |
784 | been_here= 1; | 783 | been_here= 1; |
785 | ... one time initializations ... | 784 | ... one time initializations ... |
786 | } | 785 | } |
787 | 786 | ||
788 | ... binding to Lua ... | 787 | ... binding to Lua ... |
789 | } | 788 | } |
790 | </pre> | 789 | </pre> |
@@ -804,8 +803,9 @@ used for creation and deletion of your deep userdata (the shared resource), | |||
804 | and for making metatables for the state-specific proxies for accessing it. | 803 | and for making metatables for the state-specific proxies for accessing it. |
805 | Take a look at <tt>linda_id</tt> in <tt>lanes.c</tt>. | 804 | Take a look at <tt>linda_id</tt> in <tt>lanes.c</tt>. |
806 | </li> | 805 | </li> |
807 | <li>Create your userdata using <tt>luaG_deep_userdata()</tt>, which is | 806 | <li>Instanciate your userdata using <tt>luaG_deep_userdata()</tt>, |
808 | a Lua-callable function. Given an <tt>idfunc</tt>, it sets up the support | 807 | instead of the regular <tt>lua_newuserdata()</tt>. |
808 | Given an <tt>idfunc</tt>, it sets up the support | ||
809 | structures and returns a state-specific proxy userdata for accessing your | 809 | structures and returns a state-specific proxy userdata for accessing your |
810 | data. This proxy can also be copied over to other lanes. | 810 | data. This proxy can also be copied over to other lanes. |
811 | </li> | 811 | </li> |
@@ -815,7 +815,7 @@ Take a look at <tt>linda_id</tt> in <tt>lanes.c</tt>. | |||
815 | </ol> | 815 | </ol> |
816 | 816 | ||
817 | <p>Deep userdata management will take care of tying to <tt>__gc</tt> methods, | 817 | <p>Deep userdata management will take care of tying to <tt>__gc</tt> methods, |
818 | and doing reference counting to see how many proxies are still there for | 818 | and doing reference counting to see how many proxies are still there for |
819 | accessing the data. Once there are none, the data will be freed through a call | 819 | accessing the data. Once there are none, the data will be freed through a call |
820 | to the <tt>idfunc</tt> you provided. | 820 | to the <tt>idfunc</tt> you provided. |
821 | </p> | 821 | </p> |
@@ -850,7 +850,7 @@ something like this: | |||
850 | <pre> | 850 | <pre> |
851 | A: print( 1, 2, 3, 4 ) | 851 | A: print( 1, 2, 3, 4 ) |
852 | B: print( 'a', 'b', 'c', 'd' ) | 852 | B: print( 'a', 'b', 'c', 'd' ) |
853 | 853 | ||
854 | 1 a b 2 3 c d 4 | 854 | 1 a b 2 3 c d 4 |
855 | </pre> | 855 | </pre> |
856 | 856 | ||
@@ -870,7 +870,7 @@ Here are some things one should consider, if best performance is vital: | |||
870 | <ul> | 870 | <ul> |
871 | <li>Data passing (parameters, upvalues, Linda messages) is generally fast, | 871 | <li>Data passing (parameters, upvalues, Linda messages) is generally fast, |
872 | doing two binary state-to-state copies (from source state to hidden state, | 872 | doing two binary state-to-state copies (from source state to hidden state, |
873 | hidden state to target state). Remember that not only the function you | 873 | hidden state to target state). Remember that not only the function you |
874 | specify but also its upvalues, their upvalues, etc. etc. will get copied. | 874 | specify but also its upvalues, their upvalues, etc. etc. will get copied. |
875 | </li> | 875 | </li> |
876 | <li>Lane startup is fast (1000's of lanes a second), depending on the | 876 | <li>Lane startup is fast (1000's of lanes a second), depending on the |
@@ -894,7 +894,7 @@ Here are some things one should consider, if best performance is vital: | |||
894 | merged into one main timer state (see <tt>timer.lua</tt>); no OS side | 894 | merged into one main timer state (see <tt>timer.lua</tt>); no OS side |
895 | timers are utilized. | 895 | timers are utilized. |
896 | </li> | 896 | </li> |
897 | <li>Lindas are hashed to a fixed number of "keeper states", which are a locking entity. | 897 | <li>Lindas are hashed to a fixed number of "keeper states", which are a locking entity. |
898 | If you are using a lot of Linda objects, | 898 | If you are using a lot of Linda objects, |
899 | it may be useful to try having more of these keeper states. By default, | 899 | it may be useful to try having more of these keeper states. By default, |
900 | only one is used (see <tt>KEEPER_STATES_N</tt>), but this is an implementation detail. | 900 | only one is used (see <tt>KEEPER_STATES_N</tt>), but this is an implementation detail. |
@@ -907,7 +907,7 @@ Here are some things one should consider, if best performance is vital: | |||
907 | 907 | ||
908 | <p> | 908 | <p> |
909 | Cancellation of lanes uses the Lua error mechanism with a special lightuserdata | 909 | Cancellation of lanes uses the Lua error mechanism with a special lightuserdata |
910 | error sentinel. | 910 | error sentinel. |
911 | If you use <tt>pcall</tt> in code that needs to be cancellable | 911 | If you use <tt>pcall</tt> in code that needs to be cancellable |
912 | from the outside, the special error might not get through to Lanes, thus | 912 | from the outside, the special error might not get through to Lanes, thus |
913 | preventing the Lane from being cleanly cancelled. You should throw any | 913 | preventing the Lane from being cleanly cancelled. You should throw any |
@@ -929,6 +929,13 @@ its actual value. | |||
929 | <h2 id="changes">Change log</h2> | 929 | <h2 id="changes">Change log</h2> |
930 | 930 | ||
931 | <p> | 931 | <p> |
932 | Feb-2011 (2.0.11): | ||
933 | <ul> | ||
934 | <li>Fixed bug where reference to Linda object was dropped for a short time (crashing if GC was run during that time).</li> | ||
935 | <li>Changed the atexit code to trip the timer thread's write signal.</li> | ||
936 | <li>Changed lanes.c to export functions as a module rather than writing them directly to the globals table.</li> | ||
937 | </ul> | ||
938 | |||
932 | Jan-2011 (2.0.10): | 939 | Jan-2011 (2.0.10): |
933 | <ul> | 940 | <ul> |
934 | <li>linda_send was waiting on the wrong signal</li> | 941 | <li>linda_send was waiting on the wrong signal</li> |
diff --git a/src/lanes.c b/src/lanes.c index f4bef7b..16a8edc 100644 --- a/src/lanes.c +++ b/src/lanes.c | |||
@@ -64,7 +64,7 @@ | |||
64 | * ... | 64 | * ... |
65 | */ | 65 | */ |
66 | 66 | ||
67 | const char *VERSION= "2.0.10"; | 67 | const char *VERSION= "2.0.11"; |
68 | 68 | ||
69 | /* | 69 | /* |
70 | =============================================================================== | 70 | =============================================================================== |
@@ -359,7 +359,8 @@ static const char *init_keepers(void) { | |||
359 | lua_State *L= luaL_newstate(); | 359 | lua_State *L= luaL_newstate(); |
360 | if (!L) return "out of memory"; | 360 | if (!L) return "out of memory"; |
361 | 361 | ||
362 | luaG_openlibs( L, "io,table" ); // 'io' for debugging messages | 362 | luaG_openlibs( L, "io,table,package" ); // 'io' for debugging messages, package because we need to require modules exporting idfuncs |
363 | serialize_require( L); | ||
363 | 364 | ||
364 | lua_pushlightuserdata( L, &nil_sentinel ); | 365 | lua_pushlightuserdata( L, &nil_sentinel ); |
365 | lua_setglobal( L, "nil_sentinel" ); | 366 | lua_setglobal( L, "nil_sentinel" ); |
@@ -449,9 +450,9 @@ struct s_Linda { | |||
449 | SIGNAL_T write_happened; | 450 | SIGNAL_T write_happened; |
450 | }; | 451 | }; |
451 | 452 | ||
452 | static int LG_linda_id( lua_State* ); | 453 | static void linda_id( lua_State*, char const * const which); |
453 | 454 | ||
454 | #define lua_toLinda(L,n) ((struct s_Linda *)luaG_todeep( L, LG_linda_id, n )) | 455 | #define lua_toLinda(L,n) ((struct s_Linda *)luaG_todeep( L, linda_id, n )) |
455 | 456 | ||
456 | 457 | ||
457 | /* | 458 | /* |
@@ -462,7 +463,8 @@ static int LG_linda_id( lua_State* ); | |||
462 | * Returns: 'true' if the value was queued | 463 | * Returns: 'true' if the value was queued |
463 | * 'false' for timeout (only happens when the queue size is limited) | 464 | * 'false' for timeout (only happens when the queue size is limited) |
464 | */ | 465 | */ |
465 | LUAG_FUNC( linda_send ) { | 466 | LUAG_FUNC( linda_send ) |
467 | { | ||
466 | struct s_Linda *linda= lua_toLinda( L, 1 ); | 468 | struct s_Linda *linda= lua_toLinda( L, 1 ); |
467 | bool_t ret; | 469 | bool_t ret; |
468 | bool_t cancel= FALSE; | 470 | bool_t cancel= FALSE; |
@@ -750,21 +752,22 @@ LUAG_FUNC( linda_deep ) { | |||
750 | * Returns a metatable for the proxy objects ('__gc' method not needed; will | 752 | * Returns a metatable for the proxy objects ('__gc' method not needed; will |
751 | * be added by 'luaG_...') | 753 | * be added by 'luaG_...') |
752 | * | 754 | * |
755 | * string= linda_id( "module") | ||
756 | * | ||
757 | * Returns the name of the module that a state should require | ||
758 | * in order to keep a handle on the shared library that exported the idfunc | ||
759 | * | ||
753 | * = linda_id( str, ... ) | 760 | * = linda_id( str, ... ) |
754 | * | 761 | * |
755 | * For any other strings, the ID function must not react at all. This allows | 762 | * For any other strings, the ID function must not react at all. This allows |
756 | * future extensions of the system. | 763 | * future extensions of the system. |
757 | */ | 764 | */ |
758 | LUAG_FUNC( linda_id ) { | 765 | static void linda_id( lua_State *L, char const * const which) |
759 | const char *which= lua_tostring(L,1); | 766 | { |
760 | 767 | if (strcmp( which, "new" )==0) | |
761 | if (strcmp( which, "new" )==0) { | 768 | { |
762 | struct s_Linda *s; | 769 | struct s_Linda *s; |
763 | 770 | ||
764 | /* We don't use any parameters, but one could (they're at [2..TOS]) | ||
765 | */ | ||
766 | ASSERT_L( lua_gettop(L)==1 ); | ||
767 | |||
768 | /* The deep data is allocated separately of Lua stack; we might no | 771 | /* The deep data is allocated separately of Lua stack; we might no |
769 | * longer be around when last reference to it is being released. | 772 | * longer be around when last reference to it is being released. |
770 | * One can use any memory allocation scheme. | 773 | * One can use any memory allocation scheme. |
@@ -776,11 +779,11 @@ LUAG_FUNC( linda_id ) { | |||
776 | SIGNAL_INIT( &s->write_happened ); | 779 | SIGNAL_INIT( &s->write_happened ); |
777 | 780 | ||
778 | lua_pushlightuserdata( L, s ); | 781 | lua_pushlightuserdata( L, s ); |
779 | return 1; | 782 | } |
780 | 783 | else if (strcmp( which, "delete" )==0) | |
781 | } else if (strcmp( which, "delete" )==0) { | 784 | { |
782 | struct s_Keeper *K; | 785 | struct s_Keeper *K; |
783 | struct s_Linda *s= lua_touserdata(L,2); | 786 | struct s_Linda *s= lua_touserdata(L,1); |
784 | ASSERT_L(s); | 787 | ASSERT_L(s); |
785 | 788 | ||
786 | /* Clean associated structures in the keeper state. | 789 | /* Clean associated structures in the keeper state. |
@@ -797,18 +800,20 @@ LUAG_FUNC( linda_id ) { | |||
797 | SIGNAL_FREE( &s->read_happened ); | 800 | SIGNAL_FREE( &s->read_happened ); |
798 | SIGNAL_FREE( &s->write_happened ); | 801 | SIGNAL_FREE( &s->write_happened ); |
799 | free(s); | 802 | free(s); |
803 | } | ||
804 | else if (strcmp( which, "metatable" )==0) | ||
805 | { | ||
800 | 806 | ||
801 | return 0; | 807 | STACK_CHECK(L) |
802 | |||
803 | } else if (strcmp( which, "metatable" )==0) { | ||
804 | |||
805 | STACK_CHECK(L) | ||
806 | lua_newtable(L); | 808 | lua_newtable(L); |
807 | lua_newtable(L); | 809 | // metatable is its own index |
808 | // | 810 | lua_pushvalue( L, -1); |
809 | // [-2]: linda metatable | 811 | lua_setfield( L, -2, "__index"); |
810 | // [-1]: metatable's to-be .__index table | 812 | // protect metatable from external access |
811 | 813 | lua_pushboolean( L, 0); | |
814 | lua_setfield( L, -2, "__metatable"); | ||
815 | // | ||
816 | // [-1]: linda metatable | ||
812 | lua_pushcfunction( L, LG_linda_send ); | 817 | lua_pushcfunction( L, LG_linda_send ); |
813 | lua_setfield( L, -2, "send" ); | 818 | lua_setfield( L, -2, "send" ); |
814 | 819 | ||
@@ -827,13 +832,17 @@ LUAG_FUNC( linda_id ) { | |||
827 | lua_pushcfunction( L, LG_linda_deep ); | 832 | lua_pushcfunction( L, LG_linda_deep ); |
828 | lua_setfield( L, -2, "deep" ); | 833 | lua_setfield( L, -2, "deep" ); |
829 | 834 | ||
830 | lua_setfield( L, -2, "__index" ); | 835 | STACK_END(L,1) |
831 | STACK_END(L,1) | ||
832 | |||
833 | return 1; | ||
834 | } | 836 | } |
835 | 837 | else if( strcmp( which, "module") == 0) | |
836 | return 0; // unknown request, be quiet | 838 | { |
839 | lua_pushliteral( L, "lua51-lanes"); | ||
840 | } | ||
841 | } | ||
842 | |||
843 | LUAG_FUNC( linda) | ||
844 | { | ||
845 | return luaG_deep_userdata( L, linda_id); | ||
837 | } | 846 | } |
838 | 847 | ||
839 | 848 | ||
@@ -1014,8 +1023,10 @@ static void selfdestruct_atexit( void ) { | |||
1014 | MUTEX_UNLOCK( &selfdestruct_cs ); | 1023 | MUTEX_UNLOCK( &selfdestruct_cs ); |
1015 | 1024 | ||
1016 | // Tell the timer thread to check it's cancel request | 1025 | // Tell the timer thread to check it's cancel request |
1017 | struct s_Linda *td = timer_deep->deep; | 1026 | { |
1018 | SIGNAL_ALL( &td->write_happened); | 1027 | struct s_Linda *td = timer_deep->deep; |
1028 | SIGNAL_ALL( &td->write_happened); | ||
1029 | } | ||
1019 | 1030 | ||
1020 | // When noticing their cancel, the lanes will remove themselves from | 1031 | // When noticing their cancel, the lanes will remove themselves from |
1021 | // the selfdestruct chain. | 1032 | // the selfdestruct chain. |
@@ -1855,21 +1866,21 @@ LUAG_FUNC( wakeup_conv ) | |||
1855 | */ | 1866 | */ |
1856 | 1867 | ||
1857 | static const struct luaL_reg lanes_functions [] = { | 1868 | static const struct luaL_reg lanes_functions [] = { |
1858 | {"linda_id", LG_linda_id}, | 1869 | {"linda", LG_linda}, |
1859 | {"thread_status", LG_thread_status}, | 1870 | {"thread_status", LG_thread_status}, |
1860 | {"thread_join", LG_thread_join}, | 1871 | {"thread_join", LG_thread_join}, |
1861 | {"thread_cancel", LG_thread_cancel}, | 1872 | {"thread_cancel", LG_thread_cancel}, |
1862 | {"now_secs", LG_now_secs}, | 1873 | {"now_secs", LG_now_secs}, |
1863 | {"wakeup_conv", LG_wakeup_conv}, | 1874 | {"wakeup_conv", LG_wakeup_conv}, |
1864 | {"_single", LG__single}, | 1875 | {"_single", LG__single}, |
1865 | {"_deep_userdata", luaG_deep_userdata}, | ||
1866 | {NULL, NULL} | 1876 | {NULL, NULL} |
1867 | }; | 1877 | }; |
1868 | 1878 | ||
1869 | /* | 1879 | /* |
1870 | * One-time initializations | 1880 | * One-time initializations |
1871 | */ | 1881 | */ |
1872 | static void init_once_LOCKED( lua_State *L, volatile DEEP_PRELUDE ** timer_deep_ref ) { | 1882 | static void init_once_LOCKED( lua_State *L, volatile DEEP_PRELUDE ** timer_deep_ref ) |
1883 | { | ||
1873 | const char *err; | 1884 | const char *err; |
1874 | 1885 | ||
1875 | #if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) | 1886 | #if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) |
@@ -1919,8 +1930,9 @@ static void init_once_LOCKED( lua_State *L, volatile DEEP_PRELUDE ** timer_deep_ | |||
1919 | } | 1930 | } |
1920 | #endif | 1931 | #endif |
1921 | #endif | 1932 | #endif |
1922 | err= init_keepers(); | 1933 | err= init_keepers(); |
1923 | if (err) { | 1934 | if (err) |
1935 | { | ||
1924 | luaL_error( L, "Unable to initialize: %s", err ); | 1936 | luaL_error( L, "Unable to initialize: %s", err ); |
1925 | } | 1937 | } |
1926 | 1938 | ||
@@ -1928,13 +1940,11 @@ static void init_once_LOCKED( lua_State *L, volatile DEEP_PRELUDE ** timer_deep_ | |||
1928 | // | 1940 | // |
1929 | ASSERT_L( timer_deep_ref && (!(*timer_deep_ref)) ); | 1941 | ASSERT_L( timer_deep_ref && (!(*timer_deep_ref)) ); |
1930 | 1942 | ||
1931 | STACK_CHECK(L) | 1943 | STACK_CHECK(L) |
1932 | { | 1944 | { |
1933 | // proxy_ud= deep_userdata( idfunc ) | 1945 | // proxy_ud= deep_userdata( idfunc ) |
1934 | // | 1946 | // |
1935 | lua_pushcfunction( L, luaG_deep_userdata ); | 1947 | luaG_deep_userdata( L, linda_id); |
1936 | lua_pushcfunction( L, LG_linda_id ); | ||
1937 | lua_call( L, 1 /*args*/, 1 /*retvals*/ ); | ||
1938 | 1948 | ||
1939 | ASSERT_L( lua_isuserdata(L,-1) ); | 1949 | ASSERT_L( lua_isuserdata(L,-1) ); |
1940 | 1950 | ||
@@ -1950,7 +1960,7 @@ static void init_once_LOCKED( lua_State *L, volatile DEEP_PRELUDE ** timer_deep_ | |||
1950 | lua_rawset(L, LUA_REGISTRYINDEX); | 1960 | lua_rawset(L, LUA_REGISTRYINDEX); |
1951 | 1961 | ||
1952 | } | 1962 | } |
1953 | STACK_END(L,0) | 1963 | STACK_END(L,0) |
1954 | } | 1964 | } |
1955 | 1965 | ||
1956 | int | 1966 | int |
@@ -2014,7 +2024,7 @@ __declspec(dllexport) | |||
2014 | lua_pushcclosure( L, LG_thread_new, 1 ); // metatable as closure param | 2024 | lua_pushcclosure( L, LG_thread_new, 1 ); // metatable as closure param |
2015 | lua_setfield(L, -2, "thread_new"); | 2025 | lua_setfield(L, -2, "thread_new"); |
2016 | 2026 | ||
2017 | luaG_push_proxy( L, LG_linda_id, (DEEP_PRELUDE *) timer_deep ); | 2027 | luaG_push_proxy( L, linda_id, (DEEP_PRELUDE *) timer_deep ); |
2018 | lua_setfield(L, -2, "timer_gateway"); | 2028 | lua_setfield(L, -2, "timer_gateway"); |
2019 | 2029 | ||
2020 | lua_pushstring(L, VERSION); | 2030 | lua_pushstring(L, VERSION); |
diff --git a/src/lanes.lua b/src/lanes.lua index 42e946b..b6fbc08 100644 --- a/src/lanes.lua +++ b/src/lanes.lua | |||
@@ -45,8 +45,6 @@ local mm = require "lua51-lanes" | |||
45 | assert( type(mm)=="table" ) | 45 | assert( type(mm)=="table" ) |
46 | 46 | ||
47 | 47 | ||
48 | local linda_id= assert( mm.linda_id ) | ||
49 | |||
50 | local thread_new= assert(mm.thread_new) | 48 | local thread_new= assert(mm.thread_new) |
51 | local thread_status= assert(mm.thread_status) | 49 | local thread_status= assert(mm.thread_status) |
52 | local thread_join= assert(mm.thread_join) | 50 | local thread_join= assert(mm.thread_join) |
@@ -55,8 +53,6 @@ local thread_cancel= assert(mm.thread_cancel) | |||
55 | local _single= assert(mm._single) | 53 | local _single= assert(mm._single) |
56 | local _version= assert(mm._version) | 54 | local _version= assert(mm._version) |
57 | 55 | ||
58 | local _deep_userdata= assert(mm._deep_userdata) | ||
59 | |||
60 | local now_secs= assert( mm.now_secs ) | 56 | local now_secs= assert( mm.now_secs ) |
61 | local wakeup_conv= assert( mm.wakeup_conv ) | 57 | local wakeup_conv= assert( mm.wakeup_conv ) |
62 | local timer_gateway= assert( mm.timer_gateway ) | 58 | local timer_gateway= assert( mm.timer_gateway ) |
@@ -336,11 +332,7 @@ end | |||
336 | ----- | 332 | ----- |
337 | -- linda_ud= lanes.linda() | 333 | -- linda_ud= lanes.linda() |
338 | -- | 334 | -- |
339 | function linda() | 335 | linda = mm.linda |
340 | local proxy= _deep_userdata( linda_id ) | ||
341 | assert( (type(proxy) == "userdata") and getmetatable(proxy) ) | ||
342 | return proxy | ||
343 | end | ||
344 | 336 | ||
345 | 337 | ||
346 | ---=== Timers ===--- | 338 | ---=== Timers ===--- |
@@ -505,7 +497,7 @@ if first_time then | |||
505 | -- We let the timer lane be a "free running" thread; no handle to it | 497 | -- We let the timer lane be a "free running" thread; no handle to it |
506 | -- remains. | 498 | -- remains. |
507 | -- | 499 | -- |
508 | gen( "io", { priority=max_prio, globals={threadName="LanesTimer"} }, function() | 500 | gen( "io,package", { priority=max_prio, globals={threadName="LanesTimer"} }, function() |
509 | 501 | ||
510 | while true do | 502 | while true do |
511 | local next_wakeup= check_timers() | 503 | local next_wakeup= check_timers() |
diff --git a/src/tools.c b/src/tools.c index 6692890..d09b11e 100644 --- a/src/tools.c +++ b/src/tools.c | |||
@@ -280,26 +280,27 @@ void get_deep_lookup( lua_State *L ) { | |||
280 | * or NULL if 'index' is not a deep userdata proxy. | 280 | * or NULL if 'index' is not a deep userdata proxy. |
281 | */ | 281 | */ |
282 | static | 282 | static |
283 | lua_CFunction get_idfunc( lua_State *L, int index ) { | 283 | luaG_IdFunction get_idfunc( lua_State *L, int index ) |
284 | lua_CFunction ret; | 284 | { |
285 | luaG_IdFunction ret; | ||
285 | 286 | ||
286 | index= STACK_ABS(L,index); | 287 | index= STACK_ABS(L,index); |
287 | 288 | ||
288 | STACK_GROW(L,1); | 289 | STACK_GROW(L,1); |
289 | 290 | ||
290 | STACK_CHECK(L) | 291 | STACK_CHECK(L) |
291 | if (!lua_getmetatable( L, index )) | 292 | if (!lua_getmetatable( L, index )) |
292 | return NULL; // no metatable | 293 | return NULL; // no metatable |
293 | 294 | ||
294 | // [-1]: metatable of [index] | 295 | // [-1]: metatable of [index] |
295 | 296 | ||
296 | get_deep_lookup(L); | 297 | get_deep_lookup(L); |
297 | // | 298 | // |
298 | // [-1]: idfunc/nil | 299 | // [-1]: idfunc/nil |
299 | 300 | ||
300 | ret= lua_tocfunction(L,-1); | 301 | ret= (luaG_IdFunction)lua_touserdata(L,-1); |
301 | lua_pop(L,1); | 302 | lua_pop(L,1); |
302 | STACK_END(L,0) | 303 | STACK_END(L,0) |
303 | return ret; | 304 | return ret; |
304 | } | 305 | } |
305 | 306 | ||
@@ -311,7 +312,8 @@ lua_CFunction get_idfunc( lua_State *L, int index ) { | |||
311 | * it up if reaches 0. | 312 | * it up if reaches 0. |
312 | */ | 313 | */ |
313 | static | 314 | static |
314 | int deep_userdata_gc( lua_State *L ) { | 315 | int deep_userdata_gc( lua_State *L ) |
316 | { | ||
315 | DEEP_PRELUDE **proxy= (DEEP_PRELUDE**)lua_touserdata( L, 1 ); | 317 | DEEP_PRELUDE **proxy= (DEEP_PRELUDE**)lua_touserdata( L, 1 ); |
316 | DEEP_PRELUDE *p= *proxy; | 318 | DEEP_PRELUDE *p= *proxy; |
317 | int v; | 319 | int v; |
@@ -319,27 +321,26 @@ int deep_userdata_gc( lua_State *L ) { | |||
319 | *proxy= 0; // make sure we don't use it any more | 321 | *proxy= 0; // make sure we don't use it any more |
320 | 322 | ||
321 | MUTEX_LOCK( &deep_lock ); | 323 | MUTEX_LOCK( &deep_lock ); |
322 | v= --(p->refcount); | 324 | v= --(p->refcount); |
323 | MUTEX_UNLOCK( &deep_lock ); | 325 | MUTEX_UNLOCK( &deep_lock ); |
324 | 326 | ||
325 | if (v==0) { | 327 | if (v==0) |
326 | int pushed; | 328 | { |
327 | |||
328 | // Call 'idfunc( "delete", deep_ptr )' to make deep cleanup | 329 | // Call 'idfunc( "delete", deep_ptr )' to make deep cleanup |
329 | // | 330 | // |
330 | lua_CFunction idfunc= get_idfunc(L,1); | 331 | luaG_IdFunction idfunc = get_idfunc(L,1); |
331 | ASSERT_L(idfunc); | 332 | ASSERT_L(idfunc); |
332 | 333 | ||
333 | lua_settop(L,0); // clean stack so we can call 'idfunc' directly | 334 | lua_settop( L, 0); // clean stack so we can call 'idfunc' directly |
334 | 335 | ||
335 | // void= idfunc( "delete", lightuserdata ) | 336 | // void= idfunc( "delete", lightuserdata ) |
336 | // | 337 | // |
337 | lua_pushliteral( L, "delete" ); | ||
338 | lua_pushlightuserdata( L, p->deep ); | 338 | lua_pushlightuserdata( L, p->deep ); |
339 | pushed= idfunc(L); | 339 | idfunc( L, "delete"); |
340 | 340 | ||
341 | if (pushed) | 341 | // top was set to 0, then userdata was pushed. "delete" might want to pop the userdata (we don't care), but should not push anything! |
342 | luaL_error( L, "Bad idfunc on \"delete\": returned something" ); | 342 | if ( lua_gettop( L) > 1) |
343 | luaL_error( L, "Bad idfunc on \"delete\": returned something"); | ||
343 | 344 | ||
344 | DEEP_FREE( (void*)p ); | 345 | DEEP_FREE( (void*)p ); |
345 | } | 346 | } |
@@ -354,28 +355,31 @@ int deep_userdata_gc( lua_State *L ) { | |||
354 | * used in this Lua state (metatable, registring it). Otherwise, increments the | 355 | * used in this Lua state (metatable, registring it). Otherwise, increments the |
355 | * reference count. | 356 | * reference count. |
356 | */ | 357 | */ |
357 | void luaG_push_proxy( lua_State *L, lua_CFunction idfunc, DEEP_PRELUDE *prelude ) { | 358 | void luaG_push_proxy( lua_State *L, luaG_IdFunction idfunc, DEEP_PRELUDE *prelude ) |
359 | { | ||
358 | DEEP_PRELUDE **proxy; | 360 | DEEP_PRELUDE **proxy; |
359 | 361 | ||
360 | // Check if a proxy already exists | 362 | // Check if a proxy already exists |
361 | push_registry_subtable_mode(L, DEEP_PROXY_CACHE_KEY, "v"); | 363 | push_registry_subtable_mode(L, DEEP_PROXY_CACHE_KEY, "v"); |
362 | lua_pushlightuserdata(L, prelude->deep); | 364 | lua_pushlightuserdata(L, prelude->deep); |
363 | lua_rawget(L, -2); | 365 | lua_rawget(L, -2); |
364 | if (!lua_isnil(L, -1)) { | 366 | if (!lua_isnil(L, -1)) |
367 | { | ||
365 | lua_remove(L, -2); // deep proxy cache table | 368 | lua_remove(L, -2); // deep proxy cache table |
366 | return; | 369 | return; |
367 | } else { | 370 | } |
371 | else | ||
372 | { | ||
368 | lua_pop(L, 2); // Pop the nil and proxy cache table | 373 | lua_pop(L, 2); // Pop the nil and proxy cache table |
369 | } | 374 | } |
370 | 375 | ||
371 | |||
372 | MUTEX_LOCK( &deep_lock ); | 376 | MUTEX_LOCK( &deep_lock ); |
373 | ++(prelude->refcount); // one more proxy pointing to this deep data | 377 | ++(prelude->refcount); // one more proxy pointing to this deep data |
374 | MUTEX_UNLOCK( &deep_lock ); | 378 | MUTEX_UNLOCK( &deep_lock ); |
375 | 379 | ||
376 | STACK_GROW(L,4); | 380 | STACK_GROW(L,4); |
377 | 381 | ||
378 | STACK_CHECK(L) | 382 | STACK_CHECK(L) |
379 | 383 | ||
380 | proxy= lua_newuserdata( L, sizeof( DEEP_PRELUDE* ) ); | 384 | proxy= lua_newuserdata( L, sizeof( DEEP_PRELUDE* ) ); |
381 | ASSERT_L(proxy); | 385 | ASSERT_L(proxy); |
@@ -383,28 +387,32 @@ void luaG_push_proxy( lua_State *L, lua_CFunction idfunc, DEEP_PRELUDE *prelude | |||
383 | 387 | ||
384 | // Get/create metatable for 'idfunc' (in this state) | 388 | // Get/create metatable for 'idfunc' (in this state) |
385 | // | 389 | // |
386 | lua_pushcfunction( L, idfunc ); // key | 390 | lua_pushlightuserdata( L, idfunc ); // key |
387 | get_deep_lookup(L); | 391 | get_deep_lookup(L); |
388 | // | 392 | // |
389 | // [-2]: proxy | 393 | // [-2]: proxy |
390 | // [-1]: metatable / nil | 394 | // [-1]: metatable / nil |
391 | 395 | ||
392 | if (lua_isnil(L,-1)) { | 396 | if (lua_isnil(L,-1)) |
393 | // No metatable yet; make one and register it | 397 | { |
394 | // | 398 | int oldtop; |
399 | // No metatable yet. We have two things to do: | ||
400 | |||
401 | // 1 - make one and register it | ||
395 | lua_pop(L,1); | 402 | lua_pop(L,1); |
396 | 403 | ||
397 | // tbl= idfunc( "metatable" ) | 404 | // tbl= idfunc( "metatable" ) |
398 | // | 405 | // |
399 | lua_pushcfunction( L, idfunc ); | 406 | oldtop = lua_gettop( L); |
400 | lua_pushliteral( L, "metatable" ); | 407 | idfunc( L, "metatable"); |
401 | lua_call( L, 1 /*args*/, 1 /*results*/ ); | 408 | // |
402 | // | 409 | // [-2]: proxy |
403 | // [-2]: proxy | 410 | // [-1]: metatable (returned by 'idfunc') |
404 | // [-1]: metatable (returned by 'idfunc') | ||
405 | 411 | ||
406 | if (!lua_istable(L,-1)) | 412 | if (lua_gettop( L) - oldtop != 1 || !lua_istable(L, -1)) |
413 | { | ||
407 | luaL_error( L, "Bad idfunc on \"metatable\": did not return one" ); | 414 | luaL_error( L, "Bad idfunc on \"metatable\": did not return one" ); |
415 | } | ||
408 | 416 | ||
409 | // Add '__gc' method | 417 | // Add '__gc' method |
410 | // | 418 | // |
@@ -414,16 +422,40 @@ void luaG_push_proxy( lua_State *L, lua_CFunction idfunc, DEEP_PRELUDE *prelude | |||
414 | // Memorize for later rounds | 422 | // Memorize for later rounds |
415 | // | 423 | // |
416 | lua_pushvalue( L,-1 ); | 424 | lua_pushvalue( L,-1 ); |
417 | lua_pushcfunction( L, idfunc ); | 425 | lua_pushlightuserdata( L, idfunc ); |
418 | // | 426 | // |
419 | // [-4]: proxy | 427 | // [-4]: proxy |
420 | // [-3]: metatable (2nd ref) | 428 | // [-3]: metatable (2nd ref) |
421 | // [-2]: metatable | 429 | // [-2]: metatable |
422 | // [-1]: idfunc | 430 | // [-1]: idfunc |
423 | 431 | ||
424 | set_deep_lookup(L); | 432 | set_deep_lookup(L); |
425 | } | 433 | |
426 | STACK_MID(L,2) | 434 | // 2 - cause the target state to require the module that exported the idfunc |
435 | // this is needed because we must make sure the shared library is still loaded as long as we hold a pointer on the idfunc | ||
436 | lua_getglobal( L, "require"); | ||
437 | if( lua_isfunction( L, -1)) // just in case... | ||
438 | { | ||
439 | // make sure the function pushed a single value on the stack! | ||
440 | int oldtop = lua_gettop( L); | ||
441 | idfunc( L, "module"); | ||
442 | if( lua_gettop( L) - oldtop != 1 || !lua_isstring( L, -1)) | ||
443 | { | ||
444 | luaL_error( L, "Bad idfunc on \"module\": should return a string"); | ||
445 | } | ||
446 | // if we are inside a call to require, this will raise a "reentrency" error that we absorb silently (we don't care, this probably means the module is already being required, which is what we need) | ||
447 | if( lua_pcall( L, 1, 0, 0) != 0) | ||
448 | { | ||
449 | //char const * const errMsg = lua_tostring( L, -1); // just to see it in the debugger | ||
450 | lua_pop( L, 1); | ||
451 | } | ||
452 | } | ||
453 | else | ||
454 | { | ||
455 | lua_pop( L, 1); | ||
456 | } | ||
457 | } | ||
458 | STACK_MID(L,2) | ||
427 | ASSERT_L( lua_isuserdata(L,-2) ); | 459 | ASSERT_L( lua_isuserdata(L,-2) ); |
428 | ASSERT_L( lua_istable(L,-1) ); | 460 | ASSERT_L( lua_istable(L,-1) ); |
429 | 461 | ||
@@ -439,7 +471,7 @@ void luaG_push_proxy( lua_State *L, lua_CFunction idfunc, DEEP_PRELUDE *prelude | |||
439 | lua_rawset(L, -3); | 471 | lua_rawset(L, -3); |
440 | lua_pop(L, 1); // Remove the cache proxy table | 472 | lua_pop(L, 1); // Remove the cache proxy table |
441 | 473 | ||
442 | STACK_END(L,1) | 474 | STACK_END(L,1) |
443 | // [-1]: proxy userdata | 475 | // [-1]: proxy userdata |
444 | } | 476 | } |
445 | 477 | ||
@@ -466,9 +498,9 @@ void luaG_push_proxy( lua_State *L, lua_CFunction idfunc, DEEP_PRELUDE *prelude | |||
466 | * | 498 | * |
467 | * Returns: 'proxy' userdata for accessing the deep data via 'luaG_todeep()' | 499 | * Returns: 'proxy' userdata for accessing the deep data via 'luaG_todeep()' |
468 | */ | 500 | */ |
469 | int luaG_deep_userdata( lua_State *L ) { | 501 | int luaG_deep_userdata( lua_State *L, luaG_IdFunction idfunc) |
470 | lua_CFunction idfunc= lua_tocfunction( L,1 ); | 502 | { |
471 | int pushed; | 503 | int oldtop; |
472 | 504 | ||
473 | DEEP_PRELUDE *prelude= DEEP_MALLOC( sizeof(DEEP_PRELUDE) ); | 505 | DEEP_PRELUDE *prelude= DEEP_MALLOC( sizeof(DEEP_PRELUDE) ); |
474 | ASSERT_L(prelude); | 506 | ASSERT_L(prelude); |
@@ -476,20 +508,17 @@ int luaG_deep_userdata( lua_State *L ) { | |||
476 | prelude->refcount= 0; // 'luaG_push_proxy' will lift it to 1 | 508 | prelude->refcount= 0; // 'luaG_push_proxy' will lift it to 1 |
477 | 509 | ||
478 | STACK_GROW(L,1); | 510 | STACK_GROW(L,1); |
479 | STACK_CHECK(L) | 511 | STACK_CHECK(L) |
480 | |||
481 | // Replace 'idfunc' with "new" in the stack (keep possible other params) | ||
482 | // | ||
483 | lua_remove(L,1); | ||
484 | lua_pushliteral( L, "new" ); | ||
485 | lua_insert(L,1); | ||
486 | 512 | ||
487 | // lightuserdata= idfunc( "new" [, ...] ) | 513 | // lightuserdata= idfunc( "new" [, ...] ) |
488 | // | 514 | // |
489 | pushed= idfunc(L); | 515 | oldtop = lua_gettop( L); |
516 | idfunc(L, "new"); | ||
490 | 517 | ||
491 | if ((pushed!=1) || lua_type(L,-1) != LUA_TLIGHTUSERDATA) | 518 | if( lua_gettop( L) - oldtop != 1 || lua_type( L, -1) != LUA_TLIGHTUSERDATA) |
492 | luaL_error( L, "Bad idfunc on \"new\": did not return light userdata" ); | 519 | { |
520 | luaL_error( L, "Bad idfunc on \"new\": did not return light userdata"); | ||
521 | } | ||
493 | 522 | ||
494 | prelude->deep= lua_touserdata(L,-1); | 523 | prelude->deep= lua_touserdata(L,-1); |
495 | ASSERT_L(prelude->deep); | 524 | ASSERT_L(prelude->deep); |
@@ -497,10 +526,10 @@ int luaG_deep_userdata( lua_State *L ) { | |||
497 | lua_pop(L,1); // pop deep data | 526 | lua_pop(L,1); // pop deep data |
498 | 527 | ||
499 | luaG_push_proxy( L, idfunc, prelude ); | 528 | luaG_push_proxy( L, idfunc, prelude ); |
500 | // | 529 | // |
501 | // [-1]: proxy userdata | 530 | // [-1]: proxy userdata |
502 | 531 | ||
503 | STACK_END(L,1) | 532 | STACK_END(L,1) |
504 | return 1; | 533 | return 1; |
505 | } | 534 | } |
506 | 535 | ||
@@ -511,15 +540,16 @@ int luaG_deep_userdata( lua_State *L ) { | |||
511 | * Reference count is not changed, and access to the deep userdata is not | 540 | * Reference count is not changed, and access to the deep userdata is not |
512 | * serialized. It is the module's responsibility to prevent conflicting usage. | 541 | * serialized. It is the module's responsibility to prevent conflicting usage. |
513 | */ | 542 | */ |
514 | void *luaG_todeep( lua_State *L, lua_CFunction idfunc, int index ) { | 543 | void *luaG_todeep( lua_State *L, luaG_IdFunction idfunc, int index ) |
544 | { | ||
515 | DEEP_PRELUDE **proxy; | 545 | DEEP_PRELUDE **proxy; |
516 | 546 | ||
517 | STACK_CHECK(L) | 547 | STACK_CHECK(L) |
518 | if (get_idfunc(L,index) != idfunc) | 548 | if( get_idfunc(L,index) != idfunc) |
519 | return NULL; // no metatable, or wrong kind | 549 | return NULL; // no metatable, or wrong kind |
520 | 550 | ||
521 | proxy= (DEEP_PRELUDE**)lua_touserdata( L, index ); | 551 | proxy= (DEEP_PRELUDE**)lua_touserdata( L, index ); |
522 | STACK_END(L,0) | 552 | STACK_END(L,0) |
523 | 553 | ||
524 | return (*proxy)->deep; | 554 | return (*proxy)->deep; |
525 | } | 555 | } |
@@ -533,14 +563,14 @@ void *luaG_todeep( lua_State *L, lua_CFunction idfunc, int index ) { | |||
533 | * (not copied) | 563 | * (not copied) |
534 | */ | 564 | */ |
535 | static | 565 | static |
536 | lua_CFunction luaG_copydeep( lua_State *L, lua_State *L2, int index ) { | 566 | luaG_IdFunction luaG_copydeep( lua_State *L, lua_State *L2, int index ) |
567 | { | ||
537 | DEEP_PRELUDE **proxy; | 568 | DEEP_PRELUDE **proxy; |
538 | DEEP_PRELUDE *p; | 569 | DEEP_PRELUDE *p; |
539 | 570 | ||
540 | lua_CFunction idfunc; | 571 | luaG_IdFunction idfunc = get_idfunc( L, index); |
541 | 572 | if (!idfunc) | |
542 | idfunc= get_idfunc( L, index ); | 573 | return NULL; // not a deep userdata |
543 | if (!idfunc) return NULL; // not a deep userdata | ||
544 | 574 | ||
545 | // Increment reference count | 575 | // Increment reference count |
546 | // | 576 | // |
diff --git a/src/tools.h b/src/tools.h index aad26df..05bee59 100644 --- a/src/tools.h +++ b/src/tools.h | |||
@@ -48,19 +48,21 @@ | |||
48 | 48 | ||
49 | #define luaG_typename( L, index ) lua_typename( L, lua_type(L,index) ) | 49 | #define luaG_typename( L, index ) lua_typename( L, lua_type(L,index) ) |
50 | 50 | ||
51 | typedef void (*luaG_IdFunction)( lua_State *L, char const * const which); | ||
52 | |||
51 | void luaG_dump( lua_State* L ); | 53 | void luaG_dump( lua_State* L ); |
52 | 54 | ||
53 | const char *luaG_openlibs( lua_State *L, const char *libs ); | 55 | const char *luaG_openlibs( lua_State *L, const char *libs ); |
54 | 56 | ||
55 | int luaG_deep_userdata( lua_State *L ); | 57 | int luaG_deep_userdata( lua_State *L, luaG_IdFunction idfunc); |
56 | void *luaG_todeep( lua_State *L, lua_CFunction idfunc, int index ); | 58 | void *luaG_todeep( lua_State *L, luaG_IdFunction idfunc, int index ); |
57 | 59 | ||
58 | typedef struct { | 60 | typedef struct { |
59 | volatile int refcount; | 61 | volatile int refcount; |
60 | void *deep; | 62 | void *deep; |
61 | } DEEP_PRELUDE; | 63 | } DEEP_PRELUDE; |
62 | 64 | ||
63 | void luaG_push_proxy( lua_State *L, lua_CFunction idfunc, DEEP_PRELUDE *deep_userdata ); | 65 | void luaG_push_proxy( lua_State *L, luaG_IdFunction idfunc, DEEP_PRELUDE *deep_userdata ); |
64 | 66 | ||
65 | void luaG_inter_copy( lua_State *L, lua_State *L2, uint_t n ); | 67 | void luaG_inter_copy( lua_State *L, lua_State *L2, uint_t n ); |
66 | void luaG_inter_move( lua_State *L, lua_State *L2, uint_t n ); | 68 | void luaG_inter_move( lua_State *L, lua_State *L2, uint_t n ); |
diff --git a/tests/basic.lua b/tests/basic.lua index b1e8fd3..352e029 100644 --- a/tests/basic.lua +++ b/tests/basic.lua | |||
@@ -171,7 +171,7 @@ local function PEEK() return linda:get("<-") end | |||
171 | local function SEND(...) linda:send( "->", ... ) end | 171 | local function SEND(...) linda:send( "->", ... ) end |
172 | local function RECEIVE() return linda:receive( "<-" ) end | 172 | local function RECEIVE() return linda:receive( "<-" ) end |
173 | 173 | ||
174 | local t= lanes_gen("io",chunk)(linda) -- prepare & launch | 174 | local t= lanes_gen("io,package",chunk)(linda) -- prepare & launch |
175 | 175 | ||
176 | SEND(1); WR( "1 sent\n" ) | 176 | SEND(1); WR( "1 sent\n" ) |
177 | SEND(2); WR( "2 sent\n" ) | 177 | SEND(2); WR( "2 sent\n" ) |
@@ -230,7 +230,7 @@ local tc= lanes_gen( "io", | |||
230 | end | 230 | end |
231 | STAGE("Hello") | 231 | STAGE("Hello") |
232 | STAGE("I was here first!") | 232 | STAGE("I was here first!") |
233 | STAGE("So waht?") | 233 | STAGE("So what?") |
234 | end | 234 | end |
235 | ) | 235 | ) |
236 | 236 | ||
@@ -247,7 +247,6 @@ local upvalue="123" | |||
247 | 247 | ||
248 | local function chunk2( linda ) | 248 | local function chunk2( linda ) |
249 | assert( upvalue=="123" ) -- even when running as separate thread | 249 | assert( upvalue=="123" ) -- even when running as separate thread |
250 | |||
251 | -- function name & line number should be there even as separate thread | 250 | -- function name & line number should be there even as separate thread |
252 | -- | 251 | -- |
253 | local info= debug.getinfo(1) -- 1 = us | 252 | local info= debug.getinfo(1) -- 1 = us |
@@ -256,17 +255,14 @@ local function chunk2( linda ) | |||
256 | 255 | ||
257 | assert( info.nups == 2 ) -- one upvalue + PRINT | 256 | assert( info.nups == 2 ) -- one upvalue + PRINT |
258 | assert( info.what == "Lua" ) | 257 | assert( info.what == "Lua" ) |
259 | |||
260 | --assert( info.name == "chunk2" ) -- name does not seem to come through | 258 | --assert( info.name == "chunk2" ) -- name does not seem to come through |
261 | assert( string.match( info.source, "^@basic.lua$" ) ) | 259 | assert( string.match( info.source, "^@.*basic.lua$" ) ) |
262 | assert( string.match( info.short_src, "^basic.lua$" ) ) | 260 | assert( string.match( info.short_src, "^.*basic.lua$" ) ) |
263 | |||
264 | -- These vary so let's not be picky (they're there..) | 261 | -- These vary so let's not be picky (they're there..) |
265 | -- | 262 | -- |
266 | assert( info.linedefined > 200 ) -- start of 'chunk2' | 263 | assert( info.linedefined > 200 ) -- start of 'chunk2' |
267 | assert( info.currentline > info.linedefined ) -- line of 'debug.getinfo' | 264 | assert( info.currentline > info.linedefined ) -- line of 'debug.getinfo' |
268 | assert( info.lastlinedefined > info.currentline ) -- end of 'chunk2' | 265 | assert( info.lastlinedefined > info.currentline ) -- end of 'chunk2' |
269 | |||
270 | local func,k= linda:receive( "down" ) | 266 | local func,k= linda:receive( "down" ) |
271 | assert( type(func)=="function" ) | 267 | assert( type(func)=="function" ) |
272 | assert( k=="down" ) | 268 | assert( k=="down" ) |
@@ -280,12 +276,9 @@ local function chunk2( linda ) | |||
280 | end | 276 | end |
281 | 277 | ||
282 | local linda= lanes.linda() | 278 | local linda= lanes.linda() |
283 | 279 | local t2= lanes_gen( "debug,string,io", chunk2 )(linda) -- prepare & launch | |
284 | local t2= lanes_gen( "debug,string", chunk2 )(linda) -- prepare & launch | ||
285 | |||
286 | linda:send( "down", function(linda) linda:send( "up", "ready!" ) end, | 280 | linda:send( "down", function(linda) linda:send( "up", "ready!" ) end, |
287 | "ok" ) | 281 | "ok" ) |
288 | |||
289 | -- wait to see if the tiny function gets executed | 282 | -- wait to see if the tiny function gets executed |
290 | -- | 283 | -- |
291 | local s= linda:receive( "up" ) | 284 | local s= linda:receive( "up" ) |