mirror of https://github.com/red-prig/fpPS4.git
456 lines
9.4 KiB
Plaintext
456 lines
9.4 KiB
Plaintext
unit kern_timeout;
|
|
|
|
{$mode ObjFPC}{$H+}
|
|
{$CALLING SysV_ABI_CDecl}
|
|
|
|
interface
|
|
|
|
uses
|
|
mqueue,
|
|
kern_synch,
|
|
sys_sleepqueue,
|
|
kern_thr,
|
|
kern_mtx,
|
|
kern_rwlock,
|
|
kern_callout;
|
|
|
|
type
|
|
t_callout_func=kern_callout.t_callout_func;
|
|
|
|
p_callout=kern_callout.p_callout;
|
|
t_callout=kern_callout.t_callout;
|
|
|
|
const
|
|
CALLOUT_LOCAL_ALLOC =kern_callout.CALLOUT_LOCAL_ALLOC ;
|
|
CALLOUT_ACTIVE =kern_callout.CALLOUT_ACTIVE ;
|
|
CALLOUT_PENDING =kern_callout.CALLOUT_PENDING ;
|
|
CALLOUT_MPSAFE =kern_callout.CALLOUT_MPSAFE ;
|
|
CALLOUT_RETURNUNLOCKED=kern_callout.CALLOUT_RETURNUNLOCKED;
|
|
CALLOUT_SHAREDLOCK =kern_callout.CALLOUT_SHAREDLOCK ;
|
|
CALLOUT_RWLOCK =kern_callout.CALLOUT_RWLOCK ;
|
|
|
|
procedure kern_timeout_init();
|
|
|
|
procedure CC_LOCK(cc:p_callout_cpu);
|
|
procedure CC_UNLOCK(cc:p_callout_cpu);
|
|
procedure CC_LOCK_ASSERT(cc:p_callout_cpu);
|
|
function callout_lock():p_callout_cpu;
|
|
procedure callout_cc_del(c:p_callout;cc:p_callout_cpu);
|
|
|
|
procedure callout_init (c:p_callout;mpsafe:Integer);
|
|
procedure _callout_init_lock(c:p_callout;lock:Pointer;flags:Integer);
|
|
|
|
procedure softclock_call_cc(c:p_callout;cc:p_callout_cpu);
|
|
|
|
function _callout_stop_safe(c:p_callout;safe:Integer):Integer;
|
|
function callout_reset_on(c:p_callout;to_ticks:Int64;ftn:t_callout_func;arg:Pointer):Integer;
|
|
|
|
function callout_drain(c:p_callout):Integer;
|
|
function callout_stop(c:p_callout):Integer;
|
|
procedure callout_init_mtx(c:p_callout;var mtx:mtx;flags:Integer);
|
|
procedure callout_init_rw(c:p_callout;var rw:Pointer;flags:Integer);
|
|
function callout_reset(c:p_callout;on_tick:Int64;fn,arg:Pointer):Integer;
|
|
function callout_reset_curcpu(c:p_callout;on_tick:Int64;fn,arg:Pointer):Integer;
|
|
|
|
implementation
|
|
|
|
uses
|
|
md_timeout;
|
|
|
|
var
|
|
timeout_cpu:t_callout_cpu;
|
|
|
|
rw_giant:Pointer=nil;
|
|
|
|
procedure callout_cpu_init(cc:p_callout_cpu);
|
|
begin
|
|
mtx_init(cc^.cc_lock,'callout');
|
|
TAILQ_INIT(@cc^.cc_calllist);
|
|
TAILQ_INIT(@cc^.cc_callfree);
|
|
end;
|
|
|
|
procedure kern_timeout_init();
|
|
begin
|
|
callout_cpu_init(@timeout_cpu);
|
|
md_start_softclock();
|
|
end;
|
|
|
|
procedure callout_init(c:p_callout;mpsafe:Integer);
|
|
begin
|
|
c^:=Default(t_callout);
|
|
if (mpsafe<>0) then
|
|
begin
|
|
c^.c_lock :=nil; //no locking
|
|
c^.c_flags:=CALLOUT_RETURNUNLOCKED;
|
|
end else
|
|
begin
|
|
c^.c_lock :=@rw_giant;
|
|
c^.c_flags:=CALLOUT_RWLOCK;
|
|
end;
|
|
end;
|
|
|
|
procedure _callout_init_lock(c:p_callout;lock:Pointer;flags:Integer);
|
|
begin
|
|
c^:=Default(t_callout);
|
|
c^.c_lock:=lock;
|
|
Assert((flags and (not (CALLOUT_RETURNUNLOCKED or CALLOUT_SHAREDLOCK)))=0,'callout_init_lock: bad flags');
|
|
Assert((lock<>nil) or ((flags and CALLOUT_RETURNUNLOCKED)=0),'callout_init_lock: CALLOUT_RETURNUNLOCKED with no lock');
|
|
Assert(lock<>nil,'invalid lock');
|
|
c^.c_flags:=flags and (CALLOUT_RETURNUNLOCKED or CALLOUT_SHAREDLOCK);
|
|
end;
|
|
|
|
procedure CC_LOCK(cc:p_callout_cpu); inline;
|
|
begin
|
|
mtx_lock(cc^.cc_lock);
|
|
end;
|
|
|
|
procedure CC_UNLOCK(cc:p_callout_cpu); inline;
|
|
begin
|
|
mtx_unlock(cc^.cc_lock);
|
|
end;
|
|
|
|
procedure CC_LOCK_ASSERT(cc:p_callout_cpu); inline;
|
|
begin
|
|
mtx_assert(cc^.cc_lock);
|
|
end;
|
|
|
|
function callout_lock():p_callout_cpu; inline;
|
|
begin
|
|
Result:=@timeout_cpu;
|
|
CC_LOCK(Result);
|
|
end;
|
|
|
|
procedure callout_cc_add(c:p_callout;cc:p_callout_cpu;to_ticks:Int64;func:t_callout_func;arg:Pointer);
|
|
begin
|
|
CC_LOCK_ASSERT(cc);
|
|
|
|
if (to_ticks <= 0) then
|
|
begin
|
|
to_ticks:=1;
|
|
end;
|
|
|
|
c^.c_arg:=arg;
|
|
c^.c_flags:=c^.c_flags or (CALLOUT_ACTIVE or CALLOUT_PENDING);
|
|
c^.c_func:=func;
|
|
c^.c_time:=to_ticks;
|
|
|
|
TAILQ_INSERT_TAIL(@cc^.cc_calllist,c,@c^.c_links);
|
|
|
|
md_callout_new_inserted(c,cc);
|
|
end;
|
|
|
|
procedure callout_cc_del(c:p_callout;cc:p_callout_cpu);
|
|
begin
|
|
md_callout_done(c);
|
|
if ((c^.c_flags and CALLOUT_LOCAL_ALLOC)=0) then Exit;
|
|
c^.c_func:=nil;
|
|
TAILQ_INSERT_TAIL(@cc^.cc_callfree,c,@c^.c_links);
|
|
end;
|
|
|
|
procedure lc_lock(c:p_callout);
|
|
var
|
|
c_lock :Pointer;
|
|
c_flags:QWORD;
|
|
begin
|
|
c_lock :=c^.c_lock;
|
|
c_flags:=c^.c_flags;
|
|
|
|
if (c_lock=nil) then Exit;
|
|
|
|
if ((c_flags and CALLOUT_RWLOCK)<>0) then
|
|
begin
|
|
if ((c_flags and CALLOUT_SHAREDLOCK)<>0) then
|
|
begin
|
|
rw_rlock(PPointer(c_lock)^);
|
|
end else
|
|
begin
|
|
rw_wlock(PPointer(c_lock)^);
|
|
end;
|
|
end else
|
|
begin
|
|
mtx_lock(p_mtx(c_lock)^);
|
|
end;
|
|
end;
|
|
|
|
procedure lc_unlock(c:p_callout);
|
|
var
|
|
c_lock :Pointer;
|
|
c_flags:QWORD;
|
|
begin
|
|
c_lock :=c^.c_lock;
|
|
c_flags:=c^.c_flags;
|
|
|
|
if (c_lock=nil) then Exit;
|
|
|
|
if ((c_flags and CALLOUT_RWLOCK)<>0) then
|
|
begin
|
|
if ((c_flags and CALLOUT_SHAREDLOCK)<>0) then
|
|
begin
|
|
rw_runlock(PPointer(c_lock)^);
|
|
end else
|
|
begin
|
|
rw_wunlock(PPointer(c_lock)^);
|
|
end;
|
|
end else
|
|
begin
|
|
mtx_unlock(p_mtx(c_lock)^);
|
|
end;
|
|
end;
|
|
|
|
procedure softclock_call_cc(c:p_callout;cc:p_callout_cpu);
|
|
label
|
|
skip;
|
|
var
|
|
c_arg :Pointer;
|
|
c_func :t_callout_func;
|
|
c_lock :Pointer;
|
|
c_flags:QWORD;
|
|
begin
|
|
Assert((c^.c_flags and (CALLOUT_PENDING or CALLOUT_ACTIVE))=(CALLOUT_PENDING or CALLOUT_ACTIVE),'softclock_call_cc');
|
|
|
|
c_lock :=c^.c_lock;
|
|
c_func :=c^.c_func;
|
|
c_arg :=c^.c_arg;
|
|
c_flags:=c^.c_flags;
|
|
|
|
if ((c^.c_flags and CALLOUT_LOCAL_ALLOC)<>0) then
|
|
c^.c_flags:=CALLOUT_LOCAL_ALLOC
|
|
else
|
|
c^.c_flags:=c^.c_flags and (not CALLOUT_PENDING);
|
|
|
|
cc^.cc_curr :=c;
|
|
cc^.cc_cancel:=0;
|
|
CC_UNLOCK(cc);
|
|
|
|
if (c_lock<>nil) then
|
|
begin
|
|
lc_lock(c);
|
|
|
|
if (cc^.cc_cancel<>0) then
|
|
begin
|
|
lc_unlock(c);
|
|
goto skip;
|
|
end;
|
|
|
|
cc^.cc_cancel:=1;
|
|
end;
|
|
|
|
THREAD_NO_SLEEPING();
|
|
|
|
c_func(c_arg);
|
|
|
|
THREAD_SLEEPING_OK();
|
|
|
|
if ((c_flags and CALLOUT_RETURNUNLOCKED)=0) then
|
|
begin
|
|
lc_unlock(c);
|
|
end;
|
|
|
|
skip:
|
|
CC_LOCK(cc);
|
|
|
|
Assert(cc^.cc_curr=c,'mishandled cc_curr');
|
|
|
|
cc^.cc_curr:=nil;
|
|
|
|
if (cc^.cc_waiting<>0) then
|
|
begin
|
|
cc^.cc_waiting:=0;
|
|
CC_UNLOCK(cc);
|
|
wakeup(@cc^.cc_waiting);
|
|
CC_LOCK(cc);
|
|
end;
|
|
|
|
Assert(((c_flags and CALLOUT_LOCAL_ALLOC)=0) or (c^.c_flags=CALLOUT_LOCAL_ALLOC),'corrupted callout');
|
|
|
|
if ((c_flags and CALLOUT_LOCAL_ALLOC)<>0) then
|
|
begin
|
|
callout_cc_del(c, cc);
|
|
end;
|
|
end;
|
|
|
|
{
|
|
* New interface; clients allocate their own callout structures.
|
|
*
|
|
* callout_reset() - establish or change a timeout
|
|
* callout_stop() - disestablish a timeout
|
|
* callout_init() - initialize a callout structure so that it can
|
|
* safely be passed to callout_reset() and callout_stop()
|
|
*
|
|
* defines three convenience macros:
|
|
*
|
|
* callout_active() - returns truth if callout has not been stopped,
|
|
* drained, or deactivated since the last time the callout was
|
|
* reset.
|
|
* callout_pending() - returns truth if callout is still waiting for timeout
|
|
* callout_deactivate() - marks the callout as having been serviced
|
|
}
|
|
function callout_reset_on(c:p_callout;to_ticks:Int64;ftn:t_callout_func;arg:Pointer):Integer;
|
|
var
|
|
cc:p_callout_cpu;
|
|
cancelled:Integer;
|
|
begin
|
|
cancelled:=0;
|
|
|
|
cc:=callout_lock();
|
|
|
|
md_callout_reset(c);
|
|
|
|
if (cc^.cc_curr=c) then
|
|
begin
|
|
{
|
|
* We're being asked to reschedule a callout which is
|
|
* currently in progress. If there is a lock then we
|
|
* can cancel the callout if it has not really started.
|
|
}
|
|
if (c^.c_lock<>nil) and (cc^.cc_cancel=0) then
|
|
begin
|
|
cancelled:=1;
|
|
cc^.cc_cancel:=1;
|
|
end;
|
|
|
|
if (cc^.cc_waiting<>0) then
|
|
begin
|
|
{
|
|
* Someone has called callout_drain to kill this
|
|
* callout. Don't reschedule.
|
|
}
|
|
CC_UNLOCK(cc);
|
|
Exit(cancelled);
|
|
end;
|
|
end;
|
|
|
|
if ((c^.c_flags and CALLOUT_PENDING)<>0) then
|
|
begin
|
|
TAILQ_REMOVE(@cc^.cc_calllist,c,@c^.c_links);
|
|
|
|
cancelled:=1;
|
|
c^.c_flags:=c^.c_flags and (not (CALLOUT_ACTIVE or CALLOUT_PENDING));
|
|
end;
|
|
|
|
callout_cc_add(c, cc, to_ticks, ftn, arg);
|
|
|
|
CC_UNLOCK(cc);
|
|
|
|
Exit(cancelled);
|
|
end;
|
|
|
|
function _callout_stop_safe(c:p_callout;safe:Integer):Integer;
|
|
label
|
|
again;
|
|
var
|
|
cc:p_callout_cpu;
|
|
use_lock :Integer;
|
|
sq_locked:Integer;
|
|
begin
|
|
sq_locked:=0;
|
|
|
|
use_lock:=ord((safe=0) and (c^.c_lock<>nil));
|
|
|
|
again:
|
|
cc:=callout_lock();
|
|
|
|
md_callout_stop(c);
|
|
|
|
if ((c^.c_flags and CALLOUT_PENDING)=0) then
|
|
begin
|
|
c^.c_flags:=c^.c_flags and (not CALLOUT_ACTIVE);
|
|
|
|
if (cc^.cc_curr<>c) then
|
|
begin
|
|
CC_UNLOCK(cc);
|
|
if (sq_locked<>0) then
|
|
begin
|
|
sleepq_release(@cc^.cc_waiting);
|
|
end;
|
|
Exit(0);
|
|
end;
|
|
|
|
if (safe<>0) and (not THREAD_IS_NOSLEEPING) then
|
|
begin
|
|
while (cc^.cc_curr=c) do
|
|
begin
|
|
|
|
if (sq_locked=0) then
|
|
begin
|
|
CC_UNLOCK(cc);
|
|
sleepq_lock(@cc^.cc_waiting);
|
|
sq_locked:=1;
|
|
goto again;
|
|
end;
|
|
|
|
cc^.cc_waiting:=1;
|
|
|
|
CC_UNLOCK(cc);
|
|
sleepq_add(@cc^.cc_waiting,nil,PChar('codrain'),SLEEPQ_SLEEP,0);
|
|
sleepq_wait(@cc^.cc_waiting,0);
|
|
sq_locked:=0;
|
|
|
|
CC_LOCK(cc);
|
|
end;
|
|
end else
|
|
if (use_lock<>0) and (cc^.cc_cancel=0) then
|
|
begin
|
|
cc^.cc_cancel:=1;
|
|
|
|
CC_UNLOCK(cc);
|
|
Assert(sq_locked=0,'sleepqueue chain locked');
|
|
Exit(1);
|
|
end;
|
|
|
|
CC_UNLOCK(cc);
|
|
Assert(sq_locked=0,'sleepqueue chain still locked');
|
|
Exit(0);
|
|
end;
|
|
|
|
if (sq_locked<>0) then
|
|
begin
|
|
sleepq_release(@cc^.cc_waiting);
|
|
end;
|
|
|
|
c^.c_flags:=c^.c_flags and (not (CALLOUT_ACTIVE or CALLOUT_PENDING));
|
|
|
|
TAILQ_REMOVE(@cc^.cc_calllist,c,@c^.c_links);
|
|
|
|
callout_cc_del(c, cc);
|
|
|
|
CC_UNLOCK(cc);
|
|
Exit(1);
|
|
end;
|
|
|
|
//
|
|
|
|
function callout_drain(c:p_callout):Integer;
|
|
begin
|
|
Result:=_callout_stop_safe(c,1);
|
|
end;
|
|
|
|
function callout_stop(c:p_callout):Integer;
|
|
begin
|
|
Result:=_callout_stop_safe(c,0);
|
|
end;
|
|
|
|
procedure callout_init_mtx(c:p_callout;var mtx:mtx;flags:Integer);
|
|
begin
|
|
_callout_init_lock(c,@mtx,flags);
|
|
end;
|
|
|
|
procedure callout_init_rw(c:p_callout;var rw:Pointer;flags:Integer);
|
|
begin
|
|
_callout_init_lock(c,@rw,flags or CALLOUT_RWLOCK);
|
|
end;
|
|
|
|
function callout_reset(c:p_callout;on_tick:Int64;fn,arg:Pointer):Integer;
|
|
begin
|
|
Result:=callout_reset_on(c,on_tick,t_callout_func(fn),arg);
|
|
end;
|
|
|
|
function callout_reset_curcpu(c:p_callout;on_tick:Int64;fn,arg:Pointer):Integer;
|
|
begin
|
|
Result:=callout_reset_on(c,on_tick,t_callout_func(fn),arg);
|
|
end;
|
|
|
|
|
|
end.
|
|
|