diff options
| author | Benoit Germain <bnt.germain@gmail.com> | 2013-01-30 20:28:47 +0100 |
|---|---|---|
| committer | Benoit Germain <bnt.germain@gmail.com> | 2013-01-30 20:28:47 +0100 |
| commit | b657f38535c3c27a848353ef853d6667d6acc917 (patch) | |
| tree | 8679a62c0b6343eae4781cff10a0ea60460d9cb4 /src | |
| parent | 3377b95704e611a288791fee6a7bc59c5ecebf2d (diff) | |
| download | lanes-b657f38535c3c27a848353ef853d6667d6acc917.tar.gz lanes-b657f38535c3c27a848353ef853d6667d6acc917.tar.bz2 lanes-b657f38535c3c27a848353ef853d6667d6acc917.zip | |
version 3.5.0
* new: API lanes.require(), use it instead of regular require() for modules that export C functions you need to send over.
* new: lanes no longer require 'lanes.core' by default in every created state. Use {required={"lanes.core"}} if you need to transfer lanes functions.
* internal: because of the above, reworked the timer implementation to remove upvalue-dependency on lanes.core
* new: API lanes.timer_lane, to be able to operate on timer lane if need be
* improved: if a module is a full userdata, scan its metatable for function database population
* improved: on_state_create can be a Lua function
* changed: on_state_create is called after the base libraries are loaded
* package[loaders|searchers] is no longer transfered as function naming depends on slot order
* internal: changed separator from '.' to '/' in lookup databases to be able to distinguish search levels and dot coming from module names
* added some mode debug spew
* updated tests to reflect the above changes
Diffstat (limited to 'src')
| -rw-r--r-- | src/keeper.c | 15 | ||||
| -rw-r--r-- | src/keeper.h | 2 | ||||
| -rw-r--r-- | src/lanes.c | 134 | ||||
| -rw-r--r-- | src/lanes.lua | 323 | ||||
| -rw-r--r-- | src/tools.c | 92 | ||||
| -rw-r--r-- | src/tools.h | 5 |
6 files changed, 306 insertions, 265 deletions
diff --git a/src/keeper.c b/src/keeper.c index 7485110..3511b56 100644 --- a/src/keeper.c +++ b/src/keeper.c | |||
| @@ -557,7 +557,7 @@ void close_keepers( void) | |||
| 557 | * unclosed, because it does not really matter. In production code, this | 557 | * unclosed, because it does not really matter. In production code, this |
| 558 | * function never fails. | 558 | * function never fails. |
| 559 | */ | 559 | */ |
| 560 | char const* init_keepers( lua_State* L, int const _nbKeepers, lua_CFunction _on_state_create) | 560 | char const* init_keepers( lua_State* L, int _on_state_create, int const _nbKeepers) |
| 561 | { | 561 | { |
| 562 | int i; | 562 | int i; |
| 563 | assert( _nbKeepers >= 1); | 563 | assert( _nbKeepers >= 1); |
| @@ -572,7 +572,7 @@ char const* init_keepers( lua_State* L, int const _nbKeepers, lua_CFunction _on_ | |||
| 572 | // | 572 | // |
| 573 | // 'io' for debugging messages, 'package' because we need to require modules exporting idfuncs | 573 | // 'io' for debugging messages, 'package' because we need to require modules exporting idfuncs |
| 574 | // the others because they export functions that we may store in a keeper for transfer between lanes | 574 | // the others because they export functions that we may store in a keeper for transfer between lanes |
| 575 | K = luaG_newstate( L, "*", _on_state_create); | 575 | K = luaG_newstate( L, _on_state_create, "*"); |
| 576 | 576 | ||
| 577 | STACK_CHECK( K); | 577 | STACK_CHECK( K); |
| 578 | 578 | ||
| @@ -635,8 +635,8 @@ void populate_keepers( lua_State* L) | |||
| 635 | char const* name = luaL_checklstring( L, -1, &name_len); | 635 | char const* name = luaL_checklstring( L, -1, &name_len); |
| 636 | int i; | 636 | int i; |
| 637 | 637 | ||
| 638 | STACK_CHECK( L); | 638 | DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "populate_keepers %s BEGIN\n" INDENT_END, name)); |
| 639 | STACK_GROW( L, 3); | 639 | DEBUGSPEW_CODE( ++ debugspew_indent_depth); |
| 640 | 640 | ||
| 641 | for( i = 0; i < GNbKeepers; ++ i) | 641 | for( i = 0; i < GNbKeepers; ++ i) |
| 642 | { | 642 | { |
| @@ -647,16 +647,19 @@ void populate_keepers( lua_State* L) | |||
| 647 | STACK_GROW( K, 2); | 647 | STACK_GROW( K, 2); |
| 648 | lua_getglobal( K, "require"); | 648 | lua_getglobal( K, "require"); |
| 649 | lua_pushlstring( K, name, name_len); | 649 | lua_pushlstring( K, name, name_len); |
| 650 | res = lua_pcall( K, 1, 0, 0); | 650 | res = lua_pcall( K, 1, 1, 0); |
| 651 | if( res != LUA_OK) | 651 | if( res != LUA_OK) |
| 652 | { | 652 | { |
| 653 | char const* err = luaL_checkstring( K, -1); | 653 | char const* err = luaL_checkstring( K, -1); |
| 654 | luaL_error( L, "error requiring '%s' in keeper state: %s", name, err); | 654 | luaL_error( L, "error requiring '%s' in keeper state: %s", name, err); |
| 655 | } | 655 | } |
| 656 | // after requiring the module, register the functions it exported in our name<->function database | ||
| 657 | populate_func_lookup_table( K, -1, name); | ||
| 658 | lua_pop( K, 1); | ||
| 656 | STACK_END( K, 0); | 659 | STACK_END( K, 0); |
| 657 | MUTEX_UNLOCK( &GKeepers[i].lock_); | 660 | MUTEX_UNLOCK( &GKeepers[i].lock_); |
| 658 | } | 661 | } |
| 659 | STACK_END( L, 0); | 662 | DEBUGSPEW_CODE( -- debugspew_indent_depth); |
| 660 | } | 663 | } |
| 661 | 664 | ||
| 662 | struct s_Keeper* keeper_acquire( void const* ptr) | 665 | struct s_Keeper* keeper_acquire( void const* ptr) |
diff --git a/src/keeper.h b/src/keeper.h index 15a5a41..29a19a9 100644 --- a/src/keeper.h +++ b/src/keeper.h | |||
| @@ -13,7 +13,7 @@ struct s_Keeper | |||
| 13 | // problem: maybe on some platforms (linux) atexit() is called after DLL/so are unloaded... | 13 | // problem: maybe on some platforms (linux) atexit() is called after DLL/so are unloaded... |
| 14 | #define HAVE_KEEPER_ATEXIT_DESINIT 0 | 14 | #define HAVE_KEEPER_ATEXIT_DESINIT 0 |
| 15 | 15 | ||
| 16 | char const* init_keepers( lua_State* L, int const _nbKeepers, lua_CFunction _on_state_create); | 16 | char const* init_keepers( lua_State* L, int _on_state_create, int const _nbKeepers); |
| 17 | #if !HAVE_KEEPER_ATEXIT_DESINIT | 17 | #if !HAVE_KEEPER_ATEXIT_DESINIT |
| 18 | void close_keepers( void); | 18 | void close_keepers( void); |
| 19 | #endif // HAVE_KEEPER_ATEXIT_DESINIT | 19 | #endif // HAVE_KEEPER_ATEXIT_DESINIT |
diff --git a/src/lanes.c b/src/lanes.c index 80f5b15..2b3d8ac 100644 --- a/src/lanes.c +++ b/src/lanes.c | |||
| @@ -52,7 +52,7 @@ | |||
| 52 | * ... | 52 | * ... |
| 53 | */ | 53 | */ |
| 54 | 54 | ||
| 55 | char const* VERSION = "3.4.4"; | 55 | char const* VERSION = "3.5.0"; |
| 56 | 56 | ||
| 57 | /* | 57 | /* |
| 58 | =============================================================================== | 58 | =============================================================================== |
| @@ -1529,7 +1529,6 @@ LUAG_FUNC( set_singlethreaded) | |||
| 1529 | # define STACK_TRACE_KEY ((void*)lane_error) // used as registry key | 1529 | # define STACK_TRACE_KEY ((void*)lane_error) // used as registry key |
| 1530 | # define EXTENDED_STACK_TRACE_KEY ((void*)LG_set_error_reporting) // used as registry key | 1530 | # define EXTENDED_STACK_TRACE_KEY ((void*)LG_set_error_reporting) // used as registry key |
| 1531 | 1531 | ||
| 1532 | #ifdef ERROR_FULL_STACK | ||
| 1533 | LUAG_FUNC( set_error_reporting) | 1532 | LUAG_FUNC( set_error_reporting) |
| 1534 | { | 1533 | { |
| 1535 | bool_t equal; | 1534 | bool_t equal; |
| @@ -1554,7 +1553,6 @@ done: | |||
| 1554 | lua_rawset( L, LUA_REGISTRYINDEX); | 1553 | lua_rawset( L, LUA_REGISTRYINDEX); |
| 1555 | return 0; | 1554 | return 0; |
| 1556 | } | 1555 | } |
| 1557 | #endif // ERROR_FULL_STACK | ||
| 1558 | 1556 | ||
| 1559 | static int lane_error( lua_State* L) | 1557 | static int lane_error( lua_State* L) |
| 1560 | { | 1558 | { |
| @@ -1735,8 +1733,8 @@ static THREAD_RETURN_T THREAD_CALLCONV lane_main( void *vs) | |||
| 1735 | // [3..top]: parameters | 1733 | // [3..top]: parameters |
| 1736 | // | 1734 | // |
| 1737 | rc= lua_pcall( L, lua_gettop(L)-2, LUA_MULTRET, 1 /*error handler*/ ); | 1735 | rc= lua_pcall( L, lua_gettop(L)-2, LUA_MULTRET, 1 /*error handler*/ ); |
| 1738 | // 0: no error | 1736 | // 0: no error, body return values are on the stack |
| 1739 | // LUA_ERRRUN: a runtime error (error pushed on stack) | 1737 | // LUA_ERRRUN: cancellation or a runtime error (error pushed on stack) |
| 1740 | // LUA_ERRMEM: memory allocation error | 1738 | // LUA_ERRMEM: memory allocation error |
| 1741 | // LUA_ERRERR: error while running the error handler (if any) | 1739 | // LUA_ERRERR: error while running the error handler (if any) |
| 1742 | 1740 | ||
| @@ -1744,14 +1742,12 @@ static THREAD_RETURN_T THREAD_CALLCONV lane_main( void *vs) | |||
| 1744 | 1742 | ||
| 1745 | lua_remove(L,1); // remove error handler | 1743 | lua_remove(L,1); // remove error handler |
| 1746 | 1744 | ||
| 1747 | // Lua 5.1 error handler is limited to one return value; taking stack trace | 1745 | // Lua 5.1 error handler is limited to one return value; taking stack trace via registry |
| 1748 | // via registry | 1746 | if( rc != LUA_OK) |
| 1749 | // | ||
| 1750 | if( rc != 0) | ||
| 1751 | { | 1747 | { |
| 1752 | STACK_GROW(L,1); | 1748 | STACK_GROW(L,1); |
| 1753 | lua_pushlightuserdata( L, STACK_TRACE_KEY ); | 1749 | lua_pushlightuserdata( L, STACK_TRACE_KEY ); |
| 1754 | lua_gettable(L, LUA_REGISTRYINDEX); | 1750 | lua_gettable(L, LUA_REGISTRYINDEX); // yields nil if no stack was generated (in case of cancellation for example) |
| 1755 | 1751 | ||
| 1756 | // For cancellation, a stack trace isn't placed | 1752 | // For cancellation, a stack trace isn't placed |
| 1757 | // | 1753 | // |
| @@ -1773,7 +1769,7 @@ static THREAD_RETURN_T THREAD_CALLCONV lane_main( void *vs) | |||
| 1773 | // LUA_ERRMEM(4): memory allocation error | 1769 | // LUA_ERRMEM(4): memory allocation error |
| 1774 | #endif | 1770 | #endif |
| 1775 | 1771 | ||
| 1776 | DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "Lane %p body: %s\n" INDENT_END, L, get_errcode_name( rc))); | 1772 | DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "Lane %p body: %s (%s)\n" INDENT_END, L, get_errcode_name( rc), (lua_touserdata(L,1)==CANCEL_ERROR) ? "cancelled" : lua_typename( L, lua_type( L, 1)))); |
| 1777 | //STACK_DUMP(L); | 1773 | //STACK_DUMP(L); |
| 1778 | // Call finalizers, if the script has set them up. | 1774 | // Call finalizers, if the script has set them up. |
| 1779 | // | 1775 | // |
| @@ -1831,6 +1827,26 @@ static THREAD_RETURN_T THREAD_CALLCONV lane_main( void *vs) | |||
| 1831 | return 0; // ignored | 1827 | return 0; // ignored |
| 1832 | } | 1828 | } |
| 1833 | 1829 | ||
| 1830 | // --- If a client wants to transfer stuff of a given module from the current state to another Lane, the module must be required | ||
| 1831 | // with lanes.require, that will call the regular 'require', then populate lookup databases in source and keeper states | ||
| 1832 | // module = lanes.require( "modname") | ||
| 1833 | // upvalue[1]: _G.require | ||
| 1834 | LUAG_FUNC( require) | ||
| 1835 | { | ||
| 1836 | char const* name = lua_tostring( L, 1); | ||
| 1837 | DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lanes.require %s BEGIN\n" INDENT_END, name)); | ||
| 1838 | DEBUGSPEW_CODE( ++ debugspew_indent_depth); | ||
| 1839 | lua_pushvalue( L, lua_upvalueindex(1)); // "name" require | ||
| 1840 | lua_pushvalue( L, 1); // "name" require "name" | ||
| 1841 | lua_call( L, 1, 1); // "name" module | ||
| 1842 | populate_func_lookup_table( L, -1, name); | ||
| 1843 | lua_insert( L, -2); // module "name" | ||
| 1844 | populate_keepers( L); | ||
| 1845 | lua_pop( L, 1); // module | ||
| 1846 | DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lanes.require %s END\n" INDENT_END, name)); | ||
| 1847 | DEBUGSPEW_CODE( -- debugspew_indent_depth); | ||
| 1848 | return 1; | ||
| 1849 | } | ||
| 1834 | 1850 | ||
| 1835 | //--- | 1851 | //--- |
| 1836 | // lane_ud= thread_new( function, [libs_str], | 1852 | // lane_ud= thread_new( function, [libs_str], |
| @@ -1844,32 +1860,6 @@ static THREAD_RETURN_T THREAD_CALLCONV lane_main( void *vs) | |||
| 1844 | // Upvalues: metatable to use for 'lane_ud' | 1860 | // Upvalues: metatable to use for 'lane_ud' |
| 1845 | // | 1861 | // |
| 1846 | 1862 | ||
| 1847 | // helper function to require a module in the keeper states and in the target state | ||
| 1848 | // source state contains module name at the top of the stack | ||
| 1849 | static void require_one_module( lua_State* L, lua_State* L2, bool_t _fatal) | ||
| 1850 | { | ||
| 1851 | size_t len; | ||
| 1852 | char const* name = lua_tolstring( L, -1, &len); | ||
| 1853 | // require the module in the target lane | ||
| 1854 | STACK_GROW( L2, 2); | ||
| 1855 | lua_getglobal( L2, "require"); | ||
| 1856 | if( lua_isnil( L2, -1)) | ||
| 1857 | { | ||
| 1858 | lua_pop( L2, 1); | ||
| 1859 | if( _fatal) | ||
| 1860 | { | ||
| 1861 | luaL_error( L, "cannot pre-require modules without loading 'package' library first"); | ||
| 1862 | } | ||
| 1863 | } | ||
| 1864 | else | ||
| 1865 | { | ||
| 1866 | lua_pushlstring( L2, name, len); | ||
| 1867 | lua_pcall( L2, 1, 0, 0); | ||
| 1868 | // we need to require this module in the keeper states as well | ||
| 1869 | populate_keepers( L); | ||
| 1870 | } | ||
| 1871 | } | ||
| 1872 | |||
| 1873 | LUAG_FUNC( thread_new) | 1863 | LUAG_FUNC( thread_new) |
| 1874 | { | 1864 | { |
| 1875 | lua_State* L2; | 1865 | lua_State* L2; |
| @@ -1877,17 +1867,17 @@ LUAG_FUNC( thread_new) | |||
| 1877 | struct s_lane** ud; | 1867 | struct s_lane** ud; |
| 1878 | 1868 | ||
| 1879 | char const* libs = lua_tostring( L, 2); | 1869 | char const* libs = lua_tostring( L, 2); |
| 1880 | lua_CFunction on_state_create = lua_iscfunction( L, 3) ? lua_tocfunction( L, 3) : NULL; | 1870 | int const on_state_create = lua_isfunction( L, 3) ? 3 : 0; |
| 1881 | uint_t cs = luaG_optunsigned( L, 4, 0); | 1871 | uint_t cs = luaG_optunsigned( L, 4, 0); |
| 1882 | int prio = (int) luaL_optinteger( L, 5, 0); | 1872 | int prio = (int) luaL_optinteger( L, 5, 0); |
| 1883 | uint_t glob = luaG_isany( L, 6) ? 6 : 0; | 1873 | uint_t glob = lua_isnoneornil( L, 6) ? 0 : 6; |
| 1884 | uint_t package = luaG_isany( L,7) ? 7 : 0; | 1874 | uint_t package = lua_isnoneornil( L,7) ? 0 : 7; |
| 1885 | uint_t required = luaG_isany( L, 8) ? 8 : 0; | 1875 | uint_t required = lua_isnoneornil( L, 8) ? 0 : 8; |
| 1886 | 1876 | ||
| 1887 | #define FIXED_ARGS 8 | 1877 | #define FIXED_ARGS 8 |
| 1888 | uint_t args= lua_gettop(L) - FIXED_ARGS; | 1878 | uint_t args= lua_gettop(L) - FIXED_ARGS; |
| 1889 | 1879 | ||
| 1890 | if (prio < THREAD_PRIO_MIN || prio > THREAD_PRIO_MAX) | 1880 | if( prio < THREAD_PRIO_MIN || prio > THREAD_PRIO_MAX) |
| 1891 | { | 1881 | { |
| 1892 | return luaL_error( L, "Priority out of range: %d..+%d (%d)", THREAD_PRIO_MIN, THREAD_PRIO_MAX, prio); | 1882 | return luaL_error( L, "Priority out of range: %d..+%d (%d)", THREAD_PRIO_MIN, THREAD_PRIO_MAX, prio); |
| 1893 | } | 1883 | } |
| @@ -1898,7 +1888,7 @@ LUAG_FUNC( thread_new) | |||
| 1898 | 1888 | ||
| 1899 | // populate with selected libraries at the same time | 1889 | // populate with selected libraries at the same time |
| 1900 | // | 1890 | // |
| 1901 | L2 = luaG_newstate( L, libs, on_state_create); | 1891 | L2 = luaG_newstate( L, on_state_create, libs); |
| 1902 | 1892 | ||
| 1903 | STACK_GROW( L, 2); | 1893 | STACK_GROW( L, 2); |
| 1904 | STACK_GROW( L2, 3); | 1894 | STACK_GROW( L2, 3); |
| @@ -1918,17 +1908,6 @@ LUAG_FUNC( thread_new) | |||
| 1918 | 1908 | ||
| 1919 | // modules to require in the target lane *before* the function is transfered! | 1909 | // modules to require in the target lane *before* the function is transfered! |
| 1920 | 1910 | ||
| 1921 | DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "thread_new: require 'lanes.core'\n" INDENT_END)); | ||
| 1922 | //start by requiring lanes.core, since it is a bit special | ||
| 1923 | // it is not fatal if 'require' isn't loaded, just ignore (may cause function transfer errors later on if the lane pulls the lanes module itself) | ||
| 1924 | STACK_CHECK( L); | ||
| 1925 | STACK_CHECK( L2); | ||
| 1926 | lua_pushliteral( L, "lanes.core"); | ||
| 1927 | require_one_module( L, L2, FALSE); | ||
| 1928 | lua_pop( L, 1); | ||
| 1929 | STACK_END( L2, 0); | ||
| 1930 | STACK_END (L, 0); | ||
| 1931 | |||
| 1932 | STACK_CHECK( L); | 1911 | STACK_CHECK( L); |
| 1933 | STACK_CHECK( L2); | 1912 | STACK_CHECK( L2); |
| 1934 | if( required) | 1913 | if( required) |
| @@ -1941,6 +1920,7 @@ LUAG_FUNC( thread_new) | |||
| 1941 | { | 1920 | { |
| 1942 | return luaL_error( L, "expected required module list as a table, got %s", luaL_typename( L, required)); | 1921 | return luaL_error( L, "expected required module list as a table, got %s", luaL_typename( L, required)); |
| 1943 | } | 1922 | } |
| 1923 | |||
| 1944 | lua_pushnil( L); | 1924 | lua_pushnil( L); |
| 1945 | while( lua_next( L, required) != 0) | 1925 | while( lua_next( L, required) != 0) |
| 1946 | { | 1926 | { |
| @@ -1950,7 +1930,32 @@ LUAG_FUNC( thread_new) | |||
| 1950 | } | 1930 | } |
| 1951 | else | 1931 | else |
| 1952 | { | 1932 | { |
| 1953 | require_one_module( L, L2, TRUE); | 1933 | // require the module in the target state, and populate the lookup table there too |
| 1934 | size_t len; | ||
| 1935 | char const* name = lua_tolstring( L, -1, &len); | ||
| 1936 | |||
| 1937 | // require the module in the target lane | ||
| 1938 | STACK_GROW( L2, 2); | ||
| 1939 | STACK_CHECK( L2); | ||
| 1940 | lua_getglobal( L2, "require"); // require()? | ||
| 1941 | if( lua_isnil( L2, -1)) | ||
| 1942 | { | ||
| 1943 | lua_pop( L2, 1); // | ||
| 1944 | luaL_error( L, "cannot pre-require modules without loading 'package' library first"); | ||
| 1945 | } | ||
| 1946 | else | ||
| 1947 | { | ||
| 1948 | lua_pushlstring( L2, name, len); // require() name | ||
| 1949 | lua_pcall( L2, 1, 1, 0); // ret | ||
| 1950 | STACK_MID( L2, 1); | ||
| 1951 | // after requiring the module, register the functions it exported in our name<->function database | ||
| 1952 | populate_func_lookup_table( L2, -1, name); | ||
| 1953 | STACK_MID( L2, 1); | ||
| 1954 | lua_pop( L2, 1); | ||
| 1955 | // don't require this module in the keeper states as well, use lanes.require() for that! | ||
| 1956 | //populate_keepers( L); | ||
| 1957 | } | ||
| 1958 | STACK_END( L2, 0); | ||
| 1954 | } | 1959 | } |
| 1955 | lua_pop( L, 1); | 1960 | lua_pop( L, 1); |
| 1956 | ++ nbRequired; | 1961 | ++ nbRequired; |
| @@ -2578,7 +2583,7 @@ static const struct luaL_Reg lanes_functions [] = { | |||
| 2578 | /* | 2583 | /* |
| 2579 | * One-time initializations | 2584 | * One-time initializations |
| 2580 | */ | 2585 | */ |
| 2581 | static void init_once_LOCKED( lua_State* L, int const nbKeepers, lua_CFunction _on_state_create, lua_Number _shutdown_timeout, bool_t _track_lanes) | 2586 | static void init_once_LOCKED( lua_State* L, int const _on_state_create, int const nbKeepers, lua_Number _shutdown_timeout, bool_t _track_lanes) |
| 2582 | { | 2587 | { |
| 2583 | char const* err; | 2588 | char const* err; |
| 2584 | 2589 | ||
| @@ -2603,7 +2608,7 @@ static void init_once_LOCKED( lua_State* L, int const nbKeepers, lua_CFunction _ | |||
| 2603 | // | 2608 | // |
| 2604 | MUTEX_RECURSIVE_INIT( &require_cs ); | 2609 | MUTEX_RECURSIVE_INIT( &require_cs ); |
| 2605 | 2610 | ||
| 2606 | serialize_require( L ); | 2611 | serialize_require( L); |
| 2607 | 2612 | ||
| 2608 | // Linked chains handling | 2613 | // Linked chains handling |
| 2609 | // | 2614 | // |
| @@ -2635,7 +2640,7 @@ static void init_once_LOCKED( lua_State* L, int const nbKeepers, lua_CFunction _ | |||
| 2635 | } | 2640 | } |
| 2636 | #endif | 2641 | #endif |
| 2637 | #endif | 2642 | #endif |
| 2638 | err = init_keepers( L, nbKeepers, _on_state_create); | 2643 | err = init_keepers( L, _on_state_create, nbKeepers); |
| 2639 | if (err) | 2644 | if (err) |
| 2640 | { | 2645 | { |
| 2641 | (void) luaL_error( L, "Unable to initialize: %s", err ); | 2646 | (void) luaL_error( L, "Unable to initialize: %s", err ); |
| @@ -2691,7 +2696,7 @@ LUAG_FUNC( configure) | |||
| 2691 | char const* name = luaL_checkstring( L, lua_upvalueindex( 1)); | 2696 | char const* name = luaL_checkstring( L, lua_upvalueindex( 1)); |
| 2692 | // all parameter checks are done lua-side | 2697 | // all parameter checks are done lua-side |
| 2693 | int const nbKeepers = (int)lua_tointeger( L, 1); | 2698 | int const nbKeepers = (int)lua_tointeger( L, 1); |
| 2694 | lua_CFunction on_state_create = lua_iscfunction( L, 2) ? lua_tocfunction( L, 2) : NULL; | 2699 | int const on_state_create = lua_isfunction( L, 2) ? 2 : 0; |
| 2695 | lua_Number shutdown_timeout = lua_tonumber( L, 3); | 2700 | lua_Number shutdown_timeout = lua_tonumber( L, 3); |
| 2696 | bool_t track_lanes = lua_toboolean( L, 4); | 2701 | bool_t track_lanes = lua_toboolean( L, 4); |
| 2697 | 2702 | ||
| @@ -2732,6 +2737,11 @@ LUAG_FUNC( configure) | |||
| 2732 | lua_pushcclosure( L, LG_thread_new, 1); // ... M LG_thread_new | 2737 | lua_pushcclosure( L, LG_thread_new, 1); // ... M LG_thread_new |
| 2733 | lua_setfield(L, -2, "thread_new"); // ... M | 2738 | lua_setfield(L, -2, "thread_new"); // ... M |
| 2734 | 2739 | ||
| 2740 | // we can't register 'lanes.require' normally because we want to create an upvalued closure | ||
| 2741 | lua_getglobal( L, "require"); // ... M require | ||
| 2742 | lua_pushcclosure( L, LG_require, 1); // ... M lanes.require | ||
| 2743 | lua_setfield( L, -2, "require"); // ... M | ||
| 2744 | |||
| 2735 | lua_pushstring(L, VERSION); // ... M VERSION | 2745 | lua_pushstring(L, VERSION); // ... M VERSION |
| 2736 | lua_setfield(L, -2, "version"); // ... M | 2746 | lua_setfield(L, -2, "version"); // ... M |
| 2737 | 2747 | ||
| @@ -2743,7 +2753,7 @@ LUAG_FUNC( configure) | |||
| 2743 | 2753 | ||
| 2744 | // register all native functions found in that module in the transferable functions database | 2754 | // register all native functions found in that module in the transferable functions database |
| 2745 | // we process it before _G because we don't want to find the module when scanning _G (this would generate longer names) | 2755 | // we process it before _G because we don't want to find the module when scanning _G (this would generate longer names) |
| 2746 | // for example in package.loaded.lanes.core.* | 2756 | // for example in package.loaded["lanes.core"].* |
| 2747 | populate_func_lookup_table( L, -1, name); | 2757 | populate_func_lookup_table( L, -1, name); |
| 2748 | 2758 | ||
| 2749 | // record all existing C/JIT-fast functions | 2759 | // record all existing C/JIT-fast functions |
| @@ -2768,7 +2778,7 @@ LUAG_FUNC( configure) | |||
| 2768 | static volatile int /*bool*/ go_ahead; // = 0 | 2778 | static volatile int /*bool*/ go_ahead; // = 0 |
| 2769 | if( InterlockedCompareExchange( &s_initCount, 1, 0) == 0) | 2779 | if( InterlockedCompareExchange( &s_initCount, 1, 0) == 0) |
| 2770 | { | 2780 | { |
| 2771 | init_once_LOCKED( L, nbKeepers, on_state_create, shutdown_timeout, track_lanes); | 2781 | init_once_LOCKED( L, on_state_create, nbKeepers, shutdown_timeout, track_lanes); |
| 2772 | go_ahead = 1; // let others pass | 2782 | go_ahead = 1; // let others pass |
| 2773 | } | 2783 | } |
| 2774 | else | 2784 | else |
| @@ -2786,7 +2796,7 @@ LUAG_FUNC( configure) | |||
| 2786 | // | 2796 | // |
| 2787 | if( s_initCount == 0) | 2797 | if( s_initCount == 0) |
| 2788 | { | 2798 | { |
| 2789 | init_once_LOCKED( L, nbKeepers, on_state_create, shutdown_timeout, track_lanes); | 2799 | init_once_LOCKED( L, on_state_create, nbKeepers, shutdown_timeout, track_lanes); |
| 2790 | s_initCount = 1; | 2800 | s_initCount = 1; |
| 2791 | } | 2801 | } |
| 2792 | } | 2802 | } |
diff --git a/src/lanes.lua b/src/lanes.lua index 1d50f97..f4eef81 100644 --- a/src/lanes.lua +++ b/src/lanes.lua | |||
| @@ -123,9 +123,6 @@ lanes.configure = function( _params) | |||
| 123 | 123 | ||
| 124 | local set_singlethreaded = assert( core.set_singlethreaded) | 124 | local set_singlethreaded = assert( core.set_singlethreaded) |
| 125 | 125 | ||
| 126 | local now_secs = assert( core.now_secs) | ||
| 127 | local wakeup_conv = assert( core.wakeup_conv) | ||
| 128 | |||
| 129 | local max_prio = assert( core.max_prio) | 126 | local max_prio = assert( core.max_prio) |
| 130 | 127 | ||
| 131 | lanes.ABOUT= | 128 | lanes.ABOUT= |
| @@ -311,6 +308,7 @@ local linda = core.linda | |||
| 311 | 308 | ||
| 312 | -- PUBLIC LANES API | 309 | -- PUBLIC LANES API |
| 313 | local timer = function() error "timers are not active" end | 310 | local timer = function() error "timers are not active" end |
| 311 | local timer_lane = nil | ||
| 314 | local timers = timer | 312 | local timers = timer |
| 315 | 313 | ||
| 316 | if _params.with_timers ~= false then | 314 | if _params.with_timers ~= false then |
| @@ -338,158 +336,161 @@ timer_gateway:set(first_time_key,true) | |||
| 338 | -- has 'table' always declared) | 336 | -- has 'table' always declared) |
| 339 | -- | 337 | -- |
| 340 | if first_time then | 338 | if first_time then |
| 341 | local table_remove= assert( table.remove ) | ||
| 342 | local table_insert= assert( table.insert ) | ||
| 343 | 339 | ||
| 344 | -- | 340 | ----- |
| 345 | -- { [deep_linda_lightuserdata]= { [deep_linda_lightuserdata]=linda_h, | 341 | -- Snore loop (run as a lane on the background) |
| 346 | -- [key]= { wakeup_secs [,period_secs] } [, ...] }, | 342 | -- |
| 347 | -- } | 343 | -- High priority, to get trustworthy timings. |
| 348 | -- | 344 | -- |
| 349 | -- Collection of all running timers, indexed with linda's & key. | 345 | -- We let the timer lane be a "free running" thread; no handle to it |
| 350 | -- | 346 | -- remains. |
| 351 | -- Note that we need to use the deep lightuserdata identifiers, instead | 347 | -- |
| 352 | -- of 'linda_h' themselves as table indices. Otherwise, we'd get multiple | 348 | local timer_body = function() |
| 353 | -- entries for the same timer. | 349 | -- require lanes.core inside the timer body to prevent pulling now_secs() through an uvpvalue |
| 354 | -- | 350 | local core = require "lanes.core" |
| 355 | -- The 'hidden' reference to Linda proxy is used in 'check_timers()' but | 351 | |
| 356 | -- also important to keep the Linda alive, even if all outside world threw | 352 | -- |
| 357 | -- away pointers to it (which would ruin uniqueness of the deep pointer). | 353 | -- { [deep_linda_lightuserdata]= { [deep_linda_lightuserdata]=linda_h, |
| 358 | -- Now we're safe. | 354 | -- [key]= { wakeup_secs [,period_secs] } [, ...] }, |
| 359 | -- | 355 | -- } |
| 360 | local collection= {} | 356 | -- |
| 361 | 357 | -- Collection of all running timers, indexed with linda's & key. | |
| 362 | local function get_timers() | 358 | -- |
| 363 | local r = {} | 359 | -- Note that we need to use the deep lightuserdata identifiers, instead |
| 364 | for deep, t in pairs( collection) do | 360 | -- of 'linda_h' themselves as table indices. Otherwise, we'd get multiple |
| 365 | -- WR( tostring( deep)) | 361 | -- entries for the same timer. |
| 366 | local l = t[deep] | 362 | -- |
| 367 | for key, timer_data in pairs( t) do | 363 | -- The 'hidden' reference to Linda proxy is used in 'check_timers()' but |
| 368 | if key ~= deep then | 364 | -- also important to keep the Linda alive, even if all outside world threw |
| 369 | table_insert( r, {l, key, timer_data}) | 365 | -- away pointers to it (which would ruin uniqueness of the deep pointer). |
| 370 | end | 366 | -- Now we're safe. |
| 371 | end | 367 | -- |
| 372 | end | 368 | local collection = {} |
| 373 | return r | 369 | local table_insert = assert( table.insert) |
| 374 | end | 370 | |
| 375 | -- | 371 | local get_timers = function() |
| 376 | -- set_timer( linda_h, key [,wakeup_at_secs [,period_secs]] ) | 372 | local r = {} |
| 377 | -- | 373 | for deep, t in pairs( collection) do |
| 378 | local function set_timer( linda, key, wakeup_at, period ) | 374 | -- WR( tostring( deep)) |
| 379 | assert( wakeup_at==nil or wakeup_at>0.0 ) | 375 | local l = t[deep] |
| 380 | assert( period==nil or period>0.0 ) | 376 | for key, timer_data in pairs( t) do |
| 377 | if key ~= deep then | ||
| 378 | table_insert( r, {l, key, timer_data}) | ||
| 379 | end | ||
| 380 | end | ||
| 381 | end | ||
| 382 | return r | ||
| 383 | end -- get_timers() | ||
| 381 | 384 | ||
| 382 | local linda_deep= linda:deep() | 385 | -- |
| 383 | assert( linda_deep ) | 386 | -- set_timer( linda_h, key [,wakeup_at_secs [,period_secs]] ) |
| 387 | -- | ||
| 388 | local set_timer = function( linda, key, wakeup_at, period) | ||
| 389 | assert( wakeup_at == nil or wakeup_at > 0.0) | ||
| 390 | assert( period == nil or period > 0.0) | ||
| 384 | 391 | ||
| 385 | -- Find or make a lookup for this timer | 392 | local linda_deep = linda:deep() |
| 386 | -- | 393 | assert( linda_deep) |
| 387 | local t1= collection[linda_deep] | 394 | |
| 388 | if not t1 then | 395 | -- Find or make a lookup for this timer |
| 389 | t1= { [linda_deep]= linda } -- proxy to use the Linda | 396 | -- |
| 390 | collection[linda_deep]= t1 | 397 | local t1 = collection[linda_deep] |
| 391 | end | 398 | if not t1 then |
| 392 | 399 | t1 = { [linda_deep] = linda} -- proxy to use the Linda | |
| 393 | if wakeup_at==nil then | 400 | collection[linda_deep] = t1 |
| 394 | -- Clear the timer | 401 | end |
| 395 | -- | 402 | |
| 396 | t1[key]= nil | 403 | if wakeup_at == nil then |
| 397 | 404 | -- Clear the timer | |
| 398 | -- Remove empty tables from collection; speeds timer checks and | 405 | -- |
| 399 | -- lets our 'safety reference' proxy be gc:ed as well. | 406 | t1[key]= nil |
| 400 | -- | 407 | |
| 401 | local empty= true | 408 | -- Remove empty tables from collection; speeds timer checks and |
| 402 | for k,_ in pairs(t1) do | 409 | -- lets our 'safety reference' proxy be gc:ed as well. |
| 403 | if k~= linda_deep then | 410 | -- |
| 404 | empty= false; break | 411 | local empty = true |
| 405 | end | 412 | for k, _ in pairs( t1) do |
| 406 | end | 413 | if k ~= linda_deep then |
| 407 | if empty then | 414 | empty = false |
| 408 | collection[linda_deep]= nil | 415 | break |
| 409 | end | 416 | end |
| 410 | 417 | end | |
| 411 | -- Note: any unread timer value is left at 'linda[key]' intensionally; | 418 | if empty then |
| 412 | -- clearing a timer just stops it. | 419 | collection[linda_deep] = nil |
| 413 | else | 420 | end |
| 414 | -- New timer or changing the timings | 421 | |
| 415 | -- | 422 | -- Note: any unread timer value is left at 'linda[key]' intensionally; |
| 416 | local t2= t1[key] | 423 | -- clearing a timer just stops it. |
| 417 | if not t2 then | 424 | else |
| 418 | t2= {}; t1[key]= t2 | 425 | -- New timer or changing the timings |
| 419 | end | 426 | -- |
| 420 | 427 | local t2 = t1[key] | |
| 421 | t2[1]= wakeup_at | 428 | if not t2 then |
| 422 | t2[2]= period -- can be 'nil' | 429 | t2= {} |
| 423 | end | 430 | t1[key]= t2 |
| 424 | end | 431 | end |
| 432 | |||
| 433 | t2[1] = wakeup_at | ||
| 434 | t2[2] = period -- can be 'nil' | ||
| 435 | end | ||
| 436 | end -- set_timer() | ||
| 437 | |||
| 438 | local now_secs = core.now_secs | ||
| 439 | assert( type( now_secs) == "function") | ||
| 440 | ----- | ||
| 441 | -- [next_wakeup_at]= check_timers() | ||
| 442 | -- Check timers, and wake up the ones expired (if any) | ||
| 443 | -- Returns the closest upcoming (remaining) wakeup time (or 'nil' if none). | ||
| 444 | local check_timers = function() | ||
| 445 | local now = now_secs() | ||
| 446 | local next_wakeup | ||
| 447 | |||
| 448 | for linda_deep,t1 in pairs(collection) do | ||
| 449 | for key,t2 in pairs(t1) do | ||
| 450 | -- | ||
| 451 | if key==linda_deep then | ||
| 452 | -- no 'continue' in Lua :/ | ||
| 453 | else | ||
| 454 | -- 't2': { wakeup_at_secs [,period_secs] } | ||
| 455 | -- | ||
| 456 | local wakeup_at= t2[1] | ||
| 457 | local period= t2[2] -- may be 'nil' | ||
| 458 | |||
| 459 | if wakeup_at <= now then | ||
| 460 | local linda= t1[linda_deep] | ||
| 461 | assert(linda) | ||
| 462 | |||
| 463 | linda:set( key, now ) | ||
| 464 | |||
| 465 | -- 'pairs()' allows the values to be modified (and even | ||
| 466 | -- removed) as far as keys are not touched | ||
| 467 | |||
| 468 | if not period then | ||
| 469 | -- one-time timer; gone | ||
| 470 | -- | ||
| 471 | t1[key]= nil | ||
| 472 | wakeup_at= nil -- no 'continue' in Lua :/ | ||
| 473 | else | ||
| 474 | -- repeating timer; find next wakeup (may jump multiple repeats) | ||
| 475 | -- | ||
| 476 | repeat | ||
| 477 | wakeup_at= wakeup_at+period | ||
| 478 | until wakeup_at > now | ||
| 479 | |||
| 480 | t2[1]= wakeup_at | ||
| 481 | end | ||
| 482 | end | ||
| 483 | |||
| 484 | if wakeup_at and ((not next_wakeup) or (wakeup_at < next_wakeup)) then | ||
| 485 | next_wakeup= wakeup_at | ||
| 486 | end | ||
| 487 | end | ||
| 488 | end -- t2 loop | ||
| 489 | end -- t1 loop | ||
| 490 | |||
| 491 | return next_wakeup -- may be 'nil' | ||
| 492 | end -- check_timers() | ||
| 425 | 493 | ||
| 426 | ----- | ||
| 427 | -- [next_wakeup_at]= check_timers() | ||
| 428 | -- | ||
| 429 | -- Check timers, and wake up the ones expired (if any) | ||
| 430 | -- | ||
| 431 | -- Returns the closest upcoming (remaining) wakeup time (or 'nil' if none). | ||
| 432 | -- | ||
| 433 | local function check_timers() | ||
| 434 | |||
| 435 | local now= now_secs() | ||
| 436 | local next_wakeup | ||
| 437 | |||
| 438 | for linda_deep,t1 in pairs(collection) do | ||
| 439 | for key,t2 in pairs(t1) do | ||
| 440 | -- | ||
| 441 | if key==linda_deep then | ||
| 442 | -- no 'continue' in Lua :/ | ||
| 443 | else | ||
| 444 | -- 't2': { wakeup_at_secs [,period_secs] } | ||
| 445 | -- | ||
| 446 | local wakeup_at= t2[1] | ||
| 447 | local period= t2[2] -- may be 'nil' | ||
| 448 | |||
| 449 | if wakeup_at <= now then | ||
| 450 | local linda= t1[linda_deep] | ||
| 451 | assert(linda) | ||
| 452 | |||
| 453 | linda:set( key, now ) | ||
| 454 | |||
| 455 | -- 'pairs()' allows the values to be modified (and even | ||
| 456 | -- removed) as far as keys are not touched | ||
| 457 | |||
| 458 | if not period then | ||
| 459 | -- one-time timer; gone | ||
| 460 | -- | ||
| 461 | t1[key]= nil | ||
| 462 | wakeup_at= nil -- no 'continue' in Lua :/ | ||
| 463 | else | ||
| 464 | -- repeating timer; find next wakeup (may jump multiple repeats) | ||
| 465 | -- | ||
| 466 | repeat | ||
| 467 | wakeup_at= wakeup_at+period | ||
| 468 | until wakeup_at > now | ||
| 469 | |||
| 470 | t2[1]= wakeup_at | ||
| 471 | end | ||
| 472 | end | ||
| 473 | |||
| 474 | if wakeup_at and ((not next_wakeup) or (wakeup_at < next_wakeup)) then | ||
| 475 | next_wakeup= wakeup_at | ||
| 476 | end | ||
| 477 | end | ||
| 478 | end -- t2 loop | ||
| 479 | end -- t1 loop | ||
| 480 | |||
| 481 | return next_wakeup -- may be 'nil' | ||
| 482 | end | ||
| 483 | |||
| 484 | ----- | ||
| 485 | -- Snore loop (run as a lane on the background) | ||
| 486 | -- | ||
| 487 | -- High priority, to get trustworthy timings. | ||
| 488 | -- | ||
| 489 | -- We let the timer lane be a "free running" thread; no handle to it | ||
| 490 | -- remains. | ||
| 491 | -- | ||
| 492 | local timer_body = function() | ||
| 493 | local timer_gateway_batched = timer_gateway.batched | 494 | local timer_gateway_batched = timer_gateway.batched |
| 494 | set_debug_threadname( "LanesTimer") | 495 | set_debug_threadname( "LanesTimer") |
| 495 | set_finalizer( function( err, stk) | 496 | set_finalizer( function( err, stk) |
| @@ -502,7 +503,7 @@ if first_time then | |||
| 502 | end | 503 | end |
| 503 | end) | 504 | end) |
| 504 | while true do | 505 | while true do |
| 505 | local next_wakeup= check_timers() | 506 | local next_wakeup = check_timers() |
| 506 | 507 | ||
| 507 | -- Sleep until next timer to wake up, or a set/clear command | 508 | -- Sleep until next timer to wake up, or a set/clear command |
| 508 | -- | 509 | -- |
| @@ -528,9 +529,9 @@ if first_time then | |||
| 528 | -- WR( "timer lane: no linda, aborted?") | 529 | -- WR( "timer lane: no linda, aborted?") |
| 529 | end | 530 | end |
| 530 | end | 531 | end |
| 531 | end | 532 | end -- timer_body() |
| 532 | gen( "*", { package= {}, priority = max_prio}, timer_body)() -- "*" instead of "io,package" for LuaJIT compatibility... | 533 | timer_lane = gen( "*", { package= {}, priority = max_prio}, timer_body)() -- "*" instead of "io,package" for LuaJIT compatibility... |
| 533 | end | 534 | end -- first_time |
| 534 | 535 | ||
| 535 | ----- | 536 | ----- |
| 536 | -- = timer( linda_h, key_val, date_tbl|first_secs [,period_secs] ) | 537 | -- = timer( linda_h, key_val, date_tbl|first_secs [,period_secs] ) |
| @@ -540,13 +541,13 @@ timer = function( linda, key, a, period ) | |||
| 540 | if getmetatable( linda) ~= "Linda" then | 541 | if getmetatable( linda) ~= "Linda" then |
| 541 | error "expecting a Linda" | 542 | error "expecting a Linda" |
| 542 | end | 543 | end |
| 543 | if a==0.0 then | 544 | if a == 0.0 then |
| 544 | -- Caller expects to get current time stamp in Linda, on return | 545 | -- Caller expects to get current time stamp in Linda, on return |
| 545 | -- (like the timer had expired instantly); it would be good to set this | 546 | -- (like the timer had expired instantly); it would be good to set this |
| 546 | -- as late as possible (to give most current time) but also we want it | 547 | -- as late as possible (to give most current time) but also we want it |
| 547 | -- to precede any possible timers that might start striking. | 548 | -- to precede any possible timers that might start striking. |
| 548 | -- | 549 | -- |
| 549 | linda:set( key, now_secs() ) | 550 | linda:set( key, core.now_secs()) |
| 550 | 551 | ||
| 551 | if not period or period==0.0 then | 552 | if not period or period==0.0 then |
| 552 | timer_gateway:send( TGW_KEY, linda, key, nil, nil ) -- clear the timer | 553 | timer_gateway:send( TGW_KEY, linda, key, nil, nil ) -- clear the timer |
| @@ -555,8 +556,8 @@ timer = function( linda, key, a, period ) | |||
| 555 | a= period | 556 | a= period |
| 556 | end | 557 | end |
| 557 | 558 | ||
| 558 | local wakeup_at= type(a)=="table" and wakeup_conv(a) -- given point of time | 559 | local wakeup_at= type(a)=="table" and core.wakeup_conv(a) -- given point of time |
| 559 | or (a and now_secs()+a or nil) | 560 | or (a and core.now_secs()+a or nil) |
| 560 | -- queue to timer | 561 | -- queue to timer |
| 561 | -- | 562 | -- |
| 562 | timer_gateway:send( TGW_KEY, linda, key, wakeup_at, period ) | 563 | timer_gateway:send( TGW_KEY, linda, key, wakeup_at, period ) |
| @@ -639,15 +640,17 @@ local function genatomic( linda, key, initial_val ) | |||
| 639 | end | 640 | end |
| 640 | 641 | ||
| 641 | -- activate full interface | 642 | -- activate full interface |
| 643 | lanes.require = core.require | ||
| 642 | lanes.gen = gen | 644 | lanes.gen = gen |
| 643 | lanes.linda = core.linda | 645 | lanes.linda = core.linda |
| 644 | lanes.cancel_error = core.cancel_error | 646 | lanes.cancel_error = core.cancel_error |
| 645 | lanes.nameof = core.nameof | 647 | lanes.nameof = core.nameof |
| 646 | lanes.threads = (_params.track_lanes and core.threads) and core.threads or function() error "lane tracking is not available" end | 648 | lanes.threads = (_params.track_lanes and core.threads) and core.threads or function() error "lane tracking is not available" end |
| 647 | lanes.timer = timer | 649 | lanes.timer = timer |
| 650 | lanes.timer_lane = timer_lane | ||
| 648 | lanes.timers = timers | 651 | lanes.timers = timers |
| 649 | lanes.genlock = genlock | 652 | lanes.genlock = genlock |
| 650 | lanes.now_secs = now_secs | 653 | lanes.now_secs = core.now_secs |
| 651 | lanes.genatomic = genatomic | 654 | lanes.genatomic = genatomic |
| 652 | -- from now on, calling configure does nothing but checking that we don't call it with parameters that changed compared to the first invocation | 655 | -- from now on, calling configure does nothing but checking that we don't call it with parameters that changed compared to the first invocation |
| 653 | lanes.configure = function( _params2) | 656 | lanes.configure = function( _params2) |
diff --git a/src/tools.c b/src/tools.c index 552e61e..9961c1a 100644 --- a/src/tools.c +++ b/src/tools.c | |||
| @@ -224,7 +224,7 @@ static char const* luaG_pushFQN(lua_State *L, int t, int last) | |||
| 224 | { | 224 | { |
| 225 | lua_rawgeti( L, t, i); | 225 | lua_rawgeti( L, t, i); |
| 226 | luaL_addvalue( &b); | 226 | luaL_addvalue( &b); |
| 227 | luaL_addlstring(&b, ".", 1); | 227 | luaL_addlstring(&b, "/", 1); |
| 228 | } | 228 | } |
| 229 | if( i == last) // add last value (if interval was not empty) | 229 | if( i == last) // add last value (if interval was not empty) |
| 230 | { | 230 | { |
| @@ -250,9 +250,16 @@ static void populate_func_lookup_table_recur( lua_State* L, int _ctx_base, int _ | |||
| 250 | int const breadth_first_cache = lua_gettop( L) + 1; | 250 | int const breadth_first_cache = lua_gettop( L) + 1; |
| 251 | 251 | ||
| 252 | STACK_GROW( L, 6); | 252 | STACK_GROW( L, 6); |
| 253 | // slot _i contains a table where we search for functions | 253 | // slot _i contains a table where we search for functions (or a full userdata with a metatable) |
| 254 | STACK_CHECK( L); // ... {_i} | 254 | STACK_CHECK( L); // ... {_i} |
| 255 | 255 | ||
| 256 | // if object is a userdata, replace it by its metatable | ||
| 257 | if( lua_type( L, _i) == LUA_TUSERDATA) | ||
| 258 | { | ||
| 259 | lua_getmetatable( L, _i); // ... {_i} mt | ||
| 260 | lua_replace( L, _i); // ... {_i} | ||
| 261 | } | ||
| 262 | |||
| 256 | // if table is already visited, we are done | 263 | // if table is already visited, we are done |
| 257 | lua_pushvalue( L, _i); // ... {_i} {} | 264 | lua_pushvalue( L, _i); // ... {_i} {} |
| 258 | lua_rawget( L, cache); // ... {_i} nil|n | 265 | lua_rawget( L, cache); // ... {_i} nil|n |
| @@ -437,7 +444,7 @@ void populate_func_lookup_table( lua_State* L, int _i, char const* _name) | |||
| 437 | * | 444 | * |
| 438 | */ | 445 | */ |
| 439 | 446 | ||
| 440 | lua_State* luaG_newstate( lua_State* _from, char const* libs, lua_CFunction _on_state_create) | 447 | lua_State* luaG_newstate( lua_State* _from, int const _on_state_create, char const* libs) |
| 441 | { | 448 | { |
| 442 | // reuse alloc function from the originating state | 449 | // reuse alloc function from the originating state |
| 443 | void* allocUD; | 450 | void* allocUD; |
| @@ -450,7 +457,7 @@ lua_State* luaG_newstate( lua_State* _from, char const* libs, lua_CFunction _on_ | |||
| 450 | } | 457 | } |
| 451 | 458 | ||
| 452 | // neither libs (not even 'base') nor special init func: we are done | 459 | // neither libs (not even 'base') nor special init func: we are done |
| 453 | if( !libs && !_on_state_create) | 460 | if( libs == NULL && _on_state_create <= 0) |
| 454 | { | 461 | { |
| 455 | return L; | 462 | return L; |
| 456 | } | 463 | } |
| @@ -460,13 +467,6 @@ lua_State* luaG_newstate( lua_State* _from, char const* libs, lua_CFunction _on_ | |||
| 460 | 467 | ||
| 461 | STACK_GROW( L, 2); | 468 | STACK_GROW( L, 2); |
| 462 | STACK_CHECK( L); | 469 | STACK_CHECK( L); |
| 463 | if( _on_state_create) | ||
| 464 | { | ||
| 465 | DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "calling on_state_create()\n" INDENT_END)); | ||
| 466 | lua_pushcfunction( L, _on_state_create); | ||
| 467 | lua_call( L, 0, 0); | ||
| 468 | } | ||
| 469 | |||
| 470 | // 'lua.c' stops GC during initialization so perhaps its a good idea. :) | 470 | // 'lua.c' stops GC during initialization so perhaps its a good idea. :) |
| 471 | // but do it after _on_state_create in case it does a lot of stuff... | 471 | // but do it after _on_state_create in case it does a lot of stuff... |
| 472 | lua_gc( L, LUA_GCSTOP, 0); | 472 | lua_gc( L, LUA_GCSTOP, 0); |
| @@ -520,7 +520,29 @@ lua_State* luaG_newstate( lua_State* _from, char const* libs, lua_CFunction _on_ | |||
| 520 | lua_gc( L, LUA_GCRESTART, 0); | 520 | lua_gc( L, LUA_GCRESTART, 0); |
| 521 | 521 | ||
| 522 | STACK_CHECK( L); | 522 | STACK_CHECK( L); |
| 523 | // after opening base, register the functions it exported in our name<->function database | 523 | // call this after the base libraries are loaded! |
| 524 | if( _on_state_create > 0) | ||
| 525 | { | ||
| 526 | DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "calling on_state_create()\n" INDENT_END)); | ||
| 527 | if( lua_iscfunction( _from, _on_state_create)) | ||
| 528 | { | ||
| 529 | // C function: recreate a closure in the new state, bypassing the lookup scheme | ||
| 530 | lua_CFunction osc = lua_tocfunction( _from, _on_state_create); | ||
| 531 | lua_pushcfunction( L, osc); | ||
| 532 | } | ||
| 533 | else | ||
| 534 | { | ||
| 535 | STACK_CHECK( _from); | ||
| 536 | // Lua function: transfer as usual (should work as long as it only uses base libraries) | ||
| 537 | lua_pushvalue( _from, _on_state_create); | ||
| 538 | luaG_inter_move( _from, L, 1); | ||
| 539 | STACK_END( _from, 0); | ||
| 540 | } | ||
| 541 | lua_call( L, 0, 0); | ||
| 542 | STACK_MID( L, 0); | ||
| 543 | } | ||
| 544 | |||
| 545 | // after all this, register everything we find in our name<->function database | ||
| 524 | lua_pushglobaltable( L); // Lua 5.2 no longer has LUA_GLOBALSINDEX: we must push globals table on the stack | 546 | lua_pushglobaltable( L); // Lua 5.2 no longer has LUA_GLOBALSINDEX: we must push globals table on the stack |
| 525 | populate_func_lookup_table( L, -1, NULL); | 547 | populate_func_lookup_table( L, -1, NULL); |
| 526 | lua_pop( L, 1); | 548 | lua_pop( L, 1); |
| @@ -1180,9 +1202,9 @@ static void inter_copy_func( lua_State* L2, uint_t L2_cache_i, lua_State* L, uin | |||
| 1180 | 1202 | ||
| 1181 | static void push_cached_func( lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i) | 1203 | static void push_cached_func( lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i) |
| 1182 | { | 1204 | { |
| 1183 | void * const aspointer = (void*)lua_topointer( L, i); | 1205 | void* const aspointer = (void*)lua_topointer( L, i); |
| 1184 | // TBD: Merge this and same code for tables | 1206 | // TBD: Merge this and same code for tables |
| 1185 | ASSERT_L( L2_cache_i != 0 ); | 1207 | ASSERT_L( L2_cache_i != 0); |
| 1186 | 1208 | ||
| 1187 | STACK_GROW( L2, 2); | 1209 | STACK_GROW( L2, 2); |
| 1188 | 1210 | ||
| @@ -1199,8 +1221,8 @@ static void push_cached_func( lua_State* L2, uint_t L2_cache_i, lua_State* L, ui | |||
| 1199 | 1221 | ||
| 1200 | //fprintf( stderr, "<< ID: %s >>\n", lua_tostring(L2,-1) ); | 1222 | //fprintf( stderr, "<< ID: %s >>\n", lua_tostring(L2,-1) ); |
| 1201 | 1223 | ||
| 1202 | lua_pushvalue( L2, -1 ); // ... {cache} ... p p | 1224 | lua_pushvalue( L2, -1); // ... {cache} ... p p |
| 1203 | lua_rawget( L2, L2_cache_i ); // ... {cache} ... p function|nil|true | 1225 | lua_rawget( L2, L2_cache_i); // ... {cache} ... p function|nil|true |
| 1204 | 1226 | ||
| 1205 | if( lua_isnil(L2,-1)) // function is unknown | 1227 | if( lua_isnil(L2,-1)) // function is unknown |
| 1206 | { | 1228 | { |
| @@ -1209,7 +1231,7 @@ static void push_cached_func( lua_State* L2, uint_t L2_cache_i, lua_State* L, ui | |||
| 1209 | // Set to 'true' for the duration of creation; need to find self-references | 1231 | // Set to 'true' for the duration of creation; need to find self-references |
| 1210 | // via upvalues | 1232 | // via upvalues |
| 1211 | // | 1233 | // |
| 1212 | // pushes a copy of the func, a stores a reference in the cache | 1234 | // pushes a copy of the func, stores a reference in the cache |
| 1213 | inter_copy_func( L2, L2_cache_i, L, i); // ... {cache} ... function | 1235 | inter_copy_func( L2, L2_cache_i, L, i); // ... {cache} ... function |
| 1214 | } | 1236 | } |
| 1215 | else // found function in the cache | 1237 | else // found function in the cache |
| @@ -1426,7 +1448,9 @@ static void inter_copy_func( lua_State* L2, uint_t L2_cache_i, lua_State* L, uin | |||
| 1426 | // if already on top of the stack, no need to push again | 1448 | // if already on top of the stack, no need to push again |
| 1427 | int needToPush = (i != (uint_t)lua_gettop( L)); | 1449 | int needToPush = (i != (uint_t)lua_gettop( L)); |
| 1428 | if( needToPush) | 1450 | if( needToPush) |
| 1451 | { | ||
| 1429 | lua_pushvalue( L, i); // ... f | 1452 | lua_pushvalue( L, i); // ... f |
| 1453 | } | ||
| 1430 | 1454 | ||
| 1431 | luaL_buffinit( L, &b); | 1455 | luaL_buffinit( L, &b); |
| 1432 | // | 1456 | // |
| @@ -1568,11 +1592,11 @@ static void inter_copy_func( lua_State* L2, uint_t L2_cache_i, lua_State* L, uin | |||
| 1568 | * | 1592 | * |
| 1569 | * Returns TRUE if value was pushed, FALSE if its type is non-supported. | 1593 | * Returns TRUE if value was pushed, FALSE if its type is non-supported. |
| 1570 | */ | 1594 | */ |
| 1571 | static bool_t inter_copy_one_( lua_State *L2, uint_t L2_cache_i, lua_State *L, uint_t i, enum e_vt vt ) | 1595 | static bool_t inter_copy_one_( lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i, enum e_vt vt) |
| 1572 | { | 1596 | { |
| 1573 | bool_t ret= TRUE; | 1597 | bool_t ret = TRUE; |
| 1574 | 1598 | ||
| 1575 | STACK_GROW( L2, 1 ); | 1599 | STACK_GROW( L2, 1); |
| 1576 | 1600 | ||
| 1577 | STACK_CHECK( L2); | 1601 | STACK_CHECK( L2); |
| 1578 | 1602 | ||
| @@ -1626,6 +1650,7 @@ static bool_t inter_copy_one_( lua_State *L2, uint_t L2_cache_i, lua_State *L, u | |||
| 1626 | } | 1650 | } |
| 1627 | /* Allow only deep userdata entities to be copied across | 1651 | /* Allow only deep userdata entities to be copied across |
| 1628 | */ | 1652 | */ |
| 1653 | DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "USERDATA\n" INDENT_END)); | ||
| 1629 | if( !luaG_copydeep( L, L2, i)) | 1654 | if( !luaG_copydeep( L, L2, i)) |
| 1630 | { | 1655 | { |
| 1631 | // Cannot copy it full; copy as light userdata | 1656 | // Cannot copy it full; copy as light userdata |
| @@ -1669,7 +1694,6 @@ static bool_t inter_copy_one_( lua_State *L2, uint_t L2_cache_i, lua_State *L, u | |||
| 1669 | #endif | 1694 | #endif |
| 1670 | STACK_CHECK( L2); | 1695 | STACK_CHECK( L2); |
| 1671 | push_cached_func( L2, L2_cache_i, L, i); | 1696 | push_cached_func( L2, L2_cache_i, L, i); |
| 1672 | ASSERT_L( lua_isfunction( L2, -1)); | ||
| 1673 | STACK_END( L2, 1); | 1697 | STACK_END( L2, 1); |
| 1674 | } | 1698 | } |
| 1675 | break; | 1699 | break; |
| @@ -1827,7 +1851,7 @@ static bool_t inter_copy_one_( lua_State *L2, uint_t L2_cache_i, lua_State *L, u | |||
| 1827 | * | 1851 | * |
| 1828 | * Note: Parameters are in this order ('L' = from first) to be same as 'lua_xmove'. | 1852 | * Note: Parameters are in this order ('L' = from first) to be same as 'lua_xmove'. |
| 1829 | */ | 1853 | */ |
| 1830 | int luaG_inter_copy( lua_State* L, lua_State *L2, uint_t n) | 1854 | int luaG_inter_copy( lua_State* L, lua_State* L2, uint_t n) |
| 1831 | { | 1855 | { |
| 1832 | uint_t top_L = lua_gettop( L); | 1856 | uint_t top_L = lua_gettop( L); |
| 1833 | uint_t top_L2 = lua_gettop( L2); | 1857 | uint_t top_L2 = lua_gettop( L2); |
| @@ -1903,7 +1927,9 @@ void luaG_inter_copy_package( lua_State* L, lua_State* L2, int _idx) | |||
| 1903 | { | 1927 | { |
| 1904 | int i; | 1928 | int i; |
| 1905 | // package.loaders is renamed package.searchers in Lua 5.2 | 1929 | // package.loaders is renamed package.searchers in Lua 5.2 |
| 1906 | char const* entries[] = { "path", "cpath", "preload", (LUA_VERSION_NUM == 501) ? "loaders" : "searchers", NULL}; | 1930 | // but don't copy it anyway, as the function names change depending on the slot index! |
| 1931 | // users should provide an on_state_create function to setup custom loaders instead | ||
| 1932 | char const* entries[] = { "path", "cpath", "preload"/*, (LUA_VERSION_NUM == 501) ? "loaders" : "searchers"*/, NULL}; | ||
| 1907 | for( i = 0; entries[i]; ++ i) | 1933 | for( i = 0; entries[i]; ++ i) |
| 1908 | { | 1934 | { |
| 1909 | DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "%s\n" INDENT_END, entries[i])); | 1935 | DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "%s\n" INDENT_END, entries[i])); |
| @@ -1944,7 +1970,7 @@ MUTEX_T require_cs; | |||
| 1944 | // | 1970 | // |
| 1945 | // Upvalues: [1]: original 'require' function | 1971 | // Upvalues: [1]: original 'require' function |
| 1946 | // | 1972 | // |
| 1947 | static int new_require( lua_State *L) | 1973 | int luaG_new_require( lua_State* L) |
| 1948 | { | 1974 | { |
| 1949 | int rc, i; | 1975 | int rc, i; |
| 1950 | int args = lua_gettop( L); | 1976 | int args = lua_gettop( L); |
| @@ -1955,24 +1981,24 @@ static int new_require( lua_State *L) | |||
| 1955 | 1981 | ||
| 1956 | lua_pushvalue( L, lua_upvalueindex(1)); | 1982 | lua_pushvalue( L, lua_upvalueindex(1)); |
| 1957 | for( i = 1; i <= args; ++ i) | 1983 | for( i = 1; i <= args; ++ i) |
| 1984 | { | ||
| 1958 | lua_pushvalue( L, i); | 1985 | lua_pushvalue( L, i); |
| 1986 | } | ||
| 1959 | 1987 | ||
| 1960 | // Using 'lua_pcall()' to catch errors; otherwise a failing 'require' would | 1988 | // Using 'lua_pcall()' to catch errors; otherwise a failing 'require' would |
| 1961 | // leave us locked, blocking any future 'require' calls from other lanes. | 1989 | // leave us locked, blocking any future 'require' calls from other lanes. |
| 1962 | // | 1990 | // |
| 1963 | MUTEX_LOCK( &require_cs); | 1991 | MUTEX_LOCK( &require_cs); |
| 1964 | { | 1992 | rc = lua_pcall( L, args, 1 /*retvals*/, 0 /*errfunc*/ ); |
| 1965 | rc = lua_pcall( L, args, 1 /*retvals*/, 0 /*errfunc*/ ); | ||
| 1966 | // | ||
| 1967 | // LUA_ERRRUN / LUA_ERRMEM | ||
| 1968 | } | ||
| 1969 | MUTEX_UNLOCK( &require_cs); | 1993 | MUTEX_UNLOCK( &require_cs); |
| 1970 | 1994 | ||
| 1971 | // the required module (or an error message) is left on the stack as returned value by original require function | 1995 | // the required module (or an error message) is left on the stack as returned value by original require function |
| 1972 | STACK_END( L, 1); | 1996 | STACK_END( L, 1); |
| 1973 | 1997 | ||
| 1974 | if (rc) | 1998 | if( rc != LUA_OK) // LUA_ERRRUN / LUA_ERRMEM ? |
| 1975 | lua_error(L); // error message already at [-1] | 1999 | { |
| 2000 | return lua_error( L); // error message already at [-1] | ||
| 2001 | } | ||
| 1976 | 2002 | ||
| 1977 | return 1; | 2003 | return 1; |
| 1978 | } | 2004 | } |
| @@ -1980,7 +2006,7 @@ static int new_require( lua_State *L) | |||
| 1980 | /* | 2006 | /* |
| 1981 | * Serialize calls to 'require', if it exists | 2007 | * Serialize calls to 'require', if it exists |
| 1982 | */ | 2008 | */ |
| 1983 | void serialize_require( lua_State *L ) | 2009 | void serialize_require( lua_State* L) |
| 1984 | { | 2010 | { |
| 1985 | STACK_GROW( L, 1); | 2011 | STACK_GROW( L, 1); |
| 1986 | STACK_CHECK( L); | 2012 | STACK_CHECK( L); |
| @@ -1991,7 +2017,7 @@ void serialize_require( lua_State *L ) | |||
| 1991 | if( lua_isfunction( L, -1)) | 2017 | if( lua_isfunction( L, -1)) |
| 1992 | { | 2018 | { |
| 1993 | // [-1]: original 'require' function | 2019 | // [-1]: original 'require' function |
| 1994 | lua_pushcclosure( L, new_require, 1 /*upvalues*/); | 2020 | lua_pushcclosure( L, luaG_new_require, 1 /*upvalues*/); |
| 1995 | lua_setglobal( L, "require"); | 2021 | lua_setglobal( L, "require"); |
| 1996 | } | 2022 | } |
| 1997 | else | 2023 | else |
diff --git a/src/tools.h b/src/tools.h index f79d2ad..a06e23f 100644 --- a/src/tools.h +++ b/src/tools.h | |||
| @@ -78,11 +78,9 @@ extern int debugspew_indent_depth; | |||
| 78 | #define luaG_optunsigned(L,i,d) ((uint_t) luaL_optinteger(L,i,d)) | 78 | #define luaG_optunsigned(L,i,d) ((uint_t) luaL_optinteger(L,i,d)) |
| 79 | #define luaG_tounsigned(L,i) ((uint_t) lua_tointeger(L,i)) | 79 | #define luaG_tounsigned(L,i) ((uint_t) lua_tointeger(L,i)) |
| 80 | 80 | ||
| 81 | #define luaG_isany(L,i) (!lua_isnil(L,i)) | ||
| 82 | |||
| 83 | void luaG_dump( lua_State* L ); | 81 | void luaG_dump( lua_State* L ); |
| 84 | 82 | ||
| 85 | lua_State* luaG_newstate( lua_State* _from, char const* libs, lua_CFunction _on_state_create); | 83 | lua_State* luaG_newstate( lua_State* _from, int const _on_state_create, char const* libs); |
| 86 | 84 | ||
| 87 | typedef struct { | 85 | typedef struct { |
| 88 | volatile int refcount; | 86 | volatile int refcount; |
| @@ -96,6 +94,7 @@ int luaG_inter_copy( lua_State *L, lua_State *L2, uint_t n); | |||
| 96 | int luaG_inter_move( lua_State *L, lua_State *L2, uint_t n); | 94 | int luaG_inter_move( lua_State *L, lua_State *L2, uint_t n); |
| 97 | 95 | ||
| 98 | int luaG_nameof( lua_State* L); | 96 | int luaG_nameof( lua_State* L); |
| 97 | int luaG_new_require( lua_State* L); | ||
| 99 | 98 | ||
| 100 | // Lock for reference counter inc/dec locks (to be initialized by outside code) | 99 | // Lock for reference counter inc/dec locks (to be initialized by outside code) |
| 101 | // | 100 | // |
