7 Commits
Author SHA1 Message Date
server 5edea3a6a4 Typos in readme and example file 2017-06-22 11:34:19 -04:00
server f072c2cfde Updated lanes intergration
It is now at a point where I feel okay with documenting.
2017-06-22 11:32:50 -04:00
server bf011646f7 minibug
lua looks at 'dots' as '/' so they were changed to ','
2017-06-20 23:43:08 -04:00
server 091cd90a0d Changed some features!
Updated the ReadMe to reflect said changes
2017-06-20 23:42:45 -04:00
server acbfef10cc added ned version tag
Changed enough that it merited changing the version to 1.6.0
2017-06-20 23:34:06 -04:00
server aad031062f Changed how steps and loops work
Added multi.compat.backwards[ver]
Require this to be able to use features that were changed from that version.
loops callback args are now: self,dt
steps callback args are now: self,pos
If requiring the backwards compat for the previous version do so after requiring all of the modules you are using.
2017-06-20 23:32:57 -04:00
server 3ff72de1e0 Typos and my promise
Fixed a few typos i saw while reading the Read-Me.
Currently looking over my library to see what can be added or optimized.
Due to making backwards support my #1 priority some changes that I want to make to the loop and step objects will be tricky.
The idea is to change the callback method to give the self object first then the position/index for steps and make loops callback to give the self then the time value. To make this work the user would need to import those new object separately. This though opens a new can of worms though... I'll think about it...
2017-06-20 23:08:50 -04:00
11 changed files with 453 additions and 85 deletions
+91 -19
View File
@@ -1,15 +1,87 @@
# multi Version: 1.5.0
Updated from 1.4.1
# multi Version: 1.7.0 (Taking multi-tasking to the next level)
Updated from 1.6.0 to 1.7.0
Modified: multi.intergration.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.
Example Usage:
```lua
local GLOBAL,lthread=require("multi.intergration.lanesManager").init()
require("multi.alarm")
require("multi.threading")
multi:newAlarm(2):OnRing(function(self)
GLOBAL["NumOfCores"]=lthread.getCores()
end)
multi:newAlarm(7):OnRing(function(self)
GLOBAL["AnotherTest"]=true
end)
multi:newAlarm(13):OnRing(function(self)
GLOBAL["FinalTest"]=true
end)
multi:newSystemThread("test",function() -- spawns a thread in another lua process
require("multi.all") -- now you can do all of your coding with the multi library! You could even spawn more threads from here with the intergration. You would need to require the interaction again though
print("Waiting for variable: NumOfCores")
print("Got it: ",lthread.waitFor("NumOfCores"))
lthread.hold(function()
return GLOBAL["AnotherTest"] -- note this would hold the entire systemthread. Spawn a coroutine thread using multi:newThread() or multi:newThreaded...
end)
print("Holding works!")
multi:newThread("tests",function()
thread.hold(function()
return GLOBAL["FinalTest"] -- note this will not hold the entire systemthread. As seen with the TLoop constantly going!
end)
print("Final test works!")
os.exit()
end)
local a=0
multi:newTLoop(function()
a=a+1
print(a)
end,.5)
multi:mainloop()
end)
multi:mainloop()
```
Onec I am happy with the intergration and feel it is ready I will document it better
Updated from 1.5.0 to 1.6.0
Changed: steps and loops
```lua
-- Was
step:OnStep(function(pos,self) -- same goes for tsteps as well
print(pos)
end)
multi:newLoop(function(dt,self)
print(dt)
end)
-- Is now
step:OnStep(function(self,pos) -- same goes for tsteps as well
print(pos)
end)
multi:newLoop(function(self,dt)
print(dt)
end)
```
Reasoning I wanted to keep objects consistant, but a lot of my older libraries use the old way of doing things. Therefore I added a backwards module
```lua
require("multi.all")
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
Added:
- An easy way to manage timeouts
- Small bug fixes
1.4.1 - First Public release of the library
IMPORTANT:
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.
Example at end of the readme
My multitasking library for lua</br>
To install copy the multi folder into your enviroment and you are good to go</br>
It is a pure lua binding if you ingore the intergrations (WIP)</br>
It is a pure lua binding if you ingore the intergrations (Stable!)</br>
If you find any bugs or have any issues please let me know :)
@@ -68,7 +140,7 @@ Throughout these examples I am going to do some strange things in order to show
-- Loops
require("multi.all") -- gets the entire library
count=0
loop=multi:newLoop(function(dt,self) -- dt is delta time and self is a reference to itself
loop=multi:newLoop(function(self,dt) -- dt is delta time and self is a reference to itself
count=count+1
if count > 10 then
self:Break() -- All methods on the multi objects are upper camel case, where as methods on the multi or process/queuer namespace are lower camel case
@@ -108,7 +180,7 @@ This library aims to be Async like. In reality everything is still on one thread
require("multi.all") -- gets the entire library
count=0
-- lets use the loop again to add to count!
loop=multi:newLoop(function(dt,self)
loop=multi:newLoop(function(self,dt)
count=count+1
end)
event=multi:newEvent(function() return count==100 end) -- set the event
@@ -131,7 +203,7 @@ step2=multi:newStep(10,1,-1,1) -- a second step, notice the slight changes!
step1:OnStart(function(self)
print("Step Started!")
end)
step1:OnStep(function(pos,self)
step1:OnStep(function(self,pos)
if pos<=10 then -- what what is this? the step only goes to 10!!!
print("Stepping... "..pos)
else
@@ -150,7 +222,7 @@ end)
-- step2 is bored lets give it some love :P
step2.range=step2:newRange() -- Set up a range object to have a nested step in a sense! Each nest requires a new range
-- it is in your interest not to share ranges between objects! You can however do it if it suits your needs though
step2:OnStep(function(pos,self)
step2:OnStep(function(self,pos)
-- for 1=1,math.huge do
-- print("Haha I am holding the code up because I can!!!")
--end
@@ -163,7 +235,7 @@ end)
-- TSteps are just like alarms and steps mixed together, the only difference in construction is the 4th Argument. On a TStep that argument controls time. The defualt is 1
-- The Reset(n) works just like you would figure!
step3=multi:newTStep(1,10,.5,2) -- lets go from 1 to 10 counting by .5 every 2 seconds
step3:OnStep(function(pos,self)
step3:OnStep(function(self,pos)
print("Ok "..pos.."!")
end)
multi:mainloop()
@@ -210,7 +282,7 @@ require("multi.all")
-- Lets create the events
yawn={} -- ill just leave that there
OnCustomSafeEvent=multi:newConnection(true) -- lets pcall the calls incase something goes wrong defualt
OnCustomEvent=multi:newConnection(false) -- lets pcall the calls incase something goes wrong
OnCustomEvent=multi:newConnection(false) -- lets not pcall the calls and let errors happen... We are good at coding though so lets get a speed advantage by not pcalling. Pcalling is useful for plugins and stuff that may have been coded badly and you can ingore those connections if need be.
OnCustomEvent:Bind(yawn) -- create the connection lookup data in yawn
-- Lets connect to them, a recent update adds a nice syntax to connect to these
@@ -224,7 +296,7 @@ cd3=OnCustomSafeEvent:Connect(function(arg1,arg2,...)
print("CSE3",arg1,arg2,...)
end) -- lets not give this connection a name
-- no need for connect, but I kept that function because of backwards compatibility
-- no need for connect, but I kept that function because of backwards compatibility.
OnCustomEvent(function(arg1,arg2,...)
print(arg1,arg2,...)
end)
@@ -395,7 +467,7 @@ updater:OnUpdate(function(self)
b=b+1
end)
a=0 -- a counter
loop2=proc:newLoop(function(dt,self)
loop2=proc:newLoop(function(self,dt)
print("Lets Go!")
self:hold(3) -- this will keep this object from doing anything! Note: You can only have one hold active at a time! Multiple are possible, but results may not be as they seem see * for how hold works
-- Within a process using hold will keep it alive until the hold is satisified!
@@ -463,10 +535,10 @@ queue = multi:newQueuer()
queue:newAlarm(3):OnRing(function()
print("Ring ring!!!")
end)
queue:newStep(1,10):OnStep(function(pos,self)
queue:newStep(1,10):OnStep(function(self,pos)
print(pos)
end)
queue:newLoop(function(dt,self)
queue:newLoop(function(self,dt)
if dt==3 then
self:Break()
print("Done")
@@ -515,17 +587,17 @@ require("multi.*") -- now I can use that lovely * symbol to require everything
test=multi:newThreadedProcess("main") -- you can thread processors and all Actors see note for a list of actors you can thread!
test2=multi:newThreadedProcess("main2")
count=0
test:newLoop(function(dt,self)
test:newLoop(function(self,dt)
count=count+1
thread.sleep(.01)
end)
test2:newLoop(function(dt,self)
test2:newLoop(function(self,dt)
print("Hello!")
thread.sleep(1) -- sleep for some time
end)
-- threads take a name object then the rest as normal
step=multi:newThreadedTStep("step",1,10)
step:OnStep(function(p,self)
step:OnStep(function(self,p)
print("step",p)
thread.skip(21) -- skip n cycles
end)
@@ -533,11 +605,11 @@ step:OnEnd(function()
print("Killing thread!")
thread.kill() -- kill the thread
end)
loop=multi:newThreadedLoop("loop",function(dt,self)
loop=multi:newThreadedLoop("loop",function(self,dt)
print(dt)
thread.sleep(1.1)
end)
loop2=multi:newThreadedLoop("loop",function(dt,self)
loop2=multi:newThreadedLoop("loop",function(self,dt)
print(dt)
thread.hold(function() return count>=100 end)
print("Count is "..count)
@@ -631,7 +703,7 @@ require("multi.task")
multi:newTask(function()
print("Hi!")
end)
multi:newLoop(function(dt,self)
multi:newLoop(function(self,dt)
print("Which came first the task or the loop?")
self:Break()
end)
+7
View File
@@ -0,0 +1,7 @@
package.path="?/init.lua;"..package.path
require("multi.all")
require("multi.compat.backwards[1,5,0]")
multi:newLoop(function(dt,self)
print(dt)
end)
multi:mainloop() -- start the main runner
+37
View File
@@ -0,0 +1,37 @@
package.path="?/init.lua;?.lua;"..package.path
local GLOBAL,lthread=require("multi.intergration.lanesManager").init()
require("multi.alarm")
require("multi.threading")
for i,v in pairs(lanes.ABOUT) do print(i,v) end
multi:newAlarm(2):OnRing(function(self)
GLOBAL["NumOfCores"]=lthread.getCores()
end)
multi:newAlarm(7):OnRing(function(self)
GLOBAL["AnotherTest"]=true
end)
multi:newAlarm(13):OnRing(function(self)
GLOBAL["FinalTest"]=true
end)
multi:newSystemThread("test",function() -- spawns a thread in another lua process
require("multi.all") -- now you can do all of your coding with the multi library! You could even spawn more threads from here with the intergration. You would need to require the interaction again though
print("Waiting for variable: NumOfCores")
print("Got it: ",lthread.waitFor("NumOfCores"))
lthread.hold(function()
return GLOBAL["AnotherTest"] -- note this would hold the entire systemthread. Spawn a coroutine thread using multi:newThread() or multi:newThreaded...
end)
print("Holding works!")
multi:newThread("tests",function()
thread.hold(function()
return GLOBAL["FinalTest"] -- note this will not hold the entire systemthread. As seen with the TLoop constantly going!
end)
print("Final test works!")
os.exit()
end)
local a=0
multi:newTLoop(function()
a=a+1
print(a)
end,.5)
multi:mainloop()
end)
multi:mainloop()
+282
View File
@@ -0,0 +1,282 @@
multi.OnObjectCreated(function(obj)
if obj.Type=="loop" then
function obj:Act()
for i=1,#self.func do
self.func[i](self.Parent.clock()-self.Start,self)
end
end
elseif obj.Type=="step" then
function obj:Act()
if self~=nil then
if self.spos==0 then
if self.pos==self.start then
for fe=1,#self.funcS do
self.funcS[fe](self)
end
end
for i=1,#self.func do
self.func[i](self.pos,self)
end
self.pos=self.pos+self.count
if self.pos-self.count==self.endAt then
self:Pause()
for fe=1,#self.funcE do
self.funcE[fe](self)
end
self.pos=self.start
end
end
end
self.spos=self.spos+1
if self.spos>=self.skip then
self.spos=0
end
end
elseif obj.Type=="tstep" then
function c:Act()
if self.clock()-self.timer>=self.set then
self:Reset()
if self.pos==self.start then
for fe=1,#self.funcS do
self.funcS[fe](self)
end
end
for i=1,#self.func do
self.func[i](self.pos,self)
end
self.pos=self.pos+self.count
if self.pos-self.count==self.endAt then
self:Pause()
for fe=1,#self.funcE do
self.funcE[fe](self)
end
self.pos=self.start
end
end
end
end
end)
if thread then
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)
else
for i=1,#c.func do
c.func[i](os.clock()-self.Start,c)
end
thread.sleep(c.updaterate)
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)
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)
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
c.restRate=0
multi:newThread(name,function(ref)
while true do
if c.rest then
thread.sleep(c.restRate)
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)
end
end
end)
self:create(c)
return c
end
end
+1 -1
View File
@@ -22,7 +22,7 @@ function print(...)
end
end
multi = {}
multi.Version={1,5,0}
multi.Version={1,6,1}
multi.stage='stable'
multi.__index = multi
multi.Mainloop={}
+29 -51
View File
@@ -8,7 +8,7 @@ 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
require("multi.updater") -- get it all and have it on all lanes
local multi=multi
-- Step 2 set up the linda objects
local __GlobalLinda = lanes.linda() -- handles global stuff
@@ -31,8 +31,21 @@ end
function THREAD.get(name)
__GlobalLinda:get(name)
end
local function randomString(n)
local str = ''
local strings = {'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','1','2','3','4','5','6','7','8','9','0','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'}
for i=1,n do
str = str..''..strings[math.random(1,#strings)]
end
return str
end
function THREAD.waitFor(name)
--
local function wait()
math.randomseed(os.time())
__SleepingLinda:receive(.001,randomString(12))
end
repeat wait() until __GlobalLinda:get(name)
return __GlobalLinda:get(name)
end
function THREAD.testFor(name,val,sym)
--
@@ -45,63 +58,24 @@ if os.getOS()=="windows" then
else
THREAD.__CORES=tonumber(io.popen("nproc --all"):read("*n"))
end
-- Step 4 change the coroutine threading methods to work the same, but with lanes TODO when the lanes scheduler is ready!
function THREAD.skip(n)
-- Do Nothing
end
function THREAD.kill() -- trigger the lane destruction
-- coroutine.yield({"_kill_",":)"})
end
--[[ Step 5 We need to get sleeping working so we need a lane to handle timing... We want idle wait not busy wait
--[[ Step 4 We need to get sleeping working to handle timing... We want idle wait, not busy wait
Idle wait keeps the CPU running better where busy wait wastes CPU cycles... Lanes does not have a sleep method
however, a linda recieve will in fact be a idle wait! So when wait is called we can pack the cmd up and send it to
the sleeping thread manager to send the variable for the other thread to consume, sending only after a certain time has passed!
]]
local function randomString(n)
local str = ''
local strings = {'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','1','2','3','4','5','6','7','8','9','0','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'}
for i=1,n do
str = str..''..strings[math.random(1,#strings)]
end
return str
end
however, a linda recieve will in fact be a idle wait! So we use that and wrap it in a nice package]]
function THREAD.sleep(n)
math.randomseed(os.time())
local randKey=randomString(12) -- generate a random string a-Z and 0-9
__SleepingLinda:send("tired","SLEEP|"..randKey.."|"..tostring(n)) -- send the data that needs to be managed
local dat=__SleepingLinda:receive(randKey)
return dat
__SleepingLinda:receive(n,randomString(12))
end
function THREAD.hold(n)
while not(n()) do
-- holding
local function wait()
math.randomseed(os.time())
__SleepingLinda:receive(.001,randomString(12))
end
repeat wait() until n()
end
-- start the time manager lane
--~ lanes.gen("*", function()
--~ local timers={}
--~ while true do -- forever loop!
--~ local data=__SleepingLinda:receive(.001,"tired") -- timeout after .001 seconds and handle the other stuff
--~ if data then -- the .001 is an entarnal timer that keeps this thread from using too much CPU as well!
--~ print(data)
--~ local cmd,key,sec=data:match("(%S-)|(%S-)|(.+)")
--~ if cmd=="SLEEP" then
--~ print("GOT!")
--~ timers[#timers+1]={os.clock()+tonumber(sec),key}
--~ --__SleepingLinda:set()
--~ elseif cmd=="audit" then
--~ --
--~ end
--~ end
--~ for i=1,#timers do
--~ if os.clock()>=timers[i][1] then
--~ __SleepingLinda:send(timers[i][2],true)
--~ table.remove(timers,i)
--~ end
--~ end
--~ end
--~ end)() -- The global timer is now activated, and it works great!
-- Step 6 Basic Threads!
-- Step 5 Basic Threads!
function multi:newSystemThread(name,func)
local c={}
local __self=c
@@ -123,5 +97,9 @@ function multi:newSystemThread(name,func)
end)
return c
end
_G["GLOBAL"]=GLOBAL
_G["__GlobalLinda"]=__GlobalLinda
print("Intergrated Lanes!")
multi.intergration={} -- for module creators
multi.intergration.lanes={} -- for module creators
multi.intergration.lanes.GLOBAL=GLOBAL -- for module creators
multi.intergration.lanes.THREAD=THREAD -- for module creators
return {init=function() return GLOBAL,THREAD end}
+1 -1
View File
@@ -15,7 +15,7 @@ function multi:newLoop(func)
end
function c:Act()
for i=1,#self.func do
self.func[i](self.Parent.clock()-self.Start,self)
self.func[i](self,self.Parent.clock()-self.Start)
end
end
function c:OnLoop(func)
+1 -1
View File
@@ -35,7 +35,7 @@ function multi:newStep(start,reset,count,skip)
end
end
for i=1,#self.func do
self.func[i](self.pos,self)
self.func[i](self,self.pos)
end
self.pos=self.pos+self.count
if self.pos-self.count==self.endAt then
+1 -1
View File
@@ -42,7 +42,7 @@ function multi:newTStep(start,reset,count,set)
end
end
for i=1,#self.func do
self.func[i](self.pos,self)
self.func[i](self,self.pos)
end
self.pos=self.pos+self.count
if self.pos-self.count==self.endAt then
-8
View File
@@ -1,8 +0,0 @@
package.path="?/init.lua;"..package.path
require("multi.all")
process=multi:newProcess() -- this can also load a file, to keep things really organized, I will show a simple example first then show the same thing being done within another file.
process:newTLoop(function(self)
print("Looping every second...")
end,1)
process:Start() -- starts the process
multi:mainloop() -- start the main runner