diff options
author | Benoit Germain <bnt.germain@gmail.com> | 2011-02-17 07:52:53 +0100 |
---|---|---|
committer | Benoit Germain <bnt.germain@gmail.com> | 2011-02-17 07:52:53 +0100 |
commit | ab233d0c9a1edc34836e2249c1eb6d714f1066b5 (patch) | |
tree | a91078b0ca240f870f5f15c2930bc0719a86c9d1 | |
parent | afb2da4789cdaddc5a0c3c9c2d57ccd36bcc74c7 (diff) | |
download | lanes-ab233d0c9a1edc34836e2249c1eb6d714f1066b5.tar.gz lanes-ab233d0c9a1edc34836e2249c1eb6d714f1066b5.tar.bz2 lanes-ab233d0c9a1edc34836e2249c1eb6d714f1066b5.zip |
Lane userdata implementation refactoring:
- Refactor lane proxy implementation: it is now a full userdata instead
of a table, and its methods are implemented in C instead of Lua.
* its metatable is no longer accessible.
* writing to the proxy raises an error.
* it is no longer possible to overwrite its join() and cancel() methods
- when a deep userdata idfunc requests a module to be required, manually
check that it is not loaded before requiring it instead of relying on
the require function's loop detection feature.
- when a module must be required, raise an error if the 'require' function
is not found in the target state.
- we know Lanes is loaded in the master state, so we don't force it
to be required in every lane too when a linda deep userdata is copied.
-rw-r--r-- | CHANGES | 14 | ||||
-rw-r--r-- | docs/index.html | 4 | ||||
-rw-r--r-- | src/lanes.c | 231 | ||||
-rw-r--r-- | src/lanes.lua | 107 | ||||
-rw-r--r-- | src/tools.c | 121 | ||||
-rw-r--r-- | tests/appendud.lua | 20 | ||||
-rw-r--r-- | tests/basic.lua | 2 | ||||
-rw-r--r-- | tests/error.lua | 6 | ||||
-rw-r--r-- | tests/fibonacci.lua | 2 | ||||
-rw-r--r-- | tests/protectproxy.lua | 27 |
10 files changed, 346 insertions, 188 deletions
@@ -3,6 +3,20 @@ CHANGES: | |||
3 | 3 | ||
4 | CHANGE X: | 4 | CHANGE X: |
5 | 5 | ||
6 | CHANGE 27 BGe 17-Feb-2011 | ||
7 | - we know Lanes is loaded in the master state, so we don't force it | ||
8 | to be required in every lane too when a linda deep userdata is copied | ||
9 | - Refactor lane proxy implementation: it is now a full userdata instead | ||
10 | of a table, and its methods are implemented in C instead of Lua | ||
11 | * its metatable is no longer accessible | ||
12 | * writing to the proxy raises an error | ||
13 | * it is no longer possible to overwrite its join() and cancel() methods | ||
14 | - when a deep userdata idfunc requests a module to be required, manually | ||
15 | check that it is not loaded before requiring it instead of relying on | ||
16 | the require function's loop detection feature | ||
17 | - when a module must be required, raise an error if the 'require' function | ||
18 | is not found in the target state | ||
19 | |||
6 | CHANGE 26 BGe 14-Feb-2011: | 20 | CHANGE 26 BGe 14-Feb-2011: |
7 | Fixed application hang-up because keeper state was not released in case of errors thrown by | 21 | Fixed application hang-up because keeper state was not released in case of errors thrown by |
8 | inter-state data copy for unsupported types | 22 | inter-state data copy for unsupported types |
diff --git a/docs/index.html b/docs/index.html index 9d66510..03c91f7 100644 --- a/docs/index.html +++ b/docs/index.html | |||
@@ -804,8 +804,8 @@ and for making metatables for the state-specific proxies for accessing it. | |||
804 | 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>. |
805 | </li> | 805 | </li> |
806 | <li>Instanciate your userdata using <tt>luaG_deep_userdata()</tt>, | 806 | <li>Instanciate your userdata using <tt>luaG_deep_userdata()</tt>, |
807 | instead of the regular <tt>lua_newuserdata()</tt>. | 807 | instead of the regular <tt>lua_newuserdata()</tt>. |
808 | Given an <tt>idfunc</tt>, it sets up the support | 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> |
diff --git a/src/lanes.c b/src/lanes.c index f650d9a..0a89959 100644 --- a/src/lanes.c +++ b/src/lanes.c | |||
@@ -912,7 +912,12 @@ static void linda_id( lua_State *L, char const * const which) | |||
912 | } | 912 | } |
913 | else if( strcmp( which, "module") == 0) | 913 | else if( strcmp( which, "module") == 0) |
914 | { | 914 | { |
915 | lua_pushliteral( L, "lua51-lanes"); | 915 | // linda is a special case because we know lanes must be loaded from the main lua state |
916 | // to be able to ever get here, so we know it will remain loaded as long a the main state is around | ||
917 | // in other words, forever. | ||
918 | lua_pushnil( L); | ||
919 | // other idfuncs must push a string naming the module they come from | ||
920 | //lua_pushliteral( L, "lua51-lanes"); | ||
916 | } | 921 | } |
917 | } | 922 | } |
918 | 923 | ||
@@ -1635,6 +1640,11 @@ LUAG_FUNC( thread_new ) | |||
1635 | lua_setmetatable( L, -2 ); | 1640 | lua_setmetatable( L, -2 ); |
1636 | STACK_MID(L,1) | 1641 | STACK_MID(L,1) |
1637 | 1642 | ||
1643 | // Clear environment for the userdata | ||
1644 | // | ||
1645 | lua_newtable( L); | ||
1646 | lua_setfenv( L, -2); | ||
1647 | |||
1638 | // Place 's' to registry, for 'cancel_test()' (even if 'cs'==0 we still | 1648 | // Place 's' to registry, for 'cancel_test()' (even if 'cs'==0 we still |
1639 | // do cancel tests at pending send/receive). | 1649 | // do cancel tests at pending send/receive). |
1640 | // | 1650 | // |
@@ -1726,7 +1736,6 @@ LUAG_FUNC( thread_gc ) | |||
1726 | return 0; | 1736 | return 0; |
1727 | } | 1737 | } |
1728 | 1738 | ||
1729 | |||
1730 | //--- | 1739 | //--- |
1731 | // = thread_cancel( lane_ud [,timeout_secs=0.0] [,force_kill_bool=false] ) | 1740 | // = thread_cancel( lane_ud [,timeout_secs=0.0] [,force_kill_bool=false] ) |
1732 | // | 1741 | // |
@@ -1794,7 +1803,7 @@ static bool_t thread_cancel( struct s_lane *s, double secs, bool_t force ) | |||
1794 | 1803 | ||
1795 | 1804 | ||
1796 | //--- | 1805 | //--- |
1797 | // str= thread_status( lane_ud ) | 1806 | // str= thread_status( lane ) |
1798 | // | 1807 | // |
1799 | // Returns: "pending" not started yet | 1808 | // Returns: "pending" not started yet |
1800 | // -> "running" started, doing its work.. | 1809 | // -> "running" started, doing its work.. |
@@ -1803,25 +1812,29 @@ static bool_t thread_cancel( struct s_lane *s, double secs, bool_t force ) | |||
1803 | // / "error" finished at an error, error value is there | 1812 | // / "error" finished at an error, error value is there |
1804 | // / "cancelled" execution cancelled by M (state gone) | 1813 | // / "cancelled" execution cancelled by M (state gone) |
1805 | // | 1814 | // |
1806 | LUAG_FUNC( thread_status ) | 1815 | static char const * const thread_status_string( struct s_lane *s) |
1807 | { | 1816 | { |
1808 | struct s_lane *s= lua_toLane(L,1); | 1817 | enum e_status st = s->status; // read just once (volatile) |
1809 | enum e_status st= s->status; // read just once (volatile) | 1818 | char const * str; |
1810 | const char *str; | 1819 | |
1811 | 1820 | if (s->mstatus == KILLED) | |
1812 | if (s->mstatus == KILLED) | 1821 | st= CANCELLED; |
1813 | st= CANCELLED; | 1822 | |
1814 | 1823 | str= (st==PENDING) ? "pending" : | |
1815 | str= (st==PENDING) ? "pending" : | 1824 | (st==RUNNING) ? "running" : // like in 'co.status()' |
1816 | (st==RUNNING) ? "running" : // like in 'co.status()' | 1825 | (st==WAITING) ? "waiting" : |
1817 | (st==WAITING) ? "waiting" : | 1826 | (st==DONE) ? "done" : |
1818 | (st==DONE) ? "done" : | 1827 | (st==ERROR_ST) ? "error" : |
1819 | (st==ERROR_ST) ? "error" : | 1828 | (st==CANCELLED) ? "cancelled" : NULL; |
1820 | (st==CANCELLED) ? "cancelled" : NULL; | 1829 | return str; |
1821 | ASSERT_L(str); | 1830 | } |
1822 | 1831 | ||
1823 | lua_pushstring( L, str ); | 1832 | static void push_thread_status( lua_State *L, struct s_lane *s) |
1824 | return 1; | 1833 | { |
1834 | char const * const str = thread_status_string( s); | ||
1835 | ASSERT_L( str); | ||
1836 | |||
1837 | lua_pushstring( L, str ); | ||
1825 | } | 1838 | } |
1826 | 1839 | ||
1827 | 1840 | ||
@@ -1887,6 +1900,157 @@ LUAG_FUNC( thread_join ) | |||
1887 | } | 1900 | } |
1888 | 1901 | ||
1889 | 1902 | ||
1903 | //--- | ||
1904 | // thread_index( ud, key) -> value | ||
1905 | // | ||
1906 | // If key is found in the environment, return it | ||
1907 | // If key is numeric, wait until the thread returns and populate the environment with the return values | ||
1908 | // If the return values signal an error, propagate it | ||
1909 | // If key is "status" return the thread status | ||
1910 | // Else raise an error | ||
1911 | LUAG_FUNC( thread_index) | ||
1912 | { | ||
1913 | int const UD = 1; | ||
1914 | int const KEY = 2; | ||
1915 | int const ENV = 3; | ||
1916 | struct s_lane *s = lua_toLane( L, UD); | ||
1917 | ASSERT_L( lua_gettop( L) == 2); | ||
1918 | |||
1919 | STACK_GROW( L, 8); // up to 8 positions are needed in case of error propagation | ||
1920 | |||
1921 | // If key is numeric, wait until the thread returns and populate the environment with the return values | ||
1922 | if( lua_type( L, KEY) == LUA_TNUMBER) | ||
1923 | { | ||
1924 | // first, check that we don't already have an environment that holds the requested value | ||
1925 | { | ||
1926 | // If key is found in the environment, return it | ||
1927 | lua_getfenv( L, UD); | ||
1928 | lua_pushvalue( L, KEY); | ||
1929 | lua_rawget( L, ENV); | ||
1930 | if( !lua_isnil( L, -1)) | ||
1931 | { | ||
1932 | return 1; | ||
1933 | } | ||
1934 | lua_pop( L, 1); | ||
1935 | } | ||
1936 | { | ||
1937 | // check if we already fetched the values from the thread or not | ||
1938 | bool_t fetched; | ||
1939 | lua_Integer key = lua_tointeger( L, KEY); | ||
1940 | lua_pushinteger( L, 0); | ||
1941 | lua_rawget( L, ENV); | ||
1942 | fetched = !lua_isnil( L, -1); | ||
1943 | lua_pop( L, 1); // back to our 2 args + env on the stack | ||
1944 | if( !fetched) | ||
1945 | { | ||
1946 | lua_pushinteger( L, 0); | ||
1947 | lua_pushboolean( L, 1); | ||
1948 | lua_rawset( L, ENV); | ||
1949 | // wait until thread has completed | ||
1950 | lua_pushcfunction( L, LG_thread_join); | ||
1951 | lua_pushvalue( L, UD); | ||
1952 | lua_call( L, 1, LUA_MULTRET); // all return values are on the stack, at slots 4+ | ||
1953 | switch( s->status) | ||
1954 | { | ||
1955 | case DONE: // got regular return values | ||
1956 | { | ||
1957 | int i, nvalues = lua_gettop( L) - 3; | ||
1958 | for( i = nvalues; i > 0; -- i) | ||
1959 | { | ||
1960 | // pop the last element of the stack, to store it in the environment at its proper index | ||
1961 | lua_rawseti( L, ENV, i); | ||
1962 | } | ||
1963 | } | ||
1964 | break; | ||
1965 | |||
1966 | case ERROR_ST: // got 3 values: nil, errstring, callstack table | ||
1967 | // me[-2] could carry the stack table, but even | ||
1968 | // me[-1] is rather unnecessary (and undocumented); | ||
1969 | // use ':join()' instead. --AKa 22-Jan-2009 | ||
1970 | ASSERT_L( lua_isnil( L, 4) && !lua_isnil( L, 5) && lua_istable( L, 6)); | ||
1971 | // store errstring at key -1 | ||
1972 | lua_pushnumber( L, -1); | ||
1973 | lua_pushvalue( L, 5); | ||
1974 | lua_rawset( L, ENV); | ||
1975 | break; | ||
1976 | |||
1977 | case CANCELLED: | ||
1978 | // do nothing | ||
1979 | break; | ||
1980 | |||
1981 | default: | ||
1982 | // this is an internal error, we probably never get here | ||
1983 | lua_settop( L, 0); | ||
1984 | lua_pushliteral( L, "Unexpected status: "); | ||
1985 | lua_pushstring( L, thread_status_string( s)); | ||
1986 | lua_concat( L, 2); | ||
1987 | lua_error( L); | ||
1988 | break; | ||
1989 | } | ||
1990 | } | ||
1991 | lua_settop( L, 3); // UD KEY ENV | ||
1992 | if( key != -1) | ||
1993 | { | ||
1994 | lua_pushnumber( L, -1); // UD KEY ENV -1 | ||
1995 | lua_rawget( L, ENV); // UD KEY ENV "error" | ||
1996 | if( !lua_isnil( L, -1)) // an error was stored | ||
1997 | { | ||
1998 | // Note: Lua 5.1 interpreter is not prepared to show | ||
1999 | // non-string errors, so we use 'tostring()' here | ||
2000 | // to get meaningful output. --AKa 22-Jan-2009 | ||
2001 | // | ||
2002 | // Also, the stack dump we get is no good; it only | ||
2003 | // lists our internal Lanes functions. There seems | ||
2004 | // to be no way to switch it off, though. | ||
2005 | // | ||
2006 | // Level 3 should show the line where 'h[x]' was read | ||
2007 | // but this only seems to work for string messages | ||
2008 | // (Lua 5.1.4). No idea, why. --AKa 22-Jan-2009 | ||
2009 | lua_getmetatable( L, UD); // UD KEY ENV "error" mt | ||
2010 | lua_getfield( L, -1, "cached_error"); // UD KEY ENV "error" mt error() | ||
2011 | lua_getfield( L, -2, "cached_tostring"); // UD KEY ENV "error" mt error() tostring() | ||
2012 | lua_pushvalue( L, 4); // UD KEY ENV "error" mt error() tostring() "error" | ||
2013 | lua_call( L, 1, 1); // tostring( errstring) -- just in case // UD KEY ENV "error" mt error() "error" | ||
2014 | lua_pushinteger( L, 3); // UD KEY ENV "error" mt error() "error" 3 | ||
2015 | lua_call( L, 2, 0); // error( tostring( errstring), 3) // UD KEY ENV "error" mt | ||
2016 | } | ||
2017 | else | ||
2018 | { | ||
2019 | lua_pop( L, 1); // back to our 3 arguments on the stack | ||
2020 | } | ||
2021 | } | ||
2022 | lua_rawgeti( L, ENV, (int)key); | ||
2023 | } | ||
2024 | return 1; | ||
2025 | } | ||
2026 | if( lua_type( L, KEY) == LUA_TSTRING) | ||
2027 | { | ||
2028 | char const * const keystr = lua_tostring( L, KEY); | ||
2029 | lua_settop( L, 2); // keep only our original arguments on the stack | ||
2030 | if( strcmp( keystr, "status") == 0) | ||
2031 | { | ||
2032 | push_thread_status( L, s); // push the string representing the status | ||
2033 | } | ||
2034 | else if( strcmp( keystr, "cancel") == 0 || strcmp( keystr, "join") == 0) | ||
2035 | { | ||
2036 | // return UD.metatable[key] (should be a function in both cases) | ||
2037 | lua_getmetatable( L, UD); // UD KEY mt | ||
2038 | lua_replace( L, -3); // mt KEY | ||
2039 | lua_rawget( L, -2); // mt value | ||
2040 | ASSERT_L( lua_iscfunction( L, -1)); | ||
2041 | } | ||
2042 | return 1; | ||
2043 | } | ||
2044 | // unknown key | ||
2045 | lua_getmetatable( L, UD); | ||
2046 | lua_getfield( L, -1, "cached_error"); | ||
2047 | lua_pushliteral( L, "Unknown key: "); | ||
2048 | lua_pushvalue( L, KEY); | ||
2049 | lua_concat( L, 2); | ||
2050 | lua_call( L, 1, 0); // error( "Unknown key: " .. key) -> doesn't return | ||
2051 | return 0; | ||
2052 | } | ||
2053 | |||
1890 | /*---=== Timer support ===--- | 2054 | /*---=== Timer support ===--- |
1891 | */ | 2055 | */ |
1892 | 2056 | ||
@@ -1946,15 +2110,11 @@ LUAG_FUNC( wakeup_conv ) | |||
1946 | return 1; | 2110 | return 1; |
1947 | } | 2111 | } |
1948 | 2112 | ||
1949 | |||
1950 | /*---=== Module linkage ===--- | 2113 | /*---=== Module linkage ===--- |
1951 | */ | 2114 | */ |
1952 | 2115 | ||
1953 | static const struct luaL_reg lanes_functions [] = { | 2116 | static const struct luaL_reg lanes_functions [] = { |
1954 | {"linda", LG_linda}, | 2117 | {"linda", LG_linda}, |
1955 | {"thread_status", LG_thread_status}, | ||
1956 | {"thread_join", LG_thread_join}, | ||
1957 | {"thread_cancel", LG_thread_cancel}, | ||
1958 | {"now_secs", LG_now_secs}, | 2118 | {"now_secs", LG_now_secs}, |
1959 | {"wakeup_conv", LG_wakeup_conv}, | 2119 | {"wakeup_conv", LG_wakeup_conv}, |
1960 | {"_single", LG__single}, | 2120 | {"_single", LG__single}, |
@@ -2101,10 +2261,25 @@ __declspec(dllexport) | |||
2101 | luaL_register(L, NULL, lanes_functions); | 2261 | luaL_register(L, NULL, lanes_functions); |
2102 | 2262 | ||
2103 | // metatable for threads | 2263 | // metatable for threads |
2264 | // contains keys: { __gc, __index, cached_error, cached_tostring, cancel, join } | ||
2104 | // | 2265 | // |
2105 | lua_newtable( L ); | 2266 | lua_newtable( L); |
2106 | lua_pushcfunction( L, LG_thread_gc ); | 2267 | lua_pushcfunction( L, LG_thread_gc); |
2107 | lua_setfield( L, -2, "__gc" ); | 2268 | lua_setfield( L, -2, "__gc"); |
2269 | lua_pushcfunction( L, LG_thread_index); | ||
2270 | lua_setfield( L, -2, "__index"); | ||
2271 | lua_getfield( L, LUA_GLOBALSINDEX, "error"); | ||
2272 | ASSERT_L( lua_isfunction( L, -1)); | ||
2273 | lua_setfield( L, -2, "cached_error"); | ||
2274 | lua_getfield( L, LUA_GLOBALSINDEX, "tostring"); | ||
2275 | ASSERT_L( lua_isfunction( L, -1)); | ||
2276 | lua_setfield( L, -2, "cached_tostring"); | ||
2277 | lua_pushcfunction( L, LG_thread_join); | ||
2278 | lua_setfield( L, -2, "join"); | ||
2279 | lua_pushcfunction( L, LG_thread_cancel); | ||
2280 | lua_setfield( L, -2, "cancel"); | ||
2281 | lua_pushboolean( L, 0); | ||
2282 | lua_setfield( L, -2, "__metatable"); | ||
2108 | 2283 | ||
2109 | lua_pushcclosure( L, LG_thread_new, 1 ); // metatable as closure param | 2284 | lua_pushcclosure( L, LG_thread_new, 1 ); // metatable as closure param |
2110 | lua_setfield(L, -2, "thread_new"); | 2285 | lua_setfield(L, -2, "thread_new"); |
diff --git a/src/lanes.lua b/src/lanes.lua index b6fbc08..95bdeeb 100644 --- a/src/lanes.lua +++ b/src/lanes.lua | |||
@@ -45,10 +45,7 @@ local mm = require "lua51-lanes" | |||
45 | assert( type(mm)=="table" ) | 45 | assert( type(mm)=="table" ) |
46 | 46 | ||
47 | 47 | ||
48 | local thread_new= assert(mm.thread_new) | 48 | local thread_new = assert(mm.thread_new) |
49 | local thread_status= assert(mm.thread_status) | ||
50 | local thread_join= assert(mm.thread_join) | ||
51 | local thread_cancel= assert(mm.thread_cancel) | ||
52 | 49 | ||
53 | local _single= assert(mm._single) | 50 | local _single= assert(mm._single) |
54 | local _version= assert(mm._version) | 51 | local _version= assert(mm._version) |
@@ -77,8 +74,6 @@ local type= assert( type ) | |||
77 | local pairs= assert( pairs ) | 74 | local pairs= assert( pairs ) |
78 | local tostring= assert( tostring ) | 75 | local tostring= assert( tostring ) |
79 | local error= assert( error ) | 76 | local error= assert( error ) |
80 | local setmetatable= assert( setmetatable ) | ||
81 | local rawget= assert( rawget ) | ||
82 | 77 | ||
83 | ABOUT= | 78 | ABOUT= |
84 | { | 79 | { |
@@ -127,76 +122,8 @@ end | |||
127 | -- Or, even better, 'ipairs()' should start valuing '__index' instead | 122 | -- Or, even better, 'ipairs()' should start valuing '__index' instead |
128 | -- of using raw reads that bypass it. | 123 | -- of using raw reads that bypass it. |
129 | -- | 124 | -- |
130 | local lane_mt= { | ||
131 | __index= function( me, k ) | ||
132 | if type(k) == "number" then | ||
133 | -- 'me[0]=true' marks we've already taken in the results | ||
134 | -- | ||
135 | if not rawget( me, 0 ) then | ||
136 | -- Wait indefinately; either propagates an error or | ||
137 | -- returns the return values | ||
138 | -- | ||
139 | me[0]= true -- marker, even on errors | ||
140 | |||
141 | local t= { thread_join(me._ud) } -- wait indefinate | ||
142 | -- | ||
143 | -- { ... } "done": regular return, 0..N results | ||
144 | -- { } "cancelled" | ||
145 | -- { nil, err_str, stack_tbl } "error" | ||
146 | |||
147 | local st= thread_status(me._ud) | ||
148 | if st=="done" then | ||
149 | -- Use 'pairs' and not 'ipairs' so that nil holes in | ||
150 | -- the returned values are tolerated. | ||
151 | -- | ||
152 | for i,v in pairs(t) do | ||
153 | me[i]= v | ||
154 | end | ||
155 | elseif st=="error" then | ||
156 | assert( t[1]==nil and t[2] and type(t[3])=="table" ) | ||
157 | me[-1]= t[2] | ||
158 | -- me[-2] could carry the stack table, but even | ||
159 | -- me[-1] is rather unnecessary (and undocumented); | ||
160 | -- use ':join()' instead. --AKa 22-Jan-2009 | ||
161 | elseif st=="cancelled" then | ||
162 | -- do nothing | ||
163 | else | ||
164 | error( "Unexpected status: "..st ) | ||
165 | end | ||
166 | end | ||
167 | |||
168 | -- Check errors even if we'd first peeked them via [-1] | ||
169 | -- and then came for the actual results. | ||
170 | -- | ||
171 | local err= rawget(me, -1) | ||
172 | if err~=nil and k~=-1 then | ||
173 | -- Note: Lua 5.1 interpreter is not prepared to show | ||
174 | -- non-string errors, so we use 'tostring()' here | ||
175 | -- to get meaningful output. --AKa 22-Jan-2009 | ||
176 | -- | ||
177 | -- Also, the stack dump we get is no good; it only | ||
178 | -- lists our internal Lanes functions. There seems | ||
179 | -- to be no way to switch it off, though. | ||
180 | |||
181 | -- Level 3 should show the line where 'h[x]' was read | ||
182 | -- but this only seems to work for string messages | ||
183 | -- (Lua 5.1.4). No idea, why. --AKa 22-Jan-2009 | ||
184 | -- | ||
185 | error( tostring(err), 3 ) -- level 3 should show the line where 'h[x]' was read | ||
186 | end | ||
187 | return rawget( me, k ) | ||
188 | -- | ||
189 | elseif k=="status" then -- me.status | ||
190 | return thread_status(me._ud) | ||
191 | -- | ||
192 | else | ||
193 | error( "Unknown key: "..k ) | ||
194 | end | ||
195 | end | ||
196 | } | ||
197 | |||
198 | ----- | 125 | ----- |
199 | -- h= lanes.gen( [libs_str|opt_tbl [, ...],] lane_func ) ( [...] ) | 126 | -- lanes.gen( [libs_str|opt_tbl [, ...],] lane_func ) ( [...] ) -> h |
200 | -- | 127 | -- |
201 | -- 'libs': nil: no libraries available (default) | 128 | -- 'libs': nil: no libraries available (default) |
202 | -- "": only base library ('assert', 'print', 'unpack' etc.) | 129 | -- "": only base library ('assert', 'print', 'unpack' etc.) |
@@ -220,8 +147,6 @@ local lane_mt= { | |||
220 | -- modifiers, and prepares a lane generator. One can either finish here, | 147 | -- modifiers, and prepares a lane generator. One can either finish here, |
221 | -- and call the generator later (maybe multiple times, with different parameters) | 148 | -- and call the generator later (maybe multiple times, with different parameters) |
222 | -- or add on actual thread arguments to also ignite the thread on the same call. | 149 | -- or add on actual thread arguments to also ignite the thread on the same call. |
223 | -- | ||
224 | local lane_proxy | ||
225 | 150 | ||
226 | local valid_libs= { | 151 | local valid_libs= { |
227 | ["package"]= true, | 152 | ["package"]= true, |
@@ -298,39 +223,16 @@ function gen( ... ) | |||
298 | -- Lane generator | 223 | -- Lane generator |
299 | -- | 224 | -- |
300 | return function(...) | 225 | return function(...) |
301 | return lane_proxy( thread_new( func, libs, cs, prio, g_tbl, | 226 | return thread_new( func, libs, cs, prio, g_tbl, ...) -- args |
302 | ... ) ) -- args | ||
303 | end | 227 | end |
304 | end | 228 | end |
305 | 229 | ||
306 | lane_proxy= function( ud ) | ||
307 | local proxy= { | ||
308 | _ud= ud, | ||
309 | |||
310 | -- true|false= me:cancel() | ||
311 | -- | ||
312 | cancel= function(me, time, force) return thread_cancel(me._ud, time, force) end, | ||
313 | |||
314 | |||
315 | -- [...] | [nil,err,stack_tbl]= me:join( [wait_secs=-1] ) | ||
316 | -- | ||
317 | join= function( me, wait ) | ||
318 | return thread_join( me._ud, wait ) | ||
319 | end, | ||
320 | } | ||
321 | assert( proxy._ud ) | ||
322 | setmetatable( proxy, lane_mt ) | ||
323 | |||
324 | return proxy | ||
325 | end | ||
326 | |||
327 | |||
328 | ---=== Lindas ===--- | 230 | ---=== Lindas ===--- |
329 | 231 | ||
330 | -- We let the C code attach methods to userdata directly | 232 | -- We let the C code attach methods to userdata directly |
331 | 233 | ||
332 | ----- | 234 | ----- |
333 | -- linda_ud= lanes.linda() | 235 | -- lanes.linda() -> linda_ud |
334 | -- | 236 | -- |
335 | linda = mm.linda | 237 | linda = mm.linda |
336 | 238 | ||
@@ -613,5 +515,6 @@ function genatomic( linda, key, initial_val ) | |||
613 | end | 515 | end |
614 | end | 516 | end |
615 | 517 | ||
518 | -- newuserdata = mm.newuserdata | ||
616 | 519 | ||
617 | --the end | 520 | --the end |
diff --git a/src/tools.c b/src/tools.c index 41163c4..29959a8 100644 --- a/src/tools.c +++ b/src/tools.c | |||
@@ -395,65 +395,102 @@ void luaG_push_proxy( lua_State *L, luaG_IdFunction idfunc, DEEP_PRELUDE *prelud | |||
395 | 395 | ||
396 | if (lua_isnil(L,-1)) | 396 | if (lua_isnil(L,-1)) |
397 | { | 397 | { |
398 | int oldtop; | ||
399 | // No metatable yet. We have two things to do: | 398 | // No metatable yet. We have two things to do: |
400 | |||
401 | // 1 - make one and register it | 399 | // 1 - make one and register it |
402 | lua_pop(L,1); | 400 | { |
401 | int oldtop; | ||
403 | 402 | ||
404 | // tbl= idfunc( "metatable" ) | 403 | lua_pop( L, 1); |
405 | // | ||
406 | oldtop = lua_gettop( L); | ||
407 | idfunc( L, "metatable"); | ||
408 | // | ||
409 | // [-2]: proxy | ||
410 | // [-1]: metatable (returned by 'idfunc') | ||
411 | 404 | ||
412 | if (lua_gettop( L) - oldtop != 1 || !lua_istable(L, -1)) | 405 | // tbl= idfunc( "metatable" ) |
413 | { | 406 | // |
414 | luaL_error( L, "Bad idfunc on \"metatable\": did not return one" ); | 407 | oldtop = lua_gettop( L); |
415 | } | 408 | idfunc( L, "metatable"); |
409 | // | ||
410 | // [-2]: proxy | ||
411 | // [-1]: metatable (returned by 'idfunc') | ||
416 | 412 | ||
417 | // Add '__gc' method | 413 | if (lua_gettop( L) - oldtop != 1 || !lua_istable(L, -1)) |
418 | // | 414 | { |
419 | lua_pushcfunction( L, deep_userdata_gc ); | 415 | luaL_error( L, "Bad idfunc on \"metatable\": did not return one" ); |
420 | lua_setfield( L, -2, "__gc" ); | 416 | } |
421 | 417 | ||
422 | // Memorize for later rounds | 418 | // Add '__gc' method |
423 | // | 419 | // |
424 | lua_pushvalue( L,-1 ); | 420 | lua_pushcfunction( L, deep_userdata_gc ); |
425 | lua_pushlightuserdata( L, idfunc ); | 421 | lua_setfield( L, -2, "__gc" ); |
426 | // | 422 | |
427 | // [-4]: proxy | 423 | // Memorize for later rounds |
428 | // [-3]: metatable (2nd ref) | 424 | // |
429 | // [-2]: metatable | 425 | lua_pushvalue( L,-1 ); |
430 | // [-1]: idfunc | 426 | lua_pushlightuserdata( L, idfunc ); |
427 | // | ||
428 | // [-4]: proxy | ||
429 | // [-3]: metatable (2nd ref) | ||
430 | // [-2]: metatable | ||
431 | // [-1]: idfunc | ||
431 | 432 | ||
432 | set_deep_lookup(L); | 433 | set_deep_lookup(L); |
434 | } | ||
433 | 435 | ||
434 | // 2 - cause the target state to require the module that exported the idfunc | 436 | // 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 | 437 | // 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"); | 438 | STACK_CHECK(L) |
437 | if( lua_isfunction( L, -1)) // just in case... | ||
438 | { | 439 | { |
440 | char const * modname; | ||
439 | // make sure the function pushed a single value on the stack! | 441 | // 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 | { | 442 | { |
444 | luaL_error( L, "Bad idfunc on \"module\": should return a string"); | 443 | int oldtop = lua_gettop( L); |
444 | idfunc( L, "module"); // ... "module"/nil | ||
445 | if( lua_gettop( L) - oldtop != 1) | ||
446 | { | ||
447 | luaL_error( L, "Bad idfunc on \"module\": should return a single value"); | ||
448 | } | ||
445 | } | 449 | } |
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) | 450 | modname = luaL_optstring( L, -1, NULL); // raises an error if not a string or nil |
447 | if( lua_pcall( L, 1, 0, 0) != 0) | 451 | if( modname) // we actually got a module name |
448 | { | 452 | { |
449 | //char const * const errMsg = lua_tostring( L, -1); // just to see it in the debugger | 453 | // somehow, L.registry._LOADED can exist without having registered the 'package' library. |
450 | lua_pop( L, 1); | 454 | lua_getglobal( L, "require"); // ... "module" require() |
455 | // check that the module is already loaded (or being loaded, we are happy either way) | ||
456 | if( lua_isfunction( L, -1)) | ||
457 | { | ||
458 | lua_insert( L, -2); // ... require() "module" | ||
459 | lua_getfield( L, LUA_REGISTRYINDEX, "_LOADED"); // ... require() "module" L.registry._LOADED | ||
460 | if( lua_istable( L, -1)) | ||
461 | { | ||
462 | bool_t alreadyloaded; | ||
463 | lua_pushvalue( L, -2); // ... require() "module" L.registry._LOADED "module" | ||
464 | lua_rawget( L, -2); // ... require() "module" L.registry._LOADED module | ||
465 | alreadyloaded = lua_toboolean( L, -1); | ||
466 | if( !alreadyloaded) // not loaded | ||
467 | { | ||
468 | lua_pop( L, 2); // ... require() "module" | ||
469 | lua_call( L, 1, 0); // call require "modname" // ... | ||
470 | } | ||
471 | else // already loaded, we are happy | ||
472 | { | ||
473 | lua_pop( L, 4); // ... | ||
474 | } | ||
475 | } | ||
476 | else // no L.registry._LOADED; can this ever happen? | ||
477 | { | ||
478 | luaL_error( L, "unexpected error while requiring a module"); | ||
479 | lua_pop( L, 3); // ... | ||
480 | } | ||
481 | } | ||
482 | else // a module name, but no require() function :-( | ||
483 | { | ||
484 | luaL_error( L, "lanes receiving deep userdata should register the 'package' library"); | ||
485 | lua_pop( L, 2); // ... | ||
486 | } | ||
487 | } | ||
488 | else // no module name | ||
489 | { | ||
490 | lua_pop( L, 1); // ... | ||
451 | } | 491 | } |
452 | } | 492 | } |
453 | else | 493 | STACK_END(L,0) |
454 | { | ||
455 | lua_pop( L, 1); | ||
456 | } | ||
457 | } | 494 | } |
458 | STACK_MID(L,2) | 495 | STACK_MID(L,2) |
459 | ASSERT_L( lua_isuserdata(L,-2) ); | 496 | ASSERT_L( lua_isuserdata(L,-2) ); |
diff --git a/tests/appendud.lua b/tests/appendud.lua index 65d0798..eb1f768 100644 --- a/tests/appendud.lua +++ b/tests/appendud.lua | |||
@@ -31,28 +31,30 @@ local _ud = { | |||
31 | 31 | ||
32 | 32 | ||
33 | function appendud(tab, ud) | 33 | function appendud(tab, ud) |
34 | io.stderr:write "Starting" | 34 | io.stderr:write "Starting " |
35 | tab:beginupdate() set_finalizer( function() tab:endupdate() end ) | 35 | tab:beginupdate() set_finalizer( function() tab:endupdate() end ) |
36 | ud:lock() set_finalizer( function() ud:unlock() end ) | 36 | ud:lock() set_finalizer( function() ud:unlock() end ) |
37 | for i = 1,#ud do | 37 | for i = 1,#ud do |
38 | tab[#tab+1] = ud[i] | 38 | tab[#tab+1] = ud[i] |
39 | end | 39 | end |
40 | io.stderr:write "Ending" | 40 | io.stderr:write "Ending " |
41 | return tab -- need to return 'tab' since we're running in a separate thread | 41 | return tab -- need to return 'tab' since we're running in a separate thread |
42 | -- ('tab' is passed over lanes by value, not by reference) | 42 | -- ('tab' is passed over lanes by value, not by reference) |
43 | end | 43 | end |
44 | 44 | ||
45 | local t,err= lanes.gen( "io", appendud )( _tab, _ud ) -- create & launch a thread | 45 | local t,err= lanes.gen( "base,io", appendud )( _tab, _ud ) -- create & launch a thread |
46 | assert(t) | 46 | assert(t) |
47 | assert(not err) | 47 | assert(not err) |
48 | 48 | ||
49 | -- test | 49 | -- test |
50 | 50 | -- print("t:join()") | |
51 | t:join() -- Need to explicitly wait for the thread, since 'ipairs()' does not | 51 | a,b,c = t[1],t[2],t[3] -- Need to explicitly wait for the thread, since 'ipairs()' does not |
52 | --a,b,c = t:join() -- Need to explicitly wait for the thread, since 'ipairs()' does not | ||
52 | -- value the '__index' metamethod (wouldn't it be cool if it did..?) | 53 | -- value the '__index' metamethod (wouldn't it be cool if it did..?) |
53 | 54 | ||
54 | io.stderr:write(t[1]) | 55 | print(a,b,c) |
56 | -- print("io.stderr:write(t[1])") | ||
57 | -- io.stderr:write(t[1]) | ||
58 | _ = t[0] | ||
59 | print(_) | ||
55 | 60 | ||
56 | for k,v in ipairs(t) do | ||
57 | print(k,v) | ||
58 | end | ||
diff --git a/tests/basic.lua b/tests/basic.lua index 352e029..853a8de 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,package",chunk)(linda) -- prepare & launch | 174 | local t= lanes_gen("io",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" ) |
diff --git a/tests/error.lua b/tests/error.lua index 673bcb5..4922846 100644 --- a/tests/error.lua +++ b/tests/error.lua | |||
@@ -9,9 +9,9 @@ require "lanes" | |||
9 | local function lane() | 9 | local function lane() |
10 | 10 | ||
11 | local subf= function() -- this so that we can see the call stack | 11 | local subf= function() -- this so that we can see the call stack |
12 | --error "aa" | 12 | error "aa" |
13 | error({}) | 13 | --error({}) |
14 | error(error) | 14 | --error(error) |
15 | end | 15 | end |
16 | local subf2= function() | 16 | local subf2= function() |
17 | subf() | 17 | subf() |
diff --git a/tests/fibonacci.lua b/tests/fibonacci.lua index 8867e14..667a3e9 100644 --- a/tests/fibonacci.lua +++ b/tests/fibonacci.lua | |||
@@ -70,6 +70,6 @@ assert( #right==99 ) | |||
70 | 70 | ||
71 | local N= 80 | 71 | local N= 80 |
72 | local res= fib(N) | 72 | local res= fib(N) |
73 | print( right[N] ) | 73 | print( right[N], res ) |
74 | assert( res==right[N] ) | 74 | assert( res==right[N] ) |
75 | 75 | ||
diff --git a/tests/protectproxy.lua b/tests/protectproxy.lua new file mode 100644 index 0000000..57ca831 --- /dev/null +++ b/tests/protectproxy.lua | |||
@@ -0,0 +1,27 @@ | |||
1 | require "lanes" | ||
2 | |||
3 | local body = function( param) | ||
4 | print ( "lane body: " .. param) | ||
5 | return 1 | ||
6 | end | ||
7 | |||
8 | local gen = lanes.gen( "*", body) | ||
9 | |||
10 | local mylane = gen( "hello") | ||
11 | |||
12 | local result = mylane[1] | ||
13 | |||
14 | -- make sure we have properly protected the lane | ||
15 | |||
16 | -- can't access the metatable | ||
17 | print( "metatable:" .. tostring( getmetatable( mylane))) | ||
18 | |||
19 | -- can't write to the userdata | ||
20 | print( "lane result: " .. mylane[1]) | ||
21 | |||
22 | -- read nonexistent values -> nil | ||
23 | print "reading nonexistent return value" | ||
24 | a = mylane[2] | ||
25 | |||
26 | print "writing to the lane -> error" | ||
27 | mylane[4] = true | ||