diff --git a/PManager.txt b/PManager.txt index 11dab87..8ddf23a 100644 --- a/PManager.txt +++ b/PManager.txt @@ -1,10 +1,10 @@ -C: 2731526 ~I*7 -H: 2341308 ~I*6 -A: 1951090 ~I*5 -N: 1560872 ~I*4 -B: 1170655 ~I*3 -L: 780438 ~I*2 -I: 390219 ~I +C: 3322269 ~I*7 +H: 2847660 ~I*6 +A: 2373050 ~I*5 +N: 1898440 ~I*4 +B: 1423830 ~I*3 +L: 949220 ~I*2 +I: 474610 ~I ~n=I*PRank P2 @@ -16,4 +16,14 @@ N: 104700 B: 26175 L: 6543 I: 1635 -~n=n*4 \ No newline at end of file +~n=n*4 + +P3 +---------------- +C: 2120906 +H: 2120906 +A: 2120906 +N: 2120906 +B: 2120906 +L: 2120906 +I: 2120506 \ No newline at end of file diff --git a/changes.md b/changes.md index 6fa23c0..13edb12 100644 --- a/changes.md +++ b/changes.md @@ -1,5 +1,76 @@ #Changes [TOC] +Update 12.2.0 +------------- +**Added:** +- multi.nextStep(func) +- Method chaining +- Priority 3 has been added! +- ResetPriority() -- This will set a flag for a process to be re evaluated for how much of an impact it is having on the performance of the system. +- setting: auto_priority added! -- If only lua os.clock was more fine tuned... milliseconds are not enough for this to work +- setting: auto_lowerbound added! -- when using auto_priority this will allow you to set the lowbound for pirority. The defualt is a hyrid value that was calculated to reach the max potential with a delay of .001, but can be changed to whatever. Remember this is set to processes that preform really badly! If lua could handle more detail in regards to os.clock() then i would set the value a bit lower like .0005 or something like that +- setting: auto_stretch added! -- This is another way to modify the extent of the lowest setting. This reduces the impact that a low preforming process has! Setting this higher reduces the number of times that a process is called. Only in effect when using auto_priotity +- setting: auto_delay added! -- sets the time in seconds that the system will recheck for low performing processes and manage them. Will also upgrade a process if it starts to run better. +```lua +-- All methods that did not return before now return a copy of itself. Thus allowing chaining. Most if not all mutators returned nil, so chaining can now be done. I will eventually write up a full documentation of everything which will show this. +multi = require("multi") +multi:newStep(1,100):OnStep(function(self,i) + print("Index: "..i) +end):OnEnd(function(self) + print("Step is done!") +end) +multi:mainloop{ + priority = 3 +} +``` +Priority 3 works a bit differently than the other 2. + +P1 follows a forumla that resembles this: ~n=I*PRank where n is the amount of steps given to an object with PRank and where I is the idle time see chart below. The aim of this priority scheme was to make core objects run fastest while letting idle processes get decent time as well. +``` +C: 3322269 ~I*7 +H: 2847660 ~I*6 +A: 2373050 ~I*5 +N: 1898440 ~I*4 +B: 1423830 ~I*3 +L: 949220 ~I*2 +I: 474610 ~I +~n=I*PRank +``` +P2 follows a formula that resembles this: ~n=n*4 where n is the idle time, see chart below. The goal of this one was to make core process' higher while keeping idle process' low. +``` +C: 6700821 +H: 1675205 +A: 418801 +N: 104700 +B: 26175 +L: 6543 +I: 1635 +~n=n*4 +``` +P3 Ignores using a basic funceion and instead bases its processing time on the amount of cpu time is there. If cpu-time is low and a process is set at a lower priority it will get its time reduced. There is no formula, at idle almost all process work at the same speed! +``` +C: 2120906 +H: 2120906 +A: 2120906 +N: 2120906 +B: 2120906 +L: 2120906 +I: 2120506 +``` + +Auto Priority works by seeing what should be set high or low. Due to lua not having more persicion than milliseconds, I was unable to have a detailed manager that can set things to high, above normal, normal, ect. This has either high or low. If a process takes longer than .001 millisecond it will be set to low priority. You can change this by using the setting auto_lowest = multi.Priority_[PLevel] the defualt is low, not idle, since idle tends to get about 1 process each second though you can change it to idle using that setting. + +**Improved:** +- Performance at the base level has been doubled! On my machine benchmark went from ~9mil to ~20 mil steps/s. +Note: If you write slow code this library's improbements wont make much of a difference. +- Loops have been optimised as well! Being the most used objects I felt they needed to be made as fast as possible + +I usually give an example of the changes made, but this time I have an explantion for multi.nextStep(). It's not an entirely new feature since multi:newJob() does something like this, but is completely different. nextStep addes a function that is executed first on the next step. If multiple things are added to next step, then they will be executed in the order that they were added. + +Note: +The upper limit of this libraries performance on my machine is ~39mil. This is simply a while loop counting up from 0 and stops after 1 second. The 20mil that I am currently getting is probably as fast as it can get since its half of the max performance possible, and each layer I have noticed that it doubles complexity. Throughout the years with this library I have seen massive improvements in speed. In the beginning we had only ~2000 steps per second. Fast right? then after some tweaks we went to about 300000 steps per second, then 600000. Some more tweaks brought me to ~1mil steps per second, then to ~4 mil then ~9 mil and now finally ~20 mil... the doubling effect that i have now been seeing means that odds are I have reach the limit. I will aim to add more features and optimize individule objects. If its possible to make the library even faster then I will go for it. + + Update 12.1.0 ------------- Fixed: @@ -35,7 +106,8 @@ Contunue to make small changes as I come about them. This change was inspired wh Update: 12.0.0 Big update (Lots of additions some changes) ------------------------ -**Note:** ~~After doing some testing, I have noticed that using multi-objects are slightly, quite a bit, faster than using (coroutines)multi:newthread(). Only create a thread if there is no other possibility! System threads are different and will improve performance if you know what you are doing. Using a (coroutine)thread as a loop with a timer is slower than using a TLoop! If you do not need the holding features I strongly recommend that you use the multi-objects. This could be due to the scheduler that I am using, and I am looking into improving the performance of the scheduler for (coroutine)threads. This is still a work in progress so expect things to only get better as time passes!~~ This was the reason threadloop was added. It binds the thread scheduler into the mainloop allowing threads to run much faster than before. Also the use of locals is now possible since I am not dealing with seperate objects. And finally, reduced function overhead help keeps the threads running better. +**Note:** ~~After doing some testing, I have noticed that using multi-objects are slightly, quite a bit, faster than using (coroutines)multi:newthread(). Only create a thread if there is no other possibility! System threads are different and will improve performance if you know what you are doing. Using a (coroutine)thread as a loop with a +is slower than using a TLoop! If you do not need the holding features I strongly recommend that you use the multi-objects. This could be due to the scheduler that I am using, and I am looking into improving the performance of the scheduler for (coroutine)threads. This is still a work in progress so expect things to only get better as time passes!~~ This was the reason threadloop was added. It binds the thread scheduler into the mainloop allowing threads to run much faster than before. Also the use of locals is now possible since I am not dealing with seperate objects. And finally, reduced function overhead help keeps the threads running better. #Added: - `nGLOBAL = require("multi.integration.networkManager").init()` diff --git a/multi/init.lua b/multi/init.lua index cdd2dfb..9d55029 100644 --- a/multi/init.lua +++ b/multi/init.lua @@ -23,8 +23,9 @@ SOFTWARE. ]] local bin = pcall(require,"bin") local multi = {} -multi.Version = "12.0.0" -multi._VERSION = "12.0.0" +local clock = os.clock +multi.Version = "12.2.0" +multi._VERSION = "12.2.0" multi.stage = "stable" multi.__index = multi multi.Mainloop = {} @@ -43,7 +44,11 @@ multi.jobUS = 2 multi.clock = os.clock multi.time = os.time multi.LinkedPath = multi -multi.isRunning = false +multi.lastTime = clock() +local mainloopActive = false +local isRunning = false +local next +local ncount = 0 multi.defaultSettings = { priority = 0, protect = false, @@ -144,6 +149,7 @@ function multi:setPriority(s) elseif s:lower()=='idle' or s:lower()=='i' then self.Priority=self.Priority_Idle end + self.solid = true end end -- System @@ -230,25 +236,23 @@ function multi:getError() end end function multi:benchMark(sec,p,pt) + local c = 0 local temp=self:newLoop(function(self,t) - if self.clock()-self.init>self.sec then + if t>sec then if pt then - print(pt.." "..self.c.." Steps in "..sec.." second(s)!") + print(pt.." "..c.." Steps in "..sec.." second(s)!") end - self.tt(self.sec,self.c) + self.tt(sec,c) self:Destroy() else - self.c=self.c+1 + c=c+1 end end) - temp.Priority=p or 1 + temp:setPriority(p or 1) function temp:OnBench(func) self.tt=func end self.tt=function() end - temp.sec=sec - temp.init=self.clock() - temp.c=0 return temp end function multi.startFPSMonitior() @@ -268,7 +272,9 @@ function multi.timer(func,...) local timer=multi:newTimer() timer:Start() args={func(...)} - return timer:Get(),unpack(args) + local t = timer:Get() + timer = nil + return t,unpack(args) end function multi:IsAnActor() return ({watcher=true,tstep=true,step=true,updater=true,loop=true,alarm=true,event=true})[self.Type] @@ -369,12 +375,15 @@ function multi:ResolveTimer(...) self.funcTMR[i](self,...) end self:Pause() + return self end function multi:OnTimedOut(func) self.funcTM[#self.funcTM+1]=func + return self end function multi:OnTimerResolved(func) self.funcTMR[#self.funcTMR+1]=func + return self end -- Timer stuff done function multi:Pause() @@ -386,6 +395,7 @@ function multi:Pause() table.remove(self.Parent.Mainloop,self.Id) end end + return self end function multi:Resume() if self.Type=='process' or self.Type=='mainprocess' then @@ -401,6 +411,7 @@ function multi:Resume() self.Active=true end end + return self end function multi:Destroy() if self.Type=='process' or self.Type=='mainprocess' then @@ -419,9 +430,11 @@ function multi:Destroy() end self.Active=false end + return self end function multi:Reset(n) self:Resume() + return self end function multi:isDone() return self.Active~=true @@ -482,20 +495,24 @@ function multi:newProcess(file) if self.l then self.l:Resume() end + return self end function c:Resume() if self.l then self.l:Resume() end + return self end function c:Pause() if self.l then self.l:Pause() end + return self end function c:Remove() self:Destroy() self.l:Destroy() + return self end if file then self.Cself=c @@ -512,6 +529,7 @@ function multi:newQueuer(file) c.funcE={} function c:OnQueueCompleted(func) table.insert(self.funcE,func) + return self end if file then self.Cself=c @@ -534,40 +552,45 @@ function multi:newQueuer(file) function c:Start() self.Mainloop[#self.Mainloop]:Resume() self.l:Resume() + return self end return c end function multi:newTimer() local c={} c.Type='timer' - c.time=0 - c.count=0 - c.paused=false + local time=0 + local count=0 + local paused=false function c:Start() - self.time=os.clock() + time=os.clock() + return self end function c:Get() - if self:isPaused() then return self.time end - return (os.clock()-self.time)+self.count + if self:isPaused() then return time end + return (clock()-time)+count end function c:isPaused() - return self.paused + return paused end c.Reset=c.Start function c:Pause() - self.time=self:Get() - self.paused=true + time=self:Get() + paused=true + return self end function c:Resume() - self.paused=false - self.time=os.clock()-self.time + paused=false + time=os.clock()-time + return self end function c:tofile(path) local m=bin.new() - self.count=self.count+self:Get() + count=count+self:Get() m:addBlock(self.Type) - m:addBlock(self.count) + m:addBlock(count) m:tofile(path) + return self end self:create(c) return c @@ -597,12 +620,14 @@ function multi:newConnection(protect) self.Parent:uManager(multi.defaultSettings) until self.waiting==false id:Destroy() + return self end c.HoldUT=c.holdUT function c:fConnect(func) local temp=self:connect(func) table.insert(self.fconnections,temp) self.FC=self.FC+1 + return self end c.FConnect=c.fConnect function c:getConnection(name,ingore) @@ -633,9 +658,11 @@ function multi:newConnection(protect) end function c:Bind(t) self.func=t + return self end function c:Remove() self.func={} + return self end function c:connect(func,name,num) self.ID=self.ID+1 @@ -687,6 +714,7 @@ function multi:newConnection(protect) m:addBlock(self.Type) m:addBlock(self.func) m:tofile(path) + return self end return c end @@ -723,6 +751,14 @@ function multi:newJob(func,name) end) end end +function multi.nextStep(func) + ncount = ncount+1 + if not next then + next = {func} + else + next[#self.next+1] = func + end +end function multi:newRange() local selflink=self local temp={ @@ -819,26 +855,54 @@ function multi:mainloop(settings) multi.defaultSettings = settings or multi.defaultSettings self.uManager=self.uManagerRef multi.OnPreLoad:Fire() - if not multi.isRunning then + local p_c,p_h,p_an,p_n,p_bn,p_l,p_i = self.Priority_Core,self.Priority_High,self.Priority_Above_Normal,self.Priority_Normal,self.Priority_Below_Normal,self.Priority_Low,self.Priority_Idle + local P_LB = p_i + if not isRunning then local protect = false local priority = false local stopOnError = true + local delay = 3 if settings then + priority = settings.priority + if settings.auto_priority then + priority = -1 + end if settings.preLoop then settings.preLoop(self) end if settings.stopOnError then stopOnError = settings.stopOnError end + if settings.auto_stretch then + p_i = p_i * settings.auto_stretch + end + if settings.auto_delay then + delay = settings.auto_delay + end + if settings.auto_lowerbound then + P_LB = settings.auto_lowerbound + end protect = settings.protect - priority = settings.priority end - multi.isRunning=true - rawset(self,'Start',self.clock()) - while self.Active do - if priority==1 then - local Loop=self.Mainloop - local PS=self + local t,tt = clock(),0 + isRunning=true + local lastTime = clock() + rawset(self,'Start',clock()) + mainloopActive = true + local Loop=self.Mainloop + local PS=self + local PStep = 1 + local autoP = 0 + local solid + local sRef + while mainloopActive do + if ncount ~= 0 then + for i = 1, ncount do + next[i]() + end + ncount = 0 + end + if priority == 1 then for _D=#Loop,1,-1 do for P=1,7 do if Loop[_D] then @@ -862,12 +926,10 @@ function multi:mainloop(settings) end end end - elseif priority==2 then - local Loop=self.Mainloop - local PS=self + elseif priority == 2 then for _D=#Loop,1,-1 do if Loop[_D] then - if (PS.PStep)%Loop[_D].Priority==0 then + if (PStep)%Loop[_D].Priority==0 then if Loop[_D].Active then self.CID=_D if not protect then @@ -886,12 +948,96 @@ function multi:mainloop(settings) end end end - PS.PStep=PS.PStep+1 - if PS.PStep>self.Priority_Idle then - PS.PStep=1 + PStep=PStep+1 + if PStep==p_i then + PStep=0 + end + elseif priority == 3 then + tt = clock()-t + t = clock() + for _D=#Loop,1,-1 do + if Loop[_D] then + if Loop[_D].Priority == p_c or (Loop[_D].Priority == p_h and tt<.5) or (Loop[_D].Priority == p_an and tt<.125) or (Loop[_D].Priority == p_n and tt<.063) or (Loop[_D].Priority == p_bn and tt<.016) or (Loop[_D].Priority == p_l and tt<.003) or (Loop[_D].Priority == p_i and tt<.001) then + if Loop[_D].Active then + self.CID=_D + if not protect then + Loop[_D]:Act() + else + local status, err=pcall(Loop[_D].Act,Loop[_D]) + if err then + Loop[_D].error=err + self.OnError:Fire(Loop[_D],err) + if stopOnError then + Loop[_D]:Destroy() + end + end + end + end + end + end + end + elseif priority == -1 then + for _D=#Loop,1,-1 do + sRef = Loop[_D] + if Loop[_D] then + if (sRef.Priority == p_c) or PStep==0 then + if sRef.Active then + self.CID=_D + if not protect then + if sRef.solid then + sRef:Act() + solid = true + else + time = multi.timer(sRef.Act,sRef) + sRef.solid = true + solid = false + end + if Loop[_D] and not solid then + if time == 0 then + Loop[_D].Priority = p_c + else + Loop[_D].Priority = P_LB + end + end + else + if Loop[_D].solid then + Loop[_D]:Act() + solid = true + else + time, status, err=multi.timer(pcall,Loop[_D].Act,Loop[_D]) + Loop[_D].solid = true + solid = false + end + if Loop[_D] and not solid then + if time == 0 then + Loop[_D].Priority = p_c + else + Loop[_D].Priority = P_LB + end + end + if err then + Loop[_D].error=err + self.OnError:Fire(Loop[_D],err) + if stopOnError then + Loop[_D]:Destroy() + end + end + end + end + end + end + end + PStep=PStep+1 + if PStep>p_i then + PStep=0 + if clock()-lastTime>delay then + lastTime = clock() + for i = 1,#Loop do + Loop[i]:ResetPriority() + end + end end else - local Loop=self.Mainloop for _D=#Loop,1,-1 do if Loop[_D] then if Loop[_D].Active then @@ -919,19 +1065,37 @@ function multi:mainloop(settings) end function multi:uManager(settings) multi.OnPreLoad:Fire() + multi.defaultSettings = settings or multi.defaultSettings + self.t,self.tt = clock(),0 if settings then + priority = settings.priority + if settings.auto_priority then + priority = -1 + end if settings.preLoop then settings.preLoop(self) end + if settings.stopOnError then + stopOnError = settings.stopOnError + end + multi.defaultSettings.p_i = settings.auto_stretch*self.Priority_Idle or self.Priority_Idle + multi.defaultSettings.delay = settings.auto_delay or 3 + multi.defaultSettings.auto_lowerbound = settings.auto_lowerbound or self.Priority_Idle + protect = settings.protect end - multi.defaultSettings = settings or multi.defaultSettings self.uManager=self.uManagerRef end function multi:uManagerRef(settings) if self.Active then + if ncount ~= 0 then + for i = 1, ncount do + next[i]() + end + ncount = 0 + end + local Loop=self.Mainloop + local PS=self if multi.defaultSettings.priority==1 then - local Loop=self.Mainloop - local PS=self for _D=#Loop,1,-1 do for P=1,7 do if Loop[_D] then @@ -945,6 +1109,9 @@ function multi:uManagerRef(settings) if err then Loop[_D].error=err self.OnError:Fire(Loop[_D],err) + if multi.defaultSettings.stopOnError then + Loop[_D]:Destroy() + end end end end @@ -953,8 +1120,6 @@ function multi:uManagerRef(settings) end end elseif multi.defaultSettings.priority==2 then - local Loop=self.Mainloop - local PS=self for _D=#Loop,1,-1 do if Loop[_D] then if (PS.PStep)%Loop[_D].Priority==0 then @@ -967,6 +1132,9 @@ function multi:uManagerRef(settings) if err then Loop[_D].error=err self.OnError:Fire(Loop[_D],err) + if multi.defaultSettings.stopOnError then + Loop[_D]:Destroy() + end end end end @@ -975,10 +1143,94 @@ function multi:uManagerRef(settings) end PS.PStep=PS.PStep+1 if PS.PStep>self.Priority_Idle then - PS.PStep=1 + PS.PStep=0 + end + elseif priority == 3 then + self.tt = clock()-self.t + self.t = clock() + for _D=#Loop,1,-1 do + if Loop[_D] then + if Loop[_D].Priority == self.Priority_Core or (Loop[_D].Priority == self.Priority_High and tt<.5) or (Loop[_D].Priority == self.Priority_Above_Normal and tt<.125) or (Loop[_D].Priority == self.Priority_Normal and tt<.063) or (Loop[_D].Priority == self.Priority_Below_Normal and tt<.016) or (Loop[_D].Priority == self.Priority_Low and tt<.003) or (Loop[_D].Priority == self.Priority_Idle and tt<.001) then + if Loop[_D].Active then + self.CID=_D + if not protect then + Loop[_D]:Act() + else + local status, err=pcall(Loop[_D].Act,Loop[_D]) + if err then + Loop[_D].error=err + self.OnError:Fire(Loop[_D],err) + if multi.defaultSettings.stopOnError then + Loop[_D]:Destroy() + end + end + end + end + end + end + end + elseif priority == -1 then + for _D=#Loop,1,-1 do + local sRef = Loop[_D] + if Loop[_D] then + if (sRef.Priority == self.Priority_Core) or PStep==0 then + if sRef.Active then + self.CID=_D + if not protect then + if sRef.solid then + sRef:Act() + solid = true + else + time = multi.timer(sRef.Act,sRef) + sRef.solid = true + solid = false + end + if Loop[_D] and not solid then + if time == 0 then + Loop[_D].Priority = self.Priority_Core + else + Loop[_D].Priority = multi.defaultSettings.auto_lowerbound + end + end + else + if Loop[_D].solid then + Loop[_D]:Act() + solid = true + else + time, status, err=multi.timer(pcall,Loop[_D].Act,Loop[_D]) + Loop[_D].solid = true + solid = false + end + if Loop[_D] and not solid then + if time == 0 then + Loop[_D].Priority = self.Priority_Core + else + Loop[_D].Priority = multi.defaultSettings.auto_lowerbound + end + end + if err then + Loop[_D].error=err + self.OnError:Fire(Loop[_D],err) + if multi.defaultSettings.stopOnError then + Loop[_D]:Destroy() + end + end + end + end + end + end + end + self.PStep=self.PStep+1 + if self.PStep>multi.defaultSettings.p_i then + self.PStep=0 + if clock()-self.lastTime>multi.defaultSettings.delay then + self.lastTime = clock() + for i = 1,#Loop do + Loop[i]:ResetPriority() + end + end end else - local Loop=self.Mainloop for _D=#Loop,1,-1 do if Loop[_D] then if Loop[_D].Active then @@ -1036,9 +1288,11 @@ function multi:newEvent(task) end function c:SetTask(func) self.Task=func + return self end function c:OnEvent(func) table.insert(self.func,func) + return self end self:create(c) return c @@ -1059,6 +1313,7 @@ function multi:newUpdater(skip) end function c:SetSkip(n) self.skip=n + return self end c.OnUpdate=self.OnMainConnect self:create(c) @@ -1082,18 +1337,22 @@ function multi:newAlarm(set) function c:Resume() self.Parent.Resume(self) self.timer:Resume() + return self end function c:Reset(n) if n then self.set=n end self:Resume() self.timer:Reset() + return self end function c:OnRing(func) table.insert(self.func,func) + return self end function c:Pause() self.timer:Pause() self.Parent.Pause(self) + return self end self:create(c) return c @@ -1101,17 +1360,19 @@ end function multi:newLoop(func) local c=self:newBase() c.Type='loop' - c.Start=self.clock() + local start=self.clock() + local funcs = {} if func then - c.func={func} + funcs={func} end function c:Act() - for i=1,#self.func do - self.func[i](self,self.Parent.clock()-self.Start) + for i=1,#funcs do + funcs[i](self,clock()-start) end end function c:OnLoop(func) - table.insert(self.func,func) + table.insert(funcs,func) + return self end self:create(c) return c @@ -1126,9 +1387,11 @@ function multi:newFunction(func) c.Parent=self function c:Pause() self.Active=false + return self end function c:Resume() self.Active=true + return self end setmetatable(c,mt) self:create(c) @@ -1180,15 +1443,19 @@ function multi:newStep(start,reset,count,skip) c.Reset=c.Resume function c:OnStart(func) table.insert(self.funcS,func) + return self end function c:OnStep(func) table.insert(self.func,1,func) + return self end function c:OnEnd(func) table.insert(self.funcE,func) + return self end function c:Break() self.Active=nil + return self end function c:Update(start,reset,count,skip) self.start=start or self.start @@ -1196,6 +1463,7 @@ function multi:newStep(start,reset,count,skip) self.skip=skip or self.skip self.count=count or self.count self:Resume() + return self end self:create(c) return c @@ -1221,13 +1489,16 @@ function multi:newTLoop(func,set) function c:Resume() self.Parent.Resume(self) self.timer:Resume() + return self end function c:Pause() self.timer:Pause() self.Parent.Pause(self) + return self end function c:OnLoop(func) table.insert(self.func,func) + return self end self:create(c) return c @@ -1238,6 +1509,7 @@ function multi:newTrigger(func) c.trigfunc=func or function() end function c:Fire(...) self:trigfunc(...) + return self end self:create(c) return c @@ -1265,6 +1537,7 @@ function multi:newTStep(start,reset,count,set) self.count=count or self.count or 1 self.timer=self.clock() self:Resume() + return self end function c:Act() if self.clock()-self.timer>=self.set then @@ -1289,20 +1562,25 @@ function multi:newTStep(start,reset,count,set) end function c:OnStart(func) table.insert(self.funcS,func) + return self end function c:OnStep(func) table.insert(self.func,func) + return self end function c:OnEnd(func) table.insert(self.funcE,func) + return self end function c:Break() self.Active=nil + return self end function c:Reset(n) if n then self.set=n end self.timer=self.clock() self:Resume() + return self end self:create(c) return c @@ -1391,24 +1669,31 @@ function multi:newTimeStamper() else self.time[#self.time+1]={hour,minute,true} end + return self end function c:OnHour(hour,func) self.hour[#self.hour+1]={string.format("%02d",hour),func,true} + return self end function c:OnMinute(minute,func) self.minute[#self.minute+1]={string.format("%02d",minute),func,true} + return self end function c:OnSecond(second,func) self.second[#self.second+1]={string.format("%02d",second),func,true} + return self end function c:OnDay(day,func) self.day[#self.day+1]={day,func,true} + return self end function c:OnMonth(month,func) self.month[#self.month+1]={string.format("%02d",month),func,true} + return self end function c:OnYear(year,func) self.year[#self.year+1]={string.format("%02d",year),func,true} + return self end self:create(c) return c @@ -1426,6 +1711,7 @@ function multi:newWatcher(namespace,name) c.cv=ns[n] function c:OnValueChanged(func) table.insert(self.func,func) + return self end function c:Act() if self.cv~=self.ns[self.n] then @@ -1631,18 +1917,22 @@ function multi:newThreadedAlarm(name,set) function c:Resume() self.rest=false self.timer:Resume() + return self end function c:Reset(n) if n then self.set=n end self.rest=false self.timer:Reset(n) + return self end function c:OnRing(func) table.insert(self.func,func) + return self end function c:Pause() self.timer:Pause() self.rest=true + return self end c.rest=false c.updaterate=multi.Priority_Low -- skips @@ -1672,9 +1962,11 @@ function multi:newThreadedUpdater(name,skip) c.skip=skip or 1 function c:Resume() self.rest=false + return self end function c:Pause() self.rest=true + return self end c.OnUpdate=self.OnMainConnect c.rest=false @@ -1718,29 +2010,37 @@ function multi:newThreadedTStep(name,start,reset,count,set) self.count=count or self.count or 1 self.timer=os.clock() self:Resume() + return self end function c:Resume() self.rest=false + return self end function c:Pause() self.rest=true + return self end function c:OnStart(func) table.insert(self.funcS,func) + return self end function c:OnStep(func) table.insert(self.func,func) + return self end function c:OnEnd(func) table.insert(self.funcE,func) + return self end function c:Break() self.Active=nil + return self end function c:Reset(n) if n then self.set=n end self.timer=os.clock() self:Resume() + return self end c.updaterate=0--multi.Priority_Low -- skips c.restRate=0 @@ -1784,12 +2084,15 @@ function multi:newThreadedTLoop(name,func,n) end function c:Resume() self.rest=false + return self end function c:Pause() self.rest=true + return self end function c:OnLoop(func) table.insert(self.func,func) + return self end c.rest=false c.updaterate=0 @@ -1828,22 +2131,28 @@ function multi:newThreadedStep(name,start,reset,count,skip) end function c:Resume() self.rest=false + return self end function c:Pause() self.rest=true + return self end c.Reset=c.Resume function c:OnStart(func) table.insert(self.funcS,func) + return self end function c:OnStep(func) table.insert(self.func,1,func) + return self end function c:OnEnd(func) table.insert(self.funcE,func) + return self end function c:Break() self.rest=true + return self end function c:Update(start,reset,count,skip) self.start=start or self.start @@ -1851,6 +2160,7 @@ function multi:newThreadedStep(name,start,reset,count,skip) self.skip=skip or self.skip self.count=count or self.count self:Resume() + return self end c.updaterate=0 c.restRate=.1 @@ -1929,21 +2239,26 @@ function multi:newThreadedProcess(name) end function c:Start() self.rest=false + return self end function c:Resume() self.rest=false + return self end function c:Pause() self.rest=true + return self end function c:Remove() self.ref:kill() + return self end function c:kill() err=coroutine.yield({"_kill_"}) if err then error("Failed to kill a thread! Exiting...") end + return self end function c:sleep(n) if type(n)=="function" then @@ -1954,6 +2269,7 @@ function multi:newThreadedProcess(name) else error("Invalid Type for sleep!") end + return self end c.hold=c.sleep multi:newThread(name,function(ref) @@ -1977,12 +2293,15 @@ function multi:newThreadedLoop(name,func) end function c:Resume() self.rest=false + return self end function c:Pause() self.rest=true + return self end function c:OnLoop(func) table.insert(self.func,func) + return self end c.rest=false c.updaterate=0 @@ -2008,12 +2327,15 @@ function multi:newThreadedEvent(name,task) c.Task=task or function() end function c:OnEvent(func) table.insert(self.func,func) + return self end function c:Resume() self.rest=false + return self end function c:Pause() self.rest=true + return self end c.rest=false c.updaterate=0 @@ -2039,6 +2361,7 @@ end -- State Saving Stuff function multi:IngoreObject() self.Ingore=true + return self end multi.scheduler:IngoreObject() function multi:ToString() diff --git a/multitut.lua b/multitut.lua new file mode 100644 index 0000000..81bdd4e --- /dev/null +++ b/multitut.lua @@ -0,0 +1,15 @@ +package.path="?/init.lua;?.lua;"..package.path +local multi = require("multi") +--~ local GLOBAL, THREAD = require("multi.integration.lanesManager").init() +--~ nGLOBAL = require("multi.integration.networkManager").init() + + +local a = 0 +local clock = os.clock +b = clock() +while clock()-b <1 do + a = a +1 +end +print("a: "..a) +--~ multi:benchMark(1,nil,"Bench:") +--~ multi:mainloop() diff --git a/rockspecs/multi-12.2-0.rockspec b/rockspecs/multi-12.2-0.rockspec new file mode 100644 index 0000000..8f190e2 --- /dev/null +++ b/rockspecs/multi-12.2-0.rockspec @@ -0,0 +1,32 @@ +package = "multi" +version = "12.2-0" +source = { + url = "git://github.com/rayaman/multi.git", + tag = "v12.2.0", +} +description = { + summary = "Lua Multi tasking library", + detailed = [[ + This library contains many methods for multi tasking. From simple side by side code using multi-objs, to using coroutine based Threads and System threads(When you have lua lanes installed or are using love2d) + ]], + homepage = "https://github.com/rayaman/multi", + license = "MIT" +} +dependencies = { + "lua >= 5.1", + "bin", + "lanes", + "lua-net" +} +build = { + type = "builtin", + modules = { + ["multi.init"] = "multi/init.lua", + ["multi.compat.love2d"] = "multi/compat/love2d.lua", + ["multi.integration.lanesManager"] = "multi/integration/lanesManager.lua", + ["multi.integration.loveManager"] = "multi/integration/loveManager.lua", + ["multi.integration.luvitManager"] = "multi/integration/luvitManager.lua", + ["multi.integration.networkManager"] = "multi/integration/networkManager.lua", + ["multi.integration.shared"] = "multi/integration/shared.lua" + } +} \ No newline at end of file diff --git a/test.lua b/test.lua index a0843af..22586c1 100644 --- a/test.lua +++ b/test.lua @@ -1,17 +1,54 @@ package.path="?/init.lua;?.lua;"..package.path +--~ package.cpath="./?.dll;"..package.cpath +--~ time = require("time") +--~ local d1 = time.date(2012, 4, 30) +--~ a=time.nowlocal() +--~ while true do +--~ print(time.nowlocal():ticks()) +--~ end multi = require("multi") -local a = 0 -multi:newThread("test",function() - print("lets go") - b,c = thread.hold(function() - return b,"We did it!" +--~ multi:newTLoop(function(self) +--~ a = 0 +--~ end,.001) +--~ multi:benchMark(1,nil,"Steps/s:"):OnBench(function() +--~ os.exit() +--~ end) +function multi:ResetPriority() + self.solid = false +end +local clock = os.clock +function sleep(n) -- seconds + local t0 = clock() + while clock() - t0 <= n do end +end +local a=0 +local b=0 +local c=0 +multi:benchMark(1,multi.Priority_Core,"Regular Bench:"):OnBench(function() -- the onbench() allows us to do each bench after each other! + print("AutoP\n---------------") + multi:newLoop(function() + a=a+1 + end) + t=multi:newLoop(function() + c=c+1 + sleep(.001) + end) + multi:newLoop(function() + b=b+1 + end) + multi:benchMark(1,multi.Priority_Core,"Hmm:"):OnBench(function() + multi.nextStep(function() + print(a,b,c) +--~ os.exit() + end) end) - print(b,c) end) -multi:newTLoop(function() - a=a+1 - if a == 5 then - b = "Hello" - end -end,1) -multi:mainloop() +settings = { +--~ priority = 3, -- this is overwritten while auto_priority is being used! You can also use -1 for this setting as well + auto_priority = true, + auto_stretch = 1000, + auto_lowerbound = multi.Priority_Idle +} +while true do + multi:uManager(settings) +end diff --git a/time/init.lua b/time/init.lua new file mode 100644 index 0000000..a2b35cf --- /dev/null +++ b/time/init.lua @@ -0,0 +1,570 @@ +-------------------------------------------------------------------------------- +-- A library for the manipulation of dates and periods according to the +-- Gregorian calendar. +-- +-- Credit: the Gregorian calendar routines contained in this library are +-- ported from Claus Tøndering calendar algorithms: +-- http://www.tondering.dk/main/index.php/calendar-information . +-- +-- Copyright (C) 2011-2016 Stefano Peluchetti. All rights reserved. +-------------------------------------------------------------------------------- + +local ffi = require "ffi" + +local C = ffi.C +local format = string.format +local floor, min = math.floor, math.min +local type, new, istype, tonumber = type, ffi.new, ffi.istype, tonumber + +local int64_ct = ffi.typeof("int64_t") + +local function T_int(x) + if not (type(x) == "number" and x == floor(x)) then + error("integer number expected") + end +end + +local function T_same(x, y) + if not istype(x, y) then + error("same type expected") + end +end + +-- Period ---------------------------------------------------------------------- +-- Return string representation for a positive period. +local function posptostr(h, m, s, ms) + return format("%02i:%02i:%02i.%06i", h, m, s, ms) +end + +local p_ct + +local function T_period(x) + if not istype(p_ct, x) then + error("period expected") + end +end + +local function p_64(ticks) + return new(p_ct, ticks) +end + +local function pfirst(x, y) + if istype(p_ct, y) then + return y, x + else + return x, y + end +end + +local p_mt = { + __new = function(ct, h, m, s, ms) + h = h or 0; m = m or 0; s = s or 0; ms = ms or 0; + T_int(h); T_int(m); T_int(s); T_int(ms); + return new(ct, h*(1e6*60*60LL)+m*(1e6*60LL)+s*(1e6*1LL)+ms) + end, + copy = function(self) + return new(p_ct, self) + end, + __eq = function(self, rhs) T_same(self, rhs) + return self._ticks == rhs._ticks + end, + __lt = function(self, rhs) T_same(self, rhs) + return self._ticks < rhs._ticks + end, + __le = function(self, rhs) T_same(self, rhs) + return self._ticks <= rhs._ticks + end, + __add = function(self, rhs) T_same(self, rhs) -- Commutative. + return p_64(self._ticks + rhs._ticks) + end, + __sub = function(self, rhs) T_same(self, rhs) + return p_64(self._ticks - rhs._ticks) + end, + __unm = function(self) + return p_64(-self._ticks) + end, + __mul = function(self, rhs) -- Commutative. + local p, n = pfirst(self, rhs) + T_int(n) + return p_64(p._ticks*n) + end, + -- Approximate ratio, non-reversible in both cases. + __div = function(self, rhs) + T_period(self) -- Disallow (not-a-period)/period. + if type(rhs) == "number" then + return p_64(self._ticks/rhs) + elseif istype(p_ct, rhs) then + return tonumber(self._ticks)/tonumber(rhs._ticks) + else + error("unexpected type") + end + end, + __tostring = function(self) + local h, m, s, ms = self:parts() + if self._ticks >= 0 then + return posptostr(h, m, s, ms) + else + return "-"..posptostr(-h, -m, -s, -ms) + end + end, + ticks = function(self) -- Expose int64_t. + return self._ticks + end, + microseconds = function(self) + return tonumber(self._ticks%1e6) + end, + seconds = function(self) + return tonumber((self._ticks/1e6)%60) + end, + minutes = function(self) + return tonumber((self._ticks/(1e6*60))%60) + end, + hours = function(self) + return tonumber(self._ticks/(1e6*60*60)) + end, + parts = function(self) + return self:hours(), self:minutes(), self:seconds(), self:microseconds() + end, + tomicroseconds = function(self) + return tonumber(self._ticks) + end, + tomilliseconds = function(self) + return tonumber(self._ticks)/1e3 + end, + toseconds = function(self) + return tonumber(self._ticks)/1e6 + end, + tominutes = function(self) + return tonumber(self._ticks)/(1e6*60) + end, + tohours = function(self) + return tonumber(self._ticks)/(1e6*60*60) + end, +} +p_mt.__index = p_mt + +p_ct = ffi.metatype("struct { int64_t _ticks; }", p_mt) + +local function weeks(x) T_int(x) return p_64(x*(1e6*60*60*24*7LL)) end +local function days(x) T_int(x) return p_64(x*(1e6*60*60*24LL)) end +local function hours(x) T_int(x) return p_64(x*(1e6*60*60LL)) end +local function minutes(x) T_int(x) return p_64(x*(1e6*60LL)) end +local function seconds(x) T_int(x) return p_64(x*(1e6*1LL)) end +local function milliseconds(x) T_int(x) return p_64(x*(1e3*1LL)) end +local function microseconds(x) T_int(x) return p_64(x*1LL) end + +local function toperiod(x) + if type(x) == "string" then + local f1, l1, h, m, s, ms = x:find("(%d+):(%d+):(%d+).(%d+)") + if h == nil or ms == nil or l1 ~= #x then + error("'"..x.."' is not a string representation of a period") + end + local ton = tonumber + return p_ct(ton(h), ton(m), ton(s), ton(ms)) + elseif istype(int64_ct, x) then + return p_64(x) + else + error("unexpected type") + end +end + +-- Months ---------------------------------------------------------------------- +local months_mt = { + __new = function(ct, x) T_int(x) + return new(ct, x) + end, +} + +local months_ct = ffi.metatype("struct { int32_t _m; }", months_mt) + +-- Years ----------------------------------------------------------------------- +local years_mt = { + __new = function(ct, x) T_int(x) + return new(ct, x) + end, +} + +local years_ct = ffi.metatype("struct { int32_t _y; }", years_mt) + +-- Date ------------------------------------------------------------------------ +-- It's date(1582, 1, 1): +local d_ticks_min = 198622713600000000LL +-- It's date(9999,12,31) + period(23, 59, 59, 999999): +local d_ticks_max = 464269103999999999LL + +local d_ct + +local function T_date(x) + if not istype(d_ct, x) then + error("date expected") + end +end + +local function T_date_range(ticks) + if not (d_ticks_min <= ticks and ticks <= d_ticks_max) then + error("resulting date is outside the allowed range") + end +end + +local function d_64(ticks) T_date_range(ticks) + return new(d_ct, ticks) +end + +local function dfirst(x, y) + if istype(d_ct, y) then + return y, x + else + return x, y + end +end + +-- 1582 adoption, 9999 to keep 4 chars for years part: +local function T_year(year) T_int(year) + if not (1582 <= year and year <= 9999) then + error("year "..year.." outside the allowed range [1582, 9999]") + end +end + +local function T_month(month) T_int(month) + if not (1 <= month and month <= 12) then + error("month "..month.." outside the allowed range [1, 12]") + end +end + +local function isleapyear(year) T_year(year) + return year%4 == 0 and (year%100 ~= 0 or year%400 == 0) +end + +local eom = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } + +local function endofmonth(year, month) T_year(year) T_month(month) + return (month == 2 and isleapyear(year)) and 29 or eom[month] +end + +local function T_day(year, month, day) + if not (1 <= day and day <= endofmonth(year, month)) then + error(year.."-"..month.."-"..day.." is not a valid date") + end +end + +local function weekday(year, month, day) T_year(year) T_month(month) + T_day(year, month, day) + local a = floor((14 - month)/12) + local y = year - a + local m = month + 12*a - 2 + local d = (day + y + floor(y/4) - floor(y/100) + floor(y/400) + + floor((31*m)/12)) % 7 + return d == 0 and 7 or d -- Days of week from 1 = Monday to 7 = Sunday. +end + +local function shift_months(y, m, deltam) + local newm = (m - 1 + deltam) % 12 + 1 + local newy = y + floor((m - 1 + deltam)/12) + T_year(newy) + T_month(newm) + return newy, newm +end + +local function ymd_to_julian(year, month, day) + -- Range of numbers suffices for this: + local a = floor((14 - month)/12) + local y = year + 4800 - a + local m = month + 12*a - 3 + return day + floor((153*m + 2)/5) + 365*y + floor(y/4) - floor(y/100) + + floor(y/400) - 32045 +end + +local function julian_to_ymd(julian) + -- Range of numbers suffices for this: + local a = julian + 32044 + local b = floor((4*a + 3)/146097) + local c = a - floor((146097*b)/4) + local d = floor((4*c + 3)/1461) + local e = c - floor((1461*d)/4) + local m = floor((5*e + 2)/153) + local day = e - floor((153*m + 2)/5) + 1 + local month = m + 3 - 12*floor(m/10) + local year = 100*b + d - 4800 + floor(m/10) + return year, month, day +end + +-- Assumes only violation of valid date may be in day outside of end of month +-- due to months and years shifts. Cap the day and returns a valid date. +local function valid_date_cap_day(year, month, day) + day = min(day, endofmonth(year, month)) + return d_ct(year, month, day) +end + +local d_mt = { + __new = function(ct, year, month, day) T_year(year) T_month(month) + T_day(year, month, day) + return new(ct, ymd_to_julian(year, month, day)*(86400LL*1e6)) + end, + copy = function(self) + return new(d_ct, self) + end, + __eq = p_mt.__eq, + __lt = p_mt.__lt, + __le = p_mt.__le, + __add = function(self, rhs) -- Commutative. + local d, s = dfirst(self, rhs) + if istype(p_ct, s) then + return d_64(d._ticks + s._ticks) + elseif istype(months_ct, s) then + local year, month, day = d:ymd() + year, month = shift_months(year, month, s._m) + return valid_date_cap_day(year, month, day) + d:period() + elseif istype(years_ct, s) then + local year, month, day = d:ymd() + year = year + s._y + return valid_date_cap_day(year, month, day) + d:period() + else + error("unexpected type") + end + end, + __sub = function(self, rhs) + T_date(self) -- Disallow (not-a-date)-date. + if istype(p_ct, rhs) then + return d_64(self._ticks - rhs._ticks) + elseif istype(months_ct, rhs) then + local year, month, day = self:ymd() + year, month = shift_months(year, month, -rhs._m) + return valid_date_cap_day(year, month, day) + self:period() + elseif istype(years_ct, rhs) then + local year, month, day = self:ymd() + year = year - rhs._y + return valid_date_cap_day(year, month, day) + self:period() + elseif istype(d_ct, rhs) then + return p_64(self._ticks - rhs._ticks) + else + error("unexpected type") + end + end, + __tostring = function(self) + local year, month, day = self:ymd() + local h, m, s, ms = self:period():parts() + return format("%i-%02i-%02iT", year, month, day)..posptostr(h, m, s, ms) + end, + ticks = p_mt.ticks, + ymd = function(self) + local julian = tonumber(self._ticks/(86400LL*1e6)) + return julian_to_ymd(julian) + end, + year = function(self) local y, m, d = self:ymd() return y end, + month = function(self) local y, m, d = self:ymd() return m end, + day = function(self) local y, m, d = self:ymd() return d end, + period = function(self) + return p_64(self._ticks%(86400LL*1e6)) + end, + isleapyear = function(self) + local y = self:ymd() + return isleapyear(y) + end, + endofmonth = function(self) + local y, m = self:ymd() + return endofmonth(y, m) + end, + weekday = function(self) + local y, m, d = self:ymd() + return weekday(y, m, d) + end, +} +d_mt.__index = d_mt + +d_ct = ffi.metatype("struct { int64_t _ticks; }", d_mt) + +local function todate(x) + if type(x) == "string" then + local f1, l1, year, month, day, h, m, s, ms = + x:find("(%d+)-(%d+)-(%d+)T(%d+):(%d+):(%d+).(%d+)") + if year == nil or ms == nil or l1 ~= #x then + error("'"..x.."' is not a string representation of a date") + end + local ton = tonumber + return d_ct(ton(year), ton(month), ton(day)) + + p_ct(ton(h), ton(m), ton(s), ton(ms)) + elseif istype(int64_ct, x) then + return d_64(x) + else + error("unexpected type") + end +end + +-- System-dependent functions -------------------------------------------------- +local nowlocal, nowutc, sleep + +if jit.os == "Windows" then -- On Windows sizeof(long) == 4 on both x86 and x64. + ffi.cdef[[ + typedef unsigned long DWORD; + typedef unsigned short WORD; + typedef unsigned __int64 ULONGLONG; + + typedef struct _SYSTEMTIME { + WORD wYear; + WORD wMonth; + WORD wDayOfWeek; + WORD wDay; + WORD wHour; + WORD wMinute; + WORD wSecond; + WORD wMilliseconds; + } SYSTEMTIME, *PSYSTEMTIME; + + typedef union _ULARGE_INTEGER { + struct { + DWORD LowPart; + DWORD HighPart; + }; + struct { + DWORD LowPart; + DWORD HighPart; + } u; + ULONGLONG QuadPart; + } ULARGE_INTEGER, *PULARGE_INTEGER; + + typedef struct _FILETIME { + DWORD dwLowDateTime; + DWORD dwHighDateTime; + } FILETIME, *PFILETIME; + + void GetLocalTime( + PSYSTEMTIME lpSystemTime + ); + //void GetSystemTime( + // PSYSTEMTIME lpSystemTime + //); + void GetSystemTimeAsFileTime( + PFILETIME lpSystemTimeAsFileTime + ); + void GetSystemTimeAsPreciseFileTime( + PFILETIME lpSystemTimeAsFileTime + ); + void Sleep( + DWORD dwMilliseconds + ); + ]] + + local st = ffi.new("SYSTEMTIME") + local ft = ffi.new("FILETIME") + local ul = ffi.new("ULARGE_INTEGER") + + nowlocal = function() + C.GetLocalTime(st) + return d_ct(st.wYear, st.wMonth, st.wDay) + p_ct(st.wHour, st.wMinute, + st.wSecond, st.wMilliseconds*1000) + end + + local epoch_offset = d_ct(1601, 1, 1):ticks() + local function if_available(lib, fname) + local ok, f = pcall(function() return lib[fname] end) + return ok and f + end + local get_system_time = if_available(C, 'GetSystemTimeAsPreciseFileTime') + or C.GetSystemTimeAsFileTime + + nowutc = function() + -- Resolution: 1 microsecond. + -- Accuracy : 1 millisecond up to Windows 7, higher otherwise. + get_system_time(ft) + ul.LowPart = ft.dwLowDateTime + ul.HighPart = ft.dwHighDateTime + return new(d_ct, epoch_offset + ul.QuadPart/10) + end + + sleep = function(p) + if p < p_ct() then + error("cannot sleep a negative amount of time") + end + C.Sleep(p:ticks()/1000) + end + +else -- Linux and OSX. + ffi.cdef[[ + typedef long time_t; + typedef int useconds_t; + + typedef struct timeval { + long tv_sec; + int tv_usec; + } timeval; + typedef struct tm { + int tm_sec; + int tm_min; + int tm_hour; + int tm_mday; + int tm_mon; + int tm_year; + int tm_wday; + int tm_yday; + int tm_isdst; + long tm_gmtoff; + char *tm_zone; + } tm; + + int gettimeofday( + struct timeval * restrict, + void * restrict + ); + struct tm *localtime( + const time_t * + ); + int usleep( + useconds_t useconds + ); + ]] + + -- C.host_get_clock_service it's slower and we don't need higher resolution. + -- C.mach_absolute_time does not report real clock. + local epoch_offset = d_ct(1970, 1, 1):ticks() + local tv = ffi.new("timeval[1]") + local tt = ffi.new("time_t[1]") + + nowlocal = function() + C.gettimeofday(tv, nil) + tt[0] = tv[0].tv_sec + local tm = C.localtime(tt) + return d_ct(1900 + tm.tm_year, 1 + tm.tm_mon, tm.tm_mday) + + p_ct(tm.tm_hour, tm.tm_min, tm.tm_sec, tv[0].tv_usec) + end + + nowutc = function() + -- Resolution: 1 microsecond. + -- Accuracy : 1 microsecond. + C.gettimeofday(tv, nil) + return new(d_ct, epoch_offset + tv[0].tv_sec*1000000LL + tv[0].tv_usec) + end + + sleep = function(p) + if p < p_ct() then + error("cannot sleep a negative amount of time") + end + C.usleep(p:ticks()) + end +end + +return { + period = p_ct, + toperiod = toperiod, + + weeks = weeks, + days = days, + hours = hours, + minutes = minutes, + seconds = seconds, + milliseconds = milliseconds, + microseconds = microseconds, + + date = d_ct, + todate = todate, + + isleapyear = isleapyear, + endofmonth = endofmonth, + weekday = weekday, + + months = months_ct, + years = years_ct, + + sleep = sleep, + nowlocal = nowlocal, + nowutc = nowutc, +}