mirror of
https://github.com/rayaman/multi.git
synced 2026-09-05 07:27:35 -04:00
Compare commits
172
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8c3d66a7b1 | ||
|
|
f5e95b8c9f | ||
|
|
2f11a80a28 | ||
|
|
8b6b56164a | ||
|
|
bbc83acc0f | ||
|
|
0d6a9f92ad | ||
|
|
4cf80517eb | ||
|
|
35a86a6cda | ||
|
|
019855d81f | ||
|
|
2a47de9fd8 | ||
|
|
0d3de6e31d | ||
|
|
d6d4934a7e | ||
|
|
6369450d2f | ||
|
|
a90b1d09b1 | ||
|
|
92090f0a0a | ||
|
|
5cfd155241 | ||
|
|
0b2b2f9613 | ||
|
|
a5b59ee601 | ||
|
|
f240588edd | ||
|
|
6abfb9c3d9 | ||
|
|
e5ac4d3d9e | ||
|
|
7353fde799 | ||
|
|
4c088b9080 | ||
|
|
b727fb15b5 | ||
|
|
57b4871a85 | ||
|
|
3ae2acbd78 | ||
|
|
df429b617e | ||
|
|
0d3a7b9393 | ||
|
|
6de93c10af | ||
|
|
a14cbb45d3 | ||
|
|
827d3d8c45 | ||
|
|
7bbb217018 | ||
|
|
62e05788d2 | ||
|
|
772095431f | ||
|
|
bb7fab0857 | ||
|
|
1546076c80 | ||
|
|
8f3eb6d9a3 | ||
|
|
ccc4a15121 | ||
|
|
9050e65d93 | ||
|
|
8f7183f998 | ||
|
|
3d07e774a0 | ||
|
|
99ba411781 | ||
|
|
23eaf42cad | ||
|
|
5bca4e3383 | ||
|
|
e2bb964423 | ||
|
|
0e2119a87d | ||
|
|
7dfbf16bc1 | ||
|
|
bac91574a2 | ||
|
|
bf2f495812 | ||
|
|
beddecc742 | ||
|
|
86f31bbe52 | ||
|
|
d8f3db6972 | ||
|
|
494baf1e32 | ||
|
|
e14fa83681 | ||
|
|
278b982aff | ||
|
|
89e76ce31d | ||
|
|
831abeaccf | ||
|
|
f74f85759b | ||
|
|
7b70838567 | ||
|
|
fcc0717115 | ||
|
|
47d8ac30c4 | ||
|
|
3646a1a074 | ||
|
|
b0ab40d410 | ||
|
|
440995b1c8 | ||
|
|
8da4c4c1de | ||
|
|
2cc2a57a46 | ||
|
|
6527dc1aaa | ||
|
|
bd49805c25 | ||
|
|
40dd293bf8 | ||
|
|
568c95fa73 | ||
|
|
d30ee3788e | ||
|
|
79f58a79f9 | ||
|
|
d8aeefd202 | ||
|
|
c0fb94ddbb | ||
|
|
e8a3cd731d | ||
|
|
03ffb6bc0a | ||
|
|
4240737e00 | ||
|
|
5172dcdf01 | ||
|
|
fc18a303dd | ||
|
|
04d5500374 | ||
|
|
72e24e8a9b | ||
|
|
48bba84c08 | ||
|
|
e9a0e7bbf7 | ||
|
|
264867a0da | ||
|
|
3f046afaa1 | ||
|
|
2acce5001d | ||
|
|
78cd15681a | ||
|
|
3401a8ac61 | ||
|
|
19ac257204 | ||
|
|
c14a469069 | ||
|
|
03cea2d71a | ||
|
|
593bfd0d8c | ||
|
|
bf60a354d4 | ||
|
|
6c73220a52 | ||
|
|
c3a9ddfdbd | ||
|
|
e05f2ea400 | ||
|
|
a60aae02c6 | ||
|
|
207c5b8d69 | ||
|
|
a9111f2fa3 | ||
|
|
b572bf218d | ||
|
|
49c0bd3930 | ||
|
|
cdb4bfda11 | ||
|
|
3fbead60d9 | ||
|
|
2b122f5c77 | ||
|
|
472d1748ee | ||
|
|
47178dd3b3 | ||
|
|
3fcba8825b | ||
|
|
e194a06427 | ||
|
|
32f7b4492b | ||
|
|
913745a1bc | ||
|
|
889dc6ca68 | ||
|
|
2805137648 | ||
|
|
f7167cf972 | ||
|
|
9e1ecb3583 | ||
|
|
b9b9b51d12 | ||
|
|
a708fb3f83 | ||
|
|
4335f3ed14 | ||
|
|
8580d92c9c | ||
|
|
f1f6e30a98 | ||
|
|
6c1e9f26f0 | ||
|
|
d98f353936 | ||
|
|
14c8665910 | ||
|
|
f7452db3ec | ||
|
|
588923e1b7 | ||
|
|
4877f64ca1 | ||
|
|
b8b31253d4 | ||
|
|
af4672245e | ||
|
|
609613dbe9 | ||
|
|
b16593425b | ||
|
|
997ea48b54 | ||
|
|
537dcf0db1 | ||
|
|
b74a6c006e | ||
|
|
b3453d028c | ||
|
|
6842147522 | ||
|
|
de34b9dc59 | ||
|
|
dff19d865f | ||
|
|
544aa78d70 | ||
|
|
91d0b5f7be | ||
|
|
27e03a2546 | ||
|
|
7dbcd01c33 | ||
|
|
cc20914391 | ||
|
|
c2aa449a65 | ||
|
|
57563688ae | ||
|
|
317dacd0de | ||
|
|
a7ba146a64 | ||
|
|
efa30e30cc | ||
|
|
197b418fc5 | ||
|
|
d3d53599f7 | ||
|
|
bf517facd1 | ||
|
|
9cff2735ba | ||
|
|
ea77b934b6 | ||
|
|
74bfd571a5 | ||
|
|
06132fc1dd | ||
|
|
ade5172f26 | ||
|
|
bdc657771d | ||
|
|
8c24bcbbb0 | ||
|
|
804a117ed0 | ||
|
|
37afd37f9e | ||
|
|
4399fb6424 | ||
|
|
02a54e13ea | ||
|
|
bb2c7d6440 | ||
|
|
d1b8ed1922 | ||
|
|
9992a2c091 | ||
|
|
155466dc71 | ||
|
|
cea6508d68 | ||
|
|
726707eb8a | ||
|
|
9a9d28f62f | ||
|
|
ed924a3d9d | ||
|
|
d2ce7e070b | ||
|
|
300827b7bd | ||
|
|
9d97eac146 | ||
|
|
61dcb9da01 |
+1
-13
@@ -1,14 +1,2 @@
|
|||||||
|
|
||||||
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
|
|
||||||
*.code-workspace
|
*.code-workspace
|
||||||
*.dat
|
lua5.4/*
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2021 Ryan Ward
|
Copyright (c) 2022 Ryan Ward
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
@@ -1,47 +1,78 @@
|
|||||||
# Multi Version: 14.2.0 Documentation Complete, Bloat removed!
|
# Multi Version: 15.3.0 A world of Connections
|
||||||
|
**Key Changes**
|
||||||
|
- SystemThreadedConnections
|
||||||
|
- Restructured the directory structure of the repo (Allows for keeping multi as a submodule and being able to require it as is)
|
||||||
|
- Bug fixes
|
||||||
|
|
||||||
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.4.0](https://github.com/rayaman/multi/tree/v15.4.0)
|
||||||
|
---
|
||||||
|
|
||||||
|
</br>
|
||||||
|
|
||||||
INSTALLING
|
INSTALLING
|
||||||
----------
|
----------
|
||||||
Links to dependencies:
|
Link to optional dependencies:
|
||||||
[lanes](https://github.com/LuaLanes/lanes)
|
- [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>
|
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.**
|
luarocks install multi
|
||||||
|
```
|
||||||
|
|
||||||
Discord
|
Discord
|
||||||
-------
|
-------
|
||||||
Have a question that you need asking? Or need realtime assistance? Feel free to join the discord!</br>
|
Have a question or need realtime assistance? Feel free to join the discord!</br>
|
||||||
https://discord.gg/U8UspuA</br>
|
https://discord.gg/U8UspuA
|
||||||
|
|
||||||
Planned features/TODO
|
Planned features/TODO
|
||||||
---------------------
|
---------------------
|
||||||
- [x] ~~Finish Documentation~~ Finished
|
- [ ] Create test suite (In progress, mostly done)
|
||||||
- [ ] Network Parallelism rework
|
- [ ] Network Parallelism rework
|
||||||
- [ ] Fix some bugs
|
|
||||||
|
|
||||||
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
|
```lua
|
||||||
local multi, thread = require("multi").init()
|
local multi, thread = require("multi"):init()
|
||||||
mutli:newThread("Example",function()
|
GLOBAL, THREAD = require("multi.integration.threading"):init()
|
||||||
while true do
|
|
||||||
thread.sleep(1)
|
multi:newSystemThread("System Thread",function()
|
||||||
print("Hello!")
|
while true do
|
||||||
end
|
THREAD.sleep(.1)
|
||||||
|
io.write(" World")
|
||||||
|
THREAD.kill()
|
||||||
|
end
|
||||||
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
|
while true do
|
||||||
multi:uManager()
|
multi:uManager()
|
||||||
end
|
end
|
||||||
]]
|
]]
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
Current Multi Version: 14.2.0
|
Current Multi Version: 15.1.0
|
||||||
|
|
||||||
# Multi static variables
|
# Multi static variables
|
||||||
`multi.Version` — The current version of the library
|
`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
|
`multi.Priority_Core` — Highest level of pirority that can be given to a process
|
||||||
</br>`multi.Priority_Very_High`
|
</br>`multi.Priority_Very_High`
|
||||||
</br>`multi.Priority_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
|
</br>`multi.Priority_Idle` — Lowest level of pirority that can be given to a process
|
||||||
|
|
||||||
# Multi Runners
|
# 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: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: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: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.
|
</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
|
# 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.
|
**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
|
The connect feature has some syntax sugar to it as seen below
|
||||||
- `link = conn(FUNCTION func, [STRING name nil], [NUMBER #conns+1])`
|
- `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:
|
Example:
|
||||||
```lua
|
```lua
|
||||||
local multi = require("multi")
|
multi,thread = require("multi"):init()
|
||||||
-- Let’s create the events
|
-- Let’s create the events
|
||||||
yawn={}
|
yawn={}
|
||||||
OnCustomSafeEvent=multi:newConnection(true) -- lets pcall the calls in case something goes wrong default
|
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
|
```lua
|
||||||
package.path="?.lua;?/init.lua;?.lua;?/?/init.lua;"..package.path
|
package.path="?.lua;?/init.lua;?.lua;?/?/init.lua;"..package.path
|
||||||
multi = require("multi")
|
multi,thread = require("multi"):init()
|
||||||
|
|
||||||
loop = multi:newLoop(function()
|
loop = multi:newLoop(function()
|
||||||
-- do stuff
|
-- do stuff
|
||||||
@@ -224,7 +297,7 @@ loop:OnTimerResolved(function(self,...)
|
|||||||
print(...)
|
print(...)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
multi:lightloop()
|
multi:mainloop()
|
||||||
```
|
```
|
||||||
As mentioned above this is made much easier using threads
|
As mentioned above this is made much easier using threads
|
||||||
```lua
|
```lua
|
||||||
@@ -255,14 +328,14 @@ print(func(0))
|
|||||||
Example:
|
Example:
|
||||||
```lua
|
```lua
|
||||||
package.path="?.lua;?/init.lua;?.lua;?/?/init.lua;"..package.path
|
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!
|
multi:scheduleJob({min = 30},function() -- Every hour at minute 30 this event will be triggered! You can mix and match as well!
|
||||||
print("Hi")
|
print("Hi")
|
||||||
end)
|
end)
|
||||||
multi:scheduleJob({min = 30,hour = 0},function() -- Every day at 12:30AM this event will be triggered
|
multi:scheduleJob({min = 30,hour = 0},function() -- Every day at 12:30AM this event will be triggered
|
||||||
print("Hi")
|
print("Hi")
|
||||||
end)
|
end)
|
||||||
multi:lightloop()
|
multi:mainloop()
|
||||||
```
|
```
|
||||||
|
|
||||||
# Universal Actor methods
|
# Universal Actor methods
|
||||||
@@ -287,7 +360,7 @@ All of these functions are found on actors
|
|||||||
|
|
||||||
Example:
|
Example:
|
||||||
```lua
|
```lua
|
||||||
local multi = require("multi")
|
multi,thread = require("multi"):init()
|
||||||
count=0
|
count=0
|
||||||
-- A loop object is used to demostrate how one could use an event object.
|
-- A loop object is used to demostrate how one could use an event object.
|
||||||
loop=multi:newLoop(function(self,dt)
|
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!
|
loop:Destroy() -- destroys the loop from running!
|
||||||
print("Stopped that loop!",count)
|
print("Stopped that loop!",count)
|
||||||
end) -- events like alarms need to be reset the Reset() command works here as well
|
end) -- events like alarms need to be reset the Reset() command works here as well
|
||||||
multi:lightloop()
|
multi:mainloop()
|
||||||
```
|
```
|
||||||
|
|
||||||
# Actor: Updaters
|
# Actor: Updaters
|
||||||
@@ -311,12 +384,12 @@ Updaters are a mix between both loops and steps. They were a way to add basic pr
|
|||||||
|
|
||||||
Example:
|
Example:
|
||||||
```lua
|
```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=multi:newUpdater(5000) -- simple, think of a loop with the skip feature of a step
|
||||||
updater:OnUpdate(function(self)
|
updater:OnUpdate(function(self)
|
||||||
print("updating...")
|
print("updating...")
|
||||||
end)
|
end)
|
||||||
multi:lightloop()
|
multi:mainloop()
|
||||||
```
|
```
|
||||||
|
|
||||||
# Actor: Alarms
|
# Actor: Alarms
|
||||||
@@ -328,13 +401,13 @@ Alarms ring after a certain amount of time, but you need to reset the alarm ever
|
|||||||
|
|
||||||
Example:
|
Example:
|
||||||
```lua
|
```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=multi:newAlarm(3) -- in seconds can go to .001 uses the built in os.clock()
|
||||||
alarm:OnRing(function(a)
|
alarm:OnRing(function(a)
|
||||||
print("3 Seconds have passed!")
|
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
|
a:Reset(n) -- if n were nil it will reset back to 3, or it would reset to n seconds
|
||||||
end)
|
end)
|
||||||
multi:lightloop()
|
multi:mainloop()
|
||||||
```
|
```
|
||||||
|
|
||||||
# Actor: Loops
|
# Actor: Loops
|
||||||
@@ -346,7 +419,7 @@ Loops are events that happen over and over until paused. They act like a while l
|
|||||||
Example:
|
Example:
|
||||||
```lua
|
```lua
|
||||||
package.path="?/init.lua;?.lua;"..package.path
|
package.path="?/init.lua;?.lua;"..package.path
|
||||||
local multi = require("multi")
|
multi,thread = require("multi"):init()
|
||||||
local a = 0
|
local a = 0
|
||||||
loop = multi:newLoop(function()
|
loop = multi:newLoop(function()
|
||||||
a = a + 1
|
a = a + 1
|
||||||
@@ -355,7 +428,7 @@ loop = multi:newLoop(function()
|
|||||||
loop:Pause()
|
loop:Pause()
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
multi:lightloop()
|
multi:mainloop()
|
||||||
```
|
```
|
||||||
|
|
||||||
# Actor: TLoops
|
# Actor: TLoops
|
||||||
@@ -366,7 +439,7 @@ multi:lightloop()
|
|||||||
Example:
|
Example:
|
||||||
```lua
|
```lua
|
||||||
package.path="?/init.lua;?.lua;"..package.path
|
package.path="?/init.lua;?.lua;"..package.path
|
||||||
local multi = require("multi")
|
multi,thread = require("multi"):init()
|
||||||
local a = 0
|
local a = 0
|
||||||
loop = multi:newTLoop(function()
|
loop = multi:newTLoop(function()
|
||||||
a = a + 1
|
a = a + 1
|
||||||
@@ -375,7 +448,7 @@ loop = multi:newTLoop(function()
|
|||||||
loop:Pause()
|
loop:Pause()
|
||||||
end
|
end
|
||||||
end,1)
|
end,1)
|
||||||
multi:lightloop()
|
multi:mainloop()
|
||||||
```
|
```
|
||||||
|
|
||||||
# Actor: Steps
|
# Actor: Steps
|
||||||
@@ -390,13 +463,13 @@ multi:lightloop()
|
|||||||
Example:
|
Example:
|
||||||
```lua
|
```lua
|
||||||
package.path="?/init.lua;?.lua;"..package.path
|
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)
|
multi:newStep(1,10,1,0):OnStep(function(step,pos)
|
||||||
print(step,pos)
|
print(step,pos)
|
||||||
end):OnEnd(fucntion(step)
|
end):OnEnd(fucntion(step)
|
||||||
step:Destroy()
|
step:Destroy()
|
||||||
end)
|
end)
|
||||||
multi:lightloop()
|
multi:mainloop()
|
||||||
```
|
```
|
||||||
|
|
||||||
# Actor: TSteps
|
# Actor: TSteps
|
||||||
@@ -411,13 +484,13 @@ multi:lightloop()
|
|||||||
Example:
|
Example:
|
||||||
```lua
|
```lua
|
||||||
package.path="?/init.lua;?.lua;"..package.path
|
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)
|
multi:newTStep(1,10,1,1):OnStep(function(step,pos)
|
||||||
print(step,pos)
|
print(step,pos)
|
||||||
end):OnEnd(fucntion(step)
|
end):OnEnd(fucntion(step)
|
||||||
step:Destroy()
|
step:Destroy()
|
||||||
end)
|
end)
|
||||||
multi:lightloop()
|
multi:mainloop()
|
||||||
```
|
```
|
||||||
|
|
||||||
# Coroutine based Threading (CBT)
|
# 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**
|
**threads.\* used within threaded enviroments**
|
||||||
- `thread.sleep(NUMBER n)` — Holds execution of the thread until a certain amount of time has passed
|
- `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.skip(NUMBER n)` — How many cycles should be skipped until I execute again
|
||||||
- `thread.kill()` — Kills the thread
|
- `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
|
- `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
|
- `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.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.
|
- `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
|
- `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
|
||||||
- `handler.connect(Function: func(returns))` — Connects to the event that is triggered when the returns are avaiable
|
- `func:Pause()` — Pauses a function, function will return `nil`, `"Function is paused"`
|
||||||
- `VARIAABLE returns = handler.wait()` — Waits until returns are avaiable and then returns them
|
- `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.
|
<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.
|
-- 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
|
-- Implementing a job manager using services
|
||||||
package.path="?/init.lua;?.lua;"..package.path
|
package.path="?/init.lua;?.lua;"..package.path
|
||||||
local multi = require("multi")
|
multi,thread = require("multi"):init()
|
||||||
multi.Jobs = multi:newService(function(self,jobs)
|
multi.Jobs = multi:newService(function(self,jobs)
|
||||||
local job = table.remove(jobs,1)
|
local job = table.remove(jobs,1)
|
||||||
if job and job.removed==nil then
|
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:removeJobs("test2") -- Remove all jobs names 'test2'
|
||||||
multi.Jobs.SetScheme(1) -- Jobs are internally a service, so setting scheme and priority
|
multi.Jobs.SetScheme(1) -- Jobs are internally a service, so setting scheme and priority
|
||||||
multi.Jobs.SetPriority(multi.Priority_Core)
|
multi.Jobs.SetPriority(multi.Priority_Core)
|
||||||
|
multi:mainloop()
|
||||||
```
|
```
|
||||||
|
|
||||||
# CBT: newThread()
|
# CBT: newThread()
|
||||||
@@ -556,21 +701,82 @@ Constants
|
|||||||
Examples:
|
Examples:
|
||||||
```lua
|
```lua
|
||||||
package.path="?/init.lua;?.lua;"..package.path
|
package.path="?/init.lua;?.lua;"..package.path
|
||||||
local multi = require("multi")
|
multi,thread = require("multi"):init()
|
||||||
multi:newThread("Example of basic usage",function()
|
multi:newThread("Example of basic usage",function()
|
||||||
while true do
|
while true do
|
||||||
thread.sleep(1)
|
thread.sleep(1)
|
||||||
print("We just made an alarm!")
|
print("We just made an alarm!")
|
||||||
end
|
end
|
||||||
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
|
# System Threads (ST) - Multi-Integration Getting Started
|
||||||
The system threads need to be required seperatly.
|
The system threads need to be required seperatly.
|
||||||
```lua
|
```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
|
-- 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("multi.integration.loveManager"):init()
|
||||||
GLOBAL, THREAD = require("luvitManager") --*
|
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.kill()` — Kills the thread
|
||||||
- `THREAD.getName()` — Returns the name of the working 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.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
|
- `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
|
# 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:
|
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
|
```lua
|
||||||
local multi = require("multi") -- keep this global when using lanes or implicitly define multi within the spawned thread
|
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.lanesManager").init()
|
local GLOBAL, THREAD = require("multi.integration.threading").init()
|
||||||
multi:newSystemThread("Example thread",function()
|
multi:newSystemThread("Example thread",function()
|
||||||
local multi = require("multi") -- we are in a thread so lets not refer to that upvalue!
|
local multi = require("multi") -- we are in a thread so lets not refer to that upvalue!
|
||||||
print("We have spawned a thread!")
|
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()
|
tloop = multi:newTLoop(function()
|
||||||
print("I'm still kicking!")
|
print("I'm still kicking!")
|
||||||
end,1)
|
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.
|
<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
|
```lua
|
||||||
-- Exposing a queue
|
-- Exposing a queue
|
||||||
multi = require("multi")
|
multi,thread = require("multi"):init()
|
||||||
local GLOBAL, THREAD = require("multi.integration.lanesManager").init() -- The standard setup above
|
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 = 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
|
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
|
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
|
local data = queue:pop() -- Get the data
|
||||||
print(data) -- print the data
|
print(data) -- print the data
|
||||||
end)
|
end)
|
||||||
multi:lightloop()
|
multi:mainloop()
|
||||||
```
|
```
|
||||||
|
|
||||||
# ST - SystemThreadedQueue
|
# ST - SystemThreadedQueue
|
||||||
@@ -659,9 +865,9 @@ multi:lightloop()
|
|||||||
|
|
||||||
Let's get into some examples:
|
Let's get into some examples:
|
||||||
```lua
|
```lua
|
||||||
multi = require("multi")
|
multi,thread = require("multi"):init()
|
||||||
thread_names = {"Thread_A","Thread_B","Thread_C","Thread_D"}
|
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()
|
queue = multi:newSystemThreadedQueue("myQueue"):init()
|
||||||
for _,n in pairs(thread_names) do
|
for _,n in pairs(thread_names) do
|
||||||
multi:newSystemThread(n,function()
|
multi:newSystemThread(n,function()
|
||||||
@@ -683,13 +889,24 @@ end):OnEvent(function()
|
|||||||
print("No more data within the queue!")
|
print("No more data within the queue!")
|
||||||
os.exit()
|
os.exit()
|
||||||
end)
|
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!
|
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
|
# ST - SystemThreadedJobQueue
|
||||||
`jq = multi:newSystemThreadedJobQueue([NUMBER: threads])` — Creates a system threaded job queue with an optional number of threads
|
`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.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
|
- `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
|
- `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 = 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
|
- `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
|
- `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.
|
**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:
|
Example:
|
||||||
```lua
|
```lua
|
||||||
package.path="?.lua;?/init.lua;?.lua;?/?/init.lua;"..package.path
|
package.path="?.lua;?/init.lua;?.lua;?/?/init.lua;"..package.path
|
||||||
multi = require("multi")
|
multi,thread = require("multi"):init()
|
||||||
GLOBAL, THREAD = require("multi.integration.lanesManager"):init()
|
GLOBAL, THREAD = require("multi.integration.threading"):init()
|
||||||
local jq = multi:newSystemThreadedJobQueue(4) -- job queue using 4 cores
|
local jq = multi:newSystemThreadedJobQueue(4) -- job queue using 4 cores
|
||||||
jq:doToAll(function()
|
jq:doToAll(function()
|
||||||
Important = 15
|
Important = 15
|
||||||
@@ -727,7 +945,7 @@ func(5,5).connect(function(ret)
|
|||||||
print("Connected",ret)
|
print("Connected",ret)
|
||||||
os.exit()
|
os.exit()
|
||||||
end)
|
end)
|
||||||
multi:lightloop()
|
multi:mainloop()
|
||||||
```
|
```
|
||||||
# ST - SystemThreadedTable
|
# ST - SystemThreadedTable
|
||||||
`stt = multi:newSystemThreadedTable(STRING: name)`
|
`stt = multi:newSystemThreadedTable(STRING: name)`
|
||||||
@@ -738,15 +956,15 @@ multi:lightloop()
|
|||||||
Example:
|
Example:
|
||||||
```lua
|
```lua
|
||||||
package.path="?.lua;?/init.lua;?.lua;?/?/init.lua;"..package.path
|
package.path="?.lua;?/init.lua;?.lua;?/?/init.lua;"..package.path
|
||||||
multi = require("multi")
|
multi,thread = require("multi"):init()
|
||||||
GLOBAL, THREAD = require("multi.integration.lanesManager"):init()
|
GLOBAL, THREAD = require("multi.integration.threading"):init()
|
||||||
local stt = multi:newSystemThreadedTable("stt")
|
local stt = multi:newSystemThreadedTable("stt")
|
||||||
stt["hello"] = "world"
|
stt["hello"] = "world"
|
||||||
multi:newSystemThread("test thread",function()
|
multi:newSystemThread("test thread",function()
|
||||||
local stt = GLOBAL["stt"]:init()
|
local stt = GLOBAL["stt"]:init()
|
||||||
print(stt["hello"])
|
print(stt["hello"])
|
||||||
end)
|
end)
|
||||||
multi:lightloop()
|
multi:mainloop()
|
||||||
```
|
```
|
||||||
# Network Threads - Multi-Integration WIP Being Reworked
|
# 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
|
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
|
||||||
+939
-72
File diff suppressed because it is too large
Load Diff
+155
-11
@@ -1,7 +1,7 @@
|
|||||||
--[[
|
--[[
|
||||||
MIT License
|
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
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -22,9 +22,15 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|||||||
SOFTWARE.
|
SOFTWARE.
|
||||||
]]
|
]]
|
||||||
local multi, thread = require("multi"):init()
|
local multi, thread = require("multi"):init()
|
||||||
local GLOBAL, THREAD = multi.integration.GLOBAL,multi.integration.THREAD
|
if not (GLOBAL and THREAD) then
|
||||||
|
local GLOBAL, THREAD = multi.integration.GLOBAL,multi.integration.THREAD
|
||||||
|
else
|
||||||
|
lanes = require("lanes")
|
||||||
|
end
|
||||||
function multi:newSystemThreadedQueue(name)
|
function multi:newSystemThreadedQueue(name)
|
||||||
|
local name = name or multi.randomString(16)
|
||||||
local c = {}
|
local c = {}
|
||||||
|
c.Name = name
|
||||||
c.linda = lanes.linda()
|
c.linda = lanes.linda()
|
||||||
function c:push(v)
|
function c:push(v)
|
||||||
self.linda:send("Q", v)
|
self.linda:send("Q", v)
|
||||||
@@ -41,9 +47,12 @@ function multi:newSystemThreadedQueue(name)
|
|||||||
GLOBAL[name or "_"] = c
|
GLOBAL[name or "_"] = c
|
||||||
return c
|
return c
|
||||||
end
|
end
|
||||||
|
|
||||||
function multi:newSystemThreadedTable(name)
|
function multi:newSystemThreadedTable(name)
|
||||||
|
local name = name or multi.randomString(16)
|
||||||
local c = {}
|
local c = {}
|
||||||
c.link = lanes.linda()
|
c.link = lanes.linda()
|
||||||
|
c.Name = name
|
||||||
setmetatable(c,{
|
setmetatable(c,{
|
||||||
__index = function(t,k)
|
__index = function(t,k)
|
||||||
return c.link:get(k)
|
return c.link:get(k)
|
||||||
@@ -58,16 +67,20 @@ function multi:newSystemThreadedTable(name)
|
|||||||
GLOBAL[name or "_"] = c
|
GLOBAL[name or "_"] = c
|
||||||
return c
|
return c
|
||||||
end
|
end
|
||||||
|
|
||||||
function multi:newSystemThreadedJobQueue(n)
|
function multi:newSystemThreadedJobQueue(n)
|
||||||
local c = {}
|
local c = {}
|
||||||
c.cores = n or THREAD.getCores()*2
|
c.cores = n or THREAD.getCores()*2
|
||||||
c.OnJobCompleted = multi:newConnection()
|
c.OnJobCompleted = multi:newConnection()
|
||||||
local funcs = multi:newSystemThreadedTable()
|
local funcs = multi:newSystemThreadedTable():init()
|
||||||
local queueJob = multi:newSystemThreadedQueue()
|
local queueJob = multi:newSystemThreadedQueue():init()
|
||||||
local queueReturn = multi:newSystemThreadedQueue()
|
local queueReturn = multi:newSystemThreadedQueue():init()
|
||||||
local doAll = multi:newSystemThreadedQueue()
|
local doAll = multi:newSystemThreadedQueue():init()
|
||||||
local ID=1
|
local ID=1
|
||||||
local jid = 1
|
local jid = 1
|
||||||
|
function c:isEmpty()
|
||||||
|
return queueJob:peek()==nil
|
||||||
|
end
|
||||||
function c:doToAll(func)
|
function c:doToAll(func)
|
||||||
for i=1,c.cores do
|
for i=1,c.cores do
|
||||||
doAll:push{ID,func}
|
doAll:push{ID,func}
|
||||||
@@ -100,7 +113,7 @@ function multi:newSystemThreadedJobQueue(n)
|
|||||||
link = c.OnJobCompleted(function(jid,...)
|
link = c.OnJobCompleted(function(jid,...)
|
||||||
if id==jid then
|
if id==jid then
|
||||||
rets = {...}
|
rets = {...}
|
||||||
link:Remove()
|
link:Destroy()
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
return thread.hold(function()
|
return thread.hold(function()
|
||||||
@@ -110,7 +123,7 @@ function multi:newSystemThreadedJobQueue(n)
|
|||||||
end)
|
end)
|
||||||
end,holup),name
|
end,holup),name
|
||||||
end
|
end
|
||||||
multi:newThread("JobQueueManager",function()
|
thread:newThread("JobQueueManager",function()
|
||||||
while true do
|
while true do
|
||||||
local job = thread.hold(function()
|
local job = thread.hold(function()
|
||||||
return queueReturn:pop()
|
return queueReturn:pop()
|
||||||
@@ -126,7 +139,7 @@ function multi:newSystemThreadedJobQueue(n)
|
|||||||
local clock = os.clock
|
local clock = os.clock
|
||||||
local ref = 0
|
local ref = 0
|
||||||
setmetatable(_G,{__index = funcs})
|
setmetatable(_G,{__index = funcs})
|
||||||
multi:newThread("JobHandler",function()
|
thread:newThread("JobHandler",function()
|
||||||
while true do
|
while true do
|
||||||
local dat = thread.hold(function()
|
local dat = thread.hold(function()
|
||||||
return queueJob:pop()
|
return queueJob:pop()
|
||||||
@@ -138,7 +151,7 @@ function multi:newSystemThreadedJobQueue(n)
|
|||||||
queueReturn:push{jid, funcs[name](unpack(args)),queue}
|
queueReturn:push{jid, funcs[name](unpack(args)),queue}
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
multi:newThread("DoAllHandler",function()
|
thread:newThread("DoAllHandler",function()
|
||||||
while true do
|
while true do
|
||||||
local dat = thread.hold(function()
|
local dat = thread.hold(function()
|
||||||
return doAll:peek()
|
return doAll:peek()
|
||||||
@@ -153,7 +166,7 @@ function multi:newSystemThreadedJobQueue(n)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
multi:newThread("IdleHandler",function()
|
thread:newThread("IdleHandler",function()
|
||||||
while true do
|
while true do
|
||||||
thread.hold(function()
|
thread.hold(function()
|
||||||
return clock()-idle>3
|
return clock()-idle>3
|
||||||
@@ -166,3 +179,134 @@ function multi:newSystemThreadedJobQueue(n)
|
|||||||
end
|
end
|
||||||
return c
|
return c
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function multi:newSystemThreadedConnection(name)
|
||||||
|
local name = name or multi.randomString(16)
|
||||||
|
local c = {}
|
||||||
|
c.CONN = 0x00
|
||||||
|
c.TRIG = 0x01
|
||||||
|
c.PING = 0x02
|
||||||
|
c.PONG = 0x03
|
||||||
|
local function remove(a, b)
|
||||||
|
local ai = {}
|
||||||
|
local r = {}
|
||||||
|
for k,v in pairs(a) do ai[v]=true end
|
||||||
|
for k,v in pairs(b) do
|
||||||
|
if ai[v]==nil then table.insert(r,a[k]) end
|
||||||
|
end
|
||||||
|
return r
|
||||||
|
end
|
||||||
|
c.CID = THREAD.getID()
|
||||||
|
c.subscribe = multi:newSystemThreadedQueue("SUB_STC_"..self.Name):init()
|
||||||
|
c.Name = name
|
||||||
|
c.links = {} -- All triggers sent from main connection. When a connection is triggered on another thread, they speak to the main then send stuff out.
|
||||||
|
-- Locals will only live in the thread that creates the original object
|
||||||
|
local ping
|
||||||
|
local pong = function(link, links)
|
||||||
|
local res = thread.hold(function()
|
||||||
|
return link:peek()[1] == c.PONG
|
||||||
|
end,{sleep=3})
|
||||||
|
|
||||||
|
if not res then
|
||||||
|
for i=1,#links do
|
||||||
|
if links[i] == link then
|
||||||
|
table.remove(links,i,link)
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
link:pop()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
ping = thread:newFunction(function(self)
|
||||||
|
ping:Pause()
|
||||||
|
multi.ForEach(self.links, function(link) -- Sync new connections
|
||||||
|
link:push{self.PING}
|
||||||
|
multi:newThread("pong Thread", pong, link, self.links)
|
||||||
|
end)
|
||||||
|
|
||||||
|
thread.sleep(3)
|
||||||
|
|
||||||
|
ping:Resume()
|
||||||
|
end,false)
|
||||||
|
|
||||||
|
local function fire(...)
|
||||||
|
for _, link in pairs(c.links) do
|
||||||
|
link:push {c.TRIG, {...}}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
thread:newThread("STC_SUB_MAN"..name,function()
|
||||||
|
local item
|
||||||
|
local sub_func = function() -- This will keep things held up until there is something to process
|
||||||
|
return c.subscribe:pop()
|
||||||
|
end
|
||||||
|
while true do
|
||||||
|
thread.yield()
|
||||||
|
-- We need to check on broken connections
|
||||||
|
ping(c) -- Should return instantlly and process this in another thread
|
||||||
|
item = thread.hold(sub_func)
|
||||||
|
if item[1] == c.CONN then
|
||||||
|
multi.ForEach(c.links, function(link) -- Sync new connections
|
||||||
|
item[2]:push{c.CONN, link}
|
||||||
|
end)
|
||||||
|
c.links[#c.links+1] = item[2]
|
||||||
|
elseif item[1] == c.TRIG then
|
||||||
|
fire(unpack(item[2]))
|
||||||
|
c.proxy_conn:Fire(unpack(item[2]))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
--- ^^^ This will only exist in the init thread
|
||||||
|
|
||||||
|
function c:Fire(...)
|
||||||
|
local args = {...}
|
||||||
|
if self.CID == THREAD.getID() then -- Host Call
|
||||||
|
for _, link in pairs(self.links) do
|
||||||
|
link:push {self.TRIG, args}
|
||||||
|
end
|
||||||
|
self.proxy_conn:Fire(...)
|
||||||
|
else
|
||||||
|
self.subscribe:push {self.TRIG, args}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function c:init()
|
||||||
|
local multi, thread = require("multi"):init()
|
||||||
|
self.links = {}
|
||||||
|
self.proxy_conn = multi:newConnection()
|
||||||
|
local mt = getmetatable(self.proxy_conn)
|
||||||
|
setmetatable(self, {__index = self.proxy_conn, __call = function(t,func) self.proxy_conn(func) end, __add = mt.__add})
|
||||||
|
if self.CID == THREAD.getID() then return self end
|
||||||
|
thread:newThread("STC_CONN_MAN"..name,function()
|
||||||
|
local item
|
||||||
|
local link_self_ref = multi:newSystemThreadedQueue()
|
||||||
|
self.subscribe:push{self.CONN, link_self_ref}
|
||||||
|
while true do
|
||||||
|
item = thread.hold(function()
|
||||||
|
return link_self_ref:peek()
|
||||||
|
end)
|
||||||
|
if item[1] == self.PING then
|
||||||
|
link_self_ref:push{self.PONG}
|
||||||
|
link_self_ref:pop()
|
||||||
|
elseif item[1] == self.CONN then
|
||||||
|
if item[2].Name ~= link_self_ref.Name then
|
||||||
|
table.insert(self.links, item[2])
|
||||||
|
end
|
||||||
|
link_self_ref:pop()
|
||||||
|
elseif item[1] == self.TRIG then
|
||||||
|
self.proxy_conn:Fire(unpack(item[2]))
|
||||||
|
link_self_ref:pop()
|
||||||
|
else
|
||||||
|
-- This shouldn't be the case
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
GLOBAL[name] = c
|
||||||
|
|
||||||
|
return c
|
||||||
|
end
|
||||||
@@ -0,0 +1,180 @@
|
|||||||
|
--[[
|
||||||
|
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.
|
||||||
|
]]
|
||||||
|
|
||||||
|
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 hassle
|
||||||
|
return {
|
||||||
|
init = function()
|
||||||
|
return multi.integration.GLOBAL, multi.integration.THREAD
|
||||||
|
end
|
||||||
|
}
|
||||||
|
end
|
||||||
|
-- Step 1 get lanes
|
||||||
|
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
|
||||||
|
local __StatusLinda = lanes.linda() -- handles pushstatus for stfunctions
|
||||||
|
|
||||||
|
local GLOBAL,THREAD = require("multi.integration.lanesManager.threads").init(__GlobalLinda, __SleepingLinda, __StatusLinda, __ConsoleLinda)
|
||||||
|
local count = 1
|
||||||
|
local started = false
|
||||||
|
local livingThreads = {}
|
||||||
|
|
||||||
|
function THREAD:newFunction(func,holdme)
|
||||||
|
return thread:newFunctionBase(function(...)
|
||||||
|
return multi:newSystemThread("TempSystemThread",func,...)
|
||||||
|
end,holdme)()
|
||||||
|
end
|
||||||
|
|
||||||
|
function multi:newSystemThread(name, func, ...)
|
||||||
|
local name = name or multi.randomString(16)
|
||||||
|
multi.InitSystemThreadErrorHandler()
|
||||||
|
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 multi_settings = multi.defaultSettings
|
||||||
|
for i,v in pairs(multi_settings) do
|
||||||
|
print(i,v)
|
||||||
|
end
|
||||||
|
c.thread = lanes.gen("*",
|
||||||
|
{
|
||||||
|
globals={ -- Set up some globals
|
||||||
|
THREAD_NAME = name,
|
||||||
|
THREAD_ID = count,
|
||||||
|
THREAD = THREAD,
|
||||||
|
GLOBAL = GLOBAL,
|
||||||
|
_Console = __ConsoleLinda
|
||||||
|
},
|
||||||
|
priority=c.priority
|
||||||
|
},function(...)
|
||||||
|
require("multi"):init(multi_settings)
|
||||||
|
require("multi.integration.lanesManager.extensions")
|
||||||
|
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()
|
||||||
|
self.alive = false
|
||||||
|
end
|
||||||
|
table.insert(multi.SystemThreads, c)
|
||||||
|
c.OnDeath = multi:newConnection()
|
||||||
|
c.OnError = multi:newConnection()
|
||||||
|
GLOBAL["__THREADS__"] = livingThreads
|
||||||
|
return c
|
||||||
|
end
|
||||||
|
|
||||||
|
THREAD.newSystemThread = multi.newSystemThread
|
||||||
|
|
||||||
|
function multi.InitSystemThreadErrorHandler()
|
||||||
|
if started == true then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
started = true
|
||||||
|
thread:newThread("SystemThreadScheduler",function()
|
||||||
|
local threads = multi.SystemThreads
|
||||||
|
local _,data,status,push,temp
|
||||||
|
while true do
|
||||||
|
thread.yield()
|
||||||
|
_,data = __ConsoleLinda:receive(0, "Q")
|
||||||
|
if data then print(unpack(data)) end
|
||||||
|
for i = #threads, 1, -1 do
|
||||||
|
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 status == "running" then
|
||||||
|
--
|
||||||
|
elseif status == "waiting" then
|
||||||
|
--
|
||||||
|
elseif status == "error" then
|
||||||
|
livingThreads[temp.Id] = {false, temp.Name}
|
||||||
|
temp.alive = false
|
||||||
|
temp.OnError:Fire(temp,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,"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,"thread_killed")
|
||||||
|
GLOBAL["__THREADS__"] = livingThreads
|
||||||
|
table.remove(threads, i)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
multi.print("Integrated Lanes!")
|
||||||
|
multi.integration = {} -- for module creators
|
||||||
|
multi.integration.GLOBAL = GLOBAL
|
||||||
|
multi.integration.THREAD = THREAD
|
||||||
|
require("multi.integration.lanesManager.extensions")
|
||||||
|
return {
|
||||||
|
init = function()
|
||||||
|
return GLOBAL, THREAD
|
||||||
|
end
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
--[[
|
--[[
|
||||||
MIT License
|
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
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -28,7 +28,8 @@ local function getOS()
|
|||||||
return "unix"
|
return "unix"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
local function INIT(__GlobalLinda,__SleepingLinda)
|
|
||||||
|
local function INIT(__GlobalLinda, __SleepingLinda, __StatusLinda, __Console)
|
||||||
local THREAD = {}
|
local THREAD = {}
|
||||||
THREAD.Priority_Core = 3
|
THREAD.Priority_Core = 3
|
||||||
THREAD.Priority_High = 2
|
THREAD.Priority_High = 2
|
||||||
@@ -37,12 +38,15 @@ local function INIT(__GlobalLinda,__SleepingLinda)
|
|||||||
THREAD.Priority_Below_Normal = -1
|
THREAD.Priority_Below_Normal = -1
|
||||||
THREAD.Priority_Low = -2
|
THREAD.Priority_Low = -2
|
||||||
THREAD.Priority_Idle = -3
|
THREAD.Priority_Idle = -3
|
||||||
|
|
||||||
function THREAD.set(name, val)
|
function THREAD.set(name, val)
|
||||||
__GlobalLinda:set(name, val)
|
__GlobalLinda:set(name, val)
|
||||||
end
|
end
|
||||||
|
|
||||||
function THREAD.get(name)
|
function THREAD.get(name)
|
||||||
__GlobalLinda:get(name)
|
return __GlobalLinda:get(name)
|
||||||
end
|
end
|
||||||
|
|
||||||
function THREAD.waitFor(name)
|
function THREAD.waitFor(name)
|
||||||
local function wait()
|
local function wait()
|
||||||
math.randomseed(os.time())
|
math.randomseed(os.time())
|
||||||
@@ -53,48 +57,64 @@ local function INIT(__GlobalLinda,__SleepingLinda)
|
|||||||
until __GlobalLinda:get(name)
|
until __GlobalLinda:get(name)
|
||||||
return __GlobalLinda:get(name)
|
return __GlobalLinda:get(name)
|
||||||
end
|
end
|
||||||
|
|
||||||
if getOS() == "windows" then
|
if getOS() == "windows" then
|
||||||
THREAD.__CORES = tonumber(os.getenv("NUMBER_OF_PROCESSORS"))
|
THREAD.__CORES = tonumber(os.getenv("NUMBER_OF_PROCESSORS"))
|
||||||
else
|
else
|
||||||
THREAD.__CORES = tonumber(io.popen("nproc --all"):read("*n"))
|
THREAD.__CORES = tonumber(io.popen("nproc --all"):read("*n"))
|
||||||
end
|
end
|
||||||
|
|
||||||
function THREAD.getCores()
|
function THREAD.getCores()
|
||||||
return THREAD.__CORES
|
return THREAD.__CORES
|
||||||
end
|
end
|
||||||
|
|
||||||
function THREAD.getConsole()
|
function THREAD.getConsole()
|
||||||
local c = {}
|
local c = {}
|
||||||
c.queue = _Console
|
c.queue = __Console
|
||||||
function c.print(...)
|
function c.print(...)
|
||||||
c.queue:send("Q", {...})
|
c.queue:send("Q", {...})
|
||||||
end
|
end
|
||||||
function c.error(err)
|
function c.error(err)
|
||||||
c.queue:push{"ERROR in <"..__THREADNAME__..">: "..err,__THREADID__}
|
c.queue:push("Q",{"ERROR in <"..__THREADNAME__..">: "..err,__THREADID__})
|
||||||
error(err)
|
error(err)
|
||||||
end
|
end
|
||||||
return c
|
return c
|
||||||
end
|
end
|
||||||
|
|
||||||
function THREAD.getThreads()
|
function THREAD.getThreads()
|
||||||
return GLOBAL.__THREADS__
|
return GLOBAL.__THREADS__
|
||||||
end
|
end
|
||||||
|
|
||||||
if os.getOS() == "windows" then
|
if os.getOS() == "windows" then
|
||||||
THREAD.__CORES = tonumber(os.getenv("NUMBER_OF_PROCESSORS"))
|
THREAD.__CORES = tonumber(os.getenv("NUMBER_OF_PROCESSORS"))
|
||||||
else
|
else
|
||||||
THREAD.__CORES = tonumber(io.popen("nproc --all"):read("*n"))
|
THREAD.__CORES = tonumber(io.popen("nproc --all"):read("*n"))
|
||||||
end
|
end
|
||||||
|
|
||||||
function THREAD.kill() -- trigger the lane destruction
|
function THREAD.kill() -- trigger the lane destruction
|
||||||
error("Thread was killed!")
|
error("Thread was killed!\1")
|
||||||
end
|
end
|
||||||
|
|
||||||
function THREAD.getName()
|
function THREAD.getName()
|
||||||
return THREAD_NAME
|
return THREAD_NAME
|
||||||
end
|
end
|
||||||
|
|
||||||
function THREAD.getID()
|
function THREAD.getID()
|
||||||
return THREAD_ID
|
return THREAD_ID
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function THREAD.pushStatus(...)
|
||||||
|
local args = {...}
|
||||||
|
__StatusLinda:send(nil,THREAD_ID, args)
|
||||||
|
end
|
||||||
|
|
||||||
_G.THREAD_ID = 0
|
_G.THREAD_ID = 0
|
||||||
|
|
||||||
function THREAD.sleep(n)
|
function THREAD.sleep(n)
|
||||||
math.randomseed(os.time())
|
math.randomseed(os.time())
|
||||||
__SleepingLinda:receive(n, "__non_existing_variable")
|
__SleepingLinda:receive(n, "__non_existing_variable")
|
||||||
end
|
end
|
||||||
|
|
||||||
function THREAD.hold(n)
|
function THREAD.hold(n)
|
||||||
local function wait()
|
local function wait()
|
||||||
math.randomseed(os.time())
|
math.randomseed(os.time())
|
||||||
@@ -104,6 +124,7 @@ local function INIT(__GlobalLinda,__SleepingLinda)
|
|||||||
wait()
|
wait()
|
||||||
until n()
|
until n()
|
||||||
end
|
end
|
||||||
|
|
||||||
local GLOBAL = {}
|
local GLOBAL = {}
|
||||||
setmetatable(GLOBAL, {
|
setmetatable(GLOBAL, {
|
||||||
__index = function(t, k)
|
__index = function(t, k)
|
||||||
@@ -115,6 +136,7 @@ local function INIT(__GlobalLinda,__SleepingLinda)
|
|||||||
})
|
})
|
||||||
return GLOBAL, THREAD
|
return GLOBAL, THREAD
|
||||||
end
|
end
|
||||||
return {init = function(g,s)
|
|
||||||
return INIT(g,s)
|
return {init = function(g,s,st,c)
|
||||||
|
return INIT(g,s,st,c)
|
||||||
end}
|
end}
|
||||||
@@ -0,0 +1,349 @@
|
|||||||
|
--[[
|
||||||
|
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.
|
||||||
|
]]
|
||||||
|
|
||||||
|
if not ISTHREAD then
|
||||||
|
multi, thread = require("multi").init()
|
||||||
|
GLOBAL = multi.integration.GLOBAL
|
||||||
|
THREAD = multi.integration.THREAD
|
||||||
|
else
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
function multi:newSystemThreadedQueue(name)
|
||||||
|
local name = name or multi.randomString(16)
|
||||||
|
local c = {}
|
||||||
|
c.Name = name
|
||||||
|
local fRef = {"func",nil}
|
||||||
|
function c:init()
|
||||||
|
local q = {}
|
||||||
|
q.chan = love.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 name = name or multi.randomString(16)
|
||||||
|
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 = love.thread.getChannel("__JobQueue_"..jqc.."_queue")
|
||||||
|
c.queueReturn = love.thread.getChannel("__JobQueue_"..jqc.."_queueReturn")
|
||||||
|
c.queueAll = love.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("love.timer")
|
||||||
|
local function atomic(channel)
|
||||||
|
return channel:pop()
|
||||||
|
end
|
||||||
|
local clock = os.clock
|
||||||
|
local funcs = THREAD.createStaticTable("__JobQueue_"..jqc.."_table")
|
||||||
|
local queue = love.thread.getChannel("__JobQueue_"..jqc.."_queue")
|
||||||
|
local queueReturn = love.thread.getChannel("__JobQueue_"..jqc.."_queueReturn")
|
||||||
|
local lastProc = clock()
|
||||||
|
local queueAll = love.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
|
||||||
|
|
||||||
|
function multi:newSystemThreadedConnection(name)
|
||||||
|
local name = name or multi.randomString(16)
|
||||||
|
local c = {}
|
||||||
|
c.CONN = 0x00
|
||||||
|
c.TRIG = 0x01
|
||||||
|
c.PING = 0x02
|
||||||
|
c.PONG = 0x03
|
||||||
|
|
||||||
|
local subscribe = multi:newSystemThreadedQueue("SUB_STC_" .. name):init()
|
||||||
|
|
||||||
|
function c:init()
|
||||||
|
|
||||||
|
self.subscribe = THREAD.waitFor("SUB_STC_" .. self.Name):init()
|
||||||
|
|
||||||
|
function self:Fire(...)
|
||||||
|
local args = {...}
|
||||||
|
if self.CID == THREAD.getID() then -- Host Call
|
||||||
|
for _, link in pairs(self.links) do
|
||||||
|
link:push{self.TRIG, args}
|
||||||
|
end
|
||||||
|
self.proxy_conn:Fire(...)
|
||||||
|
else
|
||||||
|
self.subscribe:push{self.TRIG, args}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local multi, thread = require("multi"):init()
|
||||||
|
self.links = {}
|
||||||
|
self.proxy_conn = multi:newConnection()
|
||||||
|
local mt = getmetatable(self.proxy_conn)
|
||||||
|
setmetatable(self, {__index = self.proxy_conn, __call = function(t,func) self.proxy_conn(func) end, __add = mt.__add})
|
||||||
|
if self.CID == THREAD.getID() then return self end
|
||||||
|
thread:newThread("STC_CONN_MAN" .. self.Name,function()
|
||||||
|
local item
|
||||||
|
local string_self_ref = "LSF_" .. multi.randomString(16)
|
||||||
|
local link_self_ref = multi:newSystemThreadedQueue():init()
|
||||||
|
self.subscribe:push{self.CONN, string_self_ref}
|
||||||
|
while true do
|
||||||
|
item = thread.hold(function()
|
||||||
|
return link_self_ref:peek()
|
||||||
|
end)
|
||||||
|
if item[1] == self.PING then
|
||||||
|
link_self_ref:push{self.PONG}
|
||||||
|
link_self_ref:pop()
|
||||||
|
elseif item[1] == self.CONN then
|
||||||
|
if string_self_ref ~= item[2] then
|
||||||
|
table.insert(self.links, THREAD.waitFor(item[2]):init())
|
||||||
|
end
|
||||||
|
link_self_ref:pop()
|
||||||
|
elseif item[1] == self.TRIG then
|
||||||
|
self.proxy_conn:Fire(unpack(item[2]))
|
||||||
|
link_self_ref:pop()
|
||||||
|
else
|
||||||
|
-- This shouldn't be the case
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end).OnError(print)
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
local function remove(a, b)
|
||||||
|
local ai = {}
|
||||||
|
local r = {}
|
||||||
|
for k,v in pairs(a) do ai[v]=true end
|
||||||
|
for k,v in pairs(b) do
|
||||||
|
if ai[v]==nil then table.insert(r,a[k]) end
|
||||||
|
end
|
||||||
|
return r
|
||||||
|
end
|
||||||
|
c.CID = THREAD.getID()
|
||||||
|
c.Name = name
|
||||||
|
c.links = {} -- All triggers sent from main connection. When a connection is triggered on another thread, they speak to the main then send stuff out.
|
||||||
|
-- Locals will only live in the thread that creates the original object
|
||||||
|
local ping
|
||||||
|
local pong = function(link, links)
|
||||||
|
local res = thread.hold(function()
|
||||||
|
return link:peek()[1] == c.PONG
|
||||||
|
end,{sleep=3})
|
||||||
|
|
||||||
|
if not res then
|
||||||
|
for i=1,#links do
|
||||||
|
if links[i] == link then
|
||||||
|
table.remove(links,i,link)
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
link:pop()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
ping = thread:newFunction(function(self)
|
||||||
|
ping:Pause()
|
||||||
|
|
||||||
|
multi.ForEach(self.links, function(link) -- Sync new connections
|
||||||
|
link:push{self.PING}
|
||||||
|
multi:newThread("pong Thread", pong, link, links)
|
||||||
|
end)
|
||||||
|
|
||||||
|
thread.sleep(3)
|
||||||
|
|
||||||
|
ping:Resume()
|
||||||
|
end,false)
|
||||||
|
|
||||||
|
local function fire(...)
|
||||||
|
for _, link in pairs(c.links) do
|
||||||
|
link:push {c.TRIG, {...}}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
thread:newThread("STC_SUB_MAN"..name,function()
|
||||||
|
local item
|
||||||
|
while true do
|
||||||
|
thread.yield()
|
||||||
|
-- We need to check on broken connections
|
||||||
|
ping(c) -- Should return instantlly and process this in another thread
|
||||||
|
item = thread.hold(function() -- This will keep things held up until there is something to process
|
||||||
|
return c.subscribe:pop()
|
||||||
|
end)
|
||||||
|
if item[1] == c.CONN then
|
||||||
|
multi.ForEach(c.links, function(link) -- Sync new connections
|
||||||
|
THREAD.waitFor(item[2]):init():push{c.CONN, link.Name}
|
||||||
|
end)
|
||||||
|
print("Adding link")
|
||||||
|
c.links[#c.links+1] = THREAD.waitFor(item[2]):init()
|
||||||
|
elseif item[1] == c.TRIG then
|
||||||
|
fire(unpack(item[2]))
|
||||||
|
c.proxy_conn:Fire(unpack(item[2]))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end).OnError(print)
|
||||||
|
--- ^^^ This will only exist in the init thread
|
||||||
|
|
||||||
|
THREAD.package(name,c)
|
||||||
|
|
||||||
|
return c
|
||||||
|
end
|
||||||
@@ -0,0 +1,116 @@
|
|||||||
|
--[[
|
||||||
|
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.
|
||||||
|
]]
|
||||||
|
if ISTHREAD then
|
||||||
|
error("You cannot require the loveManager from within a thread!")
|
||||||
|
end
|
||||||
|
local ThreadFileData = [[
|
||||||
|
ISTHREAD = true
|
||||||
|
THREAD = require("multi.integration.loveManager.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()
|
||||||
|
print(pcall(require,"multi.integration.loveManager.extensions"))
|
||||||
|
stab["returns"] = {THREAD.loadDump(__FUNC__)(unpack(__IMPORTS))}
|
||||||
|
]]
|
||||||
|
local multi, thread = require("multi"):init()
|
||||||
|
local THREAD = {}
|
||||||
|
__THREADID__ = 0
|
||||||
|
__THREADNAME__ = "MainThread"
|
||||||
|
multi.integration={}
|
||||||
|
multi.integration.love2d={}
|
||||||
|
local THREAD = require("multi.integration.loveManager.threads")
|
||||||
|
local GLOBAL = THREAD.getGlobal()
|
||||||
|
local THREAD_ID = 1
|
||||||
|
local OBJECT_ID = 0
|
||||||
|
local stf = 0
|
||||||
|
|
||||||
|
function multi:newSystemThread(name,func,...)
|
||||||
|
local c = {}
|
||||||
|
c.name = name
|
||||||
|
c.ID=THREAD_ID
|
||||||
|
c.thread=love.thread.newThread(ThreadFileData)
|
||||||
|
c.thread:start(THREAD.dump(func),c.ID,c.name,...)
|
||||||
|
c.stab = THREAD.createStaticTable(name)
|
||||||
|
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
|
||||||
|
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 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)
|
||||||
|
multi.print("Thread error!\n"..errorstr)
|
||||||
|
end
|
||||||
|
|
||||||
|
multi.integration.GLOBAL = GLOBAL
|
||||||
|
multi.integration.THREAD = THREAD
|
||||||
|
require("multi.integration.loveManager.extensions")
|
||||||
|
multi.print("Integrated Love Threading!")
|
||||||
|
return {init=function()
|
||||||
|
return GLOBAL,THREAD
|
||||||
|
end}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
--[[
|
--[[
|
||||||
MIT License
|
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
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -24,15 +24,19 @@ SOFTWARE.
|
|||||||
require("love.timer")
|
require("love.timer")
|
||||||
require("love.system")
|
require("love.system")
|
||||||
require("love.data")
|
require("love.data")
|
||||||
|
require("love.thread")
|
||||||
local socket = require("socket")
|
local socket = require("socket")
|
||||||
local multi, thread = require("multi").init()
|
local multi, thread = require("multi").init()
|
||||||
local threads = {}
|
local threads = {}
|
||||||
|
|
||||||
function threads.loadDump(d)
|
function threads.loadDump(d)
|
||||||
return loadstring(d:getString())
|
return loadstring(d:getString())
|
||||||
end
|
end
|
||||||
|
|
||||||
function threads.dump(func)
|
function threads.dump(func)
|
||||||
return love.data.newByteData(string.dump(func))
|
return love.data.newByteData(string.dump(func))
|
||||||
end
|
end
|
||||||
|
|
||||||
local fRef = {"func",nil}
|
local fRef = {"func",nil}
|
||||||
local function manage(channel, value)
|
local function manage(channel, value)
|
||||||
channel:clear()
|
channel:clear()
|
||||||
@@ -44,6 +48,7 @@ local function manage(channel, value)
|
|||||||
channel:push(value)
|
channel:push(value)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function RandomVariable(length)
|
local function RandomVariable(length)
|
||||||
local res = {}
|
local res = {}
|
||||||
math.randomseed(socket.gettime()*10000)
|
math.randomseed(socket.gettime()*10000)
|
||||||
@@ -52,12 +57,14 @@ local function RandomVariable(length)
|
|||||||
end
|
end
|
||||||
return table.concat(res)
|
return table.concat(res)
|
||||||
end
|
end
|
||||||
|
|
||||||
local GNAME = "__GLOBAL_"
|
local GNAME = "__GLOBAL_"
|
||||||
local proxy = {}
|
local proxy = {}
|
||||||
function threads.set(name,val)
|
function threads.set(name,val)
|
||||||
if not proxy[name] then proxy[name] = love.thread.getChannel(GNAME..name) end
|
if not proxy[name] then proxy[name] = love.thread.getChannel(GNAME..name) end
|
||||||
proxy[name]:performAtomic(manage, val)
|
proxy[name]:performAtomic(manage, val)
|
||||||
end
|
end
|
||||||
|
|
||||||
function threads.get(name)
|
function threads.get(name)
|
||||||
if not proxy[name] then proxy[name] = love.thread.getChannel(GNAME..name) end
|
if not proxy[name] then proxy[name] = love.thread.getChannel(GNAME..name) end
|
||||||
local dat = proxy[name]:peek()
|
local dat = proxy[name]:peek()
|
||||||
@@ -67,6 +74,7 @@ function threads.get(name)
|
|||||||
return dat
|
return dat
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function threads.waitFor(name)
|
function threads.waitFor(name)
|
||||||
if thread.isThread() then
|
if thread.isThread() then
|
||||||
return thread.hold(function()
|
return thread.hold(function()
|
||||||
@@ -82,18 +90,28 @@ function threads.waitFor(name)
|
|||||||
end
|
end
|
||||||
return dat
|
return dat
|
||||||
end
|
end
|
||||||
|
|
||||||
function threads.package(name,val)
|
function threads.package(name,val)
|
||||||
local init = val.init
|
local init = val.init
|
||||||
val.init=threads.dump(val.init)
|
val.init=threads.dump(val.init)
|
||||||
GLOBAL[name]=val
|
GLOBAL[name]=val
|
||||||
val.init=init
|
val.init=init
|
||||||
end
|
end
|
||||||
|
|
||||||
function threads.getCores()
|
function threads.getCores()
|
||||||
return love.system.getProcessorCount()
|
return love.system.getProcessorCount()
|
||||||
end
|
end
|
||||||
|
|
||||||
function threads.kill()
|
function threads.kill()
|
||||||
error("Thread Killed!")
|
error("Thread Killed!\1")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function threads.pushStatus(...)
|
||||||
|
local status_channel = love.thread.getChannel("__"..__THREADID__.."__MULTI__STATUS_CHANNEL__")
|
||||||
|
local args = {...}
|
||||||
|
status_channel:push(__THREADID__, args)
|
||||||
|
end
|
||||||
|
|
||||||
function threads.getThreads()
|
function threads.getThreads()
|
||||||
local t = {}
|
local t = {}
|
||||||
for i=1,GLOBAL["__THREAD_COUNT"] do
|
for i=1,GLOBAL["__THREAD_COUNT"] do
|
||||||
@@ -101,18 +119,23 @@ function threads.getThreads()
|
|||||||
end
|
end
|
||||||
return t
|
return t
|
||||||
end
|
end
|
||||||
|
|
||||||
function threads.getThread(n)
|
function threads.getThread(n)
|
||||||
return GLOBAL["__THREAD_"..n]
|
return GLOBAL["__THREAD_"..n]
|
||||||
end
|
end
|
||||||
|
|
||||||
function threads.getName()
|
function threads.getName()
|
||||||
return __THREADNAME__
|
return __THREADNAME__
|
||||||
end
|
end
|
||||||
|
|
||||||
function threads.getID()
|
function threads.getID()
|
||||||
return __THREADID__
|
return __THREADID__
|
||||||
end
|
end
|
||||||
|
|
||||||
function threads.sleep(n)
|
function threads.sleep(n)
|
||||||
love.timer.sleep(n)
|
love.timer.sleep(n)
|
||||||
end
|
end
|
||||||
|
|
||||||
function threads.getGlobal()
|
function threads.getGlobal()
|
||||||
return setmetatable({},
|
return setmetatable({},
|
||||||
{
|
{
|
||||||
@@ -125,6 +148,7 @@ function threads.getGlobal()
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
function threads.createTable(n)
|
function threads.createTable(n)
|
||||||
local _proxy = {}
|
local _proxy = {}
|
||||||
local function set(name,val)
|
local function set(name,val)
|
||||||
@@ -151,6 +175,7 @@ function threads.createTable(n)
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
function threads.getConsole()
|
function threads.getConsole()
|
||||||
local c = {}
|
local c = {}
|
||||||
c.queue = love.thread.getChannel("__CONSOLE__")
|
c.queue = love.thread.getChannel("__CONSOLE__")
|
||||||
@@ -163,11 +188,12 @@ function threads.getConsole()
|
|||||||
end
|
end
|
||||||
return c
|
return c
|
||||||
end
|
end
|
||||||
|
|
||||||
if not ISTHREAD then
|
if not ISTHREAD then
|
||||||
local clock = os.clock
|
local clock = os.clock
|
||||||
local lastproc = clock()
|
local lastproc = clock()
|
||||||
local queue = love.thread.getChannel("__CONSOLE__")
|
local queue = love.thread.getChannel("__CONSOLE__")
|
||||||
multi:newThread("consoleManager",function()
|
thread:newThread("consoleManager",function()
|
||||||
while true do
|
while true do
|
||||||
thread.yield()
|
thread.yield()
|
||||||
dat = queue:pop()
|
dat = queue:pop()
|
||||||
@@ -181,6 +207,7 @@ if not ISTHREAD then
|
|||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
function threads.createStaticTable(n)
|
function threads.createStaticTable(n)
|
||||||
local __proxy = {}
|
local __proxy = {}
|
||||||
local function set(name,val)
|
local function set(name,val)
|
||||||
@@ -212,10 +239,12 @@ function threads.createStaticTable(n)
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
function threads.hold(n)
|
function threads.hold(n)
|
||||||
local dat
|
local dat
|
||||||
while not(dat) do
|
while not(dat) do
|
||||||
dat = n()
|
dat = n()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return threads
|
return threads
|
||||||
+17
-14
@@ -1,7 +1,7 @@
|
|||||||
--[[
|
--[[
|
||||||
MIT License
|
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
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -30,7 +30,7 @@ function multi:newSystemThreadedQueue(name)
|
|||||||
local fRef = {"func",nil}
|
local fRef = {"func",nil}
|
||||||
function c:init()
|
function c:init()
|
||||||
local q = {}
|
local q = {}
|
||||||
q.chan = love.thread.getChannel(self.Name)
|
q.chan = lovr.thread.getChannel(self.Name)
|
||||||
function q:push(dat)
|
function q:push(dat)
|
||||||
if type(dat) == "function" then
|
if type(dat) == "function" then
|
||||||
fRef[2] = THREAD.dump(dat)
|
fRef[2] = THREAD.dump(dat)
|
||||||
@@ -76,9 +76,9 @@ function multi:newSystemThreadedJobQueue(n)
|
|||||||
c.cores = n or THREAD.getCores()
|
c.cores = n or THREAD.getCores()
|
||||||
c.registerQueue = {}
|
c.registerQueue = {}
|
||||||
c.funcs = THREAD.createStaticTable("__JobQueue_"..jqc.."_table")
|
c.funcs = THREAD.createStaticTable("__JobQueue_"..jqc.."_table")
|
||||||
c.queue = love.thread.getChannel("__JobQueue_"..jqc.."_queue")
|
c.queue = lovr.thread.getChannel("__JobQueue_"..jqc.."_queue")
|
||||||
c.queueReturn = love.thread.getChannel("__JobQueue_"..jqc.."_queueReturn")
|
c.queueReturn = lovr.thread.getChannel("__JobQueue_"..jqc.."_queueReturn")
|
||||||
c.queueAll = love.thread.getChannel("__JobQueue_"..jqc.."_queueAll")
|
c.queueAll = lovr.thread.getChannel("__JobQueue_"..jqc.."_queueAll")
|
||||||
c.id = 0
|
c.id = 0
|
||||||
c.OnJobCompleted = multi:newConnection()
|
c.OnJobCompleted = multi:newConnection()
|
||||||
local allfunc = 0
|
local allfunc = 0
|
||||||
@@ -100,6 +100,9 @@ function multi:newSystemThreadedJobQueue(n)
|
|||||||
self.queue:push{name,self.id,...}
|
self.queue:push{name,self.id,...}
|
||||||
return self.id
|
return self.id
|
||||||
end
|
end
|
||||||
|
function c:isEmpty()
|
||||||
|
return queueJob:peek()==nil
|
||||||
|
end
|
||||||
local nFunc = 0
|
local nFunc = 0
|
||||||
function c:newFunction(name,func,holup) -- This registers with the queue
|
function c:newFunction(name,func,holup) -- This registers with the queue
|
||||||
if type(name)=="function" then
|
if type(name)=="function" then
|
||||||
@@ -116,7 +119,7 @@ function multi:newSystemThreadedJobQueue(n)
|
|||||||
link = c.OnJobCompleted(function(jid,...)
|
link = c.OnJobCompleted(function(jid,...)
|
||||||
if id==jid then
|
if id==jid then
|
||||||
rets = {...}
|
rets = {...}
|
||||||
link:Remove()
|
link:Destroy()
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
return thread.hold(function()
|
return thread.hold(function()
|
||||||
@@ -126,7 +129,7 @@ function multi:newSystemThreadedJobQueue(n)
|
|||||||
end)
|
end)
|
||||||
end,holup),name
|
end,holup),name
|
||||||
end
|
end
|
||||||
multi:newThread("jobManager",function()
|
thread:newThread("jobManager",function()
|
||||||
while true do
|
while true do
|
||||||
thread.yield()
|
thread.yield()
|
||||||
local dat = c.queueReturn:pop()
|
local dat = c.queueReturn:pop()
|
||||||
@@ -138,19 +141,19 @@ function multi:newSystemThreadedJobQueue(n)
|
|||||||
for i=1,c.cores do
|
for i=1,c.cores do
|
||||||
multi:newSystemThread("JobQueue_"..jqc.."_worker_"..i,function(jqc)
|
multi:newSystemThread("JobQueue_"..jqc.."_worker_"..i,function(jqc)
|
||||||
local multi, thread = require("multi"):init()
|
local multi, thread = require("multi"):init()
|
||||||
require("love.timer")
|
require("lovr.timer")
|
||||||
local function atomic(channel)
|
local function atomic(channel)
|
||||||
return channel:pop()
|
return channel:pop()
|
||||||
end
|
end
|
||||||
local clock = os.clock
|
local clock = os.clock
|
||||||
local funcs = THREAD.createStaticTable("__JobQueue_"..jqc.."_table")
|
local funcs = THREAD.createStaticTable("__JobQueue_"..jqc.."_table")
|
||||||
local queue = love.thread.getChannel("__JobQueue_"..jqc.."_queue")
|
local queue = lovr.thread.getChannel("__JobQueue_"..jqc.."_queue")
|
||||||
local queueReturn = love.thread.getChannel("__JobQueue_"..jqc.."_queueReturn")
|
local queueReturn = lovr.thread.getChannel("__JobQueue_"..jqc.."_queueReturn")
|
||||||
local lastProc = clock()
|
local lastProc = clock()
|
||||||
local queueAll = love.thread.getChannel("__JobQueue_"..jqc.."_queueAll")
|
local queueAll = lovr.thread.getChannel("__JobQueue_"..jqc.."_queueAll")
|
||||||
local registry = {}
|
local registry = {}
|
||||||
setmetatable(_G,{__index = funcs})
|
setmetatable(_G,{__index = funcs})
|
||||||
multi:newThread("startUp",function()
|
thread:newThread("startUp",function()
|
||||||
while true do
|
while true do
|
||||||
thread.yield()
|
thread.yield()
|
||||||
local all = queueAll:peek()
|
local all = queueAll:peek()
|
||||||
@@ -160,7 +163,7 @@ function multi:newSystemThreadedJobQueue(n)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
multi:newThread("runner",function()
|
thread:newThread("runner",function()
|
||||||
thread.sleep(.1)
|
thread.sleep(.1)
|
||||||
while true do
|
while true do
|
||||||
thread.yield()
|
thread.yield()
|
||||||
@@ -182,7 +185,7 @@ function multi:newSystemThreadedJobQueue(n)
|
|||||||
end):OnError(function(...)
|
end):OnError(function(...)
|
||||||
error(...)
|
error(...)
|
||||||
end)
|
end)
|
||||||
multi:newThread("Idler",function()
|
thread:newThread("Idler",function()
|
||||||
while true do
|
while true do
|
||||||
thread.yield()
|
thread.yield()
|
||||||
if clock()-lastProc> 2 then
|
if clock()-lastProc> 2 then
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
--[[
|
--[[
|
||||||
MIT License
|
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
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -21,12 +21,13 @@ 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
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
SOFTWARE.
|
SOFTWARE.
|
||||||
]]
|
]]
|
||||||
|
-- TODO make compatible with lovr
|
||||||
if ISTHREAD then
|
if ISTHREAD then
|
||||||
error("You cannot require the loveManager from within a thread!")
|
error("You cannot require the lovrManager from within a thread!")
|
||||||
end
|
end
|
||||||
local ThreadFileData = [[
|
local ThreadFileData = [[
|
||||||
ISTHREAD = true
|
ISTHREAD = true
|
||||||
THREAD = require("multi.integration.loveManager.threads") -- order is important!
|
THREAD = require("multi.integration.lovrManager.threads") -- order is important!
|
||||||
sThread = THREAD
|
sThread = THREAD
|
||||||
__IMPORTS = {...}
|
__IMPORTS = {...}
|
||||||
__FUNC__=table.remove(__IMPORTS,1)
|
__FUNC__=table.remove(__IMPORTS,1)
|
||||||
@@ -37,13 +38,13 @@ GLOBAL = THREAD.getGlobal()
|
|||||||
multi, thread = require("multi").init()
|
multi, thread = require("multi").init()
|
||||||
stab["returns"] = {THREAD.loadDump(__FUNC__)(unpack(__IMPORTS))}
|
stab["returns"] = {THREAD.loadDump(__FUNC__)(unpack(__IMPORTS))}
|
||||||
]]
|
]]
|
||||||
local multi, thread = require("multi.compat.love2d"):init()
|
local multi, thread = require("multi.compat.lovr2d"):init()
|
||||||
local THREAD = {}
|
local THREAD = {}
|
||||||
__THREADID__ = 0
|
__THREADID__ = 0
|
||||||
__THREADNAME__ = "MainThread"
|
__THREADNAME__ = "MainThread"
|
||||||
multi.integration={}
|
multi.integration={}
|
||||||
multi.integration.love2d={}
|
multi.integration.lovr2d={}
|
||||||
local THREAD = require("multi.integration.loveManager.threads")
|
local THREAD = require("multi.integration.lovrManager.threads")
|
||||||
local GLOBAL = THREAD.getGlobal()
|
local GLOBAL = THREAD.getGlobal()
|
||||||
local THREAD_ID = 1
|
local THREAD_ID = 1
|
||||||
local OBJECT_ID = 0
|
local OBJECT_ID = 0
|
||||||
@@ -67,7 +68,7 @@ function multi:newSystemThread(name,func,...)
|
|||||||
local c = {}
|
local c = {}
|
||||||
c.name = name
|
c.name = name
|
||||||
c.ID=THREAD_ID
|
c.ID=THREAD_ID
|
||||||
c.thread=love.thread.newThread(ThreadFileData)
|
c.thread=lovr.thread.newThread(ThreadFileData)
|
||||||
c.thread:start(THREAD.dump(func),c.ID,c.name,...)
|
c.thread:start(THREAD.dump(func),c.ID,c.name,...)
|
||||||
c.stab = THREAD.createStaticTable(name)
|
c.stab = THREAD.createStaticTable(name)
|
||||||
GLOBAL["__THREAD_"..c.ID] = {ID=c.ID,Name=c.name,Thread=c.thread}
|
GLOBAL["__THREAD_"..c.ID] = {ID=c.ID,Name=c.name,Thread=c.thread}
|
||||||
@@ -75,12 +76,14 @@ function multi:newSystemThread(name,func,...)
|
|||||||
THREAD_ID=THREAD_ID+1
|
THREAD_ID=THREAD_ID+1
|
||||||
return c
|
return c
|
||||||
end
|
end
|
||||||
function love.threaderror(thread, errorstr)
|
THREAD.newSystemThread = multi.newSystemThread
|
||||||
|
function lovr.threaderror(thread, errorstr)
|
||||||
print("Thread error!\n"..errorstr)
|
print("Thread error!\n"..errorstr)
|
||||||
end
|
end
|
||||||
multi.integration.GLOBAL = GLOBAL
|
multi.integration.GLOBAL = GLOBAL
|
||||||
multi.integration.THREAD = THREAD
|
multi.integration.THREAD = THREAD
|
||||||
require("multi.integration.loveManager.extensions")
|
require("multi.integration.lovrManager.extensions")
|
||||||
|
print("Integrated lovr Threading!")
|
||||||
return {init=function()
|
return {init=function()
|
||||||
return GLOBAL,THREAD
|
return GLOBAL,THREAD
|
||||||
end}
|
end}
|
||||||
@@ -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
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
--[[
|
--[[
|
||||||
MIT License
|
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
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
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, ...)
|
luvitThread.start(entry, package.path, name, c.func, ...)
|
||||||
return c
|
return c
|
||||||
end
|
end
|
||||||
|
THREAD.newSystemThread = multi.newSystemThread
|
||||||
multi.print("Integrated Luvit!")
|
multi.print("Integrated Luvit!")
|
||||||
multi.integration = {} -- for module creators
|
multi.integration = {} -- for module creators
|
||||||
multi.integration.GLOBAL = GLOBAL
|
multi.integration.GLOBAL = GLOBAL
|
||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
--[[
|
--[[
|
||||||
MIT License
|
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
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
--[[
|
--[[
|
||||||
MIT License
|
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
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
--[[
|
--[[
|
||||||
MIT License
|
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
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
--[[
|
--[[
|
||||||
MIT License
|
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
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
--[[
|
--[[
|
||||||
MIT License
|
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
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
--[[
|
--[[
|
||||||
MIT License
|
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
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -23,7 +23,7 @@ SOFTWARE.
|
|||||||
]]
|
]]
|
||||||
local multi, thread = require("multi"):init()
|
local multi, thread = require("multi"):init()
|
||||||
local net = require("net")
|
local net = require("net")
|
||||||
local bin = require("bin")
|
--local bin = require("bin")
|
||||||
local char = string.char
|
local char = string.char
|
||||||
local byte = string.byte
|
local byte = string.byte
|
||||||
bin.setBitsInterface(infinabits)
|
bin.setBitsInterface(infinabits)
|
||||||
+3
-3
@@ -1,7 +1,7 @@
|
|||||||
--[[
|
--[[
|
||||||
MIT License
|
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
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -72,7 +72,7 @@ function master:newNetworkThread(nodeName,func,...)
|
|||||||
local ret
|
local ret
|
||||||
local nID = netID
|
local nID = netID
|
||||||
local conn = multi:newConnection()
|
local conn = multi:newConnection()
|
||||||
multi:newThread(function()
|
thread:newthread(function()
|
||||||
dat:addBlock{
|
dat:addBlock{
|
||||||
args = args,
|
args = args,
|
||||||
func = func,
|
func = func,
|
||||||
@@ -143,7 +143,7 @@ function multi:newMasterNode(cd)
|
|||||||
else
|
else
|
||||||
c:getNodesFromBroadcast()
|
c:getNodesFromBroadcast()
|
||||||
end
|
end
|
||||||
multi:newThread("CMDQueueProcessor",function()
|
thread:newthread("CMDQueueProcessor",function()
|
||||||
while true do
|
while true do
|
||||||
thread.skip(128)
|
thread.skip(128)
|
||||||
local data = table.remove(c._queue,1)
|
local data = table.remove(c._queue,1)
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
--[[
|
--[[
|
||||||
MIT License
|
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
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
--[[
|
--[[
|
||||||
MIT License
|
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
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
--[[
|
--[[
|
||||||
MIT License
|
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
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
--[[
|
--[[
|
||||||
MIT License
|
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
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
--[[
|
--[[
|
||||||
MIT License
|
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
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
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
|
||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -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}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -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
|
|
||||||
-2188
File diff suppressed because it is too large
Load Diff
@@ -1,157 +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.
|
|
||||||
]]
|
|
||||||
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
|
|
||||||
return {
|
|
||||||
init = function()
|
|
||||||
return multi.integration.GLOBAL, multi.integration.THREAD
|
|
||||||
end
|
|
||||||
}
|
|
||||||
end
|
|
||||||
-- Step 1 get lanes
|
|
||||||
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 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
|
|
||||||
end
|
|
||||||
function multi:newSystemThread(name, func, ...)
|
|
||||||
multi.InitSystemThreadErrorHandler()
|
|
||||||
rand = math.random(1, 10000000)
|
|
||||||
local c = {}
|
|
||||||
local __self = 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.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,","),
|
|
||||||
{
|
|
||||||
globals={ -- Set up some globals
|
|
||||||
THREAD_NAME=name,
|
|
||||||
THREAD_ID=count,
|
|
||||||
THREAD = THREAD,
|
|
||||||
GLOBAL = GLOBAL,
|
|
||||||
_Console = __ConsoleLinda
|
|
||||||
},
|
|
||||||
priority=c.priority
|
|
||||||
},func)(unpack(args))
|
|
||||||
thread.kill()
|
|
||||||
end)
|
|
||||||
count = count + 1
|
|
||||||
function c:kill()
|
|
||||||
self.thread:cancel()
|
|
||||||
multi.print("Thread: '" .. self.name .. "' has been stopped!")
|
|
||||||
self.alive = false
|
|
||||||
end
|
|
||||||
table.insert(multi.SystemThreads, c)
|
|
||||||
c.OnError = multi:newConnection()
|
|
||||||
GLOBAL["__THREADS__"] = livingThreads
|
|
||||||
return c
|
|
||||||
end
|
|
||||||
multi.OnSystemThreadDied = multi:newConnection()
|
|
||||||
function multi.InitSystemThreadErrorHandler()
|
|
||||||
if started == true then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
started = true
|
|
||||||
multi:newThread(
|
|
||||||
"ThreadErrorHandler",
|
|
||||||
function()
|
|
||||||
local threads = multi.SystemThreads
|
|
||||||
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.
|
|
||||||
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)
|
|
||||||
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)
|
|
||||||
GLOBAL["__THREADS__"] = livingThreads
|
|
||||||
table.remove(threads, i)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
)
|
|
||||||
end
|
|
||||||
multi.print("Integrated Lanes!")
|
|
||||||
multi.integration = {} -- for module creators
|
|
||||||
multi.integration.GLOBAL = GLOBAL
|
|
||||||
multi.integration.THREAD = THREAD
|
|
||||||
require("multi.integration.lanesManager.extensions")
|
|
||||||
return {
|
|
||||||
init = function()
|
|
||||||
return GLOBAL, THREAD
|
|
||||||
end
|
|
||||||
}
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
-- Planned system threaded objects
|
|
||||||
-- multi:newSystemThreadedConnection(name, protect)
|
|
||||||
@@ -23,11 +23,11 @@ build = {
|
|||||||
["multi"] = "multi/init.lua",
|
["multi"] = "multi/init.lua",
|
||||||
["multi.compat.love2d"] = "multi/compat/love2d.lua",
|
["multi.compat.love2d"] = "multi/compat/love2d.lua",
|
||||||
["multi.integration.lanesManager"] = "multi/integration/lanesManager/init.lua",
|
["multi.integration.lanesManager"] = "multi/integration/lanesManager/init.lua",
|
||||||
["multi.integration.lanesManager.extensions"] = "multi/integration/lanesManager/extensions.lua",
|
["multi.integration.lanesManager.extensions"] = "multi/integration/lanesManager/extensions.lua",
|
||||||
["multi.integration.lanesManager.threads"] = "multi/integration/lanesManager/threads.lua",
|
["multi.integration.lanesManager.threads"] = "multi/integration/lanesManager/threads.lua",
|
||||||
["multi.integration.loveManager"] = "multi/integration/loveManager/init.lua",
|
["multi.integration.loveManager"] = "multi/integration/loveManager/init.lua",
|
||||||
["multi.integration.loveManager.extensions"] = "multi/integration/loveManager/extensions.lua",
|
["multi.integration.loveManager.extensions"] = "multi/integration/loveManager/extensions.lua",
|
||||||
["multi.integration.loveManager.threads"] = "multi/integration/loveManager/threads.lua",
|
["multi.integration.loveManager.threads"] = "multi/integration/loveManager/threads.lua",
|
||||||
["multi.integration.luvitManager"] = "multi/integration/luvitManager.lua",
|
["multi.integration.luvitManager"] = "multi/integration/luvitManager.lua",
|
||||||
--["multi.integration.networkManager"] = "multi/integration/networkManager.lua",
|
--["multi.integration.networkManager"] = "multi/integration/networkManager.lua",
|
||||||
["multi.integration.shared"] = "multi/integration/shared.lua"
|
["multi.integration.shared"] = "multi/integration/shared.lua"
|
||||||
|
|||||||
@@ -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",
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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",
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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",
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
package = "multi"
|
||||||
|
version = "15.2-1"
|
||||||
|
source = {
|
||||||
|
url = "git://github.com/rayaman/multi.git",
|
||||||
|
tag = "v15.2.1",
|
||||||
|
}
|
||||||
|
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",
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
package = "multi"
|
||||||
|
version = "15.3-0"
|
||||||
|
source = {
|
||||||
|
url = "git://github.com/rayaman/multi.git",
|
||||||
|
tag = "v15.3.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"] = "init.lua",
|
||||||
|
["multi.integration.lanesManager"] = "integration/lanesManager/init.lua",
|
||||||
|
["multi.integration.lanesManager.extensions"] = "integration/lanesManager/extensions.lua",
|
||||||
|
["multi.integration.lanesManager.threads"] = "integration/lanesManager/threads.lua",
|
||||||
|
["multi.integration.loveManager"] = "integration/loveManager/init.lua",
|
||||||
|
["multi.integration.loveManager.extensions"] = "integration/loveManager/extensions.lua",
|
||||||
|
["multi.integration.loveManager.threads"] = "integration/loveManager/threads.lua",
|
||||||
|
--["multi.integration.lovrManager"] = "integration/lovrManager/init.lua",
|
||||||
|
--["multi.integration.lovrManager.extensions"] = "integration/lovrManager/extensions.lua",
|
||||||
|
--["multi.integration.lovrManager.threads"] = "integration/lovrManager/threads.lua",
|
||||||
|
["multi.integration.pesudoManager"] = "integration/pesudoManager/init.lua",
|
||||||
|
["multi.integration.pesudoManager.extensions"] = "integration/pesudoManager/extensions.lua",
|
||||||
|
["multi.integration.pesudoManager.threads"] = "integration/pesudoManager/threads.lua",
|
||||||
|
["multi.integration.luvitManager"] = "integration/luvitManager.lua",
|
||||||
|
["multi.integration.threading"] = "integration/threading.lua",
|
||||||
|
--["multi.integration.networkManager"] = "integration/networkManager.lua",
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
package.path="?.lua;?/init.lua;?.lua;?/?/init.lua;"..package.path
|
|
||||||
local multi,thread = require("multi"):init()
|
|
||||||
local GLOBAL, THREAD = require("multi.integration.lanesManager"):init()
|
|
||||||
multi:newSystemThread("test",function(msg)
|
|
||||||
print("In thread:", THREAD.getID(), "Msg:", msg)
|
|
||||||
while true do
|
|
||||||
-- Hold forever :D
|
|
||||||
end
|
|
||||||
end,"Passing a message")
|
|
||||||
multi:newThread("localthread",function()
|
|
||||||
print("In local thread :D")
|
|
||||||
while true do
|
|
||||||
thread.sleep(1)
|
|
||||||
print("...")
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
multi:mainloop()
|
|
||||||
@@ -0,0 +1,185 @@
|
|||||||
|
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 = "../?/init.lua;../?.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", true, math.random(1,10000)
|
||||||
|
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: All tests Ok")
|
||||||
|
else
|
||||||
|
if s1>0 and s2>0 and s3 > 0 then
|
||||||
|
print("Thread OnStatus: Ok")
|
||||||
|
else
|
||||||
|
print("Threads OnStatus or thread.hold(conn) Error!")
|
||||||
|
end
|
||||||
|
if timeout then
|
||||||
|
print("Connection Error!")
|
||||||
|
else
|
||||||
|
print("Connection Test 1: Ok")
|
||||||
|
end
|
||||||
|
print("Connection holding Error!")
|
||||||
|
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)
|
||||||
|
|
||||||
|
print(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
|
||||||
+133
@@ -0,0 +1,133 @@
|
|||||||
|
package.path = "../?/init.lua;../?.lua;"..package.path
|
||||||
|
multi, thread = require("multi"):init{print=true,findopt=true}
|
||||||
|
GLOBAL, THREAD = require("multi.integration.lanesManager"):init()
|
||||||
|
multi:getOptimizationConnection()(function(msg)
|
||||||
|
print(msg)
|
||||||
|
end)
|
||||||
|
|
||||||
|
local conn1, conn2, conn3 = multi:newConnection(), multi:newConnection():fastMode(), multi:newConnection()
|
||||||
|
|
||||||
|
local link = conn1(function()
|
||||||
|
print("Conn1, first")
|
||||||
|
end)
|
||||||
|
|
||||||
|
local link2 = conn1(function()
|
||||||
|
print("Conn1, second")
|
||||||
|
end)
|
||||||
|
|
||||||
|
local link3 = conn1(function()
|
||||||
|
print("Conn1, third")
|
||||||
|
end)
|
||||||
|
|
||||||
|
local link4 = conn2(function()
|
||||||
|
print("Conn2, first")
|
||||||
|
end)
|
||||||
|
|
||||||
|
local link5 = conn2(function()
|
||||||
|
print("Conn2, second")
|
||||||
|
end)
|
||||||
|
|
||||||
|
local link6 = conn2(function()
|
||||||
|
print("Conn2, third")
|
||||||
|
end)
|
||||||
|
|
||||||
|
print("All conns\n-------------")
|
||||||
|
conn1:Fire()
|
||||||
|
conn2:Fire()
|
||||||
|
|
||||||
|
conn1:Unconnect(link3)
|
||||||
|
conn2:Unconnect(link6)
|
||||||
|
print("All conns Edit\n---------------------")
|
||||||
|
conn1:Fire()
|
||||||
|
conn2:Fire()
|
||||||
|
|
||||||
|
-- thread:newThread(function()
|
||||||
|
-- print("Awaiting status")
|
||||||
|
-- thread.hold(conn1 + (conn2 * conn3))
|
||||||
|
-- print("Conn or Conn2 and Conn3")
|
||||||
|
-- end)
|
||||||
|
|
||||||
|
-- multi:newAlarm(1):OnRing(function()
|
||||||
|
-- print("Conn")
|
||||||
|
-- conn1:Fire()
|
||||||
|
-- end)
|
||||||
|
-- multi:newAlarm(2):OnRing(function()
|
||||||
|
-- print("Conn2")
|
||||||
|
-- conn2:Fire()
|
||||||
|
-- end)
|
||||||
|
-- multi:newAlarm(3):OnRing(function()
|
||||||
|
-- print("Conn3")
|
||||||
|
-- conn3:Fire()
|
||||||
|
-- end)
|
||||||
|
|
||||||
|
-- local conn = multi:newSystemThreadedConnection("conn"):init()
|
||||||
|
|
||||||
|
-- multi:newSystemThread("Thread_Test_1", function()
|
||||||
|
-- local multi, thread = require("multi"):init()
|
||||||
|
-- local conn = GLOBAL["conn"]:init()
|
||||||
|
-- local console = THREAD.getConsole()
|
||||||
|
-- conn(function(a,b,c)
|
||||||
|
-- console.print(THREAD:getName().." was triggered!",a,b,c)
|
||||||
|
-- end)
|
||||||
|
-- multi:mainloop()
|
||||||
|
-- end)
|
||||||
|
|
||||||
|
-- multi:newSystemThread("Thread_Test_2", function()
|
||||||
|
-- local multi, thread = require("multi"):init()
|
||||||
|
-- local conn = GLOBAL["conn"]:init()
|
||||||
|
-- local console = THREAD.getConsole()
|
||||||
|
-- conn(function(a,b,c)
|
||||||
|
-- console.print(THREAD:getName().." was triggered!",a,b,c)
|
||||||
|
-- end)
|
||||||
|
-- multi:newAlarm(2):OnRing(function()
|
||||||
|
-- console.print("Fire 2!!!")
|
||||||
|
-- conn:Fire(4,5,6)
|
||||||
|
-- THREAD.kill()
|
||||||
|
-- end)
|
||||||
|
|
||||||
|
-- multi:mainloop()
|
||||||
|
-- end)
|
||||||
|
-- local console = THREAD.getConsole()
|
||||||
|
-- conn(function(a,b,c)
|
||||||
|
-- console.print("Mainloop conn got triggered!",a,b,c)
|
||||||
|
-- end)
|
||||||
|
|
||||||
|
-- alarm = multi:newAlarm(1)
|
||||||
|
-- alarm:OnRing(function()
|
||||||
|
-- console.print("Fire 1!!!")
|
||||||
|
-- conn:Fire(1,2,3)
|
||||||
|
-- end)
|
||||||
|
|
||||||
|
-- alarm = multi:newAlarm(3):OnRing(function()
|
||||||
|
-- multi:newSystemThread("Thread_Test_3",function()
|
||||||
|
-- local multi, thread = require("multi"):init()
|
||||||
|
-- local conn = GLOBAL["conn"]:init()
|
||||||
|
-- local console = THREAD.getConsole()
|
||||||
|
-- conn(function(a,b,c)
|
||||||
|
-- console.print(THREAD:getName().." was triggered!",a,b,c)
|
||||||
|
-- end)
|
||||||
|
-- multi:newAlarm(4):OnRing(function()
|
||||||
|
-- console.print("Fire 3!!!")
|
||||||
|
-- conn:Fire(7,8,9)
|
||||||
|
-- end)
|
||||||
|
-- multi:mainloop()
|
||||||
|
-- end)
|
||||||
|
-- end)
|
||||||
|
|
||||||
|
-- multi:newSystemThread("Thread_Test_4",function()
|
||||||
|
-- local multi, thread = require("multi"):init()
|
||||||
|
-- local conn = GLOBAL["conn"]:init()
|
||||||
|
-- local conn2 = multi:newConnection()
|
||||||
|
-- local console = THREAD.getConsole()
|
||||||
|
-- multi:newAlarm(2):OnRing(function()
|
||||||
|
-- conn2:Fire()
|
||||||
|
-- end)
|
||||||
|
-- multi:newThread(function()
|
||||||
|
-- console.print("Conn Test!")
|
||||||
|
-- thread.hold(conn + conn2)
|
||||||
|
-- console.print("It held!")
|
||||||
|
-- end)
|
||||||
|
-- multi:mainloop()
|
||||||
|
-- end)
|
||||||
|
|
||||||
|
-- multi:mainloop()
|
||||||
Reference in New Issue
Block a user