{lsocket.pas} {io and timer code by plugwash} { Copyright (C) 2005 Bas Steendijk and Peter Green For conditions of distribution and use, see copyright notice in zlib_license.txt which is included in the package ----------------------------------------------------------------------------- } {$ifdef fpc} {$ifndef ver1_0} {$define useinline} {$endif} {$endif} unit lcoreselect; interface uses {$ifdef VER1_0} linux, {$else} baseunix,unix,unixutil, {$endif} fd_utils; var maxs : longint ; exitloopflag : boolean ; {if set by app, exit mainloop} function getfdsrmaster : fdset; {$ifdef useinline}inline;{$endif} function getfdswmaster : fdset; {$ifdef useinline}inline;{$endif} procedure lcoreinit; implementation uses lcore,sysutils, classes,pgtypes,bfifo, {$ifndef nosignal} lsignal; {$endif} {$include unixstuff.inc} {$include ltimevalstuff.inc} const absoloutemaxs_select = (sizeof(fdset)*8)-1; var fdreverse:array[0..absoloutemaxs_select] of tlasio; type tselecteventcore=class(teventcore) public procedure processmessages; override; procedure messageloop; override; procedure exitmessageloop;override; procedure setfdreverse(fd : integer;reverseto : tlasio); override; procedure rmasterset(fd : integer;islistensocket : boolean); override; procedure rmasterclr(fd: integer); override; procedure wmasterset(fd : integer); override; procedure wmasterclr(fd: integer); override; end; procedure processtimers;inline; var tv ,tvnow : ttimeval ; currenttimer : tltimer ; temptimer : tltimer ; begin gettimeofday(tvnow); currenttimer := firsttimer; while assigned(currenttimer) do begin //writeln(currenttimer.enabled); if tv_compare(tvnow,ttimeval(currenttimer.nextts)) and currenttimer.enabled then begin //if assigned(currenttimer.ontimer) then begin // if currenttimer.enabled then if currenttimer.initialevent or currenttimer.initialdone then currenttimer.ontimer(currenttimer); // currenttimer.initialdone := true; //end; if assigned(currenttimer.ontimer) then currenttimer.ontimer(currenttimer); currenttimer.nextts := timeval(tvnow); tv_add(ttimeval(currenttimer.nextts),currenttimer.interval); end; temptimer := currenttimer; currenttimer := currenttimer.nexttimer; end; end; procedure processasios(var fdsr,fdsw:fdset);//inline; var currentsocket : tlasio ; tempsocket : tlasio ; socketcount : integer ; // for debugging perposes :) dw,bt:integer; begin { inc(lcoretestcount);} //the message loop will exit if all lasio's and ltimer's and lsignal's are destroyed //if (not assigned(firstasin)) and (not assigned(firsttimer)) and (not assigned(firstsignal)) then exit; {------- test optimised loop} socketcount := 0; for dw := (maxs shr 5) downto 0 do if (fdsr[dw] or fdsw[dw]) <> 0 then begin for bt := 0 to 31 do if (fdsr[dw] or fdsw[dw]) and (1 shl bt) <> 0 then begin inc(socketcount); currentsocket := fdreverse[dw shl 5 or bt]; {if not assigned(currentsocket) then raise exception.create('currentsocket not assigned'); if currentsocket.fdhandlein < 0 then raise exception.create('currentsocket.fdhandlein out of range');} {i've seen the out of range case actually happening, so it can happen. test: just close the fd - beware} if not assigned(currentsocket) then begin fdclose(dw shl 5 or bt); continue end; if currentsocket.fdhandlein < 0 then begin fdclose(dw shl 5 or bt); continue end; try currentsocket.handlefdtrigger(fd_isset(currentsocket.fdhandlein,fdsr),fd_isset(currentsocket.fdhandleout,fdsw)); except on E: exception do begin currentsocket.HandleBackGroundException(e); end; end; if mustrefreshfds then begin if select(maxs+1,@fdsr,@fdsw,nil,0) <= 0 then begin fd_zero(fdsr); fd_zero(fdsw); end; end; end; end; { !!! issues: - sockets which are released may not be freed because theyre never processed by the loop made new code for handling this, using asinreleaseflag - when/why does the mustrefreshfds select apply, sheck if i did it correctly? - what happens if calling handlefdtrigger for a socket which does not have an event } {------- original loop} (* currentsocket := firstasin; socketcount := 0; while assigned(currentsocket) do begin if mustrefreshfds then begin if select(maxs,@fdsr,@fdsw,nil,0) <= 0 then begin fd_zero(fdsr); fd_zero(fdsw); end; end; try if fd_isset(currentsocket.fdhandlein,fdsr) or fd_isset(currentsocket.fdhandleout,fdsw) then begin currentsocket.handlefdtrigger(fd_isset(currentsocket.fdhandlein,fdsr),fd_isset(currentsocket.fdhandleout,fdsw)); end; except on E: exception do begin currentsocket.HandleBackGroundException(e); end; end; tempsocket := currentsocket; currentsocket := currentsocket.nextasin; inc(socketcount); if tempsocket.released then begin tempsocket.free; end; end; *) { debugout('socketcount='+inttostr(socketcount));} end; procedure tselecteventcore.processmessages; var fdsr , fdsw : fdset ; selectresult : longint ; begin mustrefreshfds := false; {$ifndef nosignal} prepsigpipe; {$endif} selectresult := select(maxs+1,@fdsr,@fdsw,nil,0); while (selectresult>0) or assigned(firsttask) or assigned(currenttask) do begin; processtasks; processtimers; if selectresult > 0 then begin processasios(fdsr,fdsw); end; selectresult := select(maxs+1,@fdsr,@fdsw,nil,0); end; mustrefreshfds := true; end; var FDSR , FDSW : fdset; var fdsrmaster , fdswmaster : fdset ; function getfdsrmaster : fdset; {$ifdef fpc}inline;{$endif} begin result := fdsrmaster; end; function getfdswmaster : fdset; {$ifdef fpc}inline;{$endif} begin result := fdswmaster; end; Function doSelect(timeOut:PTimeVal):longint;//inline; var localtimeval : ttimeval; maxslocal : integer; begin //unblock signals //zeromemory(@sset,sizeof(sset)); //sset[0] := ; fdsr := getfdsrmaster; fdsw := getfdswmaster; if assigned(firsttask) then begin localtimeval.tv_sec := 0; localtimeval.tv_usec := 0; timeout := @localtimeval; end; maxslocal := maxs; mustrefreshfds := false; { debugout('about to call select');} {$ifndef nosignal} sigprocmask(SIG_UNBLOCK,@blockset,nil); {$endif} result := select(maxslocal+1,@FDSR,@FDSW,nil,timeout); if result <= 0 then begin fd_zero(FDSR); fd_zero(FDSW); if result=-1 then begin if linuxerror = SYS_EINTR then begin // we received a signal it's not a problem end else begin raise esocketexception.create('select returned error '+inttostr(linuxerror)); end; end; end; {$ifndef nosignal} sigprocmask(SIG_BLOCK,@blockset,nil); {$endif} { debugout('select complete');} end; procedure tselecteventcore.exitmessageloop; begin exitloopflag := true end; procedure tselecteventcore.messageloop; var tv ,tvnow : ttimeval ; currenttimer : tltimer ; selectresult:integer; begin {$ifndef nosignal} prepsigpipe; {$endif} {currentsocket := firstasin; if not assigned(currentsocket) then exit; //the message loop will exit if all lsockets are destroyed repeat if currentsocket.state = wsconnected then currentsocket.sendflush; currentsocket := currentsocket.nextasin; until not assigned(currentsocket);} repeat //the message loop will exit if all lasio's and ltimer's and lsignal's are destroyed processtasks; //currenttask := nil; {beware} //if assigned(firsttimer) then begin // tv.tv_sec := maxlongint; tv := tv_invalidtimebig; currenttimer := firsttimer; while assigned(currenttimer) do begin if tv_compare(tv,currenttimer.nextts) and currenttimer.enabled then tv := currenttimer.nextts; currenttimer := currenttimer.nexttimer; end; if tv_compare(tv,tv_invalidtimebig) then begin //writeln('no timers active'); if exitloopflag then break; { sleep(10);} selectresult := doselect(nil); end else begin gettimeofday(tvnow); tv_substract(tv,tvnow); //writeln('timers active'); if tv.tv_sec < 0 then begin tv.tv_sec := 0; tv.tv_usec := 0; {0.1 sec} end; if exitloopflag then break; { sleep(10);} selectresult := doselect(@tv); processtimers; end; if selectresult > 0 then processasios(fdsr,fdsw); {!!!only call processasios if select has asio events -beware} {artificial delay to throttle the number of processasios per second possible and reduce cpu usage} until false; end; procedure tselecteventcore.rmasterset(fd : integer;islistensocket : boolean); begin if fd > absoloutemaxs then raise esocketexception.create('file discriptor out of range'); if fd > maxs then maxs := fd; if fd_isset(fd,fdsrmaster) then exit; fd_set(fd,fdsrmaster); end; procedure tselecteventcore.rmasterclr(fd: integer); begin if not fd_isset(fd,fdsrmaster) then exit; fd_clr(fd,fdsrmaster); end; procedure tselecteventcore.wmasterset(fd : integer); begin if fd > absoloutemaxs then raise esocketexception.create('file discriptor out of range'); if fd > maxs then maxs := fd; if fd_isset(fd,fdswmaster) then exit; fd_set(fd,fdswmaster); end; procedure tselecteventcore.wmasterclr(fd: integer); begin if not fd_isset(fd,fdswmaster) then exit; fd_clr(fd,fdswmaster); end; procedure tselecteventcore.setfdreverse(fd : integer;reverseto : tlasio); begin fdreverse[fd] := reverseto; end; var inited:boolean; procedure lcoreinit; begin if inited then exit; inited := true; eventcore := tselecteventcore.create; absoloutemaxs := absoloutemaxs_select; maxs := 0; fd_zero(fdsrmaster); fd_zero(fdswmaster); end; end.