# LuaStepper # Description [top]

LuaStepper is a module to help lua programs control simultaneous execution of multiple Lua threads without them needing to be coroutines. So it is like pre-emptive multitasking but instead of the operating system controlling the task switch and time slicing the Lua parent process does that with the API of this module.

# Dependencies [top]

LuaStepper has no dependencies. It just needs Lua >= 5.1

# Installation and source code [top]

Compile LuaStepper.c in the repository at Github and then copy the library module LuaStepper.dll/so to package.cpath directory and lstepper.lua to package.path directory. Or use LuaRocks to install it like:
```> luarocks install luastepper
```

# Basic Usage [top]

The demo.lua file is in the test subdirectory of the source on Github. Once the module is installed the demo file can be directly run. The file is listed below:
``````lStepper = require("lStepper")print("lStepper version is:",lStepper._VERSION) -- Define script 1script1 = [[while true do     print('A') end]]-- Add script 1 to the queue with some print statements that are executed immediatelyprint("Add script 1. ID is -->",lStepper.addTask(script1,"print('Initialization step 1') print('Initialization step 2')")) -- Define script 2script2 = "while true do print('B') end"-- Add script 2 to the queue with some print statements that are executed immediatelyprint("Add script 2. ID is -->",lStepper.addTask(script2,"print('Initialization step x') print('Initialization step y')")) -- Define script 3script3 = [[            test1='Hello'             test2=23             test3=34.45             test10 = {Hello=test1}            while(true) do                 print('script3 test5=',test5)                 if test4 and test4==test3 then                    break                 end             end             while(true) do                print('script3 next loop')                 if test5 and type(test5)=='table' and test5.test6 then                     print('test6=',test5.test6)                     break                 end             end]]-- Add script 3 to the queue with a print statement initialization and a variable setting that is then printed in the scriptprint("Add script 3. ID is -->",lStepper.addTask(script3,"print('Adding Script 3') test5='script3'",5,true))    -- 5 means the number os codes to execute for each step of this script                                                                                        -- This script is also placed in the urgent task pile by setting trueprint("Number of tasks=",lStepper.getNumOfTasks())    -- displays 2   1   -- 2 regular 1 urgentprint("Start stepper loop")for i=1,50 do    x,y=lStepper.runLoop()    -- Run all the tasks for 1 step    if not x then        print(y)        break    end    if((x == "FIN" or x:sub(1,2) == "NE") and (x == "FIN" or x:sub(1,2) == "NE")) then        print("Both scripts ended")        break    end    if i==5 then        print("i=5, script 1 status is ",lStepper.taskStatus(0))        print("i=5, script 2 status is ",lStepper.taskStatus(1))    end    if i==30 then        print("i=30, script 1 status is ",lStepper.taskStatus(0))        print("i=30, script 2 status is ",lStepper.taskStatus(1))    end    if i==20 then        print("B suspended")        lStepper.suspendTask(1)    end    if i==40 then        print("B resumed")        lStepper.resumeTask(1)    end    if i==5 then        x,y = lStepper.getTaskData(4,"test3")    -- Get the value of test3 from script 3        if x then            print("Value from script3 test3=",x)            print(lStepper.setTaskData(4,{test4=x}))    -- Set value of test4 to be same as test3 in script 3        else            print("Error getting data:",y)        end    end    if i==10 then        local t = {            test6='Added Table!'        }        print(lStepper.setTaskData(4,{test5=t}))        x=lStepper.getTaskData(4,"test10")        print(x)        if type(x) == 'table' then            print("test10 in script 3 is a table:")            for k,v in pairs(x) do                print(k,v)            end        end    endend print("END",lStepper.taskStatus(0))print("END",lStepper.taskStatus(1))print("END",lStepper.taskStatus(4))lStepper.closeTask(0)lStepper.closeTask(1)lStepper.closeTask(4)
``````

The demo loads 2 scripts to run in parallel and runs them. It suspends 1 script for some time and then resumes it. It also shows how to run initialization code when the script is loaded and how to read and pass data to a script.

