unit kern_synch;

{$mode ObjFPC}{$H+}
{$CALLING SysV_ABI_CDecl}

interface

uses
 subr_sleepqueue,
 kern_mtx,
 kern_thr;

const
 PUSER=700;
 PRI_MIN_KERN=64;

 PSWP  =(PRI_MIN_KERN+ 0);
 PVM   =(PRI_MIN_KERN+ 4);
 PINOD =(PRI_MIN_KERN+ 8);
 PRIBIO=(PRI_MIN_KERN+12);
 PVFS  =(PRI_MIN_KERN+16);
 PZERO =(PRI_MIN_KERN+20);
 PSOCK =(PRI_MIN_KERN+24);
 PWAIT =(PRI_MIN_KERN+28);
 PLOCK =(PRI_MIN_KERN+32);
 PPAUSE=(PRI_MIN_KERN+36);

 PRIMASK=$0fff;
 PCATCH =$1000;
 PDROP  =$2000;
 PBDRY  =$4000;

function  msleep(ident   :Pointer;
                 lock    :p_mtx;
                 priority:Integer;
                 wmesg   :PChar;
                 timo    :Int64):Integer;

function  tsleep(ident   :Pointer;
                 priority:Integer;
                 wmesg   :PChar;
                 timo    :Int64):Integer; inline;

procedure wakeup(ident:Pointer);
procedure wakeup_one(ident:Pointer);

procedure maybe_yield();
procedure kern_yield(prio:Integer);
function  sys_yield():Integer;

implementation

uses
 kern_thread,
 sched_ule,
 vm_machdep,
 rtprio;

function msleep(ident   :Pointer;
                lock    :p_mtx;
                priority:Integer;
                wmesg   :PChar;
                timo    :Int64):Integer;
var
 td:p_kthread;
 catch,flags,pri:Integer;
begin
 td:=curkthread;

 catch:=priority and PCATCH;
 pri  :=priority and PRIMASK;

 if (TD_ON_SLEEPQ(td)) then
  sleepq_remove(td,td^.td_wchan);

 flags:=SLEEPQ_SLEEP;

 if (catch<>0) then
  flags:=flags or SLEEPQ_INTERRUPTIBLE;

 if (priority and PBDRY)<>0 then
  flags:=flags or SLEEPQ_STOP_ON_BDRY;

 sleepq_lock(ident);

 mtx_unlock(lock^);

 sleepq_add(ident,lock,wmesg,flags,0);

 if (timo<>0) then
  sleepq_set_timeout(ident,timo);

 if (timo<>catch) then
  Result:=sleepq_timedwait_sig(ident,pri)
 else if (timo<>0) then
  Result:=sleepq_timedwait(ident,pri)
 else if (catch<>0) then
  Result:=sleepq_wait_sig(ident,pri)
 else
 begin
  sleepq_wait(ident,pri);
  Result:=0;
 end;

 if (lock<>nil) and ((priority and PDROP)=0) then
 begin
  mtx_lock(lock^);
 end;
end;

function tsleep(ident   :Pointer;
                priority:Integer;
                wmesg   :PChar;
                timo    :Int64):Integer; inline;
begin
 Result:=msleep(ident,nil,priority,wmesg,timo);
end;

procedure wakeup(ident:Pointer);
begin
 sleepq_lock(ident);
 sleepq_broadcast(ident,SLEEPQ_SLEEP,0,0);
 sleepq_release(ident);
end;

procedure wakeup_one(ident:Pointer);
begin
 sleepq_lock(ident);
 sleepq_signal(ident,SLEEPQ_SLEEP,0,0);
 sleepq_release(ident);
end;

procedure maybe_yield();
begin
 kern_yield(PRI_USER);
end;

procedure kern_yield(prio:Integer);
var
 td:p_kthread;
begin
 td:=curkthread;
 thread_lock(td);
 if (prio=PRI_USER) then
  prio:=td^.td_user_pri;
 if (prio>=0) then
  sched_prio(td, prio);
 thread_unlock(td);
 mi_switch();
end;

function sys_yield():Integer;
var
 td:p_kthread;
begin
 td:=curkthread;
 thread_lock(td);
 if (PRI_BASE(td^.td_pri_class)=PRI_TIMESHARE) then
  sched_prio(td, PRI_MAX_TIMESHARE);
 thread_unlock(td);
 mi_switch();
 td^.td_retval[0]:=0;
 Exit(0);
end;

end.

