1.8.4 Update! #3
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,2 +1,3 @@
|
|||||||
|
|
||||||
test.lua
|
test.lua
|
||||||
|
test2.lua
|
||||||
|
|||||||
185
README.html
185
README.html
File diff suppressed because one or more lines are too long
102
README.md
102
README.md
@ -1,6 +1,15 @@
|
|||||||
# multi Version: 1.8.4 (System Threaded Job Queues)
|
# multi Version: 1.8.4 (System Threaded Job Queues)
|
||||||
**Note: The changes section has information on how to use the new features as they come out. Why put the infomation twice on the readme?** Also I added a Testing Branch. That Branch will have the latest updates, but those updates may be unstable. I like to keep the master as bug free as possible</br>
|
**Note: The changes section has information on how to use the new features as they come out. Why put the infomation twice on the readme?** Also I added a Testing Branch. That Branch will have the latest updates, but those updates may be unstable. I like to keep the master as bug free as possible</br>
|
||||||
|
|
||||||
|
In Changes you'll find documentation for(In Order):
|
||||||
|
- System Threaded Job Queues
|
||||||
|
- New mainloop functions
|
||||||
|
- System Threaded Tables
|
||||||
|
- System Threaded Benchmark
|
||||||
|
- System Threaded Queues
|
||||||
|
- Threading related features
|
||||||
|
- And backwards compat stuff
|
||||||
|
|
||||||
My multitasking library for lua</br>
|
My multitasking library for lua</br>
|
||||||
To install copy the multi folder into your enviroment and you are good to go</br>
|
To install copy the multi folder into your enviroment and you are good to go</br>
|
||||||
|
|
||||||
@ -8,6 +17,8 @@ It is a pure lua binding if you ingore the integrations and the love2d compat</b
|
|||||||
|
|
||||||
If you find any bugs or have any issues please let me know :)
|
If you find any bugs or have any issues please let me know :)
|
||||||
|
|
||||||
|
If you don't see a table of contents try using the ReadMe.html file. It is eaiser to navigate the readme</br>
|
||||||
|
|
||||||
[TOC]
|
[TOC]
|
||||||
|
|
||||||
INSTALLING
|
INSTALLING
|
||||||
@ -24,7 +35,8 @@ Planned features/TODO
|
|||||||
---------------------
|
---------------------
|
||||||
- [x] ~~Add system threads for love2d that works like the lanesManager (loveManager, slight differences).~~
|
- [x] ~~Add system threads for love2d that works like the lanesManager (loveManager, slight differences).~~
|
||||||
- [x] ~~Improve performance of the library~~
|
- [x] ~~Improve performance of the library~~
|
||||||
- [ ] Improve coroutine based threading scheduling
|
- [x] ~~Improve coroutine based threading scheduling~~
|
||||||
|
- [ ] Improve love2d Idle thread cpu usage... Tricky Look at the rambling section for insight.
|
||||||
- [x] ~~Add more features to support module creators~~
|
- [x] ~~Add more features to support module creators~~
|
||||||
- [x] ~~Make a framework for eaiser thread task distributing~~
|
- [x] ~~Make a framework for eaiser thread task distributing~~
|
||||||
- [x] ~~Fix Error handling on threaded multi objects~~ Non threaded multiobjs will crash your program if they error though! Use multi:newThread() of multi:newSystemThread() if your code can error! Unless you use multi:protect() this however lowers performance!
|
- [x] ~~Fix Error handling on threaded multi objects~~ Non threaded multiobjs will crash your program if they error though! Use multi:newThread() of multi:newSystemThread() if your code can error! Unless you use multi:protect() this however lowers performance!
|
||||||
@ -40,6 +52,8 @@ Known Bugs/Issues
|
|||||||
-----------------
|
-----------------
|
||||||
In regards to integrations, thread cancellation works slightly different for love2d and lanes. Within love2d I was unable to (To lazy to...) not use the multi library within the thread. A fix for this is to call `multi:Stop()` when you are done with your threaded code! This may change however if I find a way to work around this. In love2d in order to mimic the GLOBAL table I needed the library to constantly sync tha data... You can use the sThread.waitFor(varname), or sThread.hold(func) methods to sync the globals, to get the value instead of using GLOBAL and this could work. If you want to go this route I suggest setting multi.isRunning=true to prevent the auto runner from doing its thing! This will make the multi manager no longer function, but thats the point :P
|
In regards to integrations, thread cancellation works slightly different for love2d and lanes. Within love2d I was unable to (To lazy to...) not use the multi library within the thread. A fix for this is to call `multi:Stop()` when you are done with your threaded code! This may change however if I find a way to work around this. In love2d in order to mimic the GLOBAL table I needed the library to constantly sync tha data... You can use the sThread.waitFor(varname), or sThread.hold(func) methods to sync the globals, to get the value instead of using GLOBAL and this could work. If you want to go this route I suggest setting multi.isRunning=true to prevent the auto runner from doing its thing! This will make the multi manager no longer function, but thats the point :P
|
||||||
|
|
||||||
|
Another bug concerns the SystemThreadedJobQueue, Only 1 can be used for now... Vreating more may not be a good idea.
|
||||||
|
|
||||||
Usage:</br>
|
Usage:</br>
|
||||||
-----
|
-----
|
||||||
```lua
|
```lua
|
||||||
@ -785,7 +799,53 @@ We did it! 1 2 3</br>
|
|||||||
|
|
||||||
Changes
|
Changes
|
||||||
-------
|
-------
|
||||||
Updated from 1.8.2 to 1.8.3</br>
|
Updated from 1.8.3 to 1.8.4
|
||||||
|
---------------------------
|
||||||
|
Added:
|
||||||
|
- multi:newSystemThreadedJobQueue()
|
||||||
|
- Improved stability of the library
|
||||||
|
- Fixed a bug that made the benchmark and getload commands non-thread(coroutine) safe
|
||||||
|
- Tweaked the loveManager to help improve idle cpu usage
|
||||||
|
- Minor tweaks to the coroutine scheduling
|
||||||
|
|
||||||
|
# Using multi:newSystemThreadedJobQueue()
|
||||||
|
First you need to create the object
|
||||||
|
This works the same way as love2d as it does with lanes... It is getting increasing harder to make both work the same way with speed in mind... Anyway...
|
||||||
|
```lua
|
||||||
|
-- Creating the object using lanes manager to show case this. Examples has the file for love2d
|
||||||
|
local GLOBAL,sThread=require("multi.integration.lanesManager").init()
|
||||||
|
jQueue=multi:newSystemThreadedJobQueue(n) -- this internally creates System threads. By defualt it will use the # of processors on your system You can set this number though.
|
||||||
|
-- Only create 1 jobqueue! For now making more than 1 is buggy. You only really need one though. Just register new functions if you want 1 queue to do more. The one reason though is keeping track of jobIDs. I have an idea that I will roll out in the next update.
|
||||||
|
jQueue:registerJob("TEST_JOB",function(a,s)
|
||||||
|
math.randomseed(s)
|
||||||
|
-- We will push a random #
|
||||||
|
TEST_JOB2() -- You can call other registered functions as well!
|
||||||
|
return math.random(0,255) -- send the result to the main thread
|
||||||
|
end)
|
||||||
|
jQueue:registerJob("TEST_JOB2",function()
|
||||||
|
print("Test Works!") -- this is called from the job since it is registered on the same queue
|
||||||
|
end)
|
||||||
|
tableOfOrder={} -- This is how we will keep order of our completed jobs. There is no guarantee that the order will be correct
|
||||||
|
jQueue.OnJobCompleted(function(JOBID,n) -- whenever a job is completed you hook to the event that is called. This passes the JOBID folled by the returns of the job
|
||||||
|
-- JOBID is the completed job, starts at 1 and counts up by 1.
|
||||||
|
-- Threads finish at different times so jobids may be passed out of order! Be sure to have a way to order them
|
||||||
|
tableOfOrder[JOBID]=n -- we order ours by putting them into a table
|
||||||
|
if #tableOfOrder==10 then
|
||||||
|
print("We got all of the pieces!")
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
-- Lets push the jobs now
|
||||||
|
for i=1,10 do -- Job Name of registered function, ... varargs
|
||||||
|
jQueue:pushJob("TEST_JOB","This is a test!",math.random(1,1000000))
|
||||||
|
end
|
||||||
|
print("I pushed all of the jobs :)")
|
||||||
|
multi:mainloop() -- Start the main loop :D
|
||||||
|
```
|
||||||
|
|
||||||
|
Thats it from this version!
|
||||||
|
|
||||||
|
Updated from 1.8.2 to 1.8.3
|
||||||
|
---------------------------
|
||||||
Added:</br>
|
Added:</br>
|
||||||
**New Mainloop functions** Below you can see the slight differences... Function overhead is not too bad in lua, but has a real difference. multi:mainloop() and multi:unprotectedMainloop() use the same algorithm yet the dedicated unprotected one is slightly faster due to having less function overhead.
|
**New Mainloop functions** Below you can see the slight differences... Function overhead is not too bad in lua, but has a real difference. multi:mainloop() and multi:unprotectedMainloop() use the same algorithm yet the dedicated unprotected one is slightly faster due to having less function overhead.
|
||||||
- multi:mainloop()\* -- Bench: 16830003 Steps in 3 second(s)!
|
- multi:mainloop()\* -- Bench: 16830003 Steps in 3 second(s)!
|
||||||
@ -801,7 +861,9 @@ These new methods help by removing function overhead that is caused through the
|
|||||||
However there is a work around! You can use processes to run multiobjs as well and use the other methods on them.
|
However there is a work around! You can use processes to run multiobjs as well and use the other methods on them.
|
||||||
|
|
||||||
I may make a full comparison between each method and which is faster, but for now trust that the dedicated ones with less function overhead are infact faster. Not by much but still faster. :D
|
I may make a full comparison between each method and which is faster, but for now trust that the dedicated ones with less function overhead are infact faster. Not by much but still faster. :D
|
||||||
Updated from 1.8.1 to 1.8.2</br>
|
|
||||||
|
Updated from 1.8.1 to 1.8.2
|
||||||
|
---------------------------
|
||||||
Added:</br>
|
Added:</br>
|
||||||
- multi:newsystemThreadedTable(name) NOTE: Metatables are not supported in transfers. However there is a work around obj:init() that you see does this. Take a look in the multi/integration/shared/shared.lua files to see how I did it!
|
- multi:newsystemThreadedTable(name) NOTE: Metatables are not supported in transfers. However there is a work around obj:init() that you see does this. Take a look in the multi/integration/shared/shared.lua files to see how I did it!
|
||||||
- Modified the GLOBAL metatable to sync before doing its tests
|
- Modified the GLOBAL metatable to sync before doing its tests
|
||||||
@ -864,7 +926,8 @@ t:centerX()
|
|||||||
t:centerY()
|
t:centerY()
|
||||||
```
|
```
|
||||||
|
|
||||||
Updated from 1.8.0 to 1.8.1</br>
|
Updated from 1.8.0 to 1.8.1
|
||||||
|
---------------------------
|
||||||
No real change!</br>
|
No real change!</br>
|
||||||
Changed the structure of the library. Combined the coroutine based threads into the core!</br>
|
Changed the structure of the library. Combined the coroutine based threads into the core!</br>
|
||||||
Only compat and integrations are not part of the core and never will be by nature.</br>
|
Only compat and integrations are not part of the core and never will be by nature.</br>
|
||||||
@ -991,7 +1054,8 @@ multi:newThread("test!",function() -- this is a lua thread
|
|||||||
end)
|
end)
|
||||||
multi:mainloop()
|
multi:mainloop()
|
||||||
```
|
```
|
||||||
Updated from 1.7.5 to 1.7.6</br>
|
Updated from 1.7.5 to 1.7.6
|
||||||
|
---------------------------
|
||||||
Fixed:
|
Fixed:
|
||||||
Typos like always
|
Typos like always
|
||||||
Added:</br>
|
Added:</br>
|
||||||
@ -1003,7 +1067,8 @@ The old way still works and is more convient to be honest, but I felt a method t
|
|||||||
Updated:
|
Updated:
|
||||||
some example files to reflect changes to the core. Changes allow for less typing</br>
|
some example files to reflect changes to the core. Changes allow for less typing</br>
|
||||||
loveManager to require the compat if used so you don't need 2 require line to retrieve the library</br>
|
loveManager to require the compat if used so you don't need 2 require line to retrieve the library</br>
|
||||||
Updated from 1.7.4 to 1.7.5</br>
|
Updated from 1.7.4 to 1.7.5
|
||||||
|
---------------------------
|
||||||
Fixed some typos in the readme... (I am sure there are more there are always more)</br>
|
Fixed some typos in the readme... (I am sure there are more there are always more)</br>
|
||||||
Added more features for module support</br>
|
Added more features for module support</br>
|
||||||
TODO:</br>
|
TODO:</br>
|
||||||
@ -1011,7 +1076,8 @@ Work on performance of the library... I see 3 places where I can make this thing
|
|||||||
|
|
||||||
I'll show case some old versions of the multitasking library eventually so you can see its changes in days past!</br>
|
I'll show case some old versions of the multitasking library eventually so you can see its changes in days past!</br>
|
||||||
|
|
||||||
Updated from 1.7.3 to 1.7.4</br>
|
Updated from 1.7.3 to 1.7.4
|
||||||
|
---------------------------
|
||||||
Added: the example folder which will be populated with more examples in the near future!</br>
|
Added: the example folder which will be populated with more examples in the near future!</br>
|
||||||
The loveManager integration that mimics the lanesManager integration almost exactly to keep coding in both enviroments as close to possible. This is done mostly for library creation support!</br>
|
The loveManager integration that mimics the lanesManager integration almost exactly to keep coding in both enviroments as close to possible. This is done mostly for library creation support!</br>
|
||||||
An example of the loveManager in action using almost the same code as the lanesintergreationtest2.lua</br>
|
An example of the loveManager in action using almost the same code as the lanesintergreationtest2.lua</br>
|
||||||
@ -1088,7 +1154,8 @@ t=gui:newTextLabel("no done yet!",0,0,300,100)
|
|||||||
t:centerX()
|
t:centerX()
|
||||||
t:centerY()
|
t:centerY()
|
||||||
```
|
```
|
||||||
Updated from 1.7.2 to 1.7.3</br>
|
Updated from 1.7.2 to 1.7.3
|
||||||
|
---------------------------
|
||||||
Changed how requiring the library works!
|
Changed how requiring the library works!
|
||||||
`require("multi.all")` Will still work as expected; however, with the exception of threading, compat, and integrations everything else has been moved into the core of the library.
|
`require("multi.all")` Will still work as expected; however, with the exception of threading, compat, and integrations everything else has been moved into the core of the library.
|
||||||
```lua
|
```lua
|
||||||
@ -1104,12 +1171,15 @@ require("multi.task")
|
|||||||
-- ^ they are all part of the core now
|
-- ^ they are all part of the core now
|
||||||
```
|
```
|
||||||
|
|
||||||
Updated from 1.7.1 to 1.7.2</br>
|
Updated from 1.7.1 to 1.7.2
|
||||||
|
---------------------------
|
||||||
Moved updaters, loops, and alarms into the init.lua file. I consider them core features and they are referenced in the init.lua file so they need to exist there. Threaded versions are still separate though. Added another example file
|
Moved updaters, loops, and alarms into the init.lua file. I consider them core features and they are referenced in the init.lua file so they need to exist there. Threaded versions are still separate though. Added another example file
|
||||||
|
|
||||||
Updated from 1.7.0 to 1.7.1 Bug fixes only
|
Updated from 1.7.0 to 1.7.1 Bug fixes only
|
||||||
|
---------------------------
|
||||||
|
|
||||||
Updated from 1.6.0 to 1.7.0</br>
|
Updated from 1.6.0 to 1.7.0
|
||||||
|
---------------------------
|
||||||
Modified: multi.integration.lanesManager.lua
|
Modified: multi.integration.lanesManager.lua
|
||||||
It is now in a stable and simple state Works with the latest lanes version! Tested with version 3.11 I cannot promise that everything will work with eariler versions. Future versions are good though.</br>
|
It is now in a stable and simple state Works with the latest lanes version! Tested with version 3.11 I cannot promise that everything will work with eariler versions. Future versions are good though.</br>
|
||||||
Example Usage:</br>
|
Example Usage:</br>
|
||||||
@ -1162,7 +1232,8 @@ end)
|
|||||||
multi:mainloop()
|
multi:mainloop()
|
||||||
```
|
```
|
||||||
|
|
||||||
Updated from 1.5.0 to 1.6.0</br>
|
Updated from 1.5.0 to 1.6.0
|
||||||
|
---------------------------
|
||||||
Changed: steps and loops
|
Changed: steps and loops
|
||||||
```lua
|
```lua
|
||||||
-- Was
|
-- Was
|
||||||
@ -1186,11 +1257,20 @@ require("multi.all")
|
|||||||
require("multi.compat.backwards[1,5,0]") -- allows for the use of features that were scrapped/changed in 1.6.0+
|
require("multi.compat.backwards[1,5,0]") -- allows for the use of features that were scrapped/changed in 1.6.0+
|
||||||
```
|
```
|
||||||
Updated from 1.4.1 to 1.5.0
|
Updated from 1.4.1 to 1.5.0
|
||||||
|
---------------------------
|
||||||
Added:
|
Added:
|
||||||
- An easy way to manage timeouts
|
- An easy way to manage timeouts
|
||||||
- Small bug fixes
|
- Small bug fixes
|
||||||
|
|
||||||
1.4.1 - First Public release of the library
|
1.4.1 - First Public release of the library
|
||||||
|
---------------------------
|
||||||
|
|
||||||
IMPORTANT:</br>
|
IMPORTANT:</br>
|
||||||
Every update I make aims to make things simpler more efficent and just better, but a lot of old code, which can be really big, uses a lot of older features. I know the pain of having to rewrite everything. My promise to my library users is that I will always have backwards support for older features! New ways may exist that are quicker and eaiser, but the old features/methods will be supported.</br>
|
Every update I make aims to make things simpler more efficent and just better, but a lot of old code, which can be really big, uses a lot of older features. I know the pain of having to rewrite everything. My promise to my library users is that I will always have backwards support for older features! New ways may exist that are quicker and eaiser, but the old features/methods will be supported.</br>
|
||||||
|
Rambling
|
||||||
|
--------
|
||||||
|
Love2d: Sleeping reduces the cpu time making my load detection think the system is under more load, thus preventing it from sleeping... I will look into other means. As of right now it will not eat all of your cpu if threads are active. For now I suggest killing threads that aren't needed anymore. On lanes threads at idle use 0% cpu and it is amazing. A state machine may solve what I need though. One state being idle state that sleeps and only goes into the active state if a job request or data is sent to it... after some time of not being under load it wil switch back into the idle state... We'll see what happens.
|
||||||
|
|
||||||
|
Love2d doesn't like to send functions through channels. By defualt it does not support this. I achieve this by dumping the function and loadstring it on the thread. This however is slow. For the System Threaded Job Queue I had to change my original idea of sending functions as jobs. The current way you do it now is register a job functions once and then call that job across the thread through a queue. Each worker thread pops from the queue and returns the job. The Job ID is automatically updated and allows you to keep track of the order that the data comes in. A table with # indexes can be used to originze the data...
|
||||||
|
|
||||||
|
In regards to benchmarking. If you see my bench marks and are wondering they are 10x better its because I am using luajit for my tests. I highly recommend using luajit for my library, but lua 5.1 will work just as well, but not as fast.
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
package.path="?/init.lua;"..package.path
|
-- Creating the object using lanes manager to show case this. Examples has the file for love2d
|
||||||
local GLOBAL,sThread=require("multi.integration.lanesManager").init()
|
local GLOBAL,sThread=require("multi.integration.lanesManager").init()
|
||||||
jQueue=multi:newSystemThreadedJobQueue() -- this internally creates System threads, We told it to use a maximum of 3 cores at any given time
|
jQueue=multi:newSystemThreadedJobQueue(n) -- this internally creates System threads. By defualt it will use the # of processors on your system You can set this number though.
|
||||||
|
-- Only create 1 jobqueue! For now making more than 1 is buggy. You only really need one though. Just register new functions if you want 1 queue to do more. The one reason though is keeping track of jobIDs. I have an idea that I will roll out in the next update.
|
||||||
jQueue:registerJob("TEST_JOB",function(a,s)
|
jQueue:registerJob("TEST_JOB",function(a,s)
|
||||||
math.randomseed(s)
|
math.randomseed(s)
|
||||||
-- We will push a random #
|
-- We will push a random #
|
||||||
@ -8,19 +9,20 @@ jQueue:registerJob("TEST_JOB",function(a,s)
|
|||||||
return math.random(0,255) -- send the result to the main thread
|
return math.random(0,255) -- send the result to the main thread
|
||||||
end)
|
end)
|
||||||
jQueue:registerJob("TEST_JOB2",function()
|
jQueue:registerJob("TEST_JOB2",function()
|
||||||
print("Test Works!")
|
print("Test Works!") -- this is called from the job since it is registered on the same queue
|
||||||
end)
|
end)
|
||||||
tableOfOrder={}
|
tableOfOrder={} -- This is how we will keep order of our completed jobs. There is no guarantee that the order will be correct
|
||||||
jQueue.OnJobCompleted(function(JOBID,n)
|
jQueue.OnJobCompleted(function(JOBID,n) -- whenever a job is completed you hook to the event that is called. This passes the JOBID folled by the returns of the job
|
||||||
-- JOBID is the completed job, starts at 1 and counts up by 1.
|
-- JOBID is the completed job, starts at 1 and counts up by 1.
|
||||||
-- Threads finish at different times so jobids may be returned out of order! Be sure to have a way to order them
|
-- Threads finish at different times so jobids may be passed out of order! Be sure to have a way to order them
|
||||||
tableOfOrder[JOBID]=n -- we order ours by putting them into a table
|
tableOfOrder[JOBID]=n -- we order ours by putting them into a table
|
||||||
if #tableOfOrder==10 then
|
if #tableOfOrder==10 then
|
||||||
print("We got all of the pieces!")
|
print("We got all of the pieces!")
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
-- LEts push the jobs now
|
||||||
for i=1,10 do -- Job Name of registered function, ... varargs
|
for i=1,10 do -- Job Name of registered function, ... varargs
|
||||||
jQueue:pushJob("TEST_JOB","This is a test!",math.random(1,1000000))
|
jQueue:pushJob("TEST_JOB","This is a test!",math.random(1,1000000))
|
||||||
end
|
end
|
||||||
print("I pushed all of the jobs :)")
|
print("I pushed all of the jobs :)")
|
||||||
multi:mainloop()
|
multi:mainloop() -- Start the main loop :D
|
||||||
|
|||||||
@ -1,24 +1,3 @@
|
|||||||
--~ require("core.Library")
|
|
||||||
--~ GLOBAL,sThread=require("multi.integration.loveManager").init() -- load the love2d version of the lanesManager and requires the entire multi library
|
|
||||||
--~ require("core.GuiManager")
|
|
||||||
--~ gui.ff.Color=Color.Black
|
|
||||||
--~ test=multi:newSystemThreadedTable("YO"):init()
|
|
||||||
--~ test["test1"]="lol"
|
|
||||||
--~ multi:newSystemThread("test",function()
|
|
||||||
--~ tab=sThread.waitFor("YO"):init()
|
|
||||||
--~ print(tab["test1"])
|
|
||||||
--~ sThread.sleep(3)
|
|
||||||
--~ tab["test2"]="Whats so funny?"
|
|
||||||
--~ end)
|
|
||||||
--~ multi:newThread("test2",function()
|
|
||||||
--~ thread.sleep(1)
|
|
||||||
--~ print(test:waitFor("test2"))
|
|
||||||
--~ t.text="DONE!"
|
|
||||||
--~ end)
|
|
||||||
--~ t=gui:newTextLabel("no done yet!",0,0,300,100)
|
|
||||||
--~ t:centerX()
|
|
||||||
--~ t:centerY()
|
|
||||||
|
|
||||||
require("core.Library")
|
require("core.Library")
|
||||||
GLOBAL,sThread=require("multi.integration.loveManager").init() -- load the love2d version of the lanesManager and requires the entire multi library
|
GLOBAL,sThread=require("multi.integration.loveManager").init() -- load the love2d version of the lanesManager and requires the entire multi library
|
||||||
require("core.GuiManager")
|
require("core.GuiManager")
|
||||||
|
|||||||
@ -45,8 +45,8 @@ function print(...)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
multi = {}
|
multi = {}
|
||||||
multi.Version="1.8.2"
|
multi.Version="1.8.4"
|
||||||
multi._VERSION="1.8.2"
|
multi._VERSION="1.8.4"
|
||||||
multi.stage='stable'
|
multi.stage='stable'
|
||||||
multi.__index = multi
|
multi.__index = multi
|
||||||
multi.Mainloop={}
|
multi.Mainloop={}
|
||||||
@ -108,11 +108,13 @@ function multi:setThrestimed(n)
|
|||||||
end
|
end
|
||||||
function multi:getLoad()
|
function multi:getLoad()
|
||||||
return multi:newFunction(function(self)
|
return multi:newFunction(function(self)
|
||||||
|
multi.scheduler:Pause()
|
||||||
local sample=#multi.Mainloop
|
local sample=#multi.Mainloop
|
||||||
local FFloadtest=0
|
local FFloadtest=0
|
||||||
multi:benchMark(multi.threstimed):OnBench(function(_,l3) FFloadtest=l3*(1/multi.threstimed) end)
|
multi:benchMark(multi.threstimed):OnBench(function(_,l3) FFloadtest=l3*(1/multi.threstimed) end)
|
||||||
self:hold(function() return FFloadtest~=0 end)
|
self:hold(function() return FFloadtest~=0 end)
|
||||||
local val=FFloadtest/sample
|
local val=FFloadtest/sample
|
||||||
|
multi.scheduler:Resume()
|
||||||
if val>multi.threshold then
|
if val>multi.threshold then
|
||||||
return 0
|
return 0
|
||||||
else
|
else
|
||||||
@ -617,10 +619,14 @@ function multi:hold(task)
|
|||||||
env:OnEvent(function(envt) envt:Pause() envt.Active=false end)
|
env:OnEvent(function(envt) envt:Pause() envt.Active=false end)
|
||||||
while env.Active do
|
while env.Active do
|
||||||
if love then
|
if love then
|
||||||
|
if love.graphics then
|
||||||
self.Parent:lManager()
|
self.Parent:lManager()
|
||||||
else
|
else
|
||||||
self.Parent:Do_Order()
|
self.Parent:Do_Order()
|
||||||
end
|
end
|
||||||
|
else
|
||||||
|
self.Parent:Do_Order()
|
||||||
|
end
|
||||||
end
|
end
|
||||||
env:Destroy()
|
env:Destroy()
|
||||||
self:Resume()
|
self:Resume()
|
||||||
@ -978,7 +984,110 @@ function multi:mainloop()
|
|||||||
else
|
else
|
||||||
return "Already Running!"
|
return "Already Running!"
|
||||||
end
|
end
|
||||||
--print("Did you call multi:Stop()? This method should not be used when using multi:mainloop() unless of course you wanted to stop it! you can restart the multi, by using multi:reboot() and calling multi:mainloop() again or by using multi:uManager()")
|
end
|
||||||
|
function multi:protectedMainloop()
|
||||||
|
multi:protect()
|
||||||
|
if not multi.isRunning then
|
||||||
|
multi.isRunning=true
|
||||||
|
for i=1,#self.Tasks do
|
||||||
|
self.Tasks[i](self)
|
||||||
|
end
|
||||||
|
rawset(self,'Start',self.clock())
|
||||||
|
while self.Active do
|
||||||
|
self:Do_Order()
|
||||||
|
end
|
||||||
|
else
|
||||||
|
return "Already Running!"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
function multi:unprotectedMainloop()
|
||||||
|
multi:unProtect()
|
||||||
|
if not multi.isRunning then
|
||||||
|
multi.isRunning=true
|
||||||
|
for i=1,#self.Tasks do
|
||||||
|
self.Tasks[i](self)
|
||||||
|
end
|
||||||
|
rawset(self,'Start',self.clock())
|
||||||
|
while self.Active do
|
||||||
|
local Loop=self.Mainloop
|
||||||
|
_G.ID=0
|
||||||
|
for _D=#Loop,1,-1 do
|
||||||
|
if Loop[_D] then
|
||||||
|
if Loop[_D].Active then
|
||||||
|
Loop[_D].Id=_D
|
||||||
|
self.CID=_D
|
||||||
|
Loop[_D]:Act()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
return "Already Running!"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
function multi:prioritizedMainloop1()
|
||||||
|
multi:enablePriority()
|
||||||
|
if not multi.isRunning then
|
||||||
|
multi.isRunning=true
|
||||||
|
for i=1,#self.Tasks do
|
||||||
|
self.Tasks[i](self)
|
||||||
|
end
|
||||||
|
rawset(self,'Start',self.clock())
|
||||||
|
while self.Active do
|
||||||
|
local Loop=self.Mainloop
|
||||||
|
_G.ID=0
|
||||||
|
local PS=self
|
||||||
|
for _D=#Loop,1,-1 do
|
||||||
|
if Loop[_D] then
|
||||||
|
if (PS.PList[PS.PStep])%Loop[_D].Priority==0 then
|
||||||
|
if Loop[_D].Active then
|
||||||
|
Loop[_D].Id=_D
|
||||||
|
self.CID=_D
|
||||||
|
Loop[_D]:Act()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
PS.PStep=PS.PStep+1
|
||||||
|
if PS.PStep>7 then
|
||||||
|
PS.PStep=1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
return "Already Running!"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
function multi:prioritizedMainloop2()
|
||||||
|
multi:enablePriority2()
|
||||||
|
if not multi.isRunning then
|
||||||
|
multi.isRunning=true
|
||||||
|
for i=1,#self.Tasks do
|
||||||
|
self.Tasks[i](self)
|
||||||
|
end
|
||||||
|
rawset(self,'Start',self.clock())
|
||||||
|
while self.Active do
|
||||||
|
local Loop=self.Mainloop
|
||||||
|
_G.ID=0
|
||||||
|
local PS=self
|
||||||
|
for _D=#Loop,1,-1 do
|
||||||
|
if Loop[_D] then
|
||||||
|
if (PS.PStep)%Loop[_D].Priority==0 then
|
||||||
|
if Loop[_D].Active then
|
||||||
|
Loop[_D].Id=_D
|
||||||
|
self.CID=_D
|
||||||
|
Loop[_D]:Act()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
PS.PStep=PS.PStep+1
|
||||||
|
if PS.PStep>self.Priority_Idle then
|
||||||
|
PS.PStep=1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
return "Already Running!"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
function multi._tFunc(self,dt)
|
function multi._tFunc(self,dt)
|
||||||
for i=1,#self.Tasks do
|
for i=1,#self.Tasks do
|
||||||
@ -1494,7 +1603,7 @@ function multi:newThread(name,func)
|
|||||||
end
|
end
|
||||||
multi:setDomainName("Threads")
|
multi:setDomainName("Threads")
|
||||||
multi:setDomainName("Globals")
|
multi:setDomainName("Globals")
|
||||||
multi.scheduler=multi:newUpdater()
|
multi.scheduler=multi:newLoop()
|
||||||
multi.scheduler.Type="scheduler"
|
multi.scheduler.Type="scheduler"
|
||||||
function multi.scheduler:setStep(n)
|
function multi.scheduler:setStep(n)
|
||||||
self.skip=tonumber(n) or 24
|
self.skip=tonumber(n) or 24
|
||||||
@ -1503,7 +1612,7 @@ multi.scheduler.skip=0
|
|||||||
multi.scheduler.counter=0
|
multi.scheduler.counter=0
|
||||||
multi.scheduler.Threads=multi:linkDomain("Threads")
|
multi.scheduler.Threads=multi:linkDomain("Threads")
|
||||||
multi.scheduler.Globals=multi:linkDomain("Globals")
|
multi.scheduler.Globals=multi:linkDomain("Globals")
|
||||||
multi.scheduler:OnUpdate(function(self)
|
multi.scheduler:OnLoop(function(self)
|
||||||
self.counter=self.counter+1
|
self.counter=self.counter+1
|
||||||
for i=#self.Threads,1,-1 do
|
for i=#self.Threads,1,-1 do
|
||||||
ret={}
|
ret={}
|
||||||
@ -1557,7 +1666,6 @@ multi.scheduler:OnUpdate(function(self)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
multi.scheduler:setStep()
|
|
||||||
multi.scheduler:Pause()
|
multi.scheduler:Pause()
|
||||||
multi.OnError=multi:newConnection()
|
multi.OnError=multi:newConnection()
|
||||||
function multi:newThreadedAlarm(name,set)
|
function multi:newThreadedAlarm(name,set)
|
||||||
|
|||||||
@ -125,12 +125,9 @@ function multi:newSystemThread(name,func)
|
|||||||
end)
|
end)
|
||||||
return c
|
return c
|
||||||
end
|
end
|
||||||
print("Intergrated Lanes!")
|
print("Integrated Lanes!")
|
||||||
multi.intergration={} -- for module creators
|
multi.integration={} -- for module creators
|
||||||
multi.intergration.GLOBAL=GLOBAL
|
multi.integration.GLOBAL=GLOBAL
|
||||||
multi.intergration.THREAD=THREAD
|
multi.integration.THREAD=THREAD
|
||||||
multi.intergration.lanes={}
|
require("multi.integration.shared.shared")
|
||||||
multi.intergration.lanes.GLOBAL=GLOBAL -- for module creators
|
|
||||||
multi.intergration.lanes.THREAD=THREAD -- for module creators
|
|
||||||
require("multi.intergration.shared.shared")
|
|
||||||
return {init=function() return GLOBAL,THREAD end}
|
return {init=function() return GLOBAL,THREAD end}
|
||||||
|
|||||||
@ -2,14 +2,14 @@ require("multi.compat.love2d")
|
|||||||
function multi:canSystemThread()
|
function multi:canSystemThread()
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
multi.intergration={}
|
multi.integration={}
|
||||||
multi.intergration.love2d={}
|
multi.integration.love2d={}
|
||||||
multi.intergration.love2d.ThreadBase=[[
|
multi.integration.love2d.ThreadBase=[[
|
||||||
__THREADNAME__=({...})[1]
|
__THREADNAME__=({...})[1]
|
||||||
require("love.filesystem")
|
require("love.filesystem")
|
||||||
require("love.system")
|
require("love.system")
|
||||||
require("love.timer")
|
require("love.timer")
|
||||||
require("multi.all")
|
require("multi")
|
||||||
GLOBAL={}
|
GLOBAL={}
|
||||||
setmetatable(GLOBAL,{
|
setmetatable(GLOBAL,{
|
||||||
__index=function(t,k)
|
__index=function(t,k)
|
||||||
@ -29,6 +29,7 @@ setmetatable(GLOBAL,{
|
|||||||
function __sync__()
|
function __sync__()
|
||||||
local data=__mythread__:pop()
|
local data=__mythread__:pop()
|
||||||
while data do
|
while data do
|
||||||
|
love.timer.sleep(.001)
|
||||||
if type(data)=="string" then
|
if type(data)=="string" then
|
||||||
local cmd,tp,name,d=data:match("(%S-) (%S-) (%S-) (.+)")
|
local cmd,tp,name,d=data:match("(%S-) (%S-) (%S-) (.+)")
|
||||||
if name=="__DIEPLZ"..__THREADNAME__.."__" then
|
if name=="__DIEPLZ"..__THREADNAME__.."__" then
|
||||||
@ -79,7 +80,7 @@ function resolveType(tp,d)
|
|||||||
elseif tp=="bool" then
|
elseif tp=="bool" then
|
||||||
return (d=="true")
|
return (d=="true")
|
||||||
elseif tp=="function" then
|
elseif tp=="function" then
|
||||||
return loadDump(d)
|
return loadDump("[==["..d.."]==]")
|
||||||
elseif tp=="table" then
|
elseif tp=="table" then
|
||||||
return loadstring("return "..d)()
|
return loadstring("return "..d)()
|
||||||
elseif tp=="nil" then
|
elseif tp=="nil" then
|
||||||
@ -91,11 +92,11 @@ end
|
|||||||
function resolveData(v)
|
function resolveData(v)
|
||||||
local data=""
|
local data=""
|
||||||
if type(v)=="table" then
|
if type(v)=="table" then
|
||||||
data=ToStr(v)
|
return ToStr(v)
|
||||||
elseif type(v)=="function" then
|
elseif type(v)=="function" then
|
||||||
data=dump(v)
|
return dump(v)
|
||||||
elseif type(v)=="string" or type(v)=="number" or type(v)=="bool" or type(v)=="nil" then
|
elseif type(v)=="string" or type(v)=="number" or type(v)=="bool" or type(v)=="nil" then
|
||||||
data=tostring(v)
|
return tostring(v)
|
||||||
end
|
end
|
||||||
return data
|
return data
|
||||||
end
|
end
|
||||||
@ -152,24 +153,16 @@ end
|
|||||||
function sThread.hold(n)
|
function sThread.hold(n)
|
||||||
repeat __sync__() until n()
|
repeat __sync__() until n()
|
||||||
end
|
end
|
||||||
updater=multi:newUpdater()
|
multi:newLoop(function(self)
|
||||||
updater:OnUpdate(function(self)
|
self:Pause()
|
||||||
local data=__mythread__:pop()
|
local ld=multi:getLoad()
|
||||||
while data do
|
self:Resume()
|
||||||
if type(data)=="string" then
|
if ld<80 then
|
||||||
local cmd,tp,name,d=data:match("(%S-) (%S-) (%S-) (.+)")
|
love.timer.sleep(.01)
|
||||||
if name=="__DIEPLZ"..__THREADNAME__.."__" then
|
|
||||||
error("Thread: "..__THREADNAME__.." has been stopped!")
|
|
||||||
end
|
|
||||||
if cmd=="SYNC" then
|
|
||||||
__proxy__[name]=resolveType(tp,d)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
__proxy__[name]=data
|
|
||||||
end
|
|
||||||
data=__mythread__:pop()
|
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
updater=multi:newUpdater()
|
||||||
|
updater:OnUpdate(__sync__)
|
||||||
func=loadDump([=[INSERT_USER_CODE]=])()
|
func=loadDump([=[INSERT_USER_CODE]=])()
|
||||||
multi:mainloop()
|
multi:mainloop()
|
||||||
]]
|
]]
|
||||||
@ -191,7 +184,7 @@ setmetatable(GLOBAL,{
|
|||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
THREAD={} -- Allow main thread to interact with these objects as well
|
THREAD={} -- Allow main thread to interact with these objects as well
|
||||||
multi.intergration.love2d.mainChannel=love.thread.getChannel("__MainChan__")
|
multi.integration.love2d.mainChannel=love.thread.getChannel("__MainChan__")
|
||||||
function ToStr(val, name, skipnewlines, depth)
|
function ToStr(val, name, skipnewlines, depth)
|
||||||
skipnewlines = skipnewlines or false
|
skipnewlines = skipnewlines or false
|
||||||
depth = depth or 0
|
depth = depth or 0
|
||||||
@ -228,7 +221,7 @@ function resolveType(tp,d)
|
|||||||
elseif tp=="bool" then
|
elseif tp=="bool" then
|
||||||
return (d=="true")
|
return (d=="true")
|
||||||
elseif tp=="function" then
|
elseif tp=="function" then
|
||||||
return loadDump(d)
|
return loadDump("[==["..d.."]==]")
|
||||||
elseif tp=="table" then
|
elseif tp=="table" then
|
||||||
return loadstring("return "..d)()
|
return loadstring("return "..d)()
|
||||||
elseif tp=="nil" then
|
elseif tp=="nil" then
|
||||||
@ -240,11 +233,11 @@ end
|
|||||||
function resolveData(v)
|
function resolveData(v)
|
||||||
local data=""
|
local data=""
|
||||||
if type(v)=="table" then
|
if type(v)=="table" then
|
||||||
data=ToStr(v)
|
return ToStr(v)
|
||||||
elseif type(v)=="function" then
|
elseif type(v)=="function" then
|
||||||
data=dump(v)
|
return dump(v)
|
||||||
elseif type(v)=="string" or type(v)=="number" or type(v)=="bool" or type(v)=="nil" then
|
elseif type(v)=="string" or type(v)=="number" or type(v)=="bool" or type(v)=="nil" then
|
||||||
data=tostring(v)
|
return tostring(v)
|
||||||
end
|
end
|
||||||
return data
|
return data
|
||||||
end
|
end
|
||||||
@ -274,10 +267,10 @@ function multi:newSystemThread(name,func) -- the main method
|
|||||||
local c={}
|
local c={}
|
||||||
c.name=name
|
c.name=name
|
||||||
c.ID=c.name.."<ID|"..randomString(8)..">"
|
c.ID=c.name.."<ID|"..randomString(8)..">"
|
||||||
c.thread=love.thread.newThread(multi.intergration.love2d.ThreadBase:gsub("INSERT_USER_CODE",dump(func)))
|
c.thread=love.thread.newThread(multi.integration.love2d.ThreadBase:gsub("INSERT_USER_CODE",dump(func)))
|
||||||
c.thread:start(c.ID)
|
c.thread:start(c.ID)
|
||||||
function c:kill()
|
function c:kill()
|
||||||
multi.intergration.GLOBAL["__DIEPLZ"..self.ID.."__"]="__DIEPLZ"..self.ID.."__"
|
multi.integration.GLOBAL["__DIEPLZ"..self.ID.."__"]="__DIEPLZ"..self.ID.."__"
|
||||||
end
|
end
|
||||||
return c
|
return c
|
||||||
end
|
end
|
||||||
@ -310,11 +303,11 @@ function THREAD.hold(n)
|
|||||||
multi.OBJ_REF:Resume()
|
multi.OBJ_REF:Resume()
|
||||||
end
|
end
|
||||||
__channels__={}
|
__channels__={}
|
||||||
multi.intergration.GLOBAL=GLOBAL
|
multi.integration.GLOBAL=GLOBAL
|
||||||
multi.intergration.THREAD=THREAD
|
multi.integration.THREAD=THREAD
|
||||||
updater=multi:newUpdater()
|
updater=multi:newUpdater()
|
||||||
updater:OnUpdate(function(self)
|
updater:OnUpdate(function(self)
|
||||||
local data=multi.intergration.love2d.mainChannel:pop()
|
local data=multi.integration.love2d.mainChannel:pop()
|
||||||
while data do
|
while data do
|
||||||
--print("MAIN:",data)
|
--print("MAIN:",data)
|
||||||
if type(data)=="string" then
|
if type(data)=="string" then
|
||||||
@ -342,19 +335,21 @@ updater:OnUpdate(function(self)
|
|||||||
else
|
else
|
||||||
__proxy__[name]=data
|
__proxy__[name]=data
|
||||||
end
|
end
|
||||||
data=multi.intergration.love2d.mainChannel:pop()
|
data=multi.integration.love2d.mainChannel:pop()
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
require("multi.intergration.shared.shared")
|
require("multi.integration.shared.shared")
|
||||||
print("Intergrated Love2d!")
|
print("Integrated Love2d!")
|
||||||
return {
|
return {
|
||||||
init=function(t)
|
init=function(t)
|
||||||
if t then
|
if t then
|
||||||
if t.threadNamespace then
|
if t.threadNamespace then
|
||||||
multi.intergration.love2d.ThreadBase:gsub("sThread",t.threadNamespace)
|
multi.integration.THREADNAME=t.threadNamespace
|
||||||
|
multi.integration.love2d.ThreadBase:gsub("sThread",t.threadNamespace)
|
||||||
end
|
end
|
||||||
if t.globalNamespace then
|
if t.globalNamespace then
|
||||||
multi.intergration.love2d.ThreadBase:gsub("GLOBAL",t.globalNamespace)
|
multi.integration.GLOBALNAME=t.globalNamespace
|
||||||
|
multi.integration.love2d.ThreadBase:gsub("GLOBAL",t.globalNamespace)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return GLOBAL,THREAD
|
return GLOBAL,THREAD
|
||||||
|
|||||||
@ -280,7 +280,6 @@ if love then
|
|||||||
GLOBAL["THREADED_JQ"]=nil -- remove it
|
GLOBAL["THREADED_JQ"]=nil -- remove it
|
||||||
GLOBAL["THREADED_JQO"]=nil -- remove it
|
GLOBAL["THREADED_JQO"]=nil -- remove it
|
||||||
multi:newLoop(function()
|
multi:newLoop(function()
|
||||||
sThread.sleep(.001) -- lets allow cpu time for other processes on our system!
|
|
||||||
local job=JQI:pop()
|
local job=JQI:pop()
|
||||||
if job then
|
if job then
|
||||||
local ID=table.remove(job,1) -- return and remove
|
local ID=table.remove(job,1) -- return and remove
|
||||||
|
|||||||
@ -108,11 +108,13 @@ function multi:setThrestimed(n)
|
|||||||
end
|
end
|
||||||
function multi:getLoad()
|
function multi:getLoad()
|
||||||
return multi:newFunction(function(self)
|
return multi:newFunction(function(self)
|
||||||
|
multi.scheduler:Pause()
|
||||||
local sample=#multi.Mainloop
|
local sample=#multi.Mainloop
|
||||||
local FFloadtest=0
|
local FFloadtest=0
|
||||||
multi:benchMark(multi.threstimed):OnBench(function(_,l3) FFloadtest=l3*(1/multi.threstimed) end)
|
multi:benchMark(multi.threstimed):OnBench(function(_,l3) FFloadtest=l3*(1/multi.threstimed) end)
|
||||||
self:hold(function() return FFloadtest~=0 end)
|
self:hold(function() return FFloadtest~=0 end)
|
||||||
local val=FFloadtest/sample
|
local val=FFloadtest/sample
|
||||||
|
multi.scheduler:Resume()
|
||||||
if val>multi.threshold then
|
if val>multi.threshold then
|
||||||
return 0
|
return 0
|
||||||
else
|
else
|
||||||
@ -617,10 +619,14 @@ function multi:hold(task)
|
|||||||
env:OnEvent(function(envt) envt:Pause() envt.Active=false end)
|
env:OnEvent(function(envt) envt:Pause() envt.Active=false end)
|
||||||
while env.Active do
|
while env.Active do
|
||||||
if love then
|
if love then
|
||||||
|
if love.graphics then
|
||||||
self.Parent:lManager()
|
self.Parent:lManager()
|
||||||
else
|
else
|
||||||
self.Parent:Do_Order()
|
self.Parent:Do_Order()
|
||||||
end
|
end
|
||||||
|
else
|
||||||
|
self.Parent:Do_Order()
|
||||||
|
end
|
||||||
end
|
end
|
||||||
env:Destroy()
|
env:Destroy()
|
||||||
self:Resume()
|
self:Resume()
|
||||||
|
|||||||
@ -153,6 +153,14 @@ end
|
|||||||
function sThread.hold(n)
|
function sThread.hold(n)
|
||||||
repeat __sync__() until n()
|
repeat __sync__() until n()
|
||||||
end
|
end
|
||||||
|
multi:newLoop(function(self)
|
||||||
|
self:Pause()
|
||||||
|
local ld=multi:getLoad()
|
||||||
|
self:Resume()
|
||||||
|
if ld<80 then
|
||||||
|
love.timer.sleep(.01)
|
||||||
|
end
|
||||||
|
end)
|
||||||
updater=multi:newUpdater()
|
updater=multi:newUpdater()
|
||||||
updater:OnUpdate(__sync__)
|
updater:OnUpdate(__sync__)
|
||||||
func=loadDump([=[INSERT_USER_CODE]=])()
|
func=loadDump([=[INSERT_USER_CODE]=])()
|
||||||
|
|||||||
@ -280,7 +280,6 @@ if love then
|
|||||||
GLOBAL["THREADED_JQ"]=nil -- remove it
|
GLOBAL["THREADED_JQ"]=nil -- remove it
|
||||||
GLOBAL["THREADED_JQO"]=nil -- remove it
|
GLOBAL["THREADED_JQO"]=nil -- remove it
|
||||||
multi:newLoop(function()
|
multi:newLoop(function()
|
||||||
sThread.sleep(.001) -- lets allow cpu time for other processes on our system!
|
|
||||||
local job=JQI:pop()
|
local job=JQI:pop()
|
||||||
if job then
|
if job then
|
||||||
local ID=table.remove(job,1) -- return and remove
|
local ID=table.remove(job,1) -- return and remove
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user