# lStepper Lua module vs LuaStepper C module [top]

The demo shows that you just need to require lStepper which is the interface to the LuaStepper C module written in Lua. The LuaStepper C module can also be used directly but lStepper makes it easier to use the C module without any loss of functionality. lStepper requires LuaStepper and uses its API.

• Each thread environment contains all standard lua libraries supplied with Lua except debug, package, io and os libraries. These libraries are excluded since they can make the thread get stuck so alternatives can be provided when needed. Although if you want to provide them you can use the runCode function described in the API below.
• The environment has a table called _LS, which has the following functions and objects:
• tableToString: FUNCTION : Function to convert a table to a string. Keys can be a number, string or table, values can be number, string table or boolean. Recursive tables are handled but metatables are not handled. This function can be used to serialize and communicate data with the parent state. This is the function that is used by the LuaStepper API to communicate data to the controller state.
• Each thread runs in its separate Lua state

# API Reference [top]

## lStepper [top]

The API provided by the lua interface is accessed by requiring 'lStepper'. This is the recommended way to access the LuaStepper module.
``````lStepper = require("lStepper")
``````

### API [top]

This adds a task to the task list. All the tasks in the task list are stepped through with the indicated number of steps when the runLoop function is executed.
Syntax:
```addTask((script: string)[, (Initialization code: string)][, (number of steps: number)][, (urgent pile: boolean)]) -> (script index: number)
```
Usage Example:
``````script = "while true do print('B') end"scriptidx,msg = lStepper.addTask(script,"print('Adding Script')",5,true)
``````

Inputs:
• script - is the string containing the Lua script that is the task
• Initialization code - is an optional Lua script as a string which is executed without stepping before the actual code starts running in the environment.
• number of steps - is the optional number of steps to step the script each time runLoop executes a loop. These number of steps may not correspond to the lines of code.
• urgent pile - is an optional boolean indicating whether this task has to be placed in the urgent quota.
NOTE: Urgent tasks are placed with task Indices beginning after the main task indices. This facility is provided to have an extra buffer to add urgent tasks without removing the normal tasks. Recommended way to use them would be add a urgent task. Suspend all the main tasks and then run the urgent task to get something done and then clean the urgent task slot and then resume the main tasks again.
Returns:
• nil and a error message on failure

#### runLoop [top]

This runs each task in the task List with their respective indicated number of steps.
Syntax:
```runLoop( [(number of runs: number)] ) ->(tasks status: table)
```
Usage Example:
``````stat,msg=lStepper.runLoop()
``````

Inputs:
• number of runs - is an integer which specifies the number of times the loop has to run, by default it is run just once.
Returns:
• nil if there are no tasks
• nil and error message if failure
• table with keys as the task index and the values are tables with at most 2 elements representing the status of each task in the pile. The 1st element of each status table can be:
• GOOD - Task is running good
• ERROR - Task produced an error and stopped. The second element is the error message
• NE-PREVERR - Task was not executed because it had a previous error. Second element is the Error code type i.e."LUA_ERRRUN" or "LUA_ERRMEM"
• NE-SUSPEND - Task was not executed because it was suspended before
In case of multiple loop runs the status is what was generated in the last iteration.

Syntax:
```suspendTask( (task index: number) ) -> (true: boolean)
```
Usage Example:
``````stat,msg = lStepper.suspendTask(1)
``````

Inputs:
• task index- index of the task that needs to be suspended
Returns:
• nil and a error message in case of failure
• true if suspend was successful

Syntax:
```resumeTask( (task index: number) ) -> (true: boolean)
```
Usage Example:
``````stat,msg = lStepper.resumeTask(1)
``````

Inputs:
• task index- index of the task that needs to be resumed
Returns:
• nil and a error message in case of failure
• nil if the task did not exist or was not suspended or was finished.
• true if resume was successful

To poll the status of a task
Syntax:
```taskStatus( (task index: number) ) -> (status: string)[, (status detail: string)
```
Usage Example:
``````stat,msg = lStepper.taskStatus(1)
``````

