From ca7657e24549acb8a2dd45fa81c309b5bf9f61ee Mon Sep 17 00:00:00 2001 From: Benoit Germain Date: Mon, 2 Dec 2024 11:53:09 +0100 Subject: Data transfer supports registered non-deep full userdata --- docs/index.html | 58 +++++++++++++++++++++++++++++---------------------------- 1 file changed, 30 insertions(+), 28 deletions(-) (limited to 'docs') diff --git a/docs/index.html b/docs/index.html index 9ac36b6..7432c53 100644 --- a/docs/index.html +++ b/docs/index.html @@ -436,11 +436,11 @@

- Once Lanes is configured, one should register with Lanes the modules exporting functions that will be transferred either during lane generation or through lindas. + Once Lanes is configured, one should register with Lanes the modules exporting functions/userdata that will be transferred either during lane generation or through lindas.
Use lanes.require() for this purpose. This will call the original require(), then add the result to the lookup databases.
- It is also possible to register a given module a posteriori with lanes.register(). This function will raise an error if the registered module is not a function or table.
+ It is also possible to register a given module a posteriori with lanes.register(). This function will raise an error if the registered module is not a function, table, or full userdata.
Embedders can call the equivalent function lanes_register() from the C side, through lua_call() or similar.

@@ -715,7 +715,7 @@ table - Lists modules that have to be required in order to be able to transfer functions they exposed. Non-Lua functions are searched in lookup tables. + Lists modules that have to be required in order to be able to transfer functions/userdata they exposed. Non-Lua functions are searched in lookup tables. These tables are built from the modules listed here. required must be an array of strings, each one being the name of a module to be required. Each module is required with require() before the lanes function is invoked. So, from the required module's point of view, requiring it manually from inside the lane body or having it required this way doesn't change anything. From the lane body's point of view, the only difference is that a module not creating a global won't be accessible. Therefore, a lane body will also have to require a module manually, but this won't do anything more (see Lua's require documentation).
@@ -785,8 +785,8 @@

- If a lane body pulls a C function imported by a module required before Lanes itself (thus not through a hooked require()), the lane generator creation will raise an error. - The function name it shows is a path where it was found by scanning _G and the registry. As a utility, the name guessing functionality is exposed as such: + If a lane body pulls a C function or userdata exposed by a module required before Lanes itself (thus not through a hooked require()), the lane generator creation will raise an error. + The name in the message is a path where it was found by scanning _G and the registry. As a utility, the name guessing functionality is exposed as such: @@ -1233,10 +1233,10 @@ Characteristics of the Lanes implementation of lindas are:
  • - Non-converted full userdata can be passed only if it is prepared using the deep userdata system, which handles its lifespan management. + Non-converted full userdata can be passed only if it registered, or prepared using the deep userdata system, which handles its lifespan management.
  • Objects (tables with a metatable) are copyable between lanes. Metatables are assumed to be immutable; they are internally indexed and only copied once per each type of objects per lane. @@ -1661,55 +1661,57 @@

    -

    Notes about passing C functions

    +

    Notes about passing C functions and full userdata

    - Functions are transfered as follows (more or less): + C functions and full userdata are transfered as follows (more or less):

  • -	// expects a C function on top of the source Lua stack
    -	copyFunction(lua_State *dest, lua_State* source)
    +	// expects a C function or full userdata on top of the source Lua stack
    +	copyValue(lua_State *dest, lua_State* source)
     	{
     		// fetch function 'name' from source lookup database
    -		char const* funcname = lookup_func_name(source, -1);
    -		// lookup a function bound to this name in the destination state, and push it on the stack
    -		push_resolved_func(dest, funcname);
    +		char const* valuename = lookup_name(source, -1);
    +		// lookup a function or userdata bound to this name in the destination state, and push it on the stack
    +		push_resolved_value(dest, valuename);
     	}
     

    - The devil lies in the details: what does "function lookup" mean? + The devil lies in the details: what does "lookup" mean?

    - Since functions are first class values, they don't have a name. All we know for sure is that when a C module registers some functions, they are accessible to the script that required the module through some exposed variables. + Since functions and full userdata are first class values, they don't have a name. All we know for sure is that when a C module registers some functions or full userdata, they are accessible to the script that required the module through some exposed variables.
    For example, loading the string base library creates a table accessible when indexing the global environment with key "string". Indexing this table with "match", "gsub", etc. will give us a function.
    - When a lane generator creates a lane and performs initializations described by the list of base libraries and the list of required modules, it recursively scans the table created by the initialisation of the module, looking for all values that are C functions. + Similarly, loading the io base library creates a table accessible when indexing the global environment with key "io". Indexing this table with "open", will give us a function, and "stdin" will give us a full userdata.
    - Each time a function is encountered, the sequence of keys that reached that function is contatenated in a (hopefully) unique name. The [name, function] and [function, name] pairs are both stored in a lookup table in all involved Lua states (main Lua state and lanes states). + When a lane generator creates a lane and performs initializations described by the list of base libraries and the list of required modules, it recursively scans the table created by the initialisation of the module, looking for all values that are C functions and full userdata.
    - Then when a function is transfered from one state to another, all we have to do is retrieve the name associated to a function in the source Lua state, then with that name retrieve the equivalent function that already exists in the destination state. + Each time a function or full userdata is encountered, the sequence of keys traversed to reach it is contatenated in a (hopefully) unique name. The [name, value] and [value, name] pairs are both stored in a lookup table in all involved Lua states (main Lua state and lanes states).
    - Note that there is no need to transfer upvalues, as they are already bound to the function registered in the destination state. (And in any event, it is not possible to create a closure from a C function pushed on the stack, it can only be created with a lua_CFunction pointer). + Then, when a function or full userdata is transfered from one state to another, all we have to do is retrieve the name associated to this value in the source Lua state, then with that name retrieve the equivalent value that already exists in the destination state. +
    + Note that there is no need to transfer upvalues/uservalues, as they are already bound to the value registered in the destination state. (And in any event, it is not possible to create a closure from a C function pushed on the stack, it can only be created with a lua_CFunction pointer).

    There are several issues here:

    - Another more immediate reason of failed transfer is when the destination state doesn't know about the C function that has to be transferred. This occurs if a function is transferred in a lane before it had a chance to scan the module. - If the C function is sent through a linda, it is enough for the destination lane body to have required the module before the function is sent. - But if the lane body provided to the generator has a C function as upvalue, the transfer itself must succeed, therefore the module that imported that C function must be required in the destination lane before the lane body starts executing. This is where the .required options play their role. + Another more immediate reason of failed transfer is when the destination state doesn't know about the C function or full userdata that has to be transferred. This occurs if a value is transferred in a lane before it had a chance to scan the module that exposed it. + If the C function or full userdata is sent through a linda, it is sufficient for the destination lane body to have required the module before the function is sent. + But if the lane body provided to the generator has a C function or full userdata as upvalue, the transfer itself must succeed, therefore the module that exposed that C function or full userdata must be required in the destination lane before the lane body starts executing. This is where the .required options play their role.

    -- cgit v1.2.3-55-g6feb