Added better priority management

This commit is contained in:
Ryan Ward 2018-09-10 22:21:57 -04:00
parent 21aed09d6d
commit 8db42e19f9
7 changed files with 1134 additions and 75 deletions

View File

@ -1,10 +1,10 @@
C: 2731526 ~I*7 C: 3322269 ~I*7
H: 2341308 ~I*6 H: 2847660 ~I*6
A: 1951090 ~I*5 A: 2373050 ~I*5
N: 1560872 ~I*4 N: 1898440 ~I*4
B: 1170655 ~I*3 B: 1423830 ~I*3
L: 780438 ~I*2 L: 949220 ~I*2
I: 390219 ~I I: 474610 ~I
~n=I*PRank ~n=I*PRank
P2 P2
@ -17,3 +17,13 @@ B: 26175
L: 6543 L: 6543
I: 1635 I: 1635
~n=n*4 ~n=n*4
P3
----------------
C: 2120906
H: 2120906
A: 2120906
N: 2120906
B: 2120906
L: 2120906
I: 2120506

View File

@ -1,5 +1,76 @@
#Changes #Changes
[TOC] [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 Update 12.1.0
------------- -------------
Fixed: 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) 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: #Added:
- `nGLOBAL = require("multi.integration.networkManager").init()` - `nGLOBAL = require("multi.integration.networkManager").init()`

View File