Inputs:
• task index - index of the task whose status is being queried
Returns:
• nil and a error message in case of failure
• GOOD - Task is running good
• SUSPENDED - Task is in suspended state and is not being executed when runLoop is called
• NE-PREVERR - Task exited with an error when it was last executed. Second return is the Error code type i.e."LUA_ERRRUN" or "LUA_ERRMEM"

To pass data to a task's environment
Syntax:
```setTaskData( (task index: number), (data table: table) ) -> (true: boolean)
```
Usage Example:
``````stat,msg = lStepper.setTaskData(4,{test5=30})
``````

Inputs:
• task index - index of the task to which data has to be passed
• data table - table containing key value pairs that have to be set in the task's Lua environment. It would overwrite same keys all values in the Global environment of the task. The keys can be number or string while the values can be string, number, boolean or table
Returns:
• nil and a error message if error
• true if successful

Syntax:
```getTaskData( (task index: number), (variable name: string) ) -> (value: any type)
```
Usage Example:
``````val,msg = lStepper.getTaskData(4,"test10")
``````

Inputs:
• task index - index of the task to which the table has to be passed
• variable name - name of the variable in the task's global environment whose value is to be retrieved
Returns:
• nil and a error message if error
• the value if success

Syntax:
```getNumOfTasks() -> (tasks in regular list: number), (tasks in urgent list: number)
```
Usage Example:
``````normal,urgent = lStepper.getNumOfTasks()
``````

Returns:
• nil and error message if error
• number of tasks in the task List as the 1st return and number of urgent tasks in the list as the 2nd return

The close the task and free up its index in the list
Syntax:
```closeTask( (task index: number) ) -> (true: boolean)
```
Usage Example:
``````stat,msg = lStepper.closeTask(0)
``````

Inputs:
• task index - index of the task which is to be closed
Returns:
• nil if task was not there at the index
• nil and error message if error
• true if successful

#### registerCallBack [top]

To register a callback function with LuaStepper. This function is accessible to any task's runCode function and other function it creates by calling lstep.callParentFunc. That means you can call this registered function by doing the following:
``````lStepper.runCode("lstep.callParentFunc(arg1,arg2)")
``````

So this allows passing arguments directly to your parent. Another way could be exposing a function to the thread which can then in turn call the call back function. So we could have:
``````lStepper.runCode([[    newFunc = function(arg)         result = lstep.callParentFunc("newFunc",arg)    end]])
``````

Here runCode exposed a function called newFunc to the thread code which can pass data to the call back function and also tells the call back function about its own name so the call back function can detect where the call came from and take appropriate action and return the result.
The Call back function
The call back function is called with the 1st argument as the task index followed by all the arguments passed to the call back function in the task. So in the above case the call back function call would be something like this:
``````callback(1,"newFunc",arg)  -- 1 is the task index of this task
``````

The second argument being the function name is not enforced but should be followed since when the task calls require the call back function is called with its second argument "require". So following this standard would lead to consistent call back function programming.
The call back function can return 2 values. Both may be strings, the 1st can be nil.

By default the require function exposed to the task's code uses the call back function to allow exposing external code to the thread. So if you don't have any call back registered in LuaStepper then require won't work in any task's code. So when the thread code calls require it calls this call back function to tell the parent thread of the require request.
Syntax:
```registerCallBack( (callback: function) ) -> (true: boolean)
```
Usage Example:
``````stat,msg = registerCallBack(myCallBackFunc)
``````

Inputs:
• callback - Call back function to be exposed to all thread's runCode function
Returns:
• nil and error message if error
• true if success

#### runCode [top]

Allows the parent thread to run some code in the thread environment without doing the stepping. This is useful to provide resources as requested by the thread in the thread environment.
Syntax:
```runCode( (task index: number), (Lua Code: string) ) -> (true: boolean)
```
Usage Example:
``````stat,msg = lStepper.runCode(1,[[x = 2+3]])
``````

