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 /src/lanes.c | |
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.
Diffstat (limited to 'src/lanes.c')
-rw-r--r-- | src/lanes.c | 231 |
1 files changed, 203 insertions, 28 deletions
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"); |