119 Commits
Author SHA1 Message Date
server 3646a1a074 Syntax changes 2022-04-19 18:45:14 -04:00
server b0ab40d410 Fixed some bugs, a little more testing needed 2022-04-19 18:39:30 -04:00
server 440995b1c8 Cleaned up rockspec file 2022-04-18 22:56:55 -04:00
server 8da4c4c1de made lanes optional(Removed it from the dependency list) 2022-04-18 22:51:38 -04:00
server 2cc2a57a46 Fixed system threads calling OnDeath incorrectly, finished full update showcase 2022-04-18 22:44:08 -04:00
server 6527dc1aaa Fixed test script to not use globally installed version and the dev version 2022-04-18 21:25:36 -04:00
server bd49805c25 testing... 2022-04-18 21:04:44 -04:00
server 40dd293bf8 Fixed issue with pesudo threading not working properly 2022-04-18 21:03:14 -04:00
server 568c95fa73 Issue with love2d system threaded functions pushstatus fixed 2022-04-16 00:02:54 -04:00
server d30ee3788e THREAD.pushStatus for lanes works, todo love 2022-04-11 23:48:45 -04:00
server 79f58a79f9 Love2d newFunction working 2022-04-10 20:00:20 -04:00
server d8aeefd202 Working on love2d threaded functions 2022-04-02 23:06:06 -04:00
server c0fb94ddbb Fixed and documented the newProcessor changes. Rockspec fix 2022-04-02 00:22:34 -04:00
server e8a3cd731d Added ST to THD namespace, old way still works 2022-04-02 00:07:06 -04:00
server 03ffb6bc0a Removed locking 2022-02-25 14:36:21 -05:00
server 4240737e00 Working on taskmanager features 2022-02-20 21:07:04 -05:00
server 5172dcdf01 Reworking the taskdetails method 2022-02-17 10:00:55 -05:00
server fc18a303dd Fixed issue with how the thread scheduler spawned threads 2022-02-15 21:47:07 -05:00
server 04d5500374 Fixed issue with the new thread scheduler(again) connections and hold get a buff 2022-02-15 15:11:28 -05:00
server 72e24e8a9b Added connection:hasConnections() 2022-02-14 18:00:02 -05:00
server 48bba84c08 Fix issue where I forgot to implement the handler for thread.yield 2022-02-13 00:16:00 -05:00
server e9a0e7bbf7 Fixed an issue with the new thread namespace 2022-02-09 22:19:27 -05:00
server 264867a0da Fixed issue with threads not returning values properly 2022-02-09 19:29:06 -05:00
server 3f046afaa1 Fixed: missing a yield 2022-02-09 17:05:30 -05:00
server 2acce5001d Small issue with functions attached to processes 2022-02-09 16:30:19 -05:00
server 78cd15681a Removed actions for now 2022-02-08 22:51:46 -05:00
server 3401a8ac61 fixing issue with actions 2022-02-08 22:48:27 -05:00
server 19ac257204 Testing actions 2022-02-08 22:47:16 -05:00
server c14a469069 Each processor has it's own thread handler, all processes now trigger the default thread handler. 2022-02-08 22:40:38 -05:00
server 03cea2d71a Processors are working nicely, mostly done with the library 2022-02-05 10:56:03 -05:00
server 593bfd0d8c Threads now working, can create many more threads without a performance hit 2022-02-05 00:55:11 -05:00
server bf60a354d4 So the bug wasn't a bug all along 2022-02-04 17:27:13 -05:00
server 6c73220a52 working on scheduler, nothing fix yet 2022-01-31 17:11:14 -05:00
server c3a9ddfdbd Reverted the extra space 2022-01-31 10:51:44 -05:00
server e05f2ea400 last test 2022-01-31 10:50:45 -05:00
server a60aae02c6 Cleanup spaces 2022-01-31 10:45:26 -05:00
server 207c5b8d69 test2 2022-01-31 09:52:43 -05:00
server a9111f2fa3 test 2022-01-31 09:49:12 -05:00
server b572bf218d Still debugging, not sure what is causing the thread to not yield properly 2022-01-31 09:47:37 -05:00
server 49c0bd3930 Fixing issues with the new thread scheduler, nested yields need handling 2022-01-31 08:31:38 -05:00
server cdb4bfda11 Error handling not working if its on the first step 2022-01-28 17:50:19 -05:00
server 3fbead60d9 connections working, todo: error catching and return catching 2022-01-26 16:52:17 -05:00
server 2b122f5c77 scheduler is much faster, missing connection holding and error handling 2022-01-26 00:03:32 -05:00
server 472d1748ee Working on thread scheduler rework 2022-01-24 14:25:23 -05:00
server 47178dd3b3 Started to work on the scheduler rework 2022-01-24 09:00:51 -05:00
server 3fcba8825b Reworked priorities 2022-01-23 17:23:32 -05:00
server e194a06427 Cleaned up the mainloop/umanager 2022-01-23 12:14:50 -05:00
server 32f7b4492b Improving performance in the mainloop 2022-01-22 23:34:15 -05:00
server 913745a1bc Merge branch 'v15.2.0' of https://github.com/rayaman/multi into v15.2.0 2022-01-20 13:07:01 -05:00
server 889dc6ca68 Removed unneeded files 2022-01-20 13:06:53 -05:00
server 2805137648 Merge branch 'master' into v15.2.0 2022-01-20 12:31:09 -05:00
server f7167cf972 Current progress in v15.2.0 updated ignore file 2022-01-20 12:29:39 -05:00
server 9e1ecb3583 Testing... 2022-01-20 09:53:56 -05:00
server b9b9b51d12 Taking testing seriously 2022-01-20 09:49:48 -05:00
server 4335f3ed14 Testing 2022-01-19 23:12:16 -05:00
server 8580d92c9c Fixing issues with priority 2022-01-19 23:09:47 -05:00
server f1f6e30a98 testing 2022-01-19 13:41:29 -05:00
server 6c1e9f26f0 Reworking the loops 2022-01-18 23:39:17 -05:00
server d98f353936 Tweaked processor object, added lightloop and lmanager 2022-01-17 23:23:56 -05:00
server 14c8665910 Update README.md 2022-01-16 16:30:45 -05:00
server f7452db3ec spaces to tabs 2022-01-16 16:30:03 -05:00
server 588923e1b7 spaces to tabs 2022-01-16 16:26:37 -05:00
server 4877f64ca1 Fix some bugs, added connection/thread tests 2022-01-16 16:18:17 -05:00
server b8b31253d4 Added key feature 2022-01-11 23:16:33 -05:00
server af4672245e Removed some tests 2022-01-11 23:04:35 -05:00
server 609613dbe9 Fixed lua 5.4 issue and the taskstatus 2022-01-11 18:59:53 -05:00
server b16593425b Fixed typo in changes.md 2022-01-09 23:32:54 -05:00
server 997ea48b54 Writing tests, fixed some bugs with the library, testing luajit support 2022-01-09 23:23:54 -05:00
server 537dcf0db1 Working on performance 2022-01-04 18:01:02 -05:00
server b74a6c006e Updated lanes integration 2022-01-02 12:18:35 -05:00
server b3453d028c Fixed the lanes issue? 2022-01-02 11:41:39 -05:00
server 6842147522 Updated gitignore 2022-01-02 11:25:42 -05:00
server de34b9dc59 Merge pull request #32 from rayaman/v16.0.0
Updated license
2022-01-01 23:21:58 -05:00
server dff19d865f Updated license 2022-01-01 23:20:48 -05:00
server 544aa78d70 Updated code 2021-12-24 22:47:38 -05:00
server 91d0b5f7be added some spaces 2021-12-19 01:05:34 -05:00
server 27e03a2546 bug fixing should be done 2021-12-18 12:55:20 -05:00
server 7dbcd01c33 Merge pull request #31 from rayaman/V15.2.0
working on 16.0
2021-12-18 12:42:58 -05:00
server cc20914391 working on 1.16 2021-12-18 12:42:14 -05:00
server c2aa449a65 Merge pull request #26 from rayaman/V15.1.0
V15.1.0
2021-11-30 21:28:18 -05:00
server 57563688ae fixed a bug 2021-11-30 21:23:45 -05:00
server 317dacd0de Updated readme and documentation 2021-11-30 20:54:54 -05:00
server a7ba146a64 Cleaning up code, fixing bugs (Almost ready for release) 2021-11-30 20:11:20 -05:00
server efa30e30cc Updated changes, connections can be added 2021-11-27 17:55:13 -05:00
server 197b418fc5 Fixed typo 2021-10-10 09:12:42 -04:00
server d3d53599f7 function's are now callable objects 2021-10-10 09:10:24 -04:00
server bf517facd1 Adding features to threaded functions 2021-07-19 17:04:43 -04:00
server 9cff2735ba threaded functions can now be paused 2021-07-05 22:23:45 -04:00
server ea77b934b6 Updated version number 2021-07-02 21:13:47 -04:00
server 74bfd571a5 Rockspecs/Readme Updated 2021-07-02 16:57:58 -04:00
server 06132fc1dd Fixed rockspec for threading.lua, tweaking code 2021-07-02 15:46:37 -04:00
server ade5172f26 Renamed lovr to lovrManager to fit theme 2021-06-24 23:11:10 -04:00
server bdc657771d Changelog/Documentation updated. Processors added 2021-06-24 23:09:15 -04:00
server 8c24bcbbb0 Modified changes.md 2021-06-19 21:09:32 -04:00
server 804a117ed0 Fixed return bug 2021-06-19 20:12:06 -04:00
server 37afd37f9e Adding tests wip 2021-05-27 16:47:45 -04:00
server 4399fb6424 updated readme 2021-05-02 22:31:29 -04:00
server 02a54e13ea Started to work on lovr integration 2021-05-02 22:26:35 -04:00
server bb2c7d6440 Merge pull request #20 from rayaman/V15.1.0
Fixed issue with lightloop not triggering the preload event
2021-05-02 22:04:54 -04:00
server d1b8ed1922 Fixed issue with lightloop not triggering the preload event 2021-05-02 22:04:25 -04:00
server 9992a2c091 Merge pull request #19 from rayaman/v15.0.0
V15.0.0
2021-05-01 17:33:58 -04:00
server 155466dc71 Merge branch 'master' into v15.0.0 2021-05-01 17:33:23 -04:00
server cea6508d68 Release ready 2021-05-01 17:31:14 -04:00
server 726707eb8a Modifying rockspec 2021-05-01 16:35:31 -04:00
server 9a9d28f62f Cleaning up... 2021-05-01 16:21:34 -04:00
server ed924a3d9d Version 15.0.0 2021-05-01 15:52:32 -04:00
server 2d239c65ea tests 2021-04-30 10:48:58 -04:00
server fd8e77555a Testing something 2021-04-30 10:00:13 -04:00
server b60aab9602 Will be coming back to this project
Plan on finally getting back to working on the network parallelism.
2021-04-29 15:34:13 -04:00
server 180176e2cf Updated License date 2021-04-03 12:40:33 -04:00
server 952b592b97 Update README.md
fixed typo
2020-12-24 12:38:07 -05:00
server d2ce7e070b Fixing pseudothreading 2020-05-15 01:38:24 -04:00
server 300827b7bd cleaning up 2020-05-08 17:44:16 -04:00
server 9d97eac146 Working on psuedo threading 2020-05-08 11:41:52 -04:00
server 61dcb9da01 Working on 15.0.0 2020-03-29 11:50:15 -04:00
server b597fbdf9b removed okd files 2020-03-14 20:21:08 -04:00
server 8fbaa76fe9 removed old files 2020-03-14 20:16:45 -04:00
server abb3da416f Merge pull request #18 from rayaman/v14.2.0
v14.2.0 release ready
2020-03-14 09:13:57 -04:00
server 8ba489dc58 Merge pull request #17 from rayaman/v14.2.0
V14.2.0
2020-03-14 08:35:29 -04:00
47 changed files with 3885 additions and 1811 deletions
+5 -12
View File
@@ -1,14 +1,7 @@
test2.lua
*.mp3
*.exe
lanestestclient.lua
lanestest.lua
sample-node.lua
sample-master.lua
Ayn Rand - The Virtue of Selfishness-Mg4QJheclsQ.m4a
Atlas Shrugged by Ayn Rand Audiobook-9s2qrEau63E.webm
test.lua
test.lua
*lua5.1
*lua5.2
*lua5.3
*lua5.4
*luajit
*.code-workspace
*.dat
-50
View File
@@ -1,50 +0,0 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "launch-lua",
"type": "lua",
"request": "launch",
"workingDirectory": "${workspaceRoot}",
"sourceBasePath": "${workspaceRoot}",
"executable": "C:\\Program Files (x86)\\Lua\\5.1\\lua.exe",
"arguments": "main.lua",
"listenPublicly": false,
"listenPort": 56789,
"encoding": "UTF-8",
"env": {}
},
{
"name": "launch-gideros",
"type": "lua",
"request": "launch",
"workingDirectory": "${workspaceRoot}",
"giderosPath": "C:/Program Files (x86)/Gideros",
"gprojPath": "${workspaceRoot}/GPROJ.gproj",
"jumpToGiderosErrorPosition": false,
"stopGiderosWhenDebuggerStops": true,
"listenPublicly": false,
"listenPort": 56789,
"encoding": "UTF-8"
},
{
"name": "wait",
"type": "lua",
"request": "attach",
"workingDirectory": "${workspaceRoot}",
"sourceBasePath": "${workspaceRoot}",
"listenPublicly": false,
"listenPort": 56789,
"encoding": "UTF-8"
},
{
"type": "lua",
"request": "launch",
"name": "Launch",
"program": "${workspaceFolder}/test.lua"
}
]
}
+263 -45
View File
@@ -1,8 +1,10 @@
Current Multi Version: 14.2.0
Current Multi Version: 15.1.0
# Multi static variables
`multi.Version` — The current version of the library
`multi.TIMEOUT` — The value returned when a timed method times out
`multi.Priority_Core` — Highest level of pirority that can be given to a process
</br>`multi.Priority_Very_High`
</br>`multi.Priority_High`
@@ -14,12 +16,80 @@ Current Multi Version: 14.2.0
</br>`multi.Priority_Idle` — Lowest level of pirority that can be given to a process
# Multi Runners
`multi:lightloop()` — A light version of the mainloop
`multi:lightloop()` — A light version of the mainloop doesn't run Coroutine based threads
</br>`multi:loveloop([BOOLEAN: light true])` — Run's all the love related features as well
</br>`multi:mainloop([TABLE settings])` — This runs the mainloop by having its own internal while loop running
</br>`multi:threadloop([TABLE settings])` — This runs the mainloop by having its own internal while loop running, but prioritizes threads over multi-objects
</br>`multi:uManager([TABLE settings])` — This runs the mainloop, but does not have its own while loop and thus needs to be within a loop of some kind.
# Global Methods
`multi:init()` — Uesd to initiate the library, should only be called once
`multi.getCurrentProcess()` — Returns currently running Process
`multi.`
# Processor Methods
These methods can be called either on the multi namespace or a process returned by `proc = multi:newProcessor()`
`proc.Stop()` — Stops the main process/child process. **Note:** If the main process is stopped all child processes are stopped as well
`proc:getTasksDetails([STRING: displaytype])` — Gets a table or string of all the running tasks
Processor Attributes
---
| Attribute | Type | Returns | Description |
---|---|---|---
Start|Method()|self| Starts the process
Stop|Method()|self| Stops the process
OnError|Connection|connection| Allows connection to the process error handler
Type|Member:`string`|"process"| Contains the type of object
Active|Member:`boolean`|variable| If false the process is not active
Name|Member:`string`|variable| The name set at process creation
process|Thread|thread| A handle to a multi thread object
[Refer to the objects for more methods](#non-actors)
Example:
```lua
package.path = "./?/init.lua;"..package.path
multi,thread = require("multi"):init()
-- Create a processor object, it works a lot like the multi object
sandbox = multi:newProcessor()
-- On our processor object create a TLoop that prints "testing..." every second
sandbox:newTLoop(function()
print("testing...")
end,1)
-- Create a thread on the processor object
sandbox:newThread("Test Thread",function()
-- Create a counter named 'a'
local a = 0
-- Start of the while loop that ends when a = 10
while true do
-- pause execution of the thread for 1 second
thread.sleep(1)
-- increment a by 1
a = a + 1
-- display the name of the current process
print("Thread Test: ".. multi.getCurrentProcess().Name)
if a == 10 then
-- Stopping the processor stops all objects created inside that process including threads. In the backend threads use a regular multiobject to handle the scheduler and all of the holding functions. These all stop when a processor is stopped. This can be really useful to sandbox processes that might need to turned on and off with ease and not having to think about it.
sandbox.Stop()
end
end
-- Catch any errors that may come up
end).OnError(function(...)
print(...)
end)
sandbox.Start() -- Start the process
multi:mainloop() -- The main loop that allows all processes to continue
```
# Multi Settings
**Note:** Most settings have been fined tuned to be at the peak of performance already, however preLoop, protect (Which drastically lowers preformance), and stopOnError should be used freely to fit your needs.
@@ -150,10 +220,13 @@ returns or nil
The connect feature has some syntax sugar to it as seen below
- `link = conn(FUNCTION func, [STRING name nil], [NUMBER #conns+1])`
- `combinedconn = conn1 + conn2` — A combined connection is triggered when all connections are triggered. See example [here](#coroutine-based-threading-cbt)
Example:
```lua
local multi = require("multi")
multi,thread = require("multi"):init()
-- Lets create the events
yawn={}
OnCustomSafeEvent=multi:newConnection(true) -- lets pcall the calls in case something goes wrong default
@@ -201,7 +274,7 @@ Timeouts are a collection of methods that allow you to handle timeouts. These on
```lua
package.path="?.lua;?/init.lua;?.lua;?/?/init.lua;"..package.path
multi = require("multi")
multi,thread = require("multi"):init()
loop = multi:newLoop(function()
-- do stuff
@@ -224,7 +297,7 @@ loop:OnTimerResolved(function(self,...)
print(...)
end)
multi:lightloop()
multi:mainloop()
```
As mentioned above this is made much easier using threads
```lua
@@ -255,14 +328,14 @@ print(func(0))
Example:
```lua
package.path="?.lua;?/init.lua;?.lua;?/?/init.lua;"..package.path
multi = require("multi")
multi,thread = require("multi"):init()
multi:scheduleJob({min = 30},function() -- Every hour at minute 30 this event will be triggered! You can mix and match as well!
print("Hi")
end)
multi:scheduleJob({min = 30,hour = 0},function() -- Every day at 12:30AM this event will be triggered
print("Hi")
end)
multi:lightloop()
multi:mainloop()
```
# Universal Actor methods
@@ -287,7 +360,7 @@ All of these functions are found on actors
Example:
```lua
local multi = require("multi")
multi,thread = require("multi"):init()
count=0
-- A loop object is used to demostrate how one could use an event object.
loop=multi:newLoop(function(self,dt)
@@ -298,7 +371,7 @@ event:OnEvent(function(self) -- connect to the event object
loop:Destroy() -- destroys the loop from running!
print("Stopped that loop!",count)
end) -- events like alarms need to be reset the Reset() command works here as well
multi:lightloop()
multi:mainloop()
```
# Actor: Updaters
@@ -311,12 +384,12 @@ Updaters are a mix between both loops and steps. They were a way to add basic pr
Example:
```lua
local multi = require("multi")
multi,thread = require("multi"):init()
updater=multi:newUpdater(5000) -- simple, think of a loop with the skip feature of a step
updater:OnUpdate(function(self)
print("updating...")
end)
multi:lightloop()
multi:mainloop()
```
# Actor: Alarms
@@ -328,13 +401,13 @@ Alarms ring after a certain amount of time, but you need to reset the alarm ever
Example:
```lua
local multi = require("multi")
multi,thread = require("multi"):init()
alarm=multi:newAlarm(3) -- in seconds can go to .001 uses the built in os.clock()
alarm:OnRing(function(a)
print("3 Seconds have passed!")
a:Reset(n) -- if n were nil it will reset back to 3, or it would reset to n seconds
end)
multi:lightloop()
multi:mainloop()
```
# Actor: Loops
@@ -346,7 +419,7 @@ Loops are events that happen over and over until paused. They act like a while l
Example:
```lua
package.path="?/init.lua;?.lua;"..package.path
local multi = require("multi")
multi,thread = require("multi"):init()
local a = 0
loop = multi:newLoop(function()
a = a + 1
@@ -355,7 +428,7 @@ loop = multi:newLoop(function()
loop:Pause()
end
end)
multi:lightloop()
multi:mainloop()
```
# Actor: TLoops
@@ -366,7 +439,7 @@ multi:lightloop()
Example:
```lua
package.path="?/init.lua;?.lua;"..package.path
local multi = require("multi")
multi,thread = require("multi"):init()
local a = 0
loop = multi:newTLoop(function()
a = a + 1
@@ -375,7 +448,7 @@ loop = multi:newTLoop(function()
loop:Pause()
end
end,1)
multi:lightloop()
multi:mainloop()
```
# Actor: Steps
@@ -390,13 +463,13 @@ multi:lightloop()
Example:
```lua
package.path="?/init.lua;?.lua;"..package.path
local multi = require("multi")
multi,thread = require("multi"):init()
multi:newStep(1,10,1,0):OnStep(function(step,pos)
print(step,pos)
end):OnEnd(fucntion(step)
step:Destroy()
end)
multi:lightloop()
multi:mainloop()
```
# Actor: TSteps
@@ -411,13 +484,13 @@ multi:lightloop()
Example:
```lua
package.path="?/init.lua;?.lua;"..package.path
local multi = require("multi")
multi,thread = require("multi"):init()
multi:newTStep(1,10,1,1):OnStep(function(step,pos)
print(step,pos)
end):OnEnd(fucntion(step)
step:Destroy()
end)
multi:lightloop()
multi:mainloop()
```
# Coroutine based Threading (CBT)
@@ -425,7 +498,23 @@ Helpful methods are wrapped around the builtin coroutine module which make it fe
**threads.\* used within threaded enviroments**
- `thread.sleep(NUMBER n)` — Holds execution of the thread until a certain amount of time has passed
- `VARIABLE returns = thread.hold(FUNCTION func)` — Hold execution until the function returns non nil. All returns are passed to the thread once the conditions have been met. To pass nil use `multi.NIL`\*
- `VARIABLE val = THREAD.hold(FUNCTION|CONNCETION|NUMBER func, TABLE options)` — Holds the current thread until a condition is met
| Option | Description |
---|---
| interval | Time between each poll |
| cycles | Number of cycles before timing out |
| sleep | Number of seconds before timing out |
| skip | Number of cycles before testing again, does not cause a timeout! |
**Note:** cycles and sleep options cannot both be used at the same time. Interval and skip cannot be used at the same time either. Cycles take priority over sleep if both are present! HoldFor and HoldWithin can be emulated using the new features. Old functions will remain for backward compatibility.
Using cycles, sleep or interval will cause a timeout; returning nil, multi.TIMEOUT
`func` can be a number and `thread.hold` will act like `thread.sleep`. When `func` is a number the option table will be ignored!
`func` can be a connection and will hold until the condition is triggered. When using a connection the option table is ignored!
- `thread.skip(NUMBER n)` — How many cycles should be skipped until I execute again
- `thread.kill()` — Kills the thread
- `thread.yeild()` — Is the same as using thread.skip(0) or thread.sleep(0), hands off control until the next cycle
@@ -438,9 +527,64 @@ Helpful methods are wrapped around the builtin coroutine module which make it fe
- `th = thread.getRunningThread()` — Returns the currently running thread
- `VARIABLE returns or nil, "TIMEOUT" = thread.holdFor(NUMBER: sec, FUNCTION: condition)` — Holds until a condidtion is met, or if there is a timeout nil,"TIMEOUT"
- `VARIABLE returns or nil, "TIMEOUT" = thread.holdWithin(NUMBER: skip, FUNCTION: func)` — Holds until a condition is met or n cycles have happened.
- `returns or handler = thread:newFunction(FUNCTION: func, [BOOLEAN: holdme false])` — func: The function you want to be threaded. holdme: If true the function waits until it has returns and then returns them. Otherwise the function returns a table
- `handler.connect(Function: func(returns))` — Connects to the event that is triggered when the returns are avaiable
- `VARIAABLE returns = handler.wait()` — Waits until returns are avaiable and then returns them
- `func = thread:newFunction(FUNCTION: func, [BOOLEAN: holdme false])` — func: The function you want to be threaded. holdme: If true the function waits until it has returns and then returns them. Otherwise the function returns a table
- `func:Pause()` — Pauses a function, function will return `nil`, `"Function is paused"`
- `func:Resume()` — Resumes a paused function
- `func:holdMe(BOOLEAN: set)` — Sets the holdme argument to `set`
- `handler = func(VARIABLE args)` — Calls the function, will return
- `handler.isTFunc` — if true then its a threaded function
- `handler.wait()` — waits for the function to finish and returns like normal
- `handler.connect(Function: func(returns))` — Connects to the event that is triggered when the returns are avaiable and returns them
- `VARIABLE returns = handler.wait()` — Waits until returns are avaiable and then
- `handler.OnStatus(connector(VARIABLE args))` — A connection to the running function's status see example below
- `handler.OnReturn(connector(VARIABLE args))` — A connection that is triggered when the running function is finished see example below
- `handler.OnError(connector(nil,error))`
Example:
```lua
package.path = "./?/init.lua;"..package.path
multi,thread = require("multi"):init()
func = thread:newFunction(function(count)
local a = 0
while true do
a = a + 1
thread.sleep(.1)
thread.pushStatus(a,count)
if a == count then break end
end
return "Done"
end)
multi:newThread("Function Status Test",function()
local ret = func(10)
local ret2 = func(15)
local ret3 = func(20)
ret.OnStatus(function(part,whole)
--[[ Print out the current status. In this case every second it will update with:
10%
20%
30%
...
100%
Function Done!
]]
print(math.ceil((part/whole)*1000)/10 .."%")
end)
ret2.OnStatus(function(part,whole)
print("Ret2: ",math.ceil((part/whole)*1000)/10 .."%")
end)
ret3.OnStatus(function(part,whole)
print("Ret3: ",math.ceil((part/whole)*1000)/10 .."%")
end)
-- Connections can now be added together, if you had multiple holds and one finished before others and wasn't consumed it would lock forever! This is now fixed
thread.hold(ret2.OnReturn + ret.OnReturn + ret3.OnReturn)
print("Function Done!")
os.exit()
end)
```
<b>\*</b>A note about multi.NIL, this should only be used within the hold and hold like methods. thread.hold(), thread.holdFor(), and thread.holdWithin() methods. This is not needed within threaded functions! The reason hold prevents nil and false is because it is testing for a condition so the first argument needs to be non nil nor false! multi.NIL should not be used anywhere else. Sometimes you may need to pass a 'nil' value or return. While you could always return true or something you could use multi.NIL to force a nil value through a hold like method.
@@ -474,7 +618,7 @@ Example:
-- Jobs are not natively part of the multi library. I planned on adding them, but decided against it. Below is the code that would have been used.
-- Implementing a job manager using services
package.path="?/init.lua;?.lua;"..package.path
local multi = require("multi")
multi,thread = require("multi"):init()
multi.Jobs = multi:newService(function(self,jobs)
local job = table.remove(jobs,1)
if job and job.removed==nil then
@@ -532,6 +676,7 @@ jobsn[1]:removeJob() -- Select a job and remove it
multi.Jobs:removeJobs("test2") -- Remove all jobs names 'test2'
multi.Jobs.SetScheme(1) -- Jobs are internally a service, so setting scheme and priority
multi.Jobs.SetPriority(multi.Priority_Core)
multi:mainloop()
```
# CBT: newThread()
@@ -556,21 +701,82 @@ Constants
Examples:
```lua
package.path="?/init.lua;?.lua;"..package.path
local multi = require("multi")
multi,thread = require("multi"):init()
multi:newThread("Example of basic usage",function()
while true do
thread.sleep(1)
print("We just made an alarm!")
end
end)
multi:lightloop()
multi:mainloop()
```
# CBT: newISOThread()
`th = multi:newThread([STRING name,] FUNCTION func, TABLE: env)` — Creates a new thread with name and function func. Sets the enviroment of the func to env. Both the thread.* and multi.* are automatically placed in the enviroment.
When within a thread, if you have any holding code you will want to use thread.* to give time to other threads while your code is running. This type of thread does not have access to outside local or globals. Only what is in the env can be seen. (This thread was made so pesudo threading could work)
Constants
---
- `th.Name` — Name of thread
- `th.Type` — Type="thread"
- `th.TID` — Thread ID
- `conn = th.OnError(FUNCTION: callback)` — Connect to an event which is triggered when an error is encountered within a thread
- `conn = th.OnDeath(FUNCTION: callback)` — Connect to an event which is triggered when the thread had either been killed or stopped running. (Not triggered when there is an error!)
- `boolean = th:isPaused()`\* — Returns true if a thread has been paused
- `self = th:Pause()`\* — Pauses a thread
- `self = th:Resume()`\* — Resumes a paused thread
- `self = th:Kill()`\* — Kills a thread
- `self = th:Destroy()`\* — Destroys a thread
<b>*</b>Using these methods on a thread directly you are making a request to a thread! The thread may not accept your request, but it most likely will. You can contorl the thread flow within the thread's function itself
```lua
package.path="?.lua;?/init.lua;?.lua;?/?/init.lua;"..package.path
multi,thread = require("multi"):init()
GLOBAL,THREAD = require("multi.integration.threading"):init() -- Auto detects your enviroment and uses what's available
jq = multi:newSystemThreadedJobQueue(5) -- Job queue with 4 worker threads
func = jq:newFunction("test",function(a,b)
THREAD.sleep(2)
return a+b
end)
for i = 1,10 do
func(i,i*3).connect(function(data)
print(data)
end)
end
local a = true
b = false
multi:newThread("Standard Thread 1",function()
while true do
thread.sleep(1)
print("Testing 1 ...",a,b,test)
end
end).OnError(function(self,msg)
print(msg)
end)
-- All upvalues are stripped! no access to the global, multi and thread are exposed however
multi:newISOThread("ISO Thread 2",function()
while true do
thread.sleep(1)
print("Testing 2 ...",a,b,test) -- a and b are nil, but test is true
end
end,{test=true,print=print})
.OnError(function(self,msg)
print(msg)
end)
multi:mainloop()
```
# System Threads (ST) - Multi-Integration Getting Started
The system threads need to be required seperatly.
```lua
-- I recommend keeping these as globals. When using lanes you can use local and things will work, but if you use love2d and locals, upvalues are not transfered over threads and this can be an issue
GLOBAL, THREAD = require("multi.integration.lanesManager"):init() -- We will talk about the global and thread interface that is returned
GLOBAL, THREAD = require("multi.integration.threading"):init() -- We will talk about the global and thread interface that is returned
GLOBAL, THREAD = require("multi.integration.loveManager"):init()
GLOBAL, THREAD = require("luvitManager") --*
```
@@ -589,7 +795,7 @@ Using this integration modifies some methods that the multi library has.
- `THREAD.kill()` — Kills the thread
- `THREAD.getName()` — Returns the name of the working thread
- `THREAD.sleep(NUMBER n)` — Sleeps for an amount of time stopping the current thread
- `THREAD.hold(FUNCTION func)` — Holds the current thread until a condition is met
- `THREAD.hold(FUNCTION func, TABLE options)` — Holds the current thread until a condition is met
- `THREAD.getID()` — returns a unique ID for the current thread. This varaiable is visible to the main thread as well as by accessing it through the returned thread object. OBJ.Id
# ST - GLOBAL namespace
@@ -609,8 +815,8 @@ ST - System Threads
System Threads are the feature that allows a user to interact with systen threads. It differs from regular coroutine based thread in how it can interact with variables. When using system threads the GLOBAL table is the "only way"* to send data. Spawning a System thread is really simple once all the required libraries are in place. See example below:
```lua
local multi = require("multi") -- keep this global when using lanes or implicitly define multi within the spawned thread
local GLOBAL, THREAD = require("multi.integration.lanesManager").init()
multi,thread = require("multi"):init() -- keep this global when using lanes or implicitly define multi within the spawned thread
local GLOBAL, THREAD = require("multi.integration.threading").init()
multi:newSystemThread("Example thread",function()
local multi = require("multi") -- we are in a thread so lets not refer to that upvalue!
print("We have spawned a thread!")
@@ -624,7 +830,7 @@ end,"A message that we are passing") -- There are restrictions on what can be pa
tloop = multi:newTLoop(function()
print("I'm still kicking!")
end,1)
multi:lightloop()
multi:mainloop()
```
<b>*</b>This isn't entirely true, as of right now the compatiablity with the lanes library and love2d engine have their own methods to share data, but if you would like to have your code work in both enviroments then using the GLOBAL table and the data structures provided by the multi library will ensure this happens. If you do not plan on having support for both platforms then feel free to use linda's in lanes and channels in love2d.
@@ -638,8 +844,8 @@ When creating objects with a name they are automatically exposed to the GLOBAL t
```lua
-- Exposing a queue
multi = require("multi")
local GLOBAL, THREAD = require("multi.integration.lanesManager").init() -- The standard setup above
multi,thread = require("multi"):init()
local GLOBAL, THREAD = require("multi.integration.threading").init() -- The standard setup above
queue = multi:newSystemThreadedQueue("myQueue"):init() -- We create and initiate the queue for the main thread
queue:push("This is a test!") -- We push some data onto the queue that other threads can consume and do stuff with
multi:newSystemThread("Example thread",function() -- Create a system thread
@@ -647,7 +853,7 @@ multi:newSystemThread("Example thread",function() -- Create a system thread
local data = queue:pop() -- Get the data
print(data) -- print the data
end)
multi:lightloop()
multi:mainloop()
```
# ST - SystemThreadedQueue
@@ -659,9 +865,9 @@ multi:lightloop()
Let's get into some examples:
```lua
multi = require("multi")
multi,thread = require("multi"):init()
thread_names = {"Thread_A","Thread_B","Thread_C","Thread_D"}
local GLOBAL, THREAD = require("multi.integration.lanesManager"):init()
local GLOBAL, THREAD = require("multi.integration.threading"):init()
queue = multi:newSystemThreadedQueue("myQueue"):init()
for _,n in pairs(thread_names) do
multi:newSystemThread(n,function()
@@ -683,13 +889,24 @@ end):OnEvent(function()
print("No more data within the queue!")
os.exit()
end)
multi:lightloop()
multi:mainloop()
```
You have probable noticed that the output from this is a total mess! Well I though so too, and created the system threaded console!
# ST - Using the Console
`console = THREAD.getConsole()`
This does guarantee an order to console output, it does ensure that all things are on nice neat lines
```lua
multi,thread = require("multi"):init()
local GLOBAL, THREAD = require("multi.integration.threading"):init()
console.print("Hello World!")
```
# ST - SystemThreadedJobQueue
`jq = multi:newSystemThreadedJobQueue([NUMBER: threads])` — Creates a system threaded job queue with an optional number of threads
- `boolean jq:isEmpty()` — Returns true if the jobqueue is empty false otherwise
- `jq.cores = (supplied number) or (the number of cores on your system*2)`
- `jq.OnJobCompleted(FUNCTION: func(jID,...))` — Connection that is triggered when a job has been completed. The jobID and returns of the job are supplies as arguments
- `self = jq:doToAll(FUNCTION: func)` — Send data to every thread in the job queue. Useful if you want to require a module and have it available on all threads
@@ -698,13 +915,14 @@ You have probable noticed that the output from this is a total mess! Well I thou
- `handler = jq:newFunction([STRING: name], FUNCTION: func)` — returns a threaded Function that wraps around jq.registerFunction, jq.pushJob() and jq.OnJobCompleted() to provide an easy way to create and work with the jobqueue
- `handler.connect(Function: func(returns))` — Connects to the event that is triggered when the returns are avaiable
- `VARIAABLE returns = handler.wait()` — Waits until returns are avaiable and then returns them
**Note:** Created functions using this method act as normal functions on the queue side of things. So you can call the functions from other queue functions as if they were normal functions.
Example:
```lua
package.path="?.lua;?/init.lua;?.lua;?/?/init.lua;"..package.path
multi = require("multi")
GLOBAL, THREAD = require("multi.integration.lanesManager"):init()
multi,thread = require("multi"):init()
GLOBAL, THREAD = require("multi.integration.threading"):init()
local jq = multi:newSystemThreadedJobQueue(4) -- job queue using 4 cores
jq:doToAll(function()
Important = 15
@@ -727,7 +945,7 @@ func(5,5).connect(function(ret)
print("Connected",ret)
os.exit()
end)
multi:lightloop()
multi:mainloop()
```
# ST - SystemThreadedTable
`stt = multi:newSystemThreadedTable(STRING: name)`
@@ -738,15 +956,15 @@ multi:lightloop()
Example:
```lua
package.path="?.lua;?/init.lua;?.lua;?/?/init.lua;"..package.path
multi = require("multi")
GLOBAL, THREAD = require("multi.integration.lanesManager"):init()
multi,thread = require("multi"):init()
GLOBAL, THREAD = require("multi.integration.threading"):init()
local stt = multi:newSystemThreadedTable("stt")
stt["hello"] = "world"
multi:newSystemThread("test thread",function()
local stt = GLOBAL["stt"]:init()
print(stt["hello"])
end)
multi:lightloop()
multi:mainloop()
```
# Network Threads - Multi-Integration WIP Being Reworked
More of a fun project of mine then anything core to to the library it will be released and documented when it is ready. I do not have a timeframe for this
+1 -1
View File
@@ -1,6 +1,6 @@
MIT License
Copyright (c) 2020 Ryan Ward
Copyright (c) 2022 Ryan Ward
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
+47 -18
View File
@@ -1,43 +1,72 @@
# Multi Version: 14.2.0 Documentation Complete, Bloat removed!
# Multi Version: 15.2.0 Upgrade Complete
**Key Changes**
- All objects now use connections internally
- Connections now about 23x faster!
- Updated getTasksDetails() to handle the new method of managing threads and processors
- Made lanes optional, install separately if needed
Found an issue? Please [submit it](https://github.com/rayaman/multi/issues) and I'll look into it!
Found an issue? Please [submit it](https://github.com/rayaman/multi/issues) and someone will look into it!
My multitasking library for lua. It is a pure lua binding, with exceptions of the integrations and the love2d compat. If you find any bugs or have any issues, please [let me know](https://github.com/rayaman/multi/issues) and I'll look into it!.
My multitasking library for lua. It is a pure lua binding, with exceptions of the integrations.
</br>
Progress is being made in [v15.3.0](https://github.com/rayaman/multi/tree/v15.3.0)
---
</br>
INSTALLING
----------
Links to dependicies:
Link to optional dependencies:
[lanes](https://github.com/LuaLanes/lanes)
[love2d](https://love2d.org/)
To install copy the multi folder into your environment and you are good to go</br>
If you want to use the system threads, then you'll need to install lanes!
If you want to use the system threads, then you'll need to install lanes or love2d game engine!
**or** use luarocks `luarocks install multi`
Going forward I will include a Release zip for love2d.
**The Network Manager rework is currently being worked on and the old version is not included in this version.**
Discord
-------
Have a question that you need asking? Or need realtime assistance? Feel free to join the discord!</br>
https://discord.gg/U8UspuA</br>
Have a question or need realtime assistance? Feel free to join the discord!</br>
https://discord.gg/U8UspuA
Planned features/TODO
---------------------
- [x] ~~Finish Documentation~~ Finished
- [ ] Create test suite (In progress, mostly done)
- [ ] Network Parallelism rework
Usage: [Check out the documentation for more info](https://github.com/rayaman/multi/blob/master/Documentation.md)</br>
Usage: [Check out the documentation for more info](https://github.com/rayaman/multi/blob/master/Documentation.md)
-----
```lua
local multi, thread = require("multi").init()
mutli:newThread("Example",function()
local multi, thread = require("multi"):init()
GLOBAL, THREAD = require("multi.integration.threading"):init()
multi:newSystemThread("System Thread",function()
while true do
thread.sleep(1)
print("Hello!")
THREAD.sleep(.1)
io.write(" World")
THREAD.kill()
end
end)
multi:lightloop()
--multi:mainloop()
multi:newThread("Coroutine Based Thread",function()
while true do
io.write("Hello")
thread.sleep(.1)
thread.kill()
end
end)
multi:newTLoop(function(loop)
print("!")
loop:Destroy()
os.exit()
end,.3)
multi:mainloop()
--[[
while true do
multi:uManager()
+772 -71
View File
File diff suppressed because it is too large Load Diff
+19
View File
@@ -0,0 +1,19 @@
commands = [[
mkdir luajit && python -m hererocks -j 2.1.0-beta3 -r latest --patch --compat all ./luajit && set "PATH=G:\VSCWorkspace\multi\luajit\bin;%PATH%" && lua -v && luarocks install multi
mkdir lua5.1 && python -m hererocks -l 5.1 -r latest --patch --compat all ./lua5.1 && set "PATH=G:\VSCWorkspace\multi\luajit\bin;%PATH%" && lua -v && luarocks install multi
mkdir lua5.2 && python -m hererocks -l 5.2 -r latest --patch --compat all ./lua5.2 && set "PATH=G:\VSCWorkspace\multi\luajit\bin;%PATH%" && lua -v && luarocks install multi
mkdir lua5.3 && python -m hererocks -l 5.3 -r latest --patch --compat all ./lua5.3 && set "PATH=G:\VSCWorkspace\multi\luajit\bin;%PATH%" && lua -v && luarocks install multi
mkdir lua5.4 && python -m hererocks -l 5.4 -r latest --patch --compat all ./lua5.4 && set "PATH=G:\VSCWorkspace\multi\luajit\bin;%PATH%" && lua -v && luarocks install multi
]]
function string.split (inputstr, sep)
local sep = sep or "\n"
local t={}
for str in string.gmatch(inputstr, "([^"..sep.."]+)") do
table.insert(t, str)
end
return t
end
local run = commands:split()
for i=1,#run do
os.execute(run[i])
end
Executable
+31
View File
@@ -0,0 +1,31 @@
#!/bin/bash
mkdir luajit
hererocks -j 2.1.0-beta3 -r latest --compat all ./luajit
. luajit/bin/activate
echo | lua -v
luarocks install multi
deactivate-lua
mkdir lua5.1
hererocks -l 5.1 -r latest --patch --compat all ./lua5.1
. lua5.1/bin/activate
echo | lua -v
luarocks install multi
deactivate-lua
mkdir lua5.2
hererocks -l 5.2 -r latest --patch --compat all ./lua5.2
. lua5.2/bin/activate
echo | lua -v
luarocks install multi
deactivate-lua
mkdir lua5.3
hererocks -l 5.3 -r latest --patch --compat all ./lua5.3
. lua5.3/bin/activate
echo | lua -v
luarocks install multi
deactivate-lua
mkdir lua5.4
hererocks -l 5.4 -r latest --patch --compat all ./lua5.4
. lua5.4/bin/activate
echo | lua -v
luarocks install multi
deactivate-lua
-95
View File
@@ -1,95 +0,0 @@
--[[
MIT License
Copyright (c) 2020 Ryan Ward
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sub-license, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
]]
local multi, thread = require("multi").init()
os.sleep = love.timer.sleep
multi.drawF = {}
function multi:onDraw(func, i)
i = i or 1
table.insert(self.drawF, i, func)
end
multi.OnKeyPressed = multi:newConnection()
multi.OnKeyReleased = multi:newConnection()
multi.OnMousePressed = multi:newConnection()
multi.OnMouseReleased = multi:newConnection()
multi.OnMouseWheelMoved = multi:newConnection()
multi.OnMouseMoved = multi:newConnection()
multi.OnDraw = multi:newConnection()
multi.OnTextInput = multi:newConnection()
multi.OnUpdate = multi:newConnection()
multi.OnQuit = multi:newConnection()
multi.OnPreLoad(function()
local function Hook(func, conn)
if love[func] ~= nil then
love[func] = Library.convert(love[func])
love[func]:inject(function(...)
conn:Fire(...)
return {...}
end,1)
elseif love[func] == nil then
love[func] = function(...)
conn:Fire(...)
end
end
end
Hook("quit", multi.OnQuit)
Hook("keypressed", multi.OnKeyPressed)
Hook("keyreleased", multi.OnKeyReleased)
Hook("mousepressed", multi.OnMousePressed)
Hook("mousereleased", multi.OnMouseReleased)
Hook("wheelmoved", multi.OnMouseWheelMoved)
Hook("mousemoved", multi.OnMouseMoved)
Hook("draw", multi.OnDraw)
Hook("textinput", multi.OnTextInput)
Hook("update", multi.OnUpdate)
multi.OnDraw(function()
for i = 1, #multi.drawF do
love.graphics.setColor(255, 255, 255, 255)
multi.drawF[i]()
end
end)
end)
function multi:loveloop(light)
local link
link = multi:newThread(function()
local mainloop = love.run()
while true do
thread.yield()
pcall(mainloop)
end
end).OnError(function(...)
print(...)
end)
if light==false then
multi:mainloop()
else
multi:lightloop()
end
end
multi.OnQuit(function()
multi.Stop()
love.event.quit()
end)
return multi
+1138 -1251
View File
File diff suppressed because it is too large Load Diff
@@ -1,7 +1,7 @@
--[[
MIT License
Copyright (c) 2020 Ryan Ward
Copyright (c) 2022 Ryan Ward
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -68,6 +68,9 @@ function multi:newSystemThreadedJobQueue(n)
local doAll = multi:newSystemThreadedQueue()
local ID=1
local jid = 1
function c:isEmpty()
return queueJob:peek()==nil
end
function c:doToAll(func)
for i=1,c.cores do
doAll:push{ID,func}
@@ -100,7 +103,7 @@ function multi:newSystemThreadedJobQueue(n)
link = c.OnJobCompleted(function(jid,...)
if id==jid then
rets = {...}
link:Remove()
link:Destroy()
end
end)
return thread.hold(function()
@@ -110,7 +113,7 @@ function multi:newSystemThreadedJobQueue(n)
end)
end,holup),name
end
multi:newThread("JobQueueManager",function()
thread:newThread("JobQueueManager",function()
while true do
local job = thread.hold(function()
return queueReturn:pop()
@@ -126,7 +129,7 @@ function multi:newSystemThreadedJobQueue(n)
local clock = os.clock
local ref = 0
setmetatable(_G,{__index = funcs})
multi:newThread("JobHandler",function()
thread:newThread("JobHandler",function()
while true do
local dat = thread.hold(function()
return queueJob:pop()
@@ -138,7 +141,7 @@ function multi:newSystemThreadedJobQueue(n)
queueReturn:push{jid, funcs[name](unpack(args)),queue}
end
end)
multi:newThread("DoAllHandler",function()
thread:newThread("DoAllHandler",function()
while true do
local dat = thread.hold(function()
return doAll:peek()
@@ -153,7 +156,7 @@ function multi:newSystemThreadedJobQueue(n)
end
end
end)
multi:newThread("IdleHandler",function()
thread:newThread("IdleHandler",function()
while true do
thread.hold(function()
return clock()-idle>3
+71 -56
View File
@@ -1,7 +1,7 @@
--[[
MIT License
Copyright (c) 2020 Ryan Ward
Copyright (c) 2022 Ryan Ward
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -21,9 +21,10 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
]]
package.path = "?/init.lua;?.lua;" .. package.path
multi, thread = require("multi").init() -- get it all and have it on all lanes
if multi.integration then -- This allows us to call the lanes manager from supporting modules without a hassel
multi, thread = require("multi"):init() -- get it all and have it on all lanes
if multi.integration then -- This allows us to call the lanes manager from supporting modules without a hassle
return {
init = function()
return multi.integration.GLOBAL, multi.integration.THREAD
@@ -34,117 +35,131 @@ end
lanes = require("lanes").configure()
multi.SystemThreads = {}
multi.isMainThread = true
function multi:canSystemThread()
return true
end
function multi:getPlatform()
return "lanes"
end
-- Step 2 set up the Linda objects
local __GlobalLinda = lanes.linda() -- handles global stuff
local __SleepingLinda = lanes.linda() -- handles sleeping stuff
local __ConsoleLinda = lanes.linda() -- handles console stuff
multi:newLoop(function()
local _,data = __ConsoleLinda:receive(0, "Q")
if data then
print(unpack(data))
end
end)
local GLOBAL,THREAD = require("multi.integration.lanesManager.threads").init(__GlobalLinda,__SleepingLinda)
local threads = {}
local __StatusLinda = lanes.linda() -- handles pushstatus for stfunctions
local GLOBAL,THREAD = require("multi.integration.lanesManager.threads").init(__GlobalLinda, __SleepingLinda, __StatusLinda)
local count = 1
local started = false
local livingThreads = {}
function THREAD:newFunction(func,holup)
return function(...)
local t = multi:newSystemThread("SystemThreadedFunction",function(...)
return func(...)
end,...)
return thread:newFunction(function()
thread.hold(function() return t.thread end)
return thread.hold(function()
return t.thread:join(.001)
end)
end,holup)()
end
function THREAD:newFunction(func,holdme)
return thread:newFunctionBase(function(...)
return multi:newSystemThread("TempSystemThread",func,...)
end,holdme)()
end
function multi:newSystemThread(name, func, ...)
multi.InitSystemThreadErrorHandler()
rand = math.random(1, 10000000)
local c = {}
local __self = c
local rand = math.random(1, 10000000)
local return_linda = lanes.linda()
c = {}
c.name = name
c.Name = name
c.Id = count
c.loadString = {"base","package","os","io","math","table","string","coroutine"}
livingThreads[count] = {true, name}
c.returns = return_linda
c.Type = "sthread"
c.creationTime = os.clock()
c.alive = true
c.priority = THREAD.Priority_Normal
local args = {...}
multi:newThread(function()
c.thread = lanes.gen(table.concat(c.loadString,","),
c.thread = lanes.gen("*",
{
globals={ -- Set up some globals
THREAD_NAME=name,
THREAD_ID=count,
THREAD_NAME = name,
THREAD_ID = count,
THREAD = THREAD,
GLOBAL = GLOBAL,
_Console = __ConsoleLinda
},
priority=c.priority
},func)(unpack(args))
thread.kill()
end)
},function(...)
local has_error = true
return_linda:set("returns",{func(...)})
has_error = false
end)(...)
count = count + 1
function c:getName()
return c.Name
end
function c:kill()
self.thread:cancel()
multi.print("Thread: '" .. self.name .. "' has been stopped!")
self.alive = false
end
table.insert(multi.SystemThreads, c)
c.OnDeath = multi:newConnection()
c.OnError = multi:newConnection()
GLOBAL["__THREADS__"] = livingThreads
return c
end
multi.OnSystemThreadDied = multi:newConnection()
THREAD.newSystemThread = multi.newSystemThread
function multi.InitSystemThreadErrorHandler()
if started == true then
return
end
started = true
multi:newThread(
"ThreadErrorHandler",
function()
thread:newThread("SystemThreadScheduler",function()
local threads = multi.SystemThreads
local _,data,status,push,temp
while true do
thread.sleep(.5) -- switching states often takes a huge hit on performance. half a second to tell me there is an error is good enough.
thread.yield()
_,data = __ConsoleLinda:receive(0, "Q")
for i = #threads, 1, -1 do
local v, err, t = threads[i].thread:join(.001)
if err then
if err:find("Thread was killed!") then
print(err)
livingThreads[threads[i].Id] = {false, threads[i].Name}
threads[i].alive = false
multi.OnSystemThreadDied:Fire(threads[i].Id)
temp = threads[i]
status = temp.thread.status
push = __StatusLinda:get(temp.Id)
if push then
temp.statusconnector:Fire(unpack(({__StatusLinda:receive(nil, temp.Id)})[2]))
end
if status == "done" or temp.returns:get("returns") then
livingThreads[temp.Id] = {false, temp.Name}
temp.alive = false
temp.OnDeath:Fire(unpack(({temp.returns:receive(0, "returns")})[2]))
GLOBAL["__THREADS__"] = livingThreads
table.remove(threads, i)
elseif err:find("stack traceback") then
print(err)
threads[i].OnError:Fire(threads[i], err, "Error in systemThread: '" .. threads[i].name .. "' <" .. err .. ">")
threads[i].alive = false
livingThreads[threads[i].Id] = {false, threads[i].Name}
multi.OnSystemThreadDied:Fire(threads[i].Id)
elseif status == "running" then
--
elseif status == "waiting" then
--
elseif status == "error" then
livingThreads[temp.Id] = {false, temp.Name}
temp.alive = false
temp.OnError:Fire(temp,nil,unpack(temp.returns:receive(0,"returns") or {"Thread Killed!"}))
GLOBAL["__THREADS__"] = livingThreads
table.remove(threads, i)
elseif status == "cancelled" then
livingThreads[temp.Id] = {false, temp.Name}
temp.alive = false
temp.OnError:Fire(temp,nil,"thread_cancelled")
GLOBAL["__THREADS__"] = livingThreads
table.remove(threads, i)
elseif status == "killed" then
livingThreads[temp.Id] = {false, temp.Name}
temp.alive = false
temp.OnError:Fire(temp,nil,"thread_killed")
GLOBAL["__THREADS__"] = livingThreads
table.remove(threads, i)
end
end
end
end
end
)
end)
end
multi.print("Integrated Lanes!")
multi.integration = {} -- for module creators
multi.integration.GLOBAL = GLOBAL
+28 -6
View File
@@ -1,7 +1,7 @@
--[[
MIT License
Copyright (c) 2020 Ryan Ward
Copyright (c) 2022 Ryan Ward
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -28,7 +28,8 @@ local function getOS()
return "unix"
end
end
local function INIT(__GlobalLinda,__SleepingLinda)
local function INIT(__GlobalLinda, __SleepingLinda, __StatusLinda)
local THREAD = {}
THREAD.Priority_Core = 3
THREAD.Priority_High = 2
@@ -37,12 +38,15 @@ local function INIT(__GlobalLinda,__SleepingLinda)
THREAD.Priority_Below_Normal = -1
THREAD.Priority_Low = -2
THREAD.Priority_Idle = -3
function THREAD.set(name, val)
__GlobalLinda:set(name, val)
end
function THREAD.get(name)
__GlobalLinda:get(name)
return __GlobalLinda:get(name)
end
function THREAD.waitFor(name)
local function wait()
math.randomseed(os.time())
@@ -53,14 +57,17 @@ local function INIT(__GlobalLinda,__SleepingLinda)
until __GlobalLinda:get(name)
return __GlobalLinda:get(name)
end
if getOS() == "windows" then
THREAD.__CORES = tonumber(os.getenv("NUMBER_OF_PROCESSORS"))
else
THREAD.__CORES = tonumber(io.popen("nproc --all"):read("*n"))
end
function THREAD.getCores()
return THREAD.__CORES
end
function THREAD.getConsole()
local c = {}
c.queue = _Console
@@ -73,28 +80,41 @@ local function INIT(__GlobalLinda,__SleepingLinda)
end
return c
end
function THREAD.getThreads()
return GLOBAL.__THREADS__
end
if os.getOS() == "windows" then
THREAD.__CORES = tonumber(os.getenv("NUMBER_OF_PROCESSORS"))
else
THREAD.__CORES = tonumber(io.popen("nproc --all"):read("*n"))
end
function THREAD.kill() -- trigger the lane destruction
error("Thread was killed!")
error("Thread was killed!\1")
end
function THREAD.getName()
return THREAD_NAME
end
function THREAD.getID()
return THREAD_ID
end
function THREAD.pushStatus(...)
local args = {...}
__StatusLinda:send(nil,THREAD_ID, args)
end
_G.THREAD_ID = 0
function THREAD.sleep(n)
math.randomseed(os.time())
__SleepingLinda:receive(n, "__non_existing_variable")
end
function THREAD.hold(n)
local function wait()
math.randomseed(os.time())
@@ -104,6 +124,7 @@ local function INIT(__GlobalLinda,__SleepingLinda)
wait()
until n()
end
local GLOBAL = {}
setmetatable(GLOBAL, {
__index = function(t, k)
@@ -115,6 +136,7 @@ local function INIT(__GlobalLinda,__SleepingLinda)
})
return GLOBAL, THREAD
end
return {init = function(g,s)
return INIT(g,s)
return {init = function(g,s,st)
return INIT(g,s,st)
end}
+11 -6
View File
@@ -1,7 +1,7 @@
--[[
MIT License
Copyright (c) 2020 Ryan Ward
Copyright (c) 2022 Ryan Ward
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -21,6 +21,8 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
]]
-- TODO make compatible with lovr
local multi, thread = require("multi").init()
GLOBAL = multi.integration.GLOBAL
THREAD = multi.integration.THREAD
@@ -99,6 +101,9 @@ function multi:newSystemThreadedJobQueue(n)
self.id = self.id + 1
self.queue:push{name,self.id,...}
return self.id
end
function c:isEmpty()
return queueJob:peek()==nil
end
local nFunc = 0
function c:newFunction(name,func,holup) -- This registers with the queue
@@ -116,7 +121,7 @@ function multi:newSystemThreadedJobQueue(n)
link = c.OnJobCompleted(function(jid,...)
if id==jid then
rets = {...}
link:Remove()
link:Destroy()
end
end)
return thread.hold(function()
@@ -126,7 +131,7 @@ function multi:newSystemThreadedJobQueue(n)
end)
end,holup),name
end
multi:newThread("jobManager",function()
thread:newThread("jobManager",function()
while true do
thread.yield()
local dat = c.queueReturn:pop()
@@ -150,7 +155,7 @@ function multi:newSystemThreadedJobQueue(n)
local queueAll = love.thread.getChannel("__JobQueue_"..jqc.."_queueAll")
local registry = {}
setmetatable(_G,{__index = funcs})
multi:newThread("startUp",function()
thread:newThread("startUp",function()
while true do
thread.yield()
local all = queueAll:peek()
@@ -160,7 +165,7 @@ function multi:newSystemThreadedJobQueue(n)
end
end
end)
multi:newThread("runner",function()
thread:newThread("runner",function()
thread.sleep(.1)
while true do
thread.yield()
@@ -182,7 +187,7 @@ function multi:newSystemThreadedJobQueue(n)
end):OnError(function(...)
error(...)
end)
multi:newThread("Idler",function()
thread:newThread("Idler",function()
while true do
thread.yield()
if clock()-lastProc> 2 then
+50 -21
View File
@@ -1,7 +1,7 @@
--[[
MIT License
Copyright (c) 2020 Ryan Ward
Copyright (c) 2022 Ryan Ward
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -37,7 +37,7 @@ GLOBAL = THREAD.getGlobal()
multi, thread = require("multi").init()
stab["returns"] = {THREAD.loadDump(__FUNC__)(unpack(__IMPORTS))}
]]
local multi, thread = require("multi.compat.love2d"):init()
local multi, thread = require("multi"):init()
local THREAD = {}
__THREADID__ = 0
__THREADNAME__ = "MainThread"
@@ -48,21 +48,7 @@ local GLOBAL = THREAD.getGlobal()
local THREAD_ID = 1
local OBJECT_ID = 0
local stf = 0
function THREAD:newFunction(func,holup)
stf = stf + 1
return function(...)
local t = multi:newSystemThread("STF"..stf,func,...)
return thread:newFunction(function()
return thread.hold(function()
if t.stab["returns"] then
local dat = t.stab.returns
t.stab.returns = nil
return unpack(dat)
end
end)
end,holup)()
end
end
function multi:newSystemThread(name,func,...)
local c = {}
c.name = name
@@ -70,17 +56,60 @@ function multi:newSystemThread(name,func,...)
c.thread=love.thread.newThread(ThreadFileData)
c.thread:start(THREAD.dump(func),c.ID,c.name,...)
c.stab = THREAD.createStaticTable(name)
GLOBAL["__THREAD_"..c.ID] = {ID=c.ID,Name=c.name,Thread=c.thread}
c.OnDeath = multi:newConnection()
c.OnError = multi:newConnection()
GLOBAL["__THREAD_"..c.ID] = {ID=c.ID, Name=c.name, Thread=c.thread}
GLOBAL["__THREAD_COUNT"] = THREAD_ID
THREAD_ID=THREAD_ID+1
THREAD_ID=THREAD_ID + 1
function c:getName()
return c.name
end
thread:newThread(function()
if name:find("TempSystemThread") then
local status_channel = love.thread.getChannel("__"..c.ID.."__MULTI__STATUS_CHANNEL__")
thread.hold(function()
-- While the thread is running we might as well do something in the loop
local status = status_channel
if status:peek()~=nil then
c.statusconnector:Fire(unpack(status:pop()))
end
return not c.thread:isRunning()
end)
else
thread.hold(function()
return not c.thread:isRunning()
end)
end
-- If the thread is not running let's handle that.
local thread_err = c.thread:getError()
if thread_err == "Thread Killed!\1" then
c.OnDeath:Fire("Thread Killed!")
elseif thread_err then
c.OnError:Fire(c,thread_err)
elseif c.stab.returns then
c.OnDeath:Fire(unpack(c.stab.returns))
c.stab.returns = nil
end
end)
return c
end
function love.threaderror(thread, errorstr)
print("Thread error!\n"..errorstr)
function THREAD:newFunction(func)
return thread:newFunctionBase(function(...)
return multi:newSystemThread("TempSystemThread"..THREAD_ID,func,...)
end)()
end
THREAD.newSystemThread = multi.newSystemThread
function love.threaderror(thread, errorstr)
mulit.print("Thread error!\n"..errorstr)
end
multi.integration.GLOBAL = GLOBAL
multi.integration.THREAD = THREAD
require("multi.integration.loveManager.extensions")
mulit.print("Integrated Love Threading!")
return {init=function()
return GLOBAL,THREAD
end}
+32 -3
View File
@@ -1,7 +1,7 @@
--[[
MIT License
Copyright (c) 2020 Ryan Ward
Copyright (c) 2022 Ryan Ward
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -24,15 +24,19 @@ SOFTWARE.
require("love.timer")
require("love.system")
require("love.data")
require("love.thread")
local socket = require("socket")
local multi, thread = require("multi").init()
local threads = {}
function threads.loadDump(d)
return loadstring(d:getString())
end
function threads.dump(func)
return love.data.newByteData(string.dump(func))
end
local fRef = {"func",nil}
local function manage(channel, value)
channel:clear()
@@ -44,6 +48,7 @@ local function manage(channel, value)
channel:push(value)
end
end
local function RandomVariable(length)
local res = {}
math.randomseed(socket.gettime()*10000)
@@ -52,12 +57,14 @@ local function RandomVariable(length)
end
return table.concat(res)
end
local GNAME = "__GLOBAL_"
local proxy = {}
function threads.set(name,val)
if not proxy[name] then proxy[name] = love.thread.getChannel(GNAME..name) end
proxy[name]:performAtomic(manage, val)
end
function threads.get(name)
if not proxy[name] then proxy[name] = love.thread.getChannel(GNAME..name) end
local dat = proxy[name]:peek()
@@ -67,6 +74,7 @@ function threads.get(name)
return dat
end
end
function threads.waitFor(name)
if thread.isThread() then
return thread.hold(function()
@@ -82,18 +90,28 @@ function threads.waitFor(name)
end
return dat
end
function threads.package(name,val)
local init = val.init
val.init=threads.dump(val.init)
GLOBAL[name]=val
val.init=init
end
function threads.getCores()
return love.system.getProcessorCount()
end
function threads.kill()
error("Thread Killed!")
error("Thread Killed!\1")
end
function THREAD.pushStatus(...)
local status_channel = love.thread.getChannel("__"..__THREADID__.."__MULTI__STATUS_CHANNEL__")
local args = {...}
status_channel:push(__THREADID__, args)
end
function threads.getThreads()
local t = {}
for i=1,GLOBAL["__THREAD_COUNT"] do
@@ -101,18 +119,23 @@ function threads.getThreads()
end
return t
end
function threads.getThread(n)
return GLOBAL["__THREAD_"..n]
end
function threads.getName()
return __THREADNAME__
end
function threads.getID()
return __THREADID__
end
function threads.sleep(n)
love.timer.sleep(n)
end
function threads.getGlobal()
return setmetatable({},
{
@@ -125,6 +148,7 @@ function threads.getGlobal()
}
)
end
function threads.createTable(n)
local _proxy = {}
local function set(name,val)
@@ -151,6 +175,7 @@ function threads.createTable(n)
}
)
end
function threads.getConsole()
local c = {}
c.queue = love.thread.getChannel("__CONSOLE__")
@@ -163,11 +188,12 @@ function threads.getConsole()
end
return c
end
if not ISTHREAD then
local clock = os.clock
local lastproc = clock()
local queue = love.thread.getChannel("__CONSOLE__")
multi:newThread("consoleManager",function()
thread:newThread("consoleManager",function()
while true do
thread.yield()
dat = queue:pop()
@@ -181,6 +207,7 @@ if not ISTHREAD then
end
end)
end
function threads.createStaticTable(n)
local __proxy = {}
local function set(name,val)
@@ -212,10 +239,12 @@ function threads.createStaticTable(n)
}
)
end
function threads.hold(n)
local dat
while not(dat) do
dat = n()
end
end
return threads
@@ -0,0 +1,203 @@
--[[
MIT License
Copyright (c) 2022 Ryan Ward
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sub-license, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
]]
local multi, thread = require("multi").init()
GLOBAL = multi.integration.GLOBAL
THREAD = multi.integration.THREAD
function multi:newSystemThreadedQueue(name)
local c = {}
c.Name = name
local fRef = {"func",nil}
function c:init()
local q = {}
q.chan = lovr.thread.getChannel(self.Name)
function q:push(dat)
if type(dat) == "function" then
fRef[2] = THREAD.dump(dat)
self.chan:push(fRef)
return
else
self.chan:push(dat)
end
end
function q:pop()
local dat = self.chan:pop()
if type(dat)=="table" and dat[1]=="func" then
return THREAD.loadDump(dat[2])
else
return dat
end
end
function q:peek()
local dat = self.chan:peek()
if type(dat)=="table" and dat[1]=="func" then
return THREAD.loadDump(dat[2])
else
return dat
end
end
return q
end
THREAD.package(name,c)
return c
end
function multi:newSystemThreadedTable(name)
local c = {}
c.name = name
function c:init()
return THREAD.createTable(self.name)
end
THREAD.package(name,c)
return c
end
local jqc = 1
function multi:newSystemThreadedJobQueue(n)
local c = {}
c.cores = n or THREAD.getCores()
c.registerQueue = {}
c.funcs = THREAD.createStaticTable("__JobQueue_"..jqc.."_table")
c.queue = lovr.thread.getChannel("__JobQueue_"..jqc.."_queue")
c.queueReturn = lovr.thread.getChannel("__JobQueue_"..jqc.."_queueReturn")
c.queueAll = lovr.thread.getChannel("__JobQueue_"..jqc.."_queueAll")
c.id = 0
c.OnJobCompleted = multi:newConnection()
local allfunc = 0
function c:doToAll(func)
local f = THREAD.dump(func)
for i = 1, self.cores do
self.queueAll:push({allfunc,f})
end
allfunc = allfunc + 1
end
function c:registerFunction(name,func)
if self.funcs[name] then
error("A function by the name "..name.." has already been registered!")
end
self.funcs[name] = func
end
function c:pushJob(name,...)
self.id = self.id + 1
self.queue:push{name,self.id,...}
return self.id
end
function c:isEmpty()
return queueJob:peek()==nil
end
local nFunc = 0
function c:newFunction(name,func,holup) -- This registers with the queue
if type(name)=="function" then
holup = func
func = name
name = "JQ_Function_"..nFunc
end
nFunc = nFunc + 1
c:registerFunction(name,func)
return thread:newFunction(function(...)
local id = c:pushJob(name,...)
local link
local rets
link = c.OnJobCompleted(function(jid,...)
if id==jid then
rets = {...}
link:Destroy()
end
end)
return thread.hold(function()
if rets then
return unpack(rets) or multi.NIL
end
end)
end,holup),name
end
thread:newThread("jobManager",function()
while true do
thread.yield()
local dat = c.queueReturn:pop()
if dat then
c.OnJobCompleted:Fire(unpack(dat))
end
end
end)
for i=1,c.cores do
multi:newSystemThread("JobQueue_"..jqc.."_worker_"..i,function(jqc)
local multi, thread = require("multi"):init()
require("lovr.timer")
local function atomic(channel)
return channel:pop()
end
local clock = os.clock
local funcs = THREAD.createStaticTable("__JobQueue_"..jqc.."_table")
local queue = lovr.thread.getChannel("__JobQueue_"..jqc.."_queue")
local queueReturn = lovr.thread.getChannel("__JobQueue_"..jqc.."_queueReturn")
local lastProc = clock()
local queueAll = lovr.thread.getChannel("__JobQueue_"..jqc.."_queueAll")
local registry = {}
setmetatable(_G,{__index = funcs})
thread:newThread("startUp",function()
while true do
thread.yield()
local all = queueAll:peek()
if all and not registry[all[1]] then
lastProc = os.clock()
THREAD.loadDump(queueAll:pop()[2])()
end
end
end)
thread:newThread("runner",function()
thread.sleep(.1)
while true do
thread.yield()
local all = queueAll:peek()
if all and not registry[all[1]] then
lastProc = os.clock()
THREAD.loadDump(queueAll:pop()[2])()
end
local dat = queue:performAtomic(atomic)
if dat then
lastProc = os.clock()
local name = table.remove(dat,1)
local id = table.remove(dat,1)
local tab = {funcs[name](unpack(dat))}
table.insert(tab,1,id)
queueReturn:push(tab)
end
end
end):OnError(function(...)
error(...)
end)
thread:newThread("Idler",function()
while true do
thread.yield()
if clock()-lastProc> 2 then
THREAD.sleep(.05)
else
THREAD.sleep(.001)
end
end
end)
multi:mainloop()
end,jqc)
end
jqc = jqc + 1
return c
end
+89
View File
@@ -0,0 +1,89 @@
--[[
MIT License
Copyright (c) 2022 Ryan Ward
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sub-license, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
]]
-- TODO make compatible with lovr
if ISTHREAD then
error("You cannot require the lovrManager from within a thread!")
end
local ThreadFileData = [[
ISTHREAD = true
THREAD = require("multi.integration.lovrManager.threads") -- order is important!
sThread = THREAD
__IMPORTS = {...}
__FUNC__=table.remove(__IMPORTS,1)
__THREADID__=table.remove(__IMPORTS,1)
__THREADNAME__=table.remove(__IMPORTS,1)
stab = THREAD.createStaticTable(__THREADNAME__)
GLOBAL = THREAD.getGlobal()
multi, thread = require("multi").init()
stab["returns"] = {THREAD.loadDump(__FUNC__)(unpack(__IMPORTS))}
]]
local multi, thread = require("multi.compat.lovr2d"):init()
local THREAD = {}
__THREADID__ = 0
__THREADNAME__ = "MainThread"
multi.integration={}
multi.integration.lovr2d={}
local THREAD = require("multi.integration.lovrManager.threads")
local GLOBAL = THREAD.getGlobal()
local THREAD_ID = 1
local OBJECT_ID = 0
local stf = 0
function THREAD:newFunction(func,holup)
stf = stf + 1
return function(...)
local t = multi:newSystemThread("STF"..stf,func,...)
return thread:newFunction(function()
return thread.hold(function()
if t.stab["returns"] then
local dat = t.stab.returns
t.stab.returns = nil
return unpack(dat)
end
end)
end,holup)()
end
end
function multi:newSystemThread(name,func,...)
local c = {}
c.name = name
c.ID=THREAD_ID
c.thread=lovr.thread.newThread(ThreadFileData)
c.thread:start(THREAD.dump(func),c.ID,c.name,...)
c.stab = THREAD.createStaticTable(name)
GLOBAL["__THREAD_"..c.ID] = {ID=c.ID,Name=c.name,Thread=c.thread}
GLOBAL["__THREAD_COUNT"] = THREAD_ID
THREAD_ID=THREAD_ID+1
return c
end
THREAD.newSystemThread = multi.newSystemThread
function lovr.threaderror(thread, errorstr)
print("Thread error!\n"..errorstr)
end
multi.integration.GLOBAL = GLOBAL
multi.integration.THREAD = THREAD
require("multi.integration.lovrManager.extensions")
print("Integrated lovr Threading!")
return {init=function()
return GLOBAL,THREAD
end}
+222
View File
@@ -0,0 +1,222 @@
--[[
MIT License
Copyright (c) 2022 Ryan Ward
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sub-license, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
]]
-- TODO make compatible with lovr
require("lovr.timer")
require("lovr.system")
require("lovr.data")
local socket = require("socket")
local multi, thread = require("multi").init()
local threads = {}
function threads.loadDump(d)
return loadstring(d:getString())
end
function threads.dump(func)
return lovr.data.newByteData(string.dump(func))
end
local fRef = {"func",nil}
local function manage(channel, value)
channel:clear()
if type(value) == "function" then
fRef[2] = THREAD.dump(value)
channel:push(fRef)
return
else
channel:push(value)
end
end
local function RandomVariable(length)
local res = {}
math.randomseed(socket.gettime()*10000)
for i = 1, length do
res[#res+1] = string.char(math.random(97, 122))
end
return table.concat(res)
end
local GNAME = "__GLOBAL_"
local proxy = {}
function threads.set(name,val)
if not proxy[name] then proxy[name] = lovr.thread.getChannel(GNAME..name) end
proxy[name]:performAtomic(manage, val)
end
function threads.get(name)
if not proxy[name] then proxy[name] = lovr.thread.getChannel(GNAME..name) end
local dat = proxy[name]:peek()
if type(dat)=="table" and dat[1]=="func" then
return THREAD.loadDump(dat[2])
else
return dat
end
end
function threads.waitFor(name)
if thread.isThread() then
return thread.hold(function()
return threads.get(name)
end)
end
while threads.get(name)==nil do
lovr.timer.sleep(.001)
end
local dat = threads.get(name)
if type(dat) == "table" and dat.init then
dat.init = threads.loadDump(dat.init)
end
return dat
end
function threads.package(name,val)
local init = val.init
val.init=threads.dump(val.init)
GLOBAL[name]=val
val.init=init
end
function threads.getCores()
return lovr.system.getProcessorCount()
end
function threads.kill()
error("Thread Killed!")
end
function threads.getThreads()
local t = {}
for i=1,GLOBAL["__THREAD_COUNT"] do
t[#t+1]=GLOBAL["__THREAD_"..i]
end
return t
end
function threads.getThread(n)
return GLOBAL["__THREAD_"..n]
end
function threads.getName()
return __THREADNAME__
end
function threads.getID()
return __THREADID__
end
function threads.sleep(n)
lovr.timer.sleep(n)
end
function threads.getGlobal()
return setmetatable({},
{
__index = function(t, k)
return THREAD.get(k)
end,
__newindex = function(t, k, v)
THREAD.set(k,v)
end
}
)
end
function threads.createTable(n)
local _proxy = {}
local function set(name,val)
if not _proxy[name] then _proxy[name] = lovr.thread.getChannel(n..name) end
_proxy[name]:performAtomic(manage, val)
end
local function get(name)
if not _proxy[name] then _proxy[name] = lovr.thread.getChannel(n..name) end
local dat = _proxy[name]:peek()
if type(dat)=="table" and dat[1]=="func" then
return THREAD.loadDump(dat[2])
else
return dat
end
end
return setmetatable({},
{
__index = function(t, k)
return get(k)
end,
__newindex = function(t, k, v)
set(k,v)
end
}
)
end
function threads.getConsole()
local c = {}
c.queue = lovr.thread.getChannel("__CONSOLE__")
function c.print(...)
c.queue:push{...}
end
function c.error(err)
c.queue:push{"ERROR in <"..__THREADNAME__..">: "..err,__THREADID__}
error(err)
end
return c
end
if not ISTHREAD then
local clock = os.clock
local lastproc = clock()
local queue = lovr.thread.getChannel("__CONSOLE__")
thread:newThread("consoleManager",function()
while true do
thread.yield()
dat = queue:pop()
if dat then
lastproc = clock()
print(unpack(dat))
end
if clock()-lastproc>2 then
thread.sleep(.1)
end
end
end)
end
function threads.createStaticTable(n)
local __proxy = {}
local function set(name,val)
if __proxy[name] then return end
local chan = lovr.thread.getChannel(n..name)
if chan:getCount()>0 then return end
chan:performAtomic(manage, val)
__proxy[name] = val
end
local function get(name)
if __proxy[name] then return __proxy[name] end
local dat = lovr.thread.getChannel(n..name):peek()
if type(dat)=="table" and dat[1]=="func" then
__proxy[name] = THREAD.loadDump(dat[2])
return __proxy[name]
else
__proxy[name] = dat
return __proxy[name]
end
end
return setmetatable({},
{
__index = function(t, k)
return get(k)
end,
__newindex = function(t, k, v)
set(k,v)
end
}
)
end
function threads.hold(n)
local dat
while not(dat) do
dat = n()
end
end
return threads
+2 -1
View File
@@ -1,7 +1,7 @@
--[[
MIT License
Copyright (c) 2020 Ryan Ward
Copyright (c) 2022 Ryan Ward
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -116,6 +116,7 @@ local function _INIT(luvitThread, timer)
luvitThread.start(entry, package.path, name, c.func, ...)
return c
end
THREAD.newSystemThread = multi.newSystemThread
multi.print("Integrated Luvit!")
multi.integration = {} -- for module creators
multi.integration.GLOBAL = GLOBAL
+1 -1
View File
@@ -1,7 +1,7 @@
--[[
MIT License
Copyright (c) 2020 Ryan Ward
Copyright (c) 2022 Ryan Ward
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -1,7 +1,7 @@
--[[
MIT License
Copyright (c) 2020 Ryan Ward
Copyright (c) 2022 Ryan Ward
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -1,7 +1,7 @@
--[[
MIT License
Copyright (c) 2020 Ryan Ward
Copyright (c) 2022 Ryan Ward
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
+1 -1
View File
@@ -1,7 +1,7 @@
--[[
MIT License
Copyright (c) 2020 Ryan Ward
Copyright (c) 2022 Ryan Ward
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -1,7 +1,7 @@
--[[
MIT License
Copyright (c) 2020 Ryan Ward
Copyright (c) 2022 Ryan Ward
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
+1 -1
View File
@@ -1,7 +1,7 @@
--[[
MIT License
Copyright (c) 2020 Ryan Ward
Copyright (c) 2022 Ryan Ward
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -1,7 +1,7 @@
--[[
MIT License
Copyright (c) 2020 Ryan Ward
Copyright (c) 2022 Ryan Ward
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -72,7 +72,7 @@ function master:newNetworkThread(nodeName,func,...)
local ret
local nID = netID
local conn = multi:newConnection()
multi:newThread(function()
thread:newthread(function()
dat:addBlock{
args = args,
func = func,
@@ -143,7 +143,7 @@ function multi:newMasterNode(cd)
else
c:getNodesFromBroadcast()
end
multi:newThread("CMDQueueProcessor",function()
thread:newthread("CMDQueueProcessor",function()
while true do
thread.skip(128)
local data = table.remove(c._queue,1)
+1 -1
View File
@@ -1,7 +1,7 @@
--[[
MIT License
Copyright (c) 2020 Ryan Ward
Copyright (c) 2022 Ryan Ward
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -1,7 +1,7 @@
--[[
MIT License
Copyright (c) 2020 Ryan Ward
Copyright (c) 2022 Ryan Ward
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -1,7 +1,7 @@
--[[
MIT License
Copyright (c) 2020 Ryan Ward
Copyright (c) 2022 Ryan Ward
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
+1 -1
View File
@@ -1,7 +1,7 @@
--[[
MIT License
Copyright (c) 2020 Ryan Ward
Copyright (c) 2022 Ryan Ward
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
+1 -1
View File
@@ -1,7 +1,7 @@
--[[
MIT License
Copyright (c) 2020 Ryan Ward
Copyright (c) 2022 Ryan Ward
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -0,0 +1,141 @@
--[[
MIT License
Copyright (c) 2022 Ryan Ward
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sub-license, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
]]
local multi, thread = require("multi"):init()
local GLOBAL, THREAD = multi.integration.GLOBAL,multi.integration.THREAD
local function stripUpValues(func)
local dmp = string.dump(func)
if setfenv then
return loadstring(dmp,"IsolatedThread_PesudoThreading")
else
return load(dmp,"IsolatedThread_PesudoThreading","bt")
end
end
function multi:newSystemThreadedQueue(name)
local c = {}
function c:push(v)
table.insert(self,v)
end
function c:pop()
return table.remove(self,1)
end
function c:peek()
return self[1]
end
function c:init()
return self
end
GLOBAL[name or "_"] = c
return c
end
function multi:newSystemThreadedTable(name)
local c = {}
function c:init()
return self
end
GLOBAL[name or "_"] = c
return c
end
local setfenv = setfenv
if not setfenv then
if not debug then
multi.print("Unable to implement setfenv in lua 5.2+ the debug module is not available!")
else
setfenv = function(f, env)
return load(string.dump(f), nil, nil, env)
end
end
end
function multi:newSystemThreadedJobQueue(n)
local c = {}
c.cores = n or THREAD.getCores()*2
c.OnJobCompleted = multi:newConnection()
local jobs = {}
local ID=1
local jid = 1
local env = {}
setmetatable(env,{
__index = _G
})
local funcs = {}
function c:doToAll(func)
setfenv(func,env)()
return self
end
function c:registerFunction(name,func)
funcs[name] = setfenv(func,env)
return self
end
function c:pushJob(name,...)
table.insert(jobs,{name,jid,{...}})
jid = jid + 1
return jid-1
end
function c:isEmpty()
print(#jobs)
return #jobs == 0
end
local nFunc = 0
function c:newFunction(name,func,holup) -- This registers with the queue
local func = stripUpValues(func)
if type(name)=="function" then
holup = func
func = name
name = "JQ_Function_"..nFunc
end
nFunc = nFunc + 1
c:registerFunction(name,func)
return thread:newFunction(function(...)
local id = c:pushJob(name,...)
local link
local rets
link = c.OnJobCompleted(function(jid,...)
if id==jid then
rets = {...}
link:Destroy()
end
end)
return thread.hold(function()
if rets then
return unpack(rets) or multi.NIL
end
end)
end,holup),name
end
for i=1,c.cores do
thread:newthread("PesudoThreadedJobQueue_"..i,function()
while true do
thread.yield()
if #jobs>0 then
local j = table.remove(jobs,1)
c.OnJobCompleted:Fire(j[2],funcs[j[1]](unpack(j[3])))
else
thread.sleep(.05)
end
end
end)
end
return c
end
+101
View File
@@ -0,0 +1,101 @@
--[[
MIT License
Copyright (c) 2020 Ryan Ward
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sub-license, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
]]
package.path = "?/init.lua;?.lua;" .. package.path
local multi, thread = require("multi"):init()
if multi.integration then
return {
init = function()
return multi.integration.GLOBAL, multi.integration.THREAD
end
}
end
local GLOBAL, THREAD = require("multi.integration.pesudoManager.threads").init(thread)
function multi:canSystemThread() -- We are emulating system threading
return true
end
function multi:getPlatform()
return "pesudo"
end
local function split(str)
local tab = {}
for word in string.gmatch(str, '([^,]+)') do
table.insert(tab,word)
end
return tab
end
local tab = [[_VERSION,io,os,require,load,debug,assert,collectgarbage,error,getfenv,getmetatable,ipairs,loadstring,module,next,pairs,pcall,print,rawequal,rawget,rawset,select,setfenv,setmetatable,tonumber,tostring,type,unpack,xpcall,math,coroutine,string,table]]
tab = split(tab)
local id = 0
function multi:newSystemThread(name,func,...)
GLOBAL["$THREAD_NAME"] = name
GLOBAL["$__THREADNAME__"] = name
GLOBAL["$THREAD_ID"] = id
GLOBAL["$thread"] = thread
local env = {
GLOBAL = GLOBAL,
THREAD = THREAD,
THREAD_NAME = name,
__THREADNAME__ = name,
THREAD_ID = id,
thread = thread
}
for i = 1,#tab do
env[tab[i]] = _G[tab[i]]
end
local th = thread:newISOThread(name,func,env,...)
id = id + 1
return th
end
THREAD.newSystemThread = multi.newSystemThread
-- System threads as implemented here cannot share memory, but use a message passing system.
-- An isolated thread allows us to mimic that behavior so if access data from the "main" thread happens things will not work. This behavior is in line with how the system threading works
function THREAD:newFunction(func,holdme)
return thread:newFunctionBase(function(...)
return multi:newSystemThread("TempSystemThread",func,...)
end,holdme)()
end
multi.print("Integrated Pesudo Threading!")
multi.integration = {} -- for module creators
multi.integration.GLOBAL = GLOBAL
multi.integration.THREAD = THREAD
require("multi.integration.pesudoManager.extensions")
return {
init = function()
return GLOBAL, THREAD
end
}
+110
View File
@@ -0,0 +1,110 @@
--[[
MIT License
Copyright (c) 2022 Ryan Ward
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sub-license, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
]]
local function getOS()
if package.config:sub(1, 1) == "\\" then
return "windows"
else
return "unix"
end
end
local function INIT(thread)
local THREAD = {}
local GLOBAL = {}
THREAD.Priority_Core = 3
THREAD.Priority_High = 2
THREAD.Priority_Above_Normal = 1
THREAD.Priority_Normal = 0
THREAD.Priority_Below_Normal = -1
THREAD.Priority_Low = -2
THREAD.Priority_Idle = -3
function THREAD.set(name, val)
GLOBAL[name] = val
end
function THREAD.get(name)
return GLOBAL[name]
end
function THREAD.waitFor(name)
return thread.hold(function() return GLOBAL[name] end)
end
if getOS() == "windows" then
THREAD.__CORES = tonumber(os.getenv("NUMBER_OF_PROCESSORS"))
else
THREAD.__CORES = tonumber(io.popen("nproc --all"):read("*n"))
end
function THREAD.getCores()
return THREAD.__CORES
end
function THREAD.getConsole()
local c = {}
function c.print(...)
print(...)
end
function c.error(err)
error("ERROR in <"..GLOBAL["$__THREADNAME__"]..">: "..err)
end
return c
end
function THREAD.getThreads()
return {}--GLOBAL.__THREADS__
end
THREAD.pushStatus = thread.pushStatus
if os.getOS() == "windows" then
THREAD.__CORES = tonumber(os.getenv("NUMBER_OF_PROCESSORS"))
else
THREAD.__CORES = tonumber(io.popen("nproc --all"):read("*n"))
end
function THREAD.kill()
error("Thread was killed!")
end
function THREAD.getName()
return GLOBAL["$THREAD_NAME"]
end
function THREAD.getID()
return GLOBAL["$THREAD_ID"]
end
THREAD.sleep = thread.sleep
THREAD.hold = thread.hold
return GLOBAL, THREAD
end
return {init = function(thread)
return INIT(thread)
end}
-2
View File
@@ -1,2 +0,0 @@
-- Planned system threaded objects
-- multi:newSystemThreadedConnection(name, protect)
+13
View File
@@ -0,0 +1,13 @@
-- We need to detect what enviroment we are running our code in.
return {
init = function()
if love then
return require("multi.integration.loveManager"):init()
else
if pcall(require,"lanes") then
return require("multi.integration.lanesManager"):init()
end
return require("multi.integration.pesudoManager"):init()
end
end
}
+38
View File
@@ -0,0 +1,38 @@
package = "multi"
version = "15.0-0"
source = {
url = "git://github.com/rayaman/multi.git",
tag = "v15.0.0",
}
description = {
summary = "Lua Multi tasking library",
detailed = [[
This library contains many methods for multi tasking. Features non coroutine based multi-tasking, coroutine based multi-tasking, and system threading (Requires use of an integration).
Check github for how to use.
]],
homepage = "https://github.com/rayaman/multi",
license = "MIT"
}
dependencies = {
"lua >= 5.1",
"lanes",
}
build = {
type = "builtin",
modules = {
["multi"] = "multi/init.lua",
["multi.compat.love2d"] = "multi/compat/love2d.lua",
["multi.integration.threading"] = "multi/integration/threading.lua",
["multi.integration.lanesManager"] = "multi/integration/lanesManager/init.lua",
["multi.integration.lanesManager.extensions"] = "multi/integration/lanesManager/extensions.lua",
["multi.integration.lanesManager.threads"] = "multi/integration/lanesManager/threads.lua",
["multi.integration.loveManager"] = "multi/integration/loveManager/init.lua",
["multi.integration.loveManager.extensions"] = "multi/integration/loveManager/extensions.lua",
["multi.integration.loveManager.threads"] = "multi/integration/loveManager/threads.lua",
["multi.integration.pesudoManager"] = "multi/integration/pesudoManager/init.lua",
["multi.integration.pesudoManager.extensions"] = "multi/integration/pesudoManager/extensions.lua",
["multi.integration.pesudoManager.threads"] = "multi/integration/pesudoManager/threads.lua",
["multi.integration.luvitManager"] = "multi/integration/luvitManager.lua",
--["multi.integration.networkManager"] = "multi/integration/networkManager.lua",
}
}
+42
View File
@@ -0,0 +1,42 @@
package = "multi"
version = "15.1-0"
source = {
url = "git://github.com/rayaman/multi.git",
tag = "V15.1.0",
}
description = {
summary = "Lua Multi tasking library",
detailed = [[
This library contains many methods for multi tasking. Features non coroutine based multi-tasking, coroutine based multi-tasking, and system threading (Requires use of an integration).
Check github for documentation.
]],
homepage = "https://github.com/rayaman/multi",
license = "MIT"
}
dependencies = {
"lua >= 5.1",
"lanes",
}
build = {
type = "builtin",
modules = {
["multi"] = "multi/init.lua",
["multi.compat.love2d"] = "multi/compat/love2d.lua",
["multi.compat.lovr"] = "multi/compat/lovr.lua",
["multi.integration.lanesManager"] = "multi/integration/lanesManager/init.lua",
["multi.integration.lanesManager.extensions"] = "multi/integration/lanesManager/extensions.lua",
["multi.integration.lanesManager.threads"] = "multi/integration/lanesManager/threads.lua",
["multi.integration.loveManager"] = "multi/integration/loveManager/init.lua",
["multi.integration.loveManager.extensions"] = "multi/integration/loveManager/extensions.lua",
["multi.integration.loveManager.threads"] = "multi/integration/loveManager/threads.lua",
--["multi.integration.lovrManager"] = "multi/integration/lovrManager/init.lua",
--["multi.integration.lovrManager.extensions"] = "multi/integration/lovrManager/extensions.lua",
--["multi.integration.lovrManager.threads"] = "multi/integration/lovrManager/threads.lua",
["multi.integration.pesudoManager"] = "multi/integration/pesudoManager/init.lua",
["multi.integration.pesudoManager.extensions"] = "multi/integration/pesudoManager/extensions.lua",
["multi.integration.pesudoManager.threads"] = "multi/integration/pesudoManager/threads.lua",
["multi.integration.luvitManager"] = "multi/integration/luvitManager.lua",
["multi.integration.threading"] = "multi/integration/threading.lua",
--["multi.integration.networkManager"] = "multi/integration/networkManager.lua",
}
}
+39
View File
@@ -0,0 +1,39 @@
package = "multi"
version = "15.2-0"
source = {
url = "git://github.com/rayaman/multi.git",
tag = "v15.2.0",
}
description = {
summary = "Lua Multi tasking library",
detailed = [[
This library contains many methods for multi tasking. Features non coroutine based multi-tasking, coroutine based multi-tasking, and system threading (Requires use of an integration).
Check github for documentation.
]],
homepage = "https://github.com/rayaman/multi",
license = "MIT"
}
dependencies = {
"lua >= 5.1"
}
build = {
type = "builtin",
modules = {
["multi"] = "multi/init.lua",
["multi.integration.lanesManager"] = "multi/integration/lanesManager/init.lua",
["multi.integration.lanesManager.extensions"] = "multi/integration/lanesManager/extensions.lua",
["multi.integration.lanesManager.threads"] = "multi/integration/lanesManager/threads.lua",
["multi.integration.loveManager"] = "multi/integration/loveManager/init.lua",
["multi.integration.loveManager.extensions"] = "multi/integration/loveManager/extensions.lua",
["multi.integration.loveManager.threads"] = "multi/integration/loveManager/threads.lua",
--["multi.integration.lovrManager"] = "multi/integration/lovrManager/init.lua",
--["multi.integration.lovrManager.extensions"] = "multi/integration/lovrManager/extensions.lua",
--["multi.integration.lovrManager.threads"] = "multi/integration/lovrManager/threads.lua",
["multi.integration.pesudoManager"] = "multi/integration/pesudoManager/init.lua",
["multi.integration.pesudoManager.extensions"] = "multi/integration/pesudoManager/extensions.lua",
["multi.integration.pesudoManager.threads"] = "multi/integration/pesudoManager/threads.lua",
["multi.integration.luvitManager"] = "multi/integration/luvitManager.lua",
["multi.integration.threading"] = "multi/integration/threading.lua",
--["multi.integration.networkManager"] = "multi/integration/networkManager.lua",
}
}
-25
View File
@@ -1,25 +0,0 @@
-- set up the package
package.path="?/init.lua;?.lua;"..package.path
-- Import the libraries
multi = require("multi")
local GLOBAL, THREAD = require("multi.integration.lanesManager").init()
nGLOBAL = require("multi.integration.networkManager").init()
-- Act as a master node
master = multi:newMaster{
name = "Main", -- the name of the master
--noBroadCast = true, -- if using the node manager, set this to true to avoid double connections
--managerDetails = {"localhost",12345}, -- the details to connect to the node manager (ip,port)
}
master.OnError(function(name,err)
print(name.." has encountered an error: "..err)
end)
master.OnNodeConnected(function(name)
-- name is the name of the node that connected
end)
-- Starting the multitasker
settings = {
priority = 0, -- 0, 1 or 2
protect = false,
}
multi:threadloop(settings) -- both mainloop and threadloop can be used. one pirotizes threads where the other pirotizes multiobjs
--multi:mainloop(settings)
-16
View File
@@ -1,16 +0,0 @@
package.path="?/init.lua;?.lua;"..package.path
multi = require("multi")
local GLOBAL, THREAD = require("multi.integration.lanesManager").init()
nGLOBAL = require("multi.integration.networkManager").init()
node = multi:newNode{
allowRemoteRegistering = true, -- allows you to register functions from the master on the node, default is false
name = nil, -- default value
--noBroadCast = true, -- if using the node manager, set this to true to prevent the node from broadcasting
--managerDetails = {"localhost",12345}, -- connects to the node manager if one exists
}
settings = {
priority = 0, -- 1 or 2
stopOnError = true, -- if an actor crashes this will prevent it from constantly crashing over and over. You can leave this false and use multi.OnError to handle crashes as well
protect = true, -- always protect a node. Not really needed since all executed xode from a master is protected on execution to prevent issues.
}
multi:mainloop(settings)
-12
View File
@@ -1,12 +0,0 @@
package.path="?/init.lua;?.lua;"..package.path
multi = require("multi")
local GLOBAL, THREAD = require("multi.integration.lanesManager").init()
nGLOBAL = require("multi.integration.networkManager").init()
multi:nodeManager(12345) -- Host a node manager on port: 12345
print("Node Manager Running...")
settings = {
priority = 0, -- 1 or 2
protect = false,
}
multi:mainloop(settings)
-- Thats all you need to run the node manager, everything else is done automatically
+100 -33
View File
@@ -1,44 +1,111 @@
package.path="?.lua;?/init.lua;?.lua;?/?/init.lua;"..package.path
local multi,thread = require("multi"):init()
package.path = "./?/init.lua;"..package.path
multi, thread = require("multi"):init{print=true}
GLOBAL, THREAD = require("multi.integration.threading"):init()
-- Testing destroying and fixed connections
c = multi:newConnection()
c1 = c(function()
print("called 1")
end)
c2 = c(function()
print("called 2")
end)
c3 = c(function()
print("called 3")
-- Using a system thread, but both system and local threads support this!
-- Don't worry if you don't have lanes or love2d. PesudoThreading will kick in to emulate the threading features if you do not have access to system threading.
func = THREAD:newFunction(function(count)
print("Starting Status test: ",count)
local a = 0
while true do
a = a + 1
THREAD.sleep(.1)
-- Push the status from the currently running threaded function to the main thread
THREAD.pushStatus(a,count)
if a == count then break end
end
return "Done"
end)
print(c1,c2.Type,c3)
c:Fire()
c2:Destroy()
print(c1,c2.Type,c3)
c:Fire()
c1:Destroy()
print(c1,c2.Type,c3)
c:Fire()
thread:newThread("test",function()
local ret = func(10)
ret.OnStatus(function(part,whole)
print("Ret1: ",math.ceil((part/whole)*1000)/10 .."%")
end)
print("TEST",func(5).wait())
-- The results from the OnReturn connection is passed by thread.hold
print("Status:",thread.hold(ret.OnReturn))
print("Function Done!")
end).OnError(function(...)
print("Error:",...)
end)
-- Destroying alarms and threads
local test = multi:newThread(function()
local ret = func(10)
local ret2 = func(15)
local ret3 = func(20)
local s1,s2,s3 = 0,0,0
ret.OnError(function(...)
print("Error:",...)
end)
ret2.OnError(function(...)
print("Error:",...)
end)
ret3.OnError(function(...)
print("Error:",...)
end)
ret.OnStatus(function(part,whole)
s1 = math.ceil((part/whole)*1000)/10
print(s1)
end)
ret2.OnStatus(function(part,whole)
s2 = math.ceil((part/whole)*1000)/10
print(s2)
end)
ret3.OnStatus(function(part,whole)
s3 = math.ceil((part/whole)*1000)/10
print(s3)
end)
loop = multi:newTLoop()
function loop:testing()
print("testing haha")
end
loop:Set(1)
t = loop:OnLoop(function()
print("Looping...")
end):testing()
local proc = multi:newProcessor("Test")
local proc2 = multi:newProcessor("Test2")
local proc3 = proc2:newProcessor("Test3")
proc.Start()
proc2.Start()
proc3.Start()
proc:newThread("TestThread_1",function()
while true do
thread.sleep(1)
end
end)
proc:newThread("TestThread_2",function()
while true do
thread.sleep(1)
end
end)
proc2:newThread("TestThread_3",function()
while true do
thread.sleep(1)
print("Hello!")
end
end)
test.OnDeath(function()
os.exit() -- This is the last thing called.
thread:newThread(function()
thread.sleep(1)
local tasks = multi:getStats()
for i,v in pairs(tasks) do
print("Process: " ..i.. "\n\tTasks:")
for ii,vv in pairs(v.tasks) do
print("\t\t"..vv:getName())
end
print("\tThreads:")
for ii,vv in pairs(v.threads) do
print("\t\t"..vv:getName())
end
end
thread.sleep(10) -- Wait 10 seconds then kill the process!
os.exit()
end)
local alarm = multi:newAlarm(4):OnRing(function(a)
print(a.Type)
a:Destroy()
print(a.Type)
test:Destroy()
end)
multi:lightloop()
multi:mainloop()
+51
View File
@@ -0,0 +1,51 @@
package.path = "./?.lua;?/init.lua;"..package.path
local multi,thread = require("multi"):init{print=true}
--local GLOBAL,THREAD = require("multi.integration.lanesManager"):init()
-- func = THREAD:newFunction(function(a,b,c)
-- print("Hello Thread!",a,b,c)
-- return 1,2,3
-- end)
-- func2 = THREAD:newFunction(function(a,b,c)
-- print("Hello Thread2!",a,b,c)
-- THREAD.sleep(1)
-- return 10,11,12
-- end)
-- multi:newThread("Test thread",function()
-- handler = func(4,5,6)
-- handler2 = func2(7,8,9)
-- thread.hold(handler.OnReturn + handler2.OnReturn)
-- print("Function Done",handler.getReturns())
-- print("Function Done",handler2.getReturns())
-- end)
-- multi:benchMark(1):OnBench(function(sec,steps)
-- print("Steps:",steps)
-- os.exit()
-- end)
print("Running benchmarks! ",_VERSION)
local sleep_for = 1
local a = 0
local c = 1
local function bench(t,step)
a = a + step
c = c + 1
os.exit()
end
--multi:benchMark(sleep_for,multi.Priority_Idle,"Idle:"):OnBench(bench)
--multi:benchMark(sleep_for,multi.Priority_Very_Low,"Very Low:"):OnBench(bench)
--multi:benchMark(sleep_for,multi.Priority_Low,"Low:"):OnBench()
--multi:benchMark(sleep_for,multi.Priority_Below_Normal,"Below Normal:"):OnBench(bench)
--multi:benchMark(sleep_for,multi.Priority_Normal,"Normal:"):OnBench(bench)
--multi:benchMark(sleep_for,multi.Priority_Above_Normal,"Above Normal:"):OnBench(bench)
--multi:benchMark(sleep_for,multi.Priority_High,"High:"):OnBench(bench)
--multi:benchMark(sleep_for,multi.Priority_Very_High,"Very High:"):OnBench(bench)
multi:benchMark(sleep_for,multi.Priority_Core,"Core:"):OnBench(bench)
multi.OnExit(function()
print("Total: ".. a)
end)
multi:mainloop()
+176
View File
@@ -0,0 +1,176 @@
if os.getenv("LOCAL_LUA_DEBUGGER_VSCODE") == "1" then
package.path="multi/?.lua;multi/?/init.lua;multi/?.lua;multi/?/?/init.lua;"..package.path
require("lldebugger").start()
else
package.path="./?.lua;../?/init.lua;../?.lua;../?/?/init.lua;"..package.path
end
--[[
This file runs all tests.
Format:
Expected:
...
...
...
Actual:
...
...
...
Each test that is ran should have a 5 second pause after the test is complete
The expected and actual should "match" (Might be impossible when playing with threads)
This will be pushed directly to the master as tests start existing.
]]
local multi, thread = require("multi"):init{print=true}--{priority=true}
local good = false
local proc = multi:newProcessor("Test",true)
proc:newAlarm(3):OnRing(function()
good = true
end)
runTest = thread:newFunction(function()
local alarms,tsteps,steps,loops,tloops,updaters,events=false,0,0,0,0,0,false
print("Testing Basic Features. If this fails most other features will probably not work!")
proc:newAlarm(2):OnRing(function(a)
alarms = true
a:Destroy()
end)
proc:newTStep(1,10,1,.1):OnStep(function(t)
tsteps = tsteps + 1
end).OnEnd(function(step)
step:Destroy()
end)
proc:newStep(1,10):OnStep(function(s)
steps = steps + 1
end).OnEnd(function(step)
step:Destroy()
end)
local loop = proc:newLoop(function(l)
loops = loops + 1
end)
proc:newTLoop(function(t)
tloops = tloops + 1
end,.1)
local updater = proc:newUpdater(1):OnUpdate(function()
updaters = updaters + 1
end)
local event = proc:newEvent(function()
return alarms
end)
event.OnEvent(function(evnt)
evnt:Destroy()
events = true
print("Alarms: Ok")
print("Events: Ok")
if tsteps == 10 then print("TSteps: Ok") else print("TSteps: Bad!") end
if steps == 10 then print("Steps: Ok") else print("Steps: Bad!") end
if loops > 100 then print("Loops: Ok") else print("Loops: Bad!") end
if tloops > 10 then print("TLoops: Ok") else print("TLoops: Bad!") end
if updaters > 100 then print("Updaters: Ok") else print("Updaters: Bad!") end
end)
thread.hold(event.OnEvent)
print("Starting Connection and Thread tests!")
func = thread:newFunction(function(count)
print("Starting Status test: ",count)
local a = 0
while true do
a = a + 1
thread.sleep(.1)
thread.pushStatus(a,count)
if a == count then break end
end
return "Done"
end)
local ret = func(10)
local ret2 = func(15)
local ret3 = func(20)
local s1,s2,s3 = 0,0,0
ret.OnError(function(...)
print("Func 1:",...)
end)
ret2.OnError(function(...)
print("Func 2:",...)
end)
ret3.OnError(function(...)
print("Func 3:",...)
end)
ret.OnStatus(function(part,whole)
s1 = math.ceil((part/whole)*1000)/10
end)
ret2.OnStatus(function(part,whole)
s2 = math.ceil((part/whole)*1000)/10
end)
ret3.OnStatus(function(part,whole)
s3 = math.ceil((part/whole)*1000)/10
end)
ret.OnReturn(function()
print("Done 1")
end)
ret2.OnReturn(function()
print("Done 2")
end)
ret3.OnReturn(function()
print("Done 3")
end)
local err, timeout = thread.hold(ret.OnReturn + ret2.OnReturn + ret3.OnReturn)
if s1 == 100 and s2 == 100 and s3 == 100 then
print("Threads: Ok")
else
print("Threads OnStatus or thread.hold(conn) Error!")
end
if timeout then
print("Threads or Connection Error!")
else
print("Connection Test 1: Ok")
end
conn1 = proc:newConnection()
conn2 = proc:newConnection()
conn3 = proc:newConnection()
local c1,c2,c3,c4 = false,false,false,false
local a = conn1(function()
c1 = true
end)
local b = conn2(function()
c2 = true
end)
local c = conn3(function()
c3 = true
end)
local d = conn3(function()
c4 = true
end)
conn1:Fire()
conn2:Fire()
conn3:Fire()
if c1 and c2 and c3 and c4 then
print("Connection Test 2: Ok")
else
print("Connection Test 2: Error")
end
c3 = false
c4 = false
d:Destroy()
conn3:Fire()
if c3 and not(c4) then
print("Connection Test 3: Ok")
else
print("Connection Test 3: Error removing connection")
end
os.exit() -- End of tests
end)
runTest().OnError(function(...)
print("Error: Something went wrong with the test!")
print(...)
os.exit(1)
end)
print("Pumping proc")
while true do
proc.run()
end