Inputs:
• task index - index of the task where the code needs to be run
• Lua Code - Lua code script to run
Returns:
• nil and error message if error
• true if success
Notes:
The environment in which the code runs is the global environment of the thread plus it has access to the following extra functions/tables which the thread cannot access:
• pack table - containing the original package table
• req function - containing the original require function
• lstep table - table containing special functions used by the thread management functions. It is only accessible via runCode or with functions provided to the task by runCode. Its members are:
• callParentFunc - Function that calls the function that is registered by the parent thread using registerCallBack
• getswitchstatus - which toggles between -1 and 1 for every switch that LuaStepper does away from the task. This can be used by the thread management functions to detect a switch.
• oslib library - containing the original os library
• iolib library - containing the original io library
Thus if you want to expose the os library to your thread then simple run the following code:
``````lStepper.runCode("os = oslib")
``````

### Objects [top]

#### _VERSION [top]

Returns the version number of the module

## LuaStepper [top]

The API provided by the LuaStepper C module is accessed by requiring 'LuaStepper'. Although this can be used directly the Lua interface is the preferred way of accessing the LuaStepper module.
``````LuaStepper = require("LuaStepper")
``````

### API [top]

The API functions are as follows:

This adds a task to the task list. All the tasks in the task list are stepped through with the indicated number of steps when the runLoop function is executed.
Syntax:
```addTask((script: string)[, (Initialization code: string)][, (number of steps: number)][, (urgent pile: boolean)]) -> (script index: number)
```
Usage Example:
``````script = "while true do print('B') end"scriptidx,msg = LuaStepper.addTask(script,"print('Adding Script')",5,true)
``````

Inputs:
• script - is the string containing the Lua script that is the task
• Initialization code - is an optional Lua script as a string which is executed without stepping before the actual code starts running in the environment.
• number of steps - is the optional number of steps to step the script each time runLoop executes a loop. These number of steps may not correspond to the lines of code.
• urgent pile - is an optional boolean indicating whether this task has to be placed in the urgent quota.
NOTE: Urgent tasks are placed with task Indices beginning after the main task indices. This facility is provided to have an extra buffer to add urgent tasks without removing the normal tasks. Recommended way to use them would be add a urgent task. Suspend all the main tasks and then run the urgent task to get something done and then clean the urgent task slot and then resume the main tasks again.
Returns:
• nil and a error message on failure

#### runLoop [top]

This runs each task in the task List with their respective indicated number of steps.
Syntax:
```runLoop( [(number of runs: number)] ) ->(tasks status: table)
```
Usage Example:
``````stat,msg=LuaStepper.runLoop()
``````

Inputs:
• number of runs - is an integer which specifies the number of times the loop has to run, by default it is run just once.
Returns:
• nil if there are no tasks
• nil and error message if failure
• table with keys as the task index and the values are tables with at most 2 elements representing the status of each task in the pile. The 1st element of each status table can be:
• GOOD - Task is running good
• ERROR - Task produced an error and stopped. The second element is the error message
• NE-PREVERR - Task was not executed because it had a previous error. Second element is the Error code type i.e."LUA_ERRRUN" or "LUA_ERRMEM"
• NE-SUSPEND - Task was not executed because it was suspended before
In case of multiple loop runs the status is what was generated in the last iteration.

Syntax:
```suspendTask( (task index: number) ) -> (true: boolean)
```
Usage Example:
``````stat,msg = LuaStepper.suspendTask(1)
``````

Inputs:
• task index- index of the task that needs to be suspended
Returns:
• nil and a error message in case of failure
• true if suspend was successful

Syntax:
```resumeTask( (task index: number) ) -> (true: boolean)
```
Usage Example:
``````stat,msg = LuaStepper.resumeTask(1)
``````

Inputs:
• task index- index of the task that needs to be resumed
Returns:
• nil and a error message in case of failure
• nil if the task did not exist or was not suspended or was finished.
• true if resume was successful

To poll the status of a task
Syntax:
```taskStatus( (task index: number) ) -> (status: string)[, (status detail: string)
```
Usage Example:
``````stat,msg = LuaStepper.taskStatus(1)
``````

Inputs:
• task index - index of the task whose status is being queried
Returns:
• nil and a error message in case of failure
• GOOD - Task is running good
• SUSPENDED - Task is in suspended state and is not being executed when runLoop is called
• NE-PREVERR - Task exited with an error when it was last executed. Second return is the Error code type i.e."LUA_ERRRUN" or "LUA_ERRMEM"

To pass data to a task's environment
Syntax:
```setTaskData( (task index: number), (data table: table) ) -> (true: boolean)
```
Usage Example:
``````stat,msg = LuaStepper.setTaskData(4,{test5=30})
``````

Inputs:
• task index - index of the task to which data has to be passed
• data table - table containing key value pairs that have to be set in the task's Lua environment. It would overwrite same keys all values in the Global environment of the task. The keys can be number or string while the values can be string, number, boolean.
Returns:
• nil and a error message if error
• true if successful

To pass a table to a task's environment. Note this function is not present in lStepper module since the function setTaskData there combines the functionality of this function as well.
Syntax:
```setTaskTable( (task index: number), (table name: string), (table script: string) ) -> (true: boolean)
```
Usage Example:
``````stat,msg = LuaStepper.setTaskTable(4,"newTable","t0={1,2,3}")
``````

Inputs:
• task index - index of the task to which the table has to be passed
• table name - is the name by which the table will be referenced in the task's environment
• table script - a Lua script as a string which when executed will generate the table in the value t0 in the script.
Returns:
• nil and a error message in case of error
• true if success.

Syntax:
```getTaskData( (task index: number), (variable name: string) ) -> (value: any type)[, ("TABLE": for value of table)]
```
Usage Example:
``````val,msg = LuaStepper.getTaskData(4,"test10")
``````

Inputs:
• task index - index of the task to which the table has to be passed
• variable name - name of the variable in the task's global environment whose value is to be retrieved
Returns:
• nil and a error message if error
• the value if the value was of the type string, number, boolean
• the table as a table generation script string and "TABLE" if the value was a table. To convert the string to the table simply load and execute the string and take the value of 't0' from the execution environment.

Syntax:
```getNumOfTasks() -> (tasks in regular list: number), (tasks in urgent list: number)
```
Usage Example:
``````normal,urgent = LuaStepper.getNumOfTasks()
``````

Returns:
• nil and error message if error
• number of tasks in the task List as the 1st return and number of urgent tasks in the list as the 2nd return

The close the task and free up its index in the list
Syntax:
```closeTask( (task index: number) ) -> (true: boolean)
```
Usage Example:
``````stat,msg = LuaStepper.closeTask(0)
``````

Inputs:
• task index - index of the task which is to be closed
Returns:
• nil if task was not there at the index
• nil and error message if error
• true if successful

#### registerCallBack [top]

To register a callback function with LuaStepper. This function is accessible to any task's runCode function and other function it creates by calling lstep.callParentFunc. That means you can call this registered function by doing the following:
``````LuaStepper.runCode("lstep.callParentFunc(arg1,arg2)")
``````

So this allows passing arguments directly to your parent. Another way could be exposing a function to the thread which can then in turn call the call back function. So we could have:
``````LuaStepper.runCode([[    newFunc = function(arg)         result = lstep.callParentFunc("newFunc",arg)    end]])
``````

Here runCode exposed a function called newFunc to the thread code which can pass data to the call back function and also tells the call back function about its own name so the call back function can detect where the call came from and take appropriate action and return the result.
The Call back function
The call back function is called with the 1st argument as the task index followed by all the arguments passed to the call back function in the task. So in the above case the call back function call would be something like this:
``````callback(1,"newFunc",arg)  -- 1 is the task index of this task
``````

The second argument being the function name is not enforced but should be followed since when the task calls require the call back function is called with its second argument "require". So following this standard would lead to consistent call back function programming.
The call back function can return 2 values. Both may be strings, the 1st can be nil.

By default the require function exposed to the task's code uses the call back function to allow exposing external code to the thread. So if you don't have any call back registered in LuaStepper then require won't work in any task's code. So when the thread code calls require it calls this call back function to tell the parent thread of the require request.
Syntax:
```registerCallBack( (callback: function) ) -> (true: boolean)
```
Usage Example:
``````stat,msg = registerCallBack(myCallBackFunc)
``````

Inputs:
• callback - Call back function to be exposed to all thread's runCode function
Returns:
• nil and error message if error
• true if success

#### runCode [top]

Allows the parent thread to run some code in the thread environment without doing the stepping. This is useful to provide resources as requested by the thread in the thread environment.
Syntax:
```runCode( (task index: number), (Lua Code: string) ) -> (true: boolean)
```
Usage Example:
``````stat,msg = LuaStepper.runCode(1,[[x = 2+3]])
``````

Inputs:
• task index - index of the task where the code needs to be run
• Lua Code - Lua code script to run
Returns:
• nil and error message if error
• true if success
Notes:
The environment in which the code runs is the global environment of the thread plus it has access to the following extra functions/tables which the thread cannot access:
• pack table - containing the original package table
• req function - containing the original require function
• lstep table - table containing special functions used by the thread management functions. It is only accessible via runCode or with functions provided to the task by runCode. Its members are:
• callParentFunc - Function that calls the function that is registered by the parent thread using registerCallBack
• getswitchstatus - which toggles between -1 and 1 for every switch that LuaStepper does away from the task. This can be used by the thread management functions to detect a switch.
• oslib library - containing the original os library
• iolib library - containing the original io library
Thus if you want to expose the os library to your thread then simple run the following code:
``````LuaStepper.runCode("os = oslib")
``````

### Objects [top]

#### _VERSION [top]

Returns the version number of the module

# To Do [top]

1. Add function for utility code to yield the thread it is running in
2. Implement coroutine.wrap in the thread code.
{"ft_lang_label":"__label__en","ft_lang_prob":0.83818287,"math_prob":0.7949401,"size":19072,"snap":"2021-43-2021-49","text_gpt3_token_len":4130,"char_repetition_ratio":0.1595343,"word_repetition_ratio":0.80588233,"special_character_ratio":0.20763423,"punctuation_ratio":0.068422556,"nsfw_num_words":0,"has_unicode_error":false,"math_prob_llama3":0.9794778,"pos_list":[0,1,2],"im_url_duplicate_count":[null,10,null],"WARC_HEADER":"{\"WARC-Type\":\"response\",\"WARC-Date\":\"2021-10-18T17:25:37Z\",\"WARC-Record-ID\":\"<urn:uuid:5ea6b8b3-7329-4938-8c8b-4dfc868f9bdb>\",\"Content-Length\":\"90743\",\"Content-Type\":\"application/http; msgtype=response\",\"WARC-Warcinfo-ID\":\"<urn:uuid:36578352-22db-424c-9362-32c9cba1bbf6>\",\"WARC-Concurrent-To\":\"<urn:uuid:e5e70723-f556-44c8-a95e-5beef9bb3b0e>\",\"WARC-IP-Address\":\"51.79.55.114\",\"WARC-Target-URI\":\"https://milindsweb.amved.com/LuaStepper.html\",\"WARC-Payload-Digest\":\"sha1:QIZOHILXNBHDLFYTSJOBIV3GOLOPKI5U\",\"WARC-Block-Digest\":\"sha1:XMRHT54GNZJXP4MPWV7QHISGBS2M535C\",\"WARC-Identified-Payload-Type\":\"text/html\",\"warc_filename\":\"/cc_download/warc_2021/CC-MAIN-2021-43/CC-MAIN-2021-43_segments_1634323585204.68_warc_CC-MAIN-20211018155442-20211018185442-00703.warc.gz\"}"}