@ -23,8 +23,9 @@ SOFTWARE.
]] ]]
local bin = pcall(require,"bin") local bin = pcall(require,"bin")
local multi = {} local multi = {}
multi.Version = "12.0.0" local clock = os.clock
multi._VERSION = "12.0.0" multi.Version = "12.2.0"
multi._VERSION = "12.2.0"
multi.stage = "stable" multi.stage = "stable"
multi.__index = multi multi.__index = multi
multi.Mainloop = {} multi.Mainloop = {}
@ -43,7 +44,11 @@ multi.jobUS = 2
multi.clock = os.clock multi.clock = os.clock
multi.time = os.time multi.time = os.time
multi.LinkedPath = multi multi.LinkedPath = multi
multi.isRunning = false multi.lastTime = clock()
local mainloopActive = false
local isRunning = false
local next
local ncount = 0
multi.defaultSettings = { multi.defaultSettings = {
priority = 0, priority = 0,
protect = false, protect = false,
@ -144,6 +149,7 @@ function multi:setPriority(s)
elseif s:lower()=='idle' or s:lower()=='i' then elseif s:lower()=='idle' or s:lower()=='i' then
self.Priority=self.Priority_Idle self.Priority=self.Priority_Idle
end end
self.solid = true
end end
end end
-- System -- System
@ -230,25 +236,23 @@ function multi:getError()
end end
end end
function multi:benchMark(sec,p,pt) function multi:benchMark(sec,p,pt)
local c = 0
local temp=self:newLoop(function(self,t) local temp=self:newLoop(function(self,t)
if self.clock()-self.init>self.sec then if t>sec then
if pt then if pt then
print(pt.." "..self.c.." Steps in "..sec.." second(s)!") print(pt.." "..c.." Steps in "..sec.." second(s)!")
end end
self.tt(self.sec,self.c) self.tt(sec,c)
self:Destroy() self:Destroy()
else else
self.c=self.c+1 c=c+1
end end
end) end)
temp.Priority=p or 1 temp:setPriority(p or 1)
function temp:OnBench(func) function temp:OnBench(func)
self.tt=func self.tt=func
end end
self.tt=function() end self.tt=function() end
temp.sec=sec
temp.init=self.clock()
temp.c=0
return temp return temp
end end
function multi.startFPSMonitior() function multi.startFPSMonitior()
@ -268,7 +272,9 @@ function multi.timer(func,...)
local timer=multi:newTimer() local timer=multi:newTimer()
timer:Start() timer:Start()
args={func(...)} args={func(...)}
return timer:Get(),unpack(args) local t = timer:Get()
timer = nil
return t,unpack(args)
end end
function multi:IsAnActor() function multi:IsAnActor()
return ({watcher=true,tstep=true,step=true,updater=true,loop=true,alarm=true,event=true})[self.Type] 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,...) self.funcTMR[i](self,...)
end end
self:Pause() self:Pause()
return self
end end
function multi:OnTimedOut(func) function multi:OnTimedOut(func)
self.funcTM[#self.funcTM+1]=func self.funcTM[#self.funcTM+1]=func
return self
end end
function multi:OnTimerResolved(func) function multi:OnTimerResolved(func)
self.funcTMR[#self.funcTMR+1]=func self.funcTMR[#self.funcTMR+1]=func
return self
end end
-- Timer stuff done -- Timer stuff done
function multi:Pause() function multi:Pause()
@ -386,6 +395,7 @@ function multi:Pause()
table.remove(self.Parent.Mainloop,self.Id) table.remove(self.Parent.Mainloop,self.Id)
end end
end end
return self
end end
function multi:Resume() function multi:Resume()
if self.Type=='process' or self.Type=='mainprocess' then if self.Type=='process' or self.Type=='mainprocess' then
@ -401,6 +411,7 @@ function multi:Resume()
self.Active=true self.Active=true
end end
end end
return self
end end
function multi:Destroy() function multi:Destroy()
if self.Type=='process' or self.Type=='mainprocess' then if self.Type=='process' or self.Type=='mainprocess' then
@ -419,9 +430,11 @@ function multi:Destroy()
end end
self.Active=false self.Active=false
end end
return self
end end
function multi:Reset(n) function multi:Reset(n)
self:Resume() self:Resume()
return self
end end
function multi:isDone() function multi:isDone()
return self.Active~=true return self.Active~=true
@ -482,20 +495,24 @@ function multi:newProcess(file)
if self.l then if self.l then
self.l:Resume() self.l:Resume()
end end
return self
end end
function c:Resume() function c:Resume()
if self.l then if self.l then
self.l:Resume() self.l:Resume()
end end
return self
end end
function c:Pause() function c:Pause()
if self.l then if self.l then
self.l:Pause() self.l:Pause()
end end
return self
end end
function c:Remove() function c:Remove()
self:Destroy() self:Destroy()
self.l:Destroy() self.l:Destroy()
return self
end end
if file then if file then
self.Cself=c self.Cself=c
@ -512,6 +529,7 @@ function multi:newQueuer(file)
c.funcE={} c.funcE={}
function c:OnQueueCompleted(func) function c:OnQueueCompleted(func)
table.insert(self.funcE,func) table.insert(self.funcE,func)
return self
end end
if file then if file then
self.Cself=c self.Cself=c
@ -534,40 +552,45 @@ function multi:newQueuer(file)
function c:Start() function c:Start()
self.Mainloop[#self.Mainloop]:Resume() self.Mainloop[#self.Mainloop]:Resume()
self.l:Resume() self.l:Resume()
return self
end end
return c return c
end end
function multi:newTimer() function multi:newTimer()
local c={} local c={}
c.Type='timer' c.Type='timer'
c.time=0 local time=0
c.count=0 local count=0
c.paused=false local paused=false
function c:Start() function c:Start()
self.time=os.clock() time=os.clock()
return self
end end
function c:Get() function c:Get()
if self:isPaused() then return self.time end if self:isPaused() then return time end
return (os.clock()-self.time)+self.count return (clock()-time)+count
end end
function c:isPaused() function c:isPaused()
return self.paused return paused
end end
c.Reset=c.Start c.Reset=c.Start
function c:Pause() function c:Pause()
self.time=self:Get() time=self:Get()
self.paused=true paused=true
return self
end end
function c:Resume() function c:Resume()
self.paused=false paused=false
self.time=os.clock()-self.time time=os.clock()-time
return self
end end
function c:tofile(path) function c:tofile(path)
local m=bin.new() local m=bin.new()
self.count=self.count+self:Get() count=count+self:Get()
m:addBlock(self.Type) m:addBlock(self.Type)
m:addBlock(self.count) m:addBlock(count)
m:tofile(path) m:tofile(path)
return self
end end
self:create(c) self:create(c)
return c return c
@ -597,12 +620,14 @@ function multi:newConnection(protect)
self.Parent:uManager(multi.defaultSettings) self.Parent:uManager(multi.defaultSettings)
until self.waiting==false until self.waiting==false
id:Destroy() id:Destroy()
return self
end end
c.HoldUT=c.holdUT c.HoldUT=c.holdUT
function c:fConnect(func) function c:fConnect(func)
local temp=self:connect(func) local temp=self:connect(func)
table.insert(self.fconnections,temp) table.insert(self.fconnections,temp)
self.FC=self.FC+1 self.FC=self.FC+1
return self
end end
c.FConnect=c.fConnect c.FConnect=c.fConnect
function c:getConnection(name,ingore) function c:getConnection(name,ingore)
@ -633,9 +658,11 @@ function multi:newConnection(protect)
end end
function c:Bind(t) function c:Bind(t)
self.func=t self.func=t
return self
end end
function c:Remove() function c:Remove()
self.func={} self.func={}
return self
end end
function c:connect(func,name,num) function c:connect(func,name,num)
self.ID=self.ID+1 self.ID=self.ID+1
@ -687,6 +714,7 @@ function multi:newConnection(protect)
m:addBlock(self.Type) m:addBlock(self.Type)
m:addBlock(self.func) m:addBlock(self.func)
m:tofile(path) m:tofile(path)
return self
end end
return c return c
end end
@ -723,6 +751,14 @@ function multi:newJob(func,name)
end) end)
end 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() function multi:newRange()
local selflink=self local selflink=self
local temp={ local temp={
@ -819,26 +855,54 @@ function multi:mainloop(settings)
multi.defaultSettings = settings or multi.defaultSettings multi.defaultSettings = settings or multi.defaultSettings
self.uManager=self.uManagerRef self.uManager=self.uManagerRef
multi.OnPreLoad:Fire() 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 protect = false
local priority = false local priority = false
local stopOnError = true local stopOnError = true
local delay = 3
if settings then if settings then
priority = settings.priority
if settings.auto_priority then
priority = -1
end
if settings.preLoop then if settings.preLoop then
settings.preLoop(self) settings.preLoop(self)
end end
if settings.stopOnError then if settings.stopOnError then
stopOnError = settings.stopOnError stopOnError = settings.stopOnError
end end
protect = settings.protect if settings.auto_stretch then
priority = settings.priority p_i = p_i * settings.auto_stretch
end end
multi.isRunning=true if settings.auto_delay then
rawset(self,'Start',self.clock()) delay = settings.auto_delay
while self.Active do end
if priority==1 then if settings.auto_lowerbound then
P_LB = settings.auto_lowerbound
end
protect = settings.protect
end
local t,tt = clock(),0
isRunning=true
local lastTime = clock()
rawset(self,'Start',clock())
mainloopActive = true
local Loop=self.Mainloop local Loop=self.Mainloop
local PS=self 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 _D=#Loop,1,-1 do
for P=1,7 do for P=1,7 do
if Loop[_D] then if Loop[_D] then
@ -863,11 +927,9 @@ function multi:mainloop(settings)
end end
end end
elseif priority == 2 then elseif priority == 2 then
local Loop=self.Mainloop
local PS=self
for _D=#Loop,1,-1 do for _D=#Loop,1,-1 do
if Loop[_D] then if Loop[_D] then
if (PS.PStep)%Loop[_D].Priority==0 then if (PStep)%Loop[_D].Priority==0 then
if Loop[_D].Active then if Loop[_D].Active then
self.CID=_D self.CID=_D
if not protect then if not protect then
@ -886,12 +948,96 @@ function multi:mainloop(settings)
end end
end end
end end
PS.PStep=PS.PStep+1 PStep=PStep+1
if PS.PStep>self.Priority_Idle then if PStep==p_i then
PS.PStep=1 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 end
else else
local Loop=self.Mainloop
for _D=#Loop,1,-1 do for _D=#Loop,1,-1 do
if Loop[_D] then if Loop[_D] then
if Loop[_D].Active then if Loop[_D].Active then
@ -919,19 +1065,37 @@ function multi:mainloop(settings)
end end
function multi:uManager(settings) function multi:uManager(settings)
multi.OnPreLoad:Fire() multi.OnPreLoad:Fire()
multi.defaultSettings = settings or multi.defaultSettings
self.t,self.tt = clock(),0
if settings then if settings then
priority = settings.priority
if settings.auto_priority then
priority = -1
end
if settings.preLoop then if settings.preLoop then
settings.preLoop(self) settings.preLoop(self)
end 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 end
multi.defaultSettings = settings or multi.defaultSettings
self.uManager=self.uManagerRef self.uManager=self.uManagerRef
end end
function multi:uManagerRef(settings) function multi:uManagerRef(settings)
if self.Active then if self.Active then
if multi.defaultSettings.priority==1 then if ncount ~= 0 then
for i = 1, ncount do
next[i]()
end
ncount = 0
end
local Loop=self.Mainloop local Loop=self.Mainloop
local PS=self local PS=self
if multi.defaultSettings.priority==1 then
for _D=#Loop,1,-1 do for _D=#Loop,1,-1 do
for P=1,7 do for P=1,7 do
if Loop[_D] then if Loop[_D] then
@ -945,6 +1109,9 @@ function multi:uManagerRef(settings)
if err then if err then
Loop[_D].error=err Loop[_D].error=err
self.OnError:Fire(Loop[_D],err) self.OnError:Fire(Loop[_D],err)
if multi.defaultSettings.stopOnError then
Loop[_D]:Destroy()
end
end end
end end
end end
@ -953,8 +1120,6 @@ function multi:uManagerRef(settings)
end end
end end
elseif multi.defaultSettings.priority==2 then elseif multi.defaultSettings.priority==2 then
local Loop=self.Mainloop
local PS=self
for _D=#Loop,1,-1 do for _D=#Loop,1,-1 do
if Loop[_D] then if Loop[_D] then
if (PS.PStep)%Loop[_D].Priority==0 then if (PS.PStep)%Loop[_D].Priority==0 then
@ -967,6 +1132,9 @@ function multi:uManagerRef(settings)
if err then if err then
Loop[_D].error=err Loop[_D].error=err
self.OnError:Fire(Loop[_D],err) self.OnError:Fire(Loop[_D],err)
if multi.defaultSettings.stopOnError then
Loop[_D]:Destroy()
end
end end
end end
end end
@ -975,10 +1143,94 @@ function multi:uManagerRef(settings)
end end
PS.PStep=PS.PStep+1 PS.PStep=PS.PStep+1
if PS.PStep>self.Priority_Idle then 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 end
else else
local Loop=self.Mainloop
for _D=#Loop,1,-1 do for _D=#Loop,1,-1 do
if Loop[_D] then if Loop[_D] then
if Loop[_D].Active then if Loop[_D].Active then
@ -1036,9 +1288,11 @@ function multi:newEvent(task)
end end
function c:SetTask(func) function c:SetTask(func)
self.Task=func self.Task=func
return self
end end
function c:OnEvent(func) function c:OnEvent(func)
table.insert(self.func,func) table.insert(self.func,func)
return self
end end
self:create(c) self:create(c)
return c return c
@ -1059,6 +1313,7 @@ function multi:newUpdater(skip)
end end
function c:SetSkip(n) function c:SetSkip(n)
self.skip=n self.skip=n
return self
end end
c.OnUpdate=self.OnMainConnect c.OnUpdate=self.OnMainConnect
self:create(c) self:create(c)
@ -1082,18 +1337,22 @@ function multi:newAlarm(set)
function c:Resume() function c:Resume()
self.Parent.Resume(self) self.Parent.Resume(self)
self.timer:Resume() self.timer:Resume()
return self
end end
function c:Reset(n) function c:Reset(n)
if n then self.set=n end if n then self.set=n end
self:Resume() self:Resume()
self.timer:Reset() self.timer:Reset()
return self
end end
function c:OnRing(func) function c:OnRing(func)
table.insert(self.func,func) table.insert(self.func,func)
return self
end end
function c:Pause() function c:Pause()
self.timer:Pause() self.timer:Pause()
self.Parent.Pause(self) self.Parent.Pause(self)
return self
end end
self:create(c) self:create(c)
return c return c
@ -1101,17 +1360,19 @@ end
function multi:newLoop(func) function multi:newLoop(func)
local c=self:newBase() local c=self:newBase()
c.Type='loop' c.Type='loop'
c.Start=self.clock() local start=self.clock()
local funcs = {}
if func then if func then
c.func={func} funcs={func}
end end
function c:Act() function c:Act()
for i=1,#self.func do for i=1,#funcs do
self.func[i](self,self.Parent.clock()-self.Start) funcs[i](self,clock()-start)
end end
end end
function c:OnLoop(func) function c:OnLoop(func)
table.insert(self.func,func) table.insert(funcs,func)
return self
end end
self:create(c) self:create(c)
return c return c
@ -1126,9 +1387,11 @@ function multi:newFunction(func)
c.Parent=self c.Parent=self
function c:Pause() function c:Pause()
self.Active=false self.Active=false
return self
end end
function c:Resume() function c:Resume()
self.Active=true self.Active=true
return self
end end
setmetatable(c,mt) setmetatable(c,mt)
self:create(c) self:create(c)
@ -1180,15 +1443,19 @@ function multi:newStep(start,reset,count,skip)
c.Reset=c.Resume c.Reset=c.Resume
function c:OnStart(func) function c:OnStart(func)
table.insert(self.funcS,func) table.insert(self.funcS,func)
return self
end end
function c:OnStep(func) function c:OnStep(func)
table.insert(self.func,1,func) table.insert(self.func,1,func)
return self
end end
function c:OnEnd(func) function c:OnEnd(func)
table.insert(self.funcE,func) table.insert(self.funcE,func)
return self
end end
function c:Break() function c:Break()
self.Active=nil self.Active=nil
return self
end end
function c:Update(start,reset,count,skip) function c:Update(start,reset,count,skip)
self.start=start or self.start self.start=start or self.start
@ -1196,6 +1463,7 @@ function multi:newStep(start,reset,count,skip)
self.skip=skip or self.skip self.skip=skip or self.skip
self.count=count or self.count self.count=count or self.count
self:Resume() self:Resume()
return self
end end
self:create(c) self:create(c)
return c return c
@ -1221,13 +1489,16 @@ function multi:newTLoop(func,set)
function c:Resume() function c:Resume()
self.Parent.Resume(self) self.Parent.Resume(self)
self.timer:Resume() self.timer:Resume()
return self
end end
function c:Pause() function c:Pause()
self.timer:Pause() self.timer:Pause()
self.Parent.Pause(self) self.Parent.Pause(self)
return self
end end
function c:OnLoop(func) function c:OnLoop(func)
table.insert(self.func,func) table.insert(self.func,func)
return self
end end
self:create(c) self:create(c)
return c return c
@ -1238,6 +1509,7 @@ function multi:newTrigger(func)
c.trigfunc=func or function() end c.trigfunc=func or function() end
function c:Fire(...) function c:Fire(...)
self:trigfunc(...) self:trigfunc(...)
return self
end end
self:create(c) self:create(c)
return c return c
@ -1265,6 +1537,7 @@ function multi:newTStep(start,reset,count,set)
self.count=count or self.count or 1 self.count=count or self.count or 1
self.timer=self.clock() self.timer=self.clock()
self:Resume() self:Resume()
return self
end end
function c:Act() function c:Act()
if self.clock()-self.timer>=self.set then if self.clock()-self.timer>=self.set then
@ -1289,20 +1562,25 @@ function multi:newTStep(start,reset,count,set)
end end
function c:OnStart(func) function c:OnStart(func)
table.insert(self.funcS,func) table.insert(self.funcS,func)
return self
end end
function c:OnStep(func) function c:OnStep(func)
table.insert(self.func,func) table.insert(self.func,func)
return self
end end
function c:OnEnd(func) function c:OnEnd(func)
table.insert(self.funcE,func) table.insert(self.funcE,func)
return self
end end
function c:Break() function c:Break()
self.Active=nil self.Active=nil
return self
end end
function c:Reset(n) function c:Reset(n)
if n then self.set=n end if n then self.set=n end
self.timer=self.clock() self.timer=self.clock()
self:Resume() self:Resume()
return self
end end
self:create(c) self:create(c)
return c return c
@ -1391,24 +1669,31 @@ function multi:newTimeStamper()
else else
self.time[#self.time+1]={hour,minute,true} self.time[#self.time+1]={hour,minute,true}
end end
return self
end end
function c:OnHour(hour,func) function c:OnHour(hour,func)
self.hour[#self.hour+1]={string.format("%02d",hour),func,true} self.hour[#self.hour+1]={string.format("%02d",hour),func,true}
return self
end end
function c:OnMinute(minute,func) function c:OnMinute(minute,func)
self.minute[#self.minute+1]={string.format("%02d",minute),func,true} self.minute[#self.minute+1]={string.format("%02d",minute),func,true}
return self
end end
function c:OnSecond(second,func) function c:OnSecond(second,func)
self.second[#self.second+1]={string.format("%02d",second),func,true} self.second[#self.second+1]={string.format("%02d",second),func,true}
return self
end end
function c:OnDay(day,func) function c:OnDay(day,func)
self.day[#self.day+1]={day,func,true} self.day[#self.day+1]={day,func,true}
return self
end end
function c:OnMonth(month,func) function c:OnMonth(month,func)
self.month[#self.month+1]={string.format("%02d",month),func,true} self.month[#self.month+1]={string.format("%02d",month),func,true}
return self
end end
function c:OnYear(year,func) function c:OnYear(year,func)
self.year[#self.year+1]={string.format("%02d",year),func,true} self.year[#self.year+1]={string.format("%02d",year),func,true}
return self
end end
self:create(c) self:create(c)
return c return c
@ -1426,6 +1711,7 @@ function multi:newWatcher(namespace,name)
c.cv=ns[n] c.cv=ns[n]
function c:OnValueChanged(func) function c:OnValueChanged(func)
table.insert(self.func,func) table.insert(self.func,func)
return self
end end
function c:Act() function c:Act()
if self.cv~=self.ns[self.n] then if self.cv~=self.ns[self.n] then
@ -1631,18 +1917,22 @@ function multi:newThreadedAlarm(name,set)
function c:Resume() function c:Resume()
self.rest=false self.rest=false
self.timer:Resume() self.timer:Resume()
return self
end end
function c:Reset(n) function c:Reset(n)
if n then self.set=n end if n then self.set=n end
self.rest=false self.rest=false
self.timer:Reset(n) self.timer:Reset(n)
return self
end end
function c:OnRing(func) function c:OnRing(func)
table.insert(self.func,func) table.insert(self.func,func)
return self
end end
function c:Pause() function c:Pause()
self.timer:Pause() self.timer:Pause()
self.rest=true self.rest=true
return self
end end
c.rest=false c.rest=false
c.updaterate=multi.Priority_Low -- skips c.updaterate=multi.Priority_Low -- skips
@ -1672,9 +1962,11 @@ function multi:newThreadedUpdater(name,skip)
c.skip=skip or 1 c.skip=skip or 1
function c:Resume() function c:Resume()
self.rest=false self.rest=false
return self
end end
function c:Pause() function c:Pause()
self.rest=true self.rest=true
return self
end end
c.OnUpdate=self.OnMainConnect c.OnUpdate=self.OnMainConnect
c.rest=false c.rest=false
@ -1718,29 +2010,37 @@ function multi:newThreadedTStep(name,start,reset,count,set)
self.count=count or self.count or 1 self.count=count or self.count or 1
self.timer=os.clock() self.timer=os.clock()
self:Resume() self:Resume()
return self
end end
function c:Resume() function c:Resume()
self.rest=false self.rest=false
return self
end end
function c:Pause() function c:Pause()
self.rest=true self.rest=true
return self
end end
function c:OnStart(func) function c:OnStart(func)
table.insert(self.funcS,func) table.insert(self.funcS,func)
return self
end end
function c:OnStep(func) function c:OnStep(func)
table.insert(self.func,func) table.insert(self.func,func)
return self
end end
function c:OnEnd(func) function c:OnEnd(func)
table.insert(self.funcE,func) table.insert(self.funcE,func)
return self
end end
function c:Break() function c:Break()
self.Active=nil self.Active=nil
return self
end end
function c:Reset(n) function c:Reset(n)
if n then self.set=n end if n then self.set=n end
self.timer=os.clock() self.timer=os.clock()
self:Resume() self:Resume()
return self
end end
c.updaterate=0--multi.Priority_Low -- skips c.updaterate=0--multi.Priority_Low -- skips
c.restRate=0 c.restRate=0
@ -1784,12 +2084,15 @@ function multi:newThreadedTLoop(name,func,n)
end end
function c:Resume() function c:Resume()
self.rest=false self.rest=false
return self
end end
function c:Pause() function c:Pause()
self.rest=true self.rest=true
return self
end end
function c:OnLoop(func) function c:OnLoop(func)
table.insert(self.func,func) table.insert(self.func,func)
return self
end end
c.rest=false c.rest=false
c.updaterate=0 c.updaterate=0
@ -1828,22 +2131,28 @@ function multi:newThreadedStep(name,start,reset,count,skip)
end end
function c:Resume() function c:Resume()
self.rest=false self.rest=false
return self
end end
function c:Pause() function c:Pause()
self.rest=true self.rest=true
return self
end end
c.Reset=c.Resume c.Reset=c.Resume
function c:OnStart(func) function c:OnStart(func)
table.insert(self.funcS,func) table.insert(self.funcS,func)
return self
end end
function c:OnStep(func) function c:OnStep(func)
table.insert(self.func,1,func) table.insert(self.func,1,func)
return self
end end
function c:OnEnd(func) function c:OnEnd(func)
table.insert(self.funcE,func) table.insert(self.funcE,func)
return self
end end
function c:Break() function c:Break()
self.rest=true self.rest=true
return self
end end
function c:Update(start,reset,count,skip) function c:Update(start,reset,count,skip)
self.start=start or self.start 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.skip=skip or self.skip
self.count=count or self.count self.count=count or self.count
self:Resume() self:Resume()
return self
end end
c.updaterate=0 c.updaterate=0
c.restRate=.1 c.restRate=.1
@ -1929,21 +2239,26 @@ function multi:newThreadedProcess(name)
end end
function c:Start() function c:Start()
self.rest=false self.rest=false
return self
end end
function c:Resume() function c:Resume()
self.rest=false self.rest=false
return self
end end
function c:Pause() function c:Pause()
self.rest=true self.rest=true
return self
end end
function c:Remove() function c:Remove()
self.ref:kill() self.ref:kill()
return self
end end
function c:kill() function c:kill()
err=coroutine.yield({"_kill_"}) err=coroutine.yield({"_kill_"})
if err then if err then
error("Failed to kill a thread! Exiting...") error("Failed to kill a thread! Exiting...")
end end
return self
end end
function c:sleep(n) function c:sleep(n)
if type(n)=="function" then if type(n)=="function" then
@ -1954,6 +2269,7 @@ function multi:newThreadedProcess(name)
else else
error("Invalid Type for sleep!") error("Invalid Type for sleep!")
end end
return self
end end
c.hold=c.sleep c.hold=c.sleep
multi:newThread(name,function(ref) multi:newThread(name,function(ref)
@ -1977,12 +2293,15 @@ function multi:newThreadedLoop(name,func)
end end
function c:Resume() function c:Resume()
self.rest=false self.rest=false
return self
end end
function c:Pause() function c:Pause()
self.rest=true self.rest=true
return self
end end
function c:OnLoop(func) function c:OnLoop(func)
table.insert(self.func,func) table.insert(self.func,func)
return self
end end
c.rest=false c.rest=false
c.updaterate=0 c.updaterate=0
@ -2008,12 +2327,15 @@ function multi:newThreadedEvent(name,task)
c.Task=task or function() end c.Task=task or function() end
function c:OnEvent(func) function c:OnEvent(func)
table.insert(self.func,func) table.insert(self.func,func)
return self
end end
function c:Resume() function c:Resume()
self.rest=false self.rest=false
return self
end end
function c:Pause() function c:Pause()
self.rest=true self.rest=true
return self
end end
c.rest=false c.rest=false
c.updaterate=0 c.updaterate=0
@ -2039,6 +2361,7 @@ end
-- State Saving Stuff -- State Saving Stuff
function multi:IngoreObject() function multi:IngoreObject()
self.Ingore=true self.Ingore=true
return self
end end
multi.scheduler:IngoreObject() multi.scheduler:IngoreObject()
function multi:ToString() function multi:ToString()

15
multitut.lua Normal file
View File

@ -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()

View File

@ -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"
}
}

View File

@ -1,17 +1,54 @@
package.path="?/init.lua;?.lua;"..package.path 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") multi = require("multi")
local a = 0 --~ multi:newTLoop(function(self)
multi:newThread("test",function() --~ a = 0
print("lets go") --~ end,.001)
b,c = thread.hold(function() --~ multi:benchMark(1,nil,"Steps/s:"):OnBench(function()
return b,"We did it!" --~ os.exit()
end) --~ end)
print(b,c) function multi:ResetPriority()
end) self.solid = false
multi:newTLoop(function() end
a=a+1 local clock = os.clock
if a == 5 then function sleep(n) -- seconds
b = "Hello" 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)
end)
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 end
end,1)
multi:mainloop()

570
time/init.lua Normal file
View File

@ -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,
}