diff --git a/README.html b/README.html index 8bcadc5..0d85ba6 100644 --- a/README.html +++ b/README.html @@ -9,7 +9,7 @@
-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?
My multitasking library for lua
To install copy the multi folder into your enviroment and you are good to go
It is a pure lua binding if you ingore the intergrations and the love2d compat
If you find any bugs or have any issues please let me know :)
Also I will eventually add an example folder with a lot of examples for how you can use this library. Don’t konw when that will be though :P Added!
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?
My multitasking library for lua
To install copy the multi folder into your enviroment and you are good to go
It is a pure lua binding if you ingore the intergrations and the love2d compat
If you find any bugs or have any issues please let me know :)
For real-time assistance with my libraries! A place where you can ask questions and get help with any of my libraries
https://discord.gg/U8UspuA
In regards to intergrations, 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
DiscordFor real-time assistance with my libraries! A place where you can ask questions and get help with any of my libraries. Also you can request features and stuff there as well.
https://discord.gg/U8UspuA
Planned features/TODO
+-
Add system threads for love2d that works like the lanesManager (loveManager, slight differences). - Improve performance of the library — It has increased a bit, but I feel I can get a little more out of it
- Improve coroutine based threading scheduling
-
Add more features to support module creators -
Make a framework for eaiser thread task distributing -
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! -
Add multi:OnError(function(obj,err)) - sThread.wrap(obj) May or may not be completed Theory: Allows interaction in one thread to affect it in another. The addition to threaded tables may make this possible!
- SystemThreaded Actors — After some tests i figured out a way to make this work… It will work slightly different though. This is due to the actor needing to be splittable…
- LoadBalancing for system threads (Once SystemThreaded Actors are done)
- Add more intergrations
- Finish the wiki stuff. (11% done)
- Test for unknown bugs
Known Bugs/Issues
In regards to intergrations, 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
Usage:
function<
end
end)
multi:mainloop()
-
Output (Change the value inc as indicated in the comment to see the outcomes!)
Looping…
Looping…
Looping…
Looping…
Looping…
Looping…
Looping…
Looping…
Looping…
Loop timed out! tloop Trying again…
Looping…
Looping…
Looping…
Looping…
Looping…
We did it! 1 2 3
Changes
Updated from 1.8.0 to 1.8.1
No real change!
Changed the structure of the library. Combined the coroutine based threads into the core!
Only compat and intergrations are not part of the core and never will be by nature.
This should make the library more convient to use.
I left multi/all.lua file so if anyone had libraries/projects that used that it will still work!
Updated from 1.7.6 to 1.8.0
(How much thread could a thread thread if a thread could thread thread?)
Added:
+
Looping…
Looping…
Looping…
Looping…
Looping…
Looping…
Looping…
Looping…
Looping…
Loop timed out! tloop Trying again…
Looping…
Looping…
Looping…
Looping…
Looping…
We did it! 1 2 3
Updated from 1.8.1 to 1.8.2
Added:
The threaded table is setup just like the threaded queue.
It provids GLOBAL like features without having to write to GLOBAL!
This is useful for module creators who want to keep their data private, but also use GLOBAL like coding.
It has a few features that makes it a bit better than plain ol GLOBAL (For now…)
(ThreadedTable - TT for short)
we also have the “sync” method, this one was made for love2d because we do a syncing trick to get data in a table format. The lanes side has a sync method as well so no worries. Using indexing calls sync once and may grab your variable. This allows you to have the lanes indexing ‘like’ syntax when doing regular indexing in love2d side of the module. As of right now both sides work flawlessly! And this effect is now the GLOBAL as well
On GLOBALS sync is a internal method for keeping the GLOBAL table in order. You can still use sThread.waitFor(name) to wait for variables that may of may not yet exist!
Time for some examples:
-- lanes Desktop lua! NOTE: this is in lanesintergratetest6.lua in the examples folder
+local GLOBAL,sThread=require("multi.intergration.lanesManager").init()
+test=multi:newSystemThreadedTable("YO"):init()
+test["test1"]="lol"
+multi:newSystemThread("test",function()
+ tab=sThread.waitFor("YO"):init()
+ print(tab:has("test1"))
+ sThread.sleep(3)
+ tab["test2"]="Whats so funny?"
+end)
+multi:newThread("test2",function()
+ print(test:waitFor("test2"))
+end)
+multi:mainloop()
+-- love2d gaming lua! NOTE: this is in main4.lua in the love2d examples
+require("core.Library")
+GLOBAL,sThread=require("multi.intergration.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()
+ print(test:waitFor("test2"))
+ t.text="DONE!"
+end)
+t=gui:newTextLabel("no done yet!",0,0,300,100)
+t:centerX()
+t:centerY()
+Updated from 1.8.0 to 1.8.1
No real change!
Changed the structure of the library. Combined the coroutine based threads into the core!
Only compat and intergrations are not part of the core and never will be by nature.
This should make the library more convient to use.
I left multi/all.lua file so if anyone had libraries/projects that used that it will still work!
Updated from 1.7.6 to 1.8.0
(How much thread could a thread thread if a thread could thread thread?)
Added:
3):OnBench(3,"All Threads: ")
end)
multi:mainloop()
-Using multi:newSystemThreadedQueue()Quick Note: queues shared across multiple objects will be pulling from the same “queue” keep this in mind when coding! Also the queue respects direction a push on the thread side cannot be popped on the thread side… Same goes for the mainthread!
+
+On GLOBALS sync is a internal method for keeping the GLOBAL table in order. You can still use sThread.waitFor(name) to wait for variables that may of may not yet exist!
+
+Time for some examples:
+# Using multi:newSystemThreadedTable(name)
+```lua
+-- lanes Desktop lua! NOTE: this is in lanesintergratetest6.lua in the examples folder
+local GLOBAL,sThread=require("multi.intergration.lanesManager").init()
+test=multi:newSystemThreadedTable("YO"):init()
+test["test1"]="lol"
+multi:newSystemThread("test",function()
+ tab=sThread.waitFor("YO"):init()
+ print(tab:has("test1"))
+ sThread.sleep(3)
+ tab["test2"]="Whats so funny?"
+end)
+multi:newThread("test2",function()
+ print(test:waitFor("test2"))
+end)
+multi:mainloop()
+```
+
+```lua
+-- love2d gaming lua! NOTE: this is in main4.lua in the love2d examples
+require("core.Library")
+GLOBAL,sThread=require("multi.intergration.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()
+ print(test:waitFor("test2"))
+ t.text="DONE!"
+end)
+t=gui:newTextLabel("no done yet!",0,0,300,100)
+t:centerX()
+t:centerY()
+```
+
Updated from 1.8.0 to 1.8.1
No real change!
Changed the structure of the library. Combined the coroutine based threads into the core!
@@ -814,6 +875,7 @@ multi:mainloop()
```
# Using multi:newSystemThreadedQueue()
+Quick Note: queues shared across multiple objects will be pulling from the same "queue" keep this in mind when coding! Also the queue respects direction a push on the thread side cannot be popped on the thread side... Same goes for the mainthread!
```lua
-- in love2d, this file will be in the same example folder as before, but is named main2.lua
require("core.Library")
diff --git a/examples/lanesintergratetest6.lua b/examples/lanesintergratetest6.lua
new file mode 100644
index 0000000..4a893fb
--- /dev/null
+++ b/examples/lanesintergratetest6.lua
@@ -0,0 +1,14 @@
+-- lanes Desktop lua! NOTE: this is in lanesintergratetest6.lua in the examples folder
+local GLOBAL,sThread=require("multi.intergration.lanesManager").init()
+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()
+ print(test:waitFor("test2"))
+end)
+multi:mainloop()
diff --git a/examples/love2d Threading Example/main4.lua b/examples/love2d Threading Example/main4.lua
new file mode 100644
index 0000000..3106b09
--- /dev/null
+++ b/examples/love2d Threading Example/main4.lua
@@ -0,0 +1,19 @@
+require("core.Library")
+GLOBAL,sThread=require("multi.intergration.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()
+ print(test:waitFor("test2"))
+ t.text="DONE!"
+end)
+t=gui:newTextLabel("no done yet!",0,0,300,100)
+t:centerX()
+t:centerY()
diff --git a/examples/love2d Threading Example/multi/all.lua b/examples/love2d Threading Example/multi/all.lua
index 7a409ba..d14aac6 100644
--- a/examples/love2d Threading Example/multi/all.lua
+++ b/examples/love2d Threading Example/multi/all.lua
@@ -1,9 +1 @@
require("multi")
-require("multi.threading")
-require("multi.threading.alarm")
-require("multi.threading.event")
-require("multi.threading.loop")
-require("multi.threading.process")
-require("multi.threading.step")
-require("multi.threading.tstep")
-require("multi.threading.updater")
diff --git a/examples/love2d Threading Example/multi/compat/love2d.lua b/examples/love2d Threading Example/multi/compat/love2d.lua
index d2d2d5b..cc4015d 100644
--- a/examples/love2d Threading Example/multi/compat/love2d.lua
+++ b/examples/love2d Threading Example/multi/compat/love2d.lua
@@ -21,21 +21,8 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
]]
-require("multi.all")
+require("multi")
os.sleep=love.timer.sleep
---~ function bin.load(file,s,r)
---~ content, size = love.filesystem.read(file)
---~ local temp=bin.new(content)
---~ temp.filepath=file
---~ return temp
---~ end
---~ function bin:tofile(filename)
---~ if not(filename) or self.Stream then return nil end
---~ love.filesystem.write(filename,self.data)
---~ end
---~ function bin.stream(file,l)
---~ error("Sorry streaming is not available when using love2d :(, I am looking for a solution though :)")
---~ end
function love.run()
if love.math then
love.math.setRandomSeed(os.time())
diff --git a/examples/love2d Threading Example/multi/init.lua b/examples/love2d Threading Example/multi/init.lua
index 66d82ba..cbd74b3 100644
--- a/examples/love2d Threading Example/multi/init.lua
+++ b/examples/love2d Threading Example/multi/init.lua
@@ -45,7 +45,8 @@ function print(...)
end
end
multi = {}
-multi.Version={1,7,6}
+multi.Version="1.8.2"
+multi._VERSION="1.8.2"
multi.stage='stable'
multi.__index = multi
multi.Mainloop={}
@@ -237,7 +238,7 @@ function multi:getChildren()
return self.Mainloop
end
function multi:getVersion()
- return multi.Version[1].."."..multi.Version[2].."."..multi.Version[3]
+ return multi.Version
end
function multi:getPlatform()
if love then
@@ -248,6 +249,9 @@ function multi:getPlatform()
return "lanes"
end
end
+function multi:canSystemThread()
+ return false
+end
--Processor
function multi:getError()
if self.error then
@@ -417,7 +421,7 @@ function multi:protect()
local status, err=pcall(Loop[_D].Act,Loop[_D])
if err and not(Loop[_D].error) then
Loop[_D].error=err
- self.OnError:Fire(err,Loop[_D])
+ self.OnError:Fire(Loop[_D],err)
end
end
end
@@ -1393,3 +1397,633 @@ function multi:newWatcher(namespace,name)
print('Warning, invalid arguments! Nothing returned!')
end
end
+-- Threading stuff
+thread={}
+multi.GlobalVariables={}
+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.sleep(n)
+ coroutine.yield({"_sleep_",n or 0})
+end
+function thread.hold(n)
+ coroutine.yield({"_hold_",n or function() return true end})
+end
+function thread.skip(n)
+ coroutine.yield({"_skip_",n or 0})
+end
+function thread.kill()
+ coroutine.yield({"_kill_",":)"})
+end
+function thread.yeild()
+ coroutine.yield({"_sleep_",0})
+end
+function thread.getCores()
+ return thread.__CORES
+end
+function thread.set(name,val)
+ multi.GlobalVariables[name]=val
+ return true
+end
+function thread.get(name)
+ return multi.GlobalVariables[name]
+end
+function thread.waitFor(name)
+ thread.hold(function() return thread.get(name)~=nil end)
+ return thread.get(name)
+end
+function thread.testFor(name,val,sym)
+ thread.hold(function() return thread.get(name)~=nil end)
+ return thread.get(name)
+end
+function multi:newTBase(ins)
+ local c = {}
+ c.Active=true
+ c.func={}
+ c.ender={}
+ c.Id=0
+ c.PId=0
+ c.Parent=self
+ c.held=false
+ return c
+end
+function multi:newThread(name,func)
+ local c={}
+ c.ref={}
+ c.Name=name
+ c.thread=coroutine.create(func)
+ c.sleep=1
+ c.Type="thread"
+ c.firstRunDone=false
+ c.timer=multi.scheduler:newTimer()
+ c.ref.Globals=self:linkDomain("Globals")
+ function c.ref:send(name,val)
+ ret=coroutine.yield({Name=name,Value=val})
+ self:syncGlobals(ret)
+ end
+ function c.ref:get(name)
+ return self.Globals[name]
+ end
+ function c.ref:kill()
+ err=coroutine.yield({"_kill_"})
+ if err then
+ error("Failed to kill a thread! Exiting...")
+ end
+ end
+ function c.ref:sleep(n)
+ if type(n)=="function" then
+ ret=coroutine.yield({"_hold_",n})
+ self:syncGlobals(ret)
+ elseif type(n)=="number" then
+ n = tonumber(n) or 0
+ ret=coroutine.yield({"_sleep_",n})
+ self:syncGlobals(ret)
+ else
+ error("Invalid Type for sleep!")
+ end
+ end
+ function c.ref:syncGlobals(v)
+ self.Globals=v
+ end
+ table.insert(self:linkDomain("Threads"),c)
+ if not multi.scheduler:isActive() then
+ multi.scheduler:Resume()
+ end
+end
+multi:setDomainName("Threads")
+multi:setDomainName("Globals")
+multi.scheduler=multi:newUpdater()
+multi.scheduler.Type="scheduler"
+function multi.scheduler:setStep(n)
+ self.skip=tonumber(n) or 24
+end
+multi.scheduler.skip=0
+multi.scheduler.counter=0
+multi.scheduler.Threads=multi:linkDomain("Threads")
+multi.scheduler.Globals=multi:linkDomain("Globals")
+multi.scheduler:OnUpdate(function(self)
+ self.counter=self.counter+1
+ for i=#self.Threads,1,-1 do
+ ret={}
+ if coroutine.status(self.Threads[i].thread)=="dead" then
+ table.remove(self.Threads,i)
+ else
+ if self.Threads[i].timer:Get()>=self.Threads[i].sleep then
+ if self.Threads[i].firstRunDone==false then
+ self.Threads[i].firstRunDone=true
+ self.Threads[i].timer:Start()
+ _,ret=coroutine.resume(self.Threads[i].thread,self.Threads[i].ref)
+ else
+ _,ret=coroutine.resume(self.Threads[i].thread,self.Globals)
+ end
+ if _==false then
+ self.Parent.OnError:Fire(self.Threads[i],ret)
+ print("Error in thread: <"..self.Threads[i].Name.."> "..ret)
+ end
+ if ret==true or ret==false then
+ print("Thread Ended!!!")
+ ret={}
+ end
+ end
+ if ret then
+ if ret[1]=="_kill_" then
+ table.remove(self.Threads,i)
+ elseif ret[1]=="_sleep_" then
+ self.Threads[i].timer:Reset()
+ self.Threads[i].sleep=ret[2]
+ elseif ret[1]=="_skip_" then
+ self.Threads[i].timer:Reset()
+ self.Threads[i].sleep=math.huge
+ local event=multi:newEvent(function(evnt) return multi.scheduler.counter>=evnt.counter end)
+ event.link=self.Threads[i]
+ event.counter=self.counter+ret[2]
+ event:OnEvent(function(evnt)
+ evnt.link.sleep=0
+ end)
+ elseif ret[1]=="_hold_" then
+ self.Threads[i].timer:Reset()
+ self.Threads[i].sleep=math.huge
+ local event=multi:newEvent(ret[2])
+ event.link=self.Threads[i]
+ event:OnEvent(function(evnt)
+ evnt.link.sleep=0
+ end)
+ elseif ret.Name then
+ self.Globals[ret.Name]=ret.Value
+ end
+ end
+ end
+ end
+end)
+multi.scheduler:setStep()
+multi.scheduler:Pause()
+multi.OnError=multi:newConnection()
+function multi:newThreadedAlarm(name,set)
+ local c=self:newTBase()
+ c.Type='alarmThread'
+ c.timer=self:newTimer()
+ c.set=set or 0
+ function c:tofile(path)
+ local m=bin.new()
+ m:addBlock(self.Type)
+ m:addBlock(self.set)
+ m:addBlock(self.Active)
+ m:tofile(path)
+ end
+ function c:Resume()
+ self.rest=false
+ self.timer:Resume()
+ end
+ function c:Reset(n)
+ if n then self.set=n end
+ self.rest=false
+ self.timer:Reset(n)
+ end
+ function c:OnRing(func)
+ table.insert(self.func,func)
+ end
+ function c:Pause()
+ self.timer:Pause()
+ self.rest=true
+ end
+ c.rest=false
+ c.updaterate=multi.Priority_Low -- skips
+ c.restRate=0 -- secs
+ multi:newThread(name,function(ref)
+ while true do
+ if c.rest then
+ thread.sleep(c.restRate) -- rest a bit more when a thread is paused
+ else
+ if c.timer:Get()>=c.set then
+ c:Pause()
+ for i=1,#c.func do
+ c.func[i](c)
+ end
+ end
+ thread.skip(c.updaterate) -- lets rest a bit
+ end
+ end
+ end)
+ self:create(c)
+ return c
+end
+function multi:newThreadedUpdater(name,skip)
+ local c=self:newTBase()
+ c.Type='updaterThread'
+ c.pos=1
+ c.skip=skip or 1
+ function c:Resume()
+ self.rest=false
+ end
+ function c:Pause()
+ self.rest=true
+ end
+ c.OnUpdate=self.OnMainConnect
+ c.rest=false
+ c.updaterate=0
+ c.restRate=.75
+ multi:newThread(name,function(ref)
+ while true do
+ if c.rest then
+ thread.sleep(c.restRate) -- rest a bit more when a thread is paused
+ else
+ for i=1,#c.func do
+ c.func[i](c)
+ end
+ c.pos=c.pos+1
+ thread.skip(c.skip)
+ end
+ end
+ end)
+ self:create(c)
+ return c
+end
+function multi:newThreadedTStep(name,start,reset,count,set)
+ local c=self:newTBase()
+ local think=1
+ c.Type='tstepThread'
+ c.Priority=self.Priority_Low
+ c.start=start or 1
+ local reset = reset or math.huge
+ c.endAt=reset
+ c.pos=start or 1
+ c.skip=skip or 0
+ c.count=count or 1*think
+ c.funcE={}
+ c.timer=os.clock()
+ c.set=set or 1
+ c.funcS={}
+ function c:Update(start,reset,count,set)
+ self.start=start or self.start
+ self.pos=self.start
+ self.endAt=reset or self.endAt
+ self.set=set or self.set
+ self.count=count or self.count or 1
+ self.timer=os.clock()
+ self:Resume()
+ end
+ function c:tofile(path)
+ local m=bin.new()
+ m:addBlock(self.Type)
+ m:addBlock(self.func)
+ m:addBlock(self.funcE)
+ m:addBlock(self.funcS)
+ m:addBlock({pos=self.pos,endAt=self.endAt,skip=self.skip,timer=self.timer,count=self.count,start=self.start,set=self.set})
+ m:addBlock(self.Active)
+ m:tofile(path)
+ end
+ function c:Resume()
+ self.rest=false
+ end
+ function c:Pause()
+ self.rest=true
+ end
+ function c:OnStart(func)
+ table.insert(self.funcS,func)
+ end
+ function c:OnStep(func)
+ table.insert(self.func,func)
+ end
+ function c:OnEnd(func)
+ table.insert(self.funcE,func)
+ end
+ function c:Break()
+ self.Active=nil
+ end
+ function c:Reset(n)
+ if n then self.set=n end
+ self.timer=os.clock()
+ self:Resume()
+ end
+ c.updaterate=0--multi.Priority_Low -- skips
+ c.restRate=0
+ multi:newThread(name,function(ref)
+ while true do
+ if c.rest then
+ thread.sleep(c.restRate) -- rest a bit more when a thread is paused
+ else
+ if os.clock()-c.timer>=c.set then
+ c:Reset()
+ if c.pos==c.start then
+ for fe=1,#c.funcS do
+ c.funcS[fe](c)
+ end
+ end
+ for i=1,#c.func do
+ c.func[i](c.pos,c)
+ end
+ c.pos=c.pos+c.count
+ if c.pos-c.count==c.endAt then
+ c:Pause()
+ for fe=1,#c.funcE do
+ c.funcE[fe](c)
+ end
+ c.pos=c.start
+ end
+ end
+ thread.skip(c.updaterate) -- lets rest a bit
+ end
+ end
+ end)
+ self:create(c)
+ return c
+end
+function multi:newThreadedTLoop(name,func,n)
+ local c=self:newTBase()
+ c.Type='tloopThread'
+ c.restN=n or 1
+ if func then
+ c.func={func}
+ end
+ function c:tofile(path)
+ local m=bin.new()
+ m:addBlock(self.Type)
+ m:addBlock(self.func)
+ m:addBlock(self.Active)
+ m:tofile(path)
+ end
+ function c:Resume()
+ self.rest=false
+ end
+ function c:Pause()
+ self.rest=true
+ end
+ function c:OnLoop(func)
+ table.insert(self.func,func)
+ end
+ c.rest=false
+ c.updaterate=0
+ c.restRate=.75
+ multi:newThread(name,function(ref)
+ while true do
+ if c.rest then
+ thread.sleep(c.restRate) -- rest a bit more when a thread is paused
+ else
+ for i=1,#c.func do
+ c.func[i](c)
+ end
+ thread.sleep(c.restN) -- lets rest a bit
+ end
+ end
+ end)
+ self:create(c)
+ return c
+end
+function multi:newThreadedStep(name,start,reset,count,skip)
+ local c=self:newTBase()
+ local think=1
+ c.Type='stepThread'
+ c.pos=start or 1
+ c.endAt=reset or math.huge
+ c.skip=skip or 0
+ c.spos=0
+ c.count=count or 1*think
+ c.funcE={}
+ c.funcS={}
+ c.start=start or 1
+ if start~=nil and reset~=nil then
+ if start>reset then
+ think=-1
+ end
+ end
+ function c:tofile(path)
+ local m=bin.new()
+ m:addBlock(self.Type)
+ m:addBlock(self.func)
+ m:addBlock(self.funcE)
+ m:addBlock(self.funcS)
+ m:addBlock({pos=self.pos,endAt=self.endAt,skip=self.skip,spos=self.spos,count=self.count,start=self.start})
+ m:addBlock(self.Active)
+ m:tofile(path)
+ end
+ function c:Resume()
+ self.rest=false
+ end
+ function c:Pause()
+ self.rest=true
+ end
+ c.Reset=c.Resume
+ function c:OnStart(func)
+ table.insert(self.funcS,func)
+ end
+ function c:OnStep(func)
+ table.insert(self.func,1,func)
+ end
+ function c:OnEnd(func)
+ table.insert(self.funcE,func)
+ end
+ function c:Break()
+ self.rest=true
+ end
+ function c:Update(start,reset,count,skip)
+ self.start=start or self.start
+ self.endAt=reset or self.endAt
+ self.skip=skip or self.skip
+ self.count=count or self.count
+ self:Resume()
+ end
+ c.updaterate=0
+ c.restRate=.1
+ multi:newThread(name,function(ref)
+ while true do
+ if c.rest then
+ ref:sleep(c.restRate) -- rest a bit more when a thread is paused
+ else
+ if c~=nil then
+ if c.spos==0 then
+ if c.pos==c.start then
+ for fe=1,#c.funcS do
+ c.funcS[fe](c)
+ end
+ end
+ for i=1,#c.func do
+ c.func[i](c.pos,c)
+ end
+ c.pos=c.pos+c.count
+ if c.pos-c.count==c.endAt then
+ c:Pause()
+ for fe=1,#c.funcE do
+ c.funcE[fe](c)
+ end
+ c.pos=c.start
+ end
+ end
+ end
+ c.spos=c.spos+1
+ if c.spos>=c.skip then
+ c.spos=0
+ end
+ ref:sleep(c.updaterate) -- lets rest a bit
+ end
+ end
+ end)
+ self:create(c)
+ return c
+end
+function multi:newThreadedProcess(name)
+ local c = {}
+ setmetatable(c, multi)
+ function c:newBase(ins)
+ local ct = {}
+ setmetatable(ct, self.Parent)
+ ct.Active=true
+ ct.func={}
+ ct.ender={}
+ ct.Id=0
+ ct.PId=0
+ ct.Act=function() end
+ ct.Parent=self
+ ct.held=false
+ ct.ref=self.ref
+ table.insert(self.Mainloop,ct)
+ return ct
+ end
+ c.Parent=self
+ c.Active=true
+ c.func={}
+ c.Id=0
+ c.Type='process'
+ c.Mainloop={}
+ c.Tasks={}
+ c.Tasks2={}
+ c.Garbage={}
+ c.Children={}
+ c.Paused={}
+ c.Active=true
+ c.Id=-1
+ c.Rest=0
+ c.updaterate=.01
+ c.restRate=.1
+ c.Jobs={}
+ c.queue={}
+ c.jobUS=2
+ c.rest=false
+ function c:getController()
+ return nil
+ end
+ function c:Start()
+ self.rest=false
+ end
+ function c:Resume()
+ self.rest=false
+ end
+ function c:Pause()
+ self.rest=true
+ end
+ function c:Remove()
+ self.ref:kill()
+ end
+ function c:kill()
+ err=coroutine.yield({"_kill_"})
+ if err then
+ error("Failed to kill a thread! Exiting...")
+ end
+ end
+ function c:sleep(n)
+ if type(n)=="function" then
+ ret=coroutine.yield({"_hold_",n})
+ elseif type(n)=="number" then
+ n = tonumber(n) or 0
+ ret=coroutine.yield({"_sleep_",n})
+ else
+ error("Invalid Type for sleep!")
+ end
+ end
+ c.hold=c.sleep
+ multi:newThread(name,function(ref)
+ while true do
+ if c.rest then
+ ref:Sleep(c.restRate) -- rest a bit more when a thread is paused
+ else
+ c:uManager()
+ ref:sleep(c.updaterate) -- lets rest a bit
+ end
+ end
+ end)
+ return c
+end
+function multi:newThreadedLoop(name,func)
+ local c=self:newTBase()
+ c.Type='loopThread'
+ c.Start=os.clock()
+ if func then
+ c.func={func}
+ end
+ function c:tofile(path)
+ local m=bin.new()
+ m:addBlock(self.Type)
+ m:addBlock(self.func)
+ m:addBlock(self.Active)
+ m:tofile(path)
+ end
+ function c:Resume()
+ self.rest=false
+ end
+ function c:Pause()
+ self.rest=true
+ end
+ function c:OnLoop(func)
+ table.insert(self.func,func)
+ end
+ c.rest=false
+ c.updaterate=0
+ c.restRate=.75
+ multi:newThread(name,function(ref)
+ while true do
+ if c.rest then
+ thread.sleep(c.restRate) -- rest a bit more when a thread is paused
+ else
+ for i=1,#c.func do
+ c.func[i](os.clock()-self.Start,c)
+ end
+ thread.sleep(c.updaterate) -- lets rest a bit
+ end
+ end
+ end)
+ self:create(c)
+ return c
+end
+function multi:newThreadedEvent(name,task)
+ local c=self:newTBase()
+ c.Type='eventThread'
+ c.Task=task or function() end
+ function c:OnEvent(func)
+ table.insert(self.func,func)
+ end
+ function c:tofile(path)
+ local m=bin.new()
+ m:addBlock(self.Type)
+ m:addBlock(self.Task)
+ m:addBlock(self.func)
+ m:addBlock(self.Active)
+ m:tofile(path)
+ end
+ function c:Resume()
+ self.rest=false
+ end
+ function c:Pause()
+ self.rest=true
+ end
+ c.rest=false
+ c.updaterate=0
+ c.restRate=1
+ multi:newThread(name,function(ref)
+ while true do
+ if c.rest then
+ ref:sleep(c.restRate) -- rest a bit more when a thread is paused
+ else
+ if c.Task(self) then
+ for _E=1,#c.func do
+ c.func[_E](c)
+ end
+ c:Pause()
+ end
+ ref:sleep(c.updaterate) -- lets rest a bit
+ end
+ end
+ end)
+ self:create(c)
+ return c
+end
diff --git a/examples/love2d Threading Example/multi/intergration/lanesManager.lua b/examples/love2d Threading Example/multi/intergration/lanesManager.lua
index 150fd00..8d07bf1 100644
--- a/examples/love2d Threading Example/multi/intergration/lanesManager.lua
+++ b/examples/love2d Threading Example/multi/intergration/lanesManager.lua
@@ -30,8 +30,11 @@ function os.getOS()
end
-- Step 1 get lanes
lanes=require("lanes").configure()
-package.path="lua/?/init.lua;lua/?.lua;"..package.path
-require("multi.all") -- get it all and have it on all lanes
+--~ package.path="lua/?/init.lua;lua/?.lua;"..package.path
+require("multi") -- get it all and have it on all lanes
+function multi:canSystemThread()
+ return true
+end
local multi=multi
-- Step 2 set up the linda objects
local __GlobalLinda = lanes.linda() -- handles global stuff
@@ -103,9 +106,10 @@ function multi:newSystemThread(name,func)
local c={}
local __self=c
c.name=name
+ c.Type="sthread"
c.thread=lanes.gen("*", func)()
function c:kill()
- self.status:Destroy()
+ --self.status:Destroy()
self.thread:cancel()
print("Thread: '"..self.name.."' has been stopped!")
end
@@ -114,7 +118,8 @@ function multi:newSystemThread(name,func)
c.status:OnUpdate(function(self)
local v,err,t=self.link.thread:join(.001)
if err then
- print("Error in thread: '"..self.link.name.."' <"..err..">")
+ multi.OnError:Fire(self.link,err)
+ print("Error in systemThread: '"..self.link.name.."' <"..err..">")
self:Destroy()
end
end)
@@ -124,7 +129,8 @@ print("Intergrated Lanes!")
multi.intergration={} -- for module creators
multi.intergration.GLOBAL=GLOBAL
multi.intergration.THREAD=THREAD
-multi.intergration.lanes={} -- for module creators
+multi.intergration.lanes={}
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}
diff --git a/examples/love2d Threading Example/multi/intergration/loveManager.lua b/examples/love2d Threading Example/multi/intergration/loveManager.lua
index e8304d9..7a332ab 100644
--- a/examples/love2d Threading Example/multi/intergration/loveManager.lua
+++ b/examples/love2d Threading Example/multi/intergration/loveManager.lua
@@ -1,7 +1,11 @@
require("multi.compat.love2d")
+function multi:canSystemThread()
+ return true
+end
multi.intergration={}
multi.intergration.love2d={}
multi.intergration.love2d.ThreadBase=[[
+__THREADNAME__=({...})[1]
require("love.filesystem")
require("love.system")
require("love.timer")
@@ -9,9 +13,11 @@ require("multi.all")
GLOBAL={}
setmetatable(GLOBAL,{
__index=function(t,k)
+ __sync__()
return __proxy__[k]
end,
__newindex=function(t,k,v)
+ __sync__()
__proxy__[k]=v
if type(v)=="userdata" then
__MainChan__:push(v)
@@ -20,6 +26,23 @@ setmetatable(GLOBAL,{
end
end,
})
+function __sync__()
+ local data=__mythread__:pop()
+ while data do
+ if type(data)=="string" then
+ local cmd,tp,name,d=data:match("(%S-) (%S-) (%S-) (.+)")
+ 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
function ToStr(val, name, skipnewlines, depth)
skipnewlines = skipnewlines or false
depth = depth or 0
@@ -43,6 +66,8 @@ function ToStr(val, name, skipnewlines, depth)
tmp = tmp .. string.format("%q", val)
elseif type(val) == "boolean" then
tmp = tmp .. (val and "true" or "false")
+ elseif type(val) == "function" then
+ tmp = tmp .. "loadDump([===["..dump(val).."]===])"
else
tmp = tmp .. "\"[inserializeable datatype:" .. type(val) .. "]\""
end
@@ -57,6 +82,8 @@ function resolveType(tp,d)
return loadDump(d)
elseif tp=="table" then
return loadstring("return "..d)()
+ elseif tp=="nil" then
+ return nil
else
return d
end
@@ -67,7 +94,7 @@ function resolveData(v)
data=ToStr(v)
elseif type(v)=="function" then
data=dump(v)
- elseif type(v)=="string" or type(v)=="number" or type(v)=="bool" then
+ elseif type(v)=="string" or type(v)=="number" or type(v)=="bool" or type(v)=="nil" then
data=tostring(v)
end
return data
@@ -77,7 +104,7 @@ local function randomString(n)
local c=os.clock()
local a=0
while os.clock()"
c.thread=love.thread.newThread(multi.intergration.love2d.ThreadBase:gsub("INSERT_USER_CODE",dump(func)))
- c.thread:start()
+ c.thread:start(c.ID)
+ function c:kill()
+ multi.intergration.GLOBAL["__DIEPLZ"..self.ID.."__"]="__DIEPLZ"..self.ID.."__"
+ end
return c
end
function love.threaderror( thread, errorstr )
- print("Error in "..tostring(thread)..": "..errorstr)
+ multi.OnError:Fire(thread,errorstr)
+ print("Error in systemThread: "..tostring(thread)..": "..errorstr)
end
local THREAD={}
function THREAD.set(name,val)
@@ -327,6 +345,7 @@ updater:OnUpdate(function(self)
data=multi.intergration.love2d.mainChannel:pop()
end
end)
+require("multi.intergration.shared.shared")
print("Intergrated Love2d!")
return {
init=function(t)
diff --git a/examples/love2d Threading Example/multi/intergration/shared/shared.lua b/examples/love2d Threading Example/multi/intergration/shared/shared.lua
new file mode 100644
index 0000000..023b38f
--- /dev/null
+++ b/examples/love2d Threading Example/multi/intergration/shared/shared.lua
@@ -0,0 +1,176 @@
+--[[
+MIT License
+
+Copyright (c) 2017 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, sublicense, 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.
+]]
+function multi:newSystemThreadedQueue(name) -- in love2d this will spawn a channel on both ends
+ local c={} -- where we will store our object
+ c.name=name -- set the name this is important for the love2d side
+ if love then -- check love
+ if love.thread then -- make sure we can use the threading module
+ function c:init() -- create an init function so we can mimic on bith love2d and lanes
+ self.chan=love.thread.getChannel(self.name) -- create channel by the name self.name
+ function self:push(v) -- push to the channel
+ self.chan:push(v)
+ end
+ function self:pop() -- pop from the channel
+ return self.chan:pop()
+ end
+ GLOBAL[self.name]=self -- send the object to the thread through the global interface
+ return self -- return the object
+ end
+ return c
+ else
+ error("Make sure you required the love.thread module!") -- tell the user if he/she didn't require said module
+ end
+ else
+ c.linda=lanes.linda() -- lanes is a bit eaiser, create the linda on the main thread
+ function c:push(v) -- push to the queue
+ self.linda:send("Q",v)
+ end
+ function c:pop() -- pop the queue
+ return ({self.linda:receive(0,"Q")})[2]
+ end
+ function c:init() -- mimic the feature that love2d requires, so code can be consistent
+ return self
+ end
+ multi.intergration.GLOBAL[name]=c -- send the object to the thread through the global interface
+ end
+ return c
+end
+function multi:systemThreadedBenchmark(n,p)
+ n=n or 1
+ local cores=multi.intergration.THREAD.getCores()
+ local queue=multi:newSystemThreadedQueue("QUEUE")
+ multi.intergration.GLOBAL["__SYSTEMBENCHMARK__"]=n
+ local sThread=multi.intergration.THREAD
+ local GLOBAL=multi.intergration.GLOBAL
+ for i=1,cores do
+ multi:newSystemThread("STHREAD_BENCH",function()
+ require("multi")
+ if multi:getPlatform()=="love2d" then
+ GLOBAL=_G.GLOBAL
+ sThread=_G.sThread
+ end -- we cannot have upvalues... in love2d globals not locals must be used
+ queue=sThread.waitFor("QUEUE"):init() -- always wait for when looking for a variable at the start of the thread!
+ multi:benchMark(sThread.waitFor("__SYSTEMBENCHMARK__")):OnBench(function(self,count)
+ queue:push(count)
+ multi:Stop()
+ end)
+ multi:mainloop()
+ end)
+ end
+ local c={}
+ c.tt=function() end
+ c.p=p
+ function c:OnBench(func)
+ self.tt=func
+ end
+ multi:newThread("THREAD_BENCH",function()
+ thread.sleep(n+.1)
+ GLOBAL["QUEUE"]=nil -- time to clean up
+ local num=0
+ data=queue:pop()
+ while data do
+ num=num+data
+ data=queue:pop()
+ end
+ if p then
+ print(tostring(p)..num)
+ end
+ c.tt(c,num)
+ end)
+ return c
+end
+function multi:newSystemThreadedTable(name)
+ local c={} -- where we will store our object
+ c.name=name -- set the name this is important for the love2d side
+ if love then -- check love
+ if love.thread then -- make sure we can use the threading module
+ function c:init() -- create an init function so we can mimic on bith love2d and lanes
+ self.tab={}
+ self.chan=love.thread.getChannel(self.name) -- create channel by the name self.name
+ function self:waitFor(name) -- pop from the channel
+ repeat self:sync() until self[name]
+ return self[name]
+ end
+ function self:sync()
+ local data=self.chan:pop()
+ while data do
+ if type(data)=="string" then
+ local cmd,tp,name,d=data:match("(%S-) (%S-) (%S-) (.+)")
+ if cmd=="SYNC" then
+ self.tab[name]=resolveType(tp,d) -- this is defined in the loveManager.lua file
+ end
+ else
+ self.tab[name]=data
+ end
+ data=self.chan:pop()
+ end
+ end
+ setmetatable(self,{
+ __index=function(t,k)
+ self:sync()
+ return self.tab[k]
+ end,
+ __newindex=function(t,k,v)
+ self:sync()
+ self.tab[k]=v
+ if type(v)=="userdata" then
+ self.chan:push(v)
+ else
+ self.chan:push("SYNC "..type(v).." "..k.." "..resolveData(v)) -- this is defined in the loveManager.lua file
+ end
+ end,
+ })
+ GLOBAL[self.name]=self -- send the object to the thread through the global interface
+ return self -- return the object
+ end
+ return c
+ else
+ error("Make sure you required the love.thread module!") -- tell the user if he/she didn't require said module
+ end
+ else
+ c.linda=lanes.linda() -- lanes is a bit eaiser, create the linda on the main thread
+ function c:waitFor(name)
+ while self[name]==nil do
+ -- Waiting
+ end
+ return self[name]
+ end
+ function c:sync()
+ return -- just so we match the love2d side
+ end
+ function c:init() -- set the metatable
+ setmetatable(self,{
+ __index=function(t,k)
+ return self.linda:get(k)
+ end,
+ __newindex=function(t,k,v)
+ self.linda:set(k,v)
+ end,
+ })
+ return self
+ end
+ multi.intergration.GLOBAL[name]=c -- send the object to the thread through the global interface
+ end
+ return c
+end
diff --git a/multi/compat/love2d.lua b/multi/compat/love2d.lua
index 50fcd2e..cc4015d 100644
--- a/multi/compat/love2d.lua
+++ b/multi/compat/love2d.lua
@@ -23,19 +23,6 @@ SOFTWARE.
]]
require("multi")
os.sleep=love.timer.sleep
---~ function bin.load(file,s,r)
---~ content, size = love.filesystem.read(file)
---~ local temp=bin.new(content)
---~ temp.filepath=file
---~ return temp
---~ end
---~ function bin:tofile(filename)
---~ if not(filename) or self.Stream then return nil end
---~ love.filesystem.write(filename,self.data)
---~ end
---~ function bin.stream(file,l)
---~ error("Sorry streaming is not available when using love2d :(, I am looking for a solution though :)")
---~ end
function love.run()
if love.math then
love.math.setRandomSeed(os.time())
diff --git a/multi/init.lua b/multi/init.lua
index 235fcfb..cbd74b3 100644
--- a/multi/init.lua
+++ b/multi/init.lua
@@ -45,7 +45,8 @@ function print(...)
end
end
multi = {}
-multi.Version={1,8,1}
+multi.Version="1.8.2"
+multi._VERSION="1.8.2"
multi.stage='stable'
multi.__index = multi
multi.Mainloop={}
@@ -237,7 +238,7 @@ function multi:getChildren()
return self.Mainloop
end
function multi:getVersion()
- return multi.Version[1].."."..multi.Version[2].."."..multi.Version[3]
+ return multi.Version
end
function multi:getPlatform()
if love then
diff --git a/multi/intergration/loveManager.lua b/multi/intergration/loveManager.lua
index 2e8e06f..7a332ab 100644
--- a/multi/intergration/loveManager.lua
+++ b/multi/intergration/loveManager.lua
@@ -13,9 +13,11 @@ require("multi.all")
GLOBAL={}
setmetatable(GLOBAL,{
__index=function(t,k)
+ __sync__()
return __proxy__[k]
end,
__newindex=function(t,k,v)
+ __sync__()
__proxy__[k]=v
if type(v)=="userdata" then
__MainChan__:push(v)
@@ -24,6 +26,23 @@ setmetatable(GLOBAL,{
end
end,
})
+function __sync__()
+ local data=__mythread__:pop()
+ while data do
+ if type(data)=="string" then
+ local cmd,tp,name,d=data:match("(%S-) (%S-) (%S-) (.+)")
+ 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
function ToStr(val, name, skipnewlines, depth)
skipnewlines = skipnewlines or false
depth = depth or 0
@@ -121,25 +140,7 @@ function sThread.get(name)
return GLOBAL[name]
end
function sThread.waitFor(name)
- --print("Waiting:",__mythreadname__)
- local function wait()
- local data=__mythread__:pop()
- while data do
- if type(data)=="string" then
- local cmd,tp,name,d=data:match("(%S-) (%S-) (%S-) (.+)")
- 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
- repeat wait() until GLOBAL[name]
+ repeat __sync__() until GLOBAL[name]
return GLOBAL[name]
end
function sThread.getCores()
@@ -149,24 +150,7 @@ function sThread.sleep(n)
love.timer.sleep(n)
end
function sThread.hold(n)
- local function wait()
- local data=__mythread__:pop()
- while data do
- if type(data)=="string" then
- local cmd,tp,name,d=data:match("(%S-) (%S-) (%S-) (.+)")
- 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
- repeat wait() until n()
+ repeat __sync__() until n()
end
updater=multi:newUpdater()
updater:OnUpdate(function(self)
diff --git a/multi/intergration/shared/shared.lua b/multi/intergration/shared/shared.lua
index 9a2d864..023b38f 100644
--- a/multi/intergration/shared/shared.lua
+++ b/multi/intergration/shared/shared.lua
@@ -100,3 +100,77 @@ function multi:systemThreadedBenchmark(n,p)
end)
return c
end
+function multi:newSystemThreadedTable(name)
+ local c={} -- where we will store our object
+ c.name=name -- set the name this is important for the love2d side
+ if love then -- check love
+ if love.thread then -- make sure we can use the threading module
+ function c:init() -- create an init function so we can mimic on bith love2d and lanes
+ self.tab={}
+ self.chan=love.thread.getChannel(self.name) -- create channel by the name self.name
+ function self:waitFor(name) -- pop from the channel
+ repeat self:sync() until self[name]
+ return self[name]
+ end
+ function self:sync()
+ local data=self.chan:pop()
+ while data do
+ if type(data)=="string" then
+ local cmd,tp,name,d=data:match("(%S-) (%S-) (%S-) (.+)")
+ if cmd=="SYNC" then
+ self.tab[name]=resolveType(tp,d) -- this is defined in the loveManager.lua file
+ end
+ else
+ self.tab[name]=data
+ end
+ data=self.chan:pop()
+ end
+ end
+ setmetatable(self,{
+ __index=function(t,k)
+ self:sync()
+ return self.tab[k]
+ end,
+ __newindex=function(t,k,v)
+ self:sync()
+ self.tab[k]=v
+ if type(v)=="userdata" then
+ self.chan:push(v)
+ else
+ self.chan:push("SYNC "..type(v).." "..k.." "..resolveData(v)) -- this is defined in the loveManager.lua file
+ end
+ end,
+ })
+ GLOBAL[self.name]=self -- send the object to the thread through the global interface
+ return self -- return the object
+ end
+ return c
+ else
+ error("Make sure you required the love.thread module!") -- tell the user if he/she didn't require said module
+ end
+ else
+ c.linda=lanes.linda() -- lanes is a bit eaiser, create the linda on the main thread
+ function c:waitFor(name)
+ while self[name]==nil do
+ -- Waiting
+ end
+ return self[name]
+ end
+ function c:sync()
+ return -- just so we match the love2d side
+ end
+ function c:init() -- set the metatable
+ setmetatable(self,{
+ __index=function(t,k)
+ return self.linda:get(k)
+ end,
+ __newindex=function(t,k,v)
+ self.linda:set(k,v)
+ end,
+ })
+ return self
+ end
+ multi.intergration.GLOBAL[name]=c -- send the object to the thread through the global interface
+ end
+ return c
+end