3 {io and timer code by plugwash}
\r 
   5 { Copyright (C) 2005 Bas Steendijk and Peter Green
\r 
   6   For conditions of distribution and use, see copyright notice in zlib_license.txt
\r 
   7   which is included in the package
\r 
   8   ----------------------------------------------------------------------------- }
\r 
  24     baseunix,unix,unixutil,sockets,
\r 
  29   exitloopflag                          : boolean    ; {if set by app, exit mainloop}
\r 
  31 function getfdsrmaster : fdset; {$ifdef useinline}inline;{$endif}
\r 
  32 function getfdswmaster : fdset; {$ifdef useinline}inline;{$endif}
\r 
  34 procedure lcoreinit;
\r 
  39   classes,pgtypes,bfifo,
\r 
  45 {$include unixstuff.inc}
\r 
  48   absolutemaxs_select = (sizeof(fdset)*8)-1;
\r 
  51   fdreverse:array[0..absolutemaxs_select] of tlasio;
\r 
  53   tselecteventcore=class(teventcore)
\r 
  55       procedure processmessages; override;
\r 
  56       procedure messageloop; override;
\r 
  57       procedure exitmessageloop;override;
\r 
  58       procedure setfdreverse(fd : integer;reverseto : tlasio); override;
\r 
  59       procedure rmasterset(fd : integer;islistensocket : boolean); override;
\r 
  60       procedure rmasterclr(fd: integer); override;
\r 
  61       procedure wmasterset(fd : integer); override;
\r 
  62       procedure wmasterclr(fd: integer); override;
\r 
  65 procedure processtimers;inline;
\r 
  68   currenttimer            : tltimer   ;
\r 
  69   temptimer               : tltimer  ;
\r 
  72   gettimemonotonic(tvnow);
\r 
  73   currenttimer := firsttimer;
\r 
  74   while assigned(currenttimer) do begin
\r 
  75     //writeln(currenttimer.enabled);
\r 
  76     if tv_compare(tvnow,ttimeval(currenttimer.nextts)) and currenttimer.enabled then begin
\r 
  77       //if assigned(currenttimer.ontimer) then begin
\r 
  78       //  if currenttimer.enabled then if currenttimer.initialevent or currenttimer.initialdone then currenttimer.ontimer(currenttimer);
\r 
  79       //  currenttimer.initialdone := true;
\r 
  81       if assigned(currenttimer.ontimer) then currenttimer.ontimer(currenttimer);
\r 
  82       currenttimer.nextts := timeval(tvnow);
\r 
  83       tv_add(ttimeval(currenttimer.nextts),currenttimer.interval);
\r 
  85     temptimer := currenttimer;
\r 
  86     currenttimer := currenttimer.nexttimer;
\r 
  90 procedure processasios(var fdsr,fdsw:fdset);//inline;
\r 
  92   currentsocket : tlasio  ;
\r 
  93   socketcount   : integer ; // for debugging purposes :)
\r 
  95   currentfdword:fdword;
\r 
  98   //writeln('entering processasios');
\r 
  99 {  inc(lcoretestcount);}
\r 
 101     //the message loop will exit if all lasios and ltimers and lsignals are destroyed
\r 
 102     //if (not assigned(firstasin)) and (not assigned(firsttimer)) and (not assigned(firstsignal)) then exit;
\r 
 105   {------- test optimised loop}
\r 
 107   for dw := (maxs shr fdwordshift) downto 0 do if (fdsr[dw] or fdsw[dw]) <> 0 then begin
\r 
 108     currentfdword := (fdsr[dw] or fdsw[dw]);
\r 
 109     for bt := fdwordmaxbit downto 0 do if currentfdword and (1 shl bt) <> 0 then begin
\r 
 111       fd := dw shl fdwordshift or bt;
\r 
 112       //writeln('reversing fd ',fd);
\r 
 113       currentsocket := fdreverse[fd];
\r 
 114       {if not assigned(currentsocket) then raise exception.create('currentsocket not assigned');
\r 
 115       if currentsocket.fdhandlein < 0 then raise exception.create('currentsocket.fdhandlein out of range');}
\r 
 116       {i've seen the out of range case actually happening, so it can happen. test: just close the fd - beware}
\r 
 117       if not assigned(currentsocket) then begin
\r 
 121       if currentsocket.fdhandlein < 0 then begin
\r 
 126         currentsocket.handlefdtrigger(fd_isset(fd,fdsr),fd_isset(fd,fdsw));
\r 
 128         on E: exception do begin
\r 
 129           currentsocket.HandleBackGroundException(e);
\r 
 133       if mustrefreshfds then begin
\r 
 134         if select(maxs+1,@fdsr,@fdsw,nil,0) <= 0 then begin
\r 
 144   - sockets which are released may not be freed because theyre never processed by the loop
\r 
 145   made new code for handling this, using asinreleaseflag
\r 
 147   - when/why does the mustrefreshfds select apply, check if i did it correctly?
\r 
 149   - what happens if calling handlefdtrigger for a socket which does not have an event
\r 
 151   {------- original loop}
\r 
 154   currentsocket := firstasin;
\r 
 156   while assigned(currentsocket) do begin
\r 
 157     if mustrefreshfds then begin
\r 
 158       if select(maxs,@fdsr,@fdsw,nil,0) <= 0 then begin
\r 
 164       if fd_isset(currentsocket.fdhandlein,fdsr) or fd_isset(currentsocket.fdhandleout,fdsw) then begin
\r 
 165         currentsocket.handlefdtrigger(fd_isset(currentsocket.fdhandlein,fdsr),fd_isset(currentsocket.fdhandleout,fdsw));
\r 
 168       on E: exception do begin
\r 
 169         currentsocket.HandleBackGroundException(e);
\r 
 172     tempsocket := currentsocket;
\r 
 173     currentsocket := currentsocket.nextasin;
\r 
 175     if tempsocket.released then begin
\r 
 179 {  debugout('socketcount='+inttostr(socketcount));}
\r 
 180   //writeln('leaving processasios');
\r 
 183 procedure tselecteventcore.processmessages;
\r 
 185   fdsr         , fdsw : fdset   ;
\r 
 186   selectresult        : longint ;
\r 
 188   mustrefreshfds := false;
\r 
 192   selectresult := select(maxs+1,@fdsr,@fdsw,nil,0);
\r 
 193   while (selectresult>0) or assigned(firsttask) do begin;
\r 
 197     if selectresult > 0 then begin
\r 
 198       processasios(fdsr,fdsw);
\r 
 200     selectresult := select(maxs+1,@fdsr,@fdsw,nil,0);
\r 
 203   mustrefreshfds := true;
\r 
 208   FDSR , FDSW : fdset;
\r 
 211   fdsrmaster , fdswmaster               : fdset      ;
\r 
 213 function getfdsrmaster : fdset; {$ifdef fpc}inline;{$endif}
\r 
 215   result := fdsrmaster;
\r 
 217 function getfdswmaster : fdset; {$ifdef fpc}inline;{$endif}
\r 
 219   result := fdswmaster;
\r 
 223 Function  doSelect(timeOut:PTimeVal):longint;//inline;
\r 
 225   localtimeval : ttimeval;
\r 
 226   maxslocal    : integer;
\r 
 229   //zeromemory(@sset,sizeof(sset));
\r 
 231   fdsr := getfdsrmaster;
\r 
 232   fdsw := getfdswmaster;
\r 
 234   if assigned(firsttask) then begin
\r 
 235     localtimeval.tv_sec  := 0;
\r 
 236     localtimeval.tv_usec := 0;
\r 
 237     timeout := @localtimeval;
\r 
 241   mustrefreshfds := false;
\r 
 242 {  debugout('about to call select');}
\r 
 244     sigprocmask(SIG_UNBLOCK,@blockset,nil);
\r 
 246   result := select(maxslocal+1,@FDSR,@FDSW,nil,timeout);
\r 
 247   if result <= 0 then begin
\r 
 250     if result=-1 then begin
\r 
 251       if linuxerror = SYS_EINTR then begin
\r 
 252         // we received a signal it is not a problem
\r 
 254         raise esocketexception.create('select returned error '+inttostr(linuxerror));
\r 
 259     sigprocmask(SIG_BLOCK,@blockset,nil);
\r 
 261 {  debugout('select complete');}
\r 
 264 procedure tselecteventcore.exitmessageloop;
\r 
 266   exitloopflag := true
\r 
 271 procedure tselecteventcore.messageloop;
\r 
 273   tv           ,tvnow     : ttimeval ;
\r 
 274   currenttimer            : tltimer  ;
\r 
 275   selectresult:integer;
\r 
 280   {currentsocket := firstasin;
\r 
 281   if not assigned(currentsocket) then exit; //the message loop will exit if all lsockets are destroyed
\r 
 284     if currentsocket.state = wsconnected then currentsocket.sendflush;
\r 
 285     currentsocket := currentsocket.nextasin;
\r 
 286   until not assigned(currentsocket);}
\r 
 291     //the message loop will exit if all lasios and ltimers and lsignals are destroyed
\r 
 293     //currenttask := nil;
\r 
 295     //if assigned(firsttimer) then begin
\r 
 296     //  tv.tv_sec := maxlongint;
\r 
 297     tv := tv_invalidtimebig;
\r 
 298     currenttimer := firsttimer;
\r 
 299     while assigned(currenttimer) do begin
\r 
 300       if tv_compare(tv,currenttimer.nextts) and currenttimer.enabled then tv := currenttimer.nextts;
\r 
 301       currenttimer := currenttimer.nexttimer;
\r 
 305     if tv_compare(tv,tv_invalidtimebig) then begin    
\r 
 306       //writeln('no timers active');
\r 
 307       if exitloopflag then break;
\r 
 309       selectresult := doselect(nil);
\r 
 312       gettimemonotonic(tvnow);
\r 
 313       tv_subtract(tv,tvnow);
\r 
 315       //writeln('timers active');
\r 
 316       if tv.tv_sec < 0 then begin
\r 
 318         tv.tv_usec := 0; {0.1 sec}
\r 
 320       if exitloopflag then break;
\r 
 322       selectresult := doselect(@tv);
\r 
 326     if selectresult > 0 then processasios(fdsr,fdsw);
\r 
 327     {!!!only call processasios if select has asio events -beware}
\r 
 329     {artificial delay to throttle the number of processasios per second possible and reduce cpu usage}
\r 
 334 procedure tselecteventcore.rmasterset(fd : integer;islistensocket : boolean);
\r 
 336   //writeln('rmasterset called with fd ',fd);
\r 
 337   if fd > absolutemaxs then raise esocketexception.create('file descriptor out of range');
\r 
 338   if fd > maxs then maxs := fd;
\r 
 339   if fd_isset(fd,fdsrmaster) then exit;
\r 
 340   fd_set(fd,fdsrmaster);
\r 
 344 procedure tselecteventcore.rmasterclr(fd: integer);
\r 
 346   //writeln('rmasterclr called with fd ',fd);
\r 
 347   if not fd_isset(fd,fdsrmaster) then exit;
\r 
 348   fd_clr(fd,fdsrmaster);
\r 
 353 procedure tselecteventcore.wmasterset(fd : integer);
\r 
 355   //writeln('wmasterset called with fd ',fd);
\r 
 356   if fd > absolutemaxs then raise esocketexception.create('file descriptor out of range');
\r 
 357   if fd > maxs then maxs := fd;
\r 
 359   if fd_isset(fd,fdswmaster) then exit;
\r 
 360   fd_set(fd,fdswmaster);
\r 
 364 procedure tselecteventcore.wmasterclr(fd: integer);
\r 
 366   //writeln('wmasterclr called with fd ',fd);
\r 
 367   if not fd_isset(fd,fdswmaster) then exit;
\r 
 368   fd_clr(fd,fdswmaster);
\r 
 371 procedure tselecteventcore.setfdreverse(fd : integer;reverseto : tlasio);
\r 
 373   fdreverse[fd] := reverseto;
\r 
 379 procedure lcoreinit;
\r 
 381   if inited then exit;
\r 
 383   eventcore := tselecteventcore.create;
\r 
 385   absolutemaxs := absolutemaxs_select;
\r 
 388   fd_zero(fdsrmaster);
\r 
 389   fd_zero(fdswmaster);
\r