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 
  10 {note: you must use the @ in the last param to tltask.create not doing so will
\r 
  11  compile without error but will cause an access violation -pg}
\r 
  13 //note: events after release are normal and are the apps responsibility to deal with safely
\r 
  29         baseunix,unix,unixutil,sockets,
\r 
  33     classes,pgtypes,bfifo,ltimevalstuff;
\r 
  34   procedure processtasks;
\r 
  38     {how this number is made up:
\r 
  39     - ethernet: MTU 1500
\r 
  40     - be safe for either "ethernet v1" or "PPPoE", both take 8 bytes
\r 
  41     - IPv6 header: 40 bytes (IPv4 is 20)
\r 
  42     - TCP/UDP header: 20 bytes
\r 
  43     packetbasesize is deprecated and should not be used anymore
\r 
  45     packetbasesize = 1432;
\r 
  46     receivebufsize=16384;
\r 
  49     absolutemaxs:integer=0;
\r 
  53       sigset= array[0..31] of longint;
\r 
  56     ESocketException   = class(Exception);
\r 
  57     TBgExceptionEvent  = procedure (Sender : TObject;
\r 
  59                                   var CanClose : Boolean) of object;
\r 
  61     // note : tsocketstate is defined in the same way as it is in François PIETTE's twsocket
\r 
  62     // however tlsocket currently only uses wsClosed wsConnecting wsconnected and wsListening
\r 
  63     TSocketState       = (wsInvalidState,
\r 
  65                         wsConnecting, wsConnected,
\r 
  66                         wsAccepting,  wsListening,
\r 
  69     TWSocketOption       = (wsoNoReceiveLoop, wsoTcpNoDelay);
\r 
  70     TWSocketOptions      = set of TWSocketOption;
\r 
  72     TSocketevent     = procedure(Sender: TObject; Error: word) of object;
\r 
  73     //Tdataavailevent  = procedure(data : string);
\r 
  74     TSendData          = procedure (Sender: TObject; BytesSent: Integer) of object;
\r 
  76     tlcomponent = class(tcomponent)
\r 
  78       procedure releasetaskhandler(wparam,lparam:longint);
\r 
  80       procedure release; virtual;
\r 
  81       destructor destroy; override;
\r 
  84     tlasio = class(tlcomponent)
\r 
  86       state              : tsocketstate      ;
\r 
  87       ComponentOptions   : TWSocketOptions;
\r 
  88       fdhandlein         : Longint           ;  {file descriptor}
\r 
  89       fdhandleout        : Longint           ;  {file descriptor}
\r 
  91       onsessionclosed    : tsocketevent      ;
\r 
  92       ondataAvailable    : tsocketevent      ;
\r 
  93       onsessionAvailable : tsocketevent      ;
\r 
  95       onsessionconnected : tsocketevent      ;
\r 
  96       onsenddata         : tsenddata      ;
\r 
  97       ondatasent         : tsocketevent      ;
\r 
  98       //connected          : boolean         ;
\r 
 101       OnBgException      : TBgExceptionEvent ;
\r 
 102       //connectread        : boolean           ;
\r 
 104       closehandles       : boolean           ;
\r 
 105       writtenthiscycle   : boolean           ;
\r 
 106       onfdwrite           : procedure (Sender: TObject; Error: word) of object; //added for bewarehttpd
\r 
 108       destroying:boolean;
\r 
 109       recvbufsize:integer;
\r 
 110       datasentcalled:boolean;
\r 
 112       sendflushlasterror:integer;
\r 
 115       sendflushmaxwrite:integer;
\r 
 116       //how much to write to the socket internally in one go. higher values allow faster throughput especially if latency is high
\r 
 117       //but it also causes onsenddata to be called less often (typically once for every sendflushmaxwrite bytes)
\r 
 119       function receivestr:tbufferstring; virtual;
\r 
 122       procedure internalclose(error:word); virtual;
\r 
 123       constructor Create(AOwner: TComponent); override;
\r 
 125       destructor destroy; override;
\r 
 126       procedure fdcleanup;
\r 
 127       procedure HandleBackGroundException(E: Exception);
\r 
 128       procedure handlefdtrigger(readtrigger,writetrigger:boolean); virtual;
\r 
 129       procedure dup(invalue:longint);
\r 
 131       function sendflush : integer;
\r 
 132       procedure sendstr(const str : tbufferstring);virtual;
\r 
 133       procedure putstringinsendbuffer(const newstring : tbufferstring);
\r 
 134       function send(data:pointer;len:integer):integer;virtual;
\r 
 135       procedure putdatainsendbuffer(data:pointer;len:integer); virtual;
\r 
 136       procedure deletebuffereddata;
\r 
 138       //procedure messageloop;
\r 
 139       function Receive(Buf:Pointer;BufSize:integer):integer; virtual;
\r 
 140       procedure flush;virtual;
\r 
 141       procedure dodatasent(wparam,lparam:longint);
\r 
 142       procedure doreceiveloop(wparam,lparam:longint);
\r 
 143       procedure sinkdata(sender:tobject;error:word);
\r 
 145       procedure release; override; {test -beware}
\r 
 147       function RealSend(Data : Pointer; Len : Integer) : Integer; //added for bewarehttpd
\r 
 149       procedure myfdclose(fd : integer); virtual;{$ifdef mswindows}abstract;{$endif}
\r 
 150       function myfdwrite(fd: LongInt;const buf;size: LongInt):LongInt; virtual;{$ifdef mswindows}abstract;{$endif}
\r 
 151       function myfdread(fd: LongInt;var buf;size: LongInt):LongInt; virtual;{$ifdef mswindows}abstract;{$endif}
\r 
 153       procedure dupnowatch(invalue:longint);
\r 
 155     ttimerwrapperinterface=class(tlcomponent)
\r 
 157       function createwrappedtimer : tobject;virtual;abstract;
\r 
 158 //      procedure setinitialevent(wrappedtimer : tobject;newvalue : boolean);virtual;abstract;
\r 
 159       procedure setontimer(wrappedtimer : tobject;newvalue:tnotifyevent);virtual;abstract;
\r 
 160       procedure setenabled(wrappedtimer : tobject;newvalue : boolean);virtual;abstract;
\r 
 161       procedure setinterval(wrappedtimer : tobject;newvalue : integer);virtual;abstract;
\r 
 165     timerwrapperinterface : ttimerwrapperinterface;
\r 
 167     tltimer=class(tlcomponent)
\r 
 171       wrappedtimer : tobject;
\r 
 174 //      finitialevent       : boolean           ;
\r 
 175       fontimer            : tnotifyevent      ;
\r 
 176       fenabled            : boolean           ;
\r 
 177       finterval           : integer          ; {milliseconds, default 1000}
\r 
 178       {$ifndef mswindows}
\r 
 179         procedure resettimes;
\r 
 181 //      procedure setinitialevent(newvalue : boolean);
\r 
 182       procedure setontimer(newvalue:tnotifyevent);
\r 
 183       procedure setenabled(newvalue : boolean);
\r 
 184       procedure setinterval(newvalue : integer);
\r 
 186       //making these public for now, this code should probably be restructured later though
\r 
 187       prevtimer          : tltimer           ;
\r 
 188       nexttimer          : tltimer           ;
\r 
 189       nextts             : ttimeval          ;
\r 
 191       constructor create(aowner:tcomponent);override;
\r 
 192       destructor destroy;override;
\r 
 193 //      property initialevent : boolean read finitialevent write setinitialevent;
\r 
 194       property ontimer : tnotifyevent read fontimer write setontimer;
\r 
 195       property enabled : boolean read fenabled write setenabled;
\r 
 196       property interval : integer read finterval write setinterval;
\r 
 200     ttaskevent=procedure(wparam,lparam:longint) of object;
\r 
 202     tltask=class(tobject)
\r 
 204       handler  : ttaskevent;
\r 
 209       constructor create(ahandler:ttaskevent;aobj:tobject;awparam,alparam:longint);
\r 
 216       procedure processmessages; virtual;abstract;
\r 
 217       procedure messageloop; virtual;abstract;
\r 
 218       procedure exitmessageloop; virtual;abstract;
\r 
 219       procedure setfdreverse(fd : integer;reverseto : tlasio);virtual;abstract;
\r 
 220       procedure rmasterset(fd : integer;islistensocket : boolean);  virtual;abstract;
\r 
 221       procedure rmasterclr(fd: integer);  virtual;abstract;
\r 
 222       procedure wmasterset(fd : integer); virtual;abstract;
\r 
 223       procedure wmasterclr(fd: integer);  virtual;abstract;
\r 
 226     eventcore : teventcore;
\r 
 228 procedure processmessages;
\r 
 229 procedure messageloop;
\r 
 230 procedure exitmessageloop;
\r 
 233   firsttimer                            : tltimer    ;
\r 
 234   firsttask  , lasttask                 : tltask     ;
\r 
 236   numread                               : integer    ;
\r 
 237   mustrefreshfds                        : boolean    ;
\r 
 238 {  lcoretestcount:integer;}
\r 
 240   asinreleaseflag:boolean;
\r 
 243 procedure disconnecttasks(aobj:tobject);
\r 
 244 procedure addtask(ahandler:ttaskevent;aobj:tobject;awparam,alparam:longint);
\r 
 246   tonaddtask = procedure(ahandler:ttaskevent;aobj:tobject;awparam,alparam:longint);
\r 
 248   onaddtask : tonaddtask;
\r 
 251 procedure sleep(i:integer);
\r 
 253   procedure prepsigpipe;{$ifndef ver1_0}inline;{$endif}
\r 
 259   uses {sockets,}lloopback,lsignal;
\r 
 262   uses windows,winsock;
\r 
 264 {$ifndef mswindows}
\r 
 265   {$include unixstuff.inc}
\r 
 269 {!!! added sleep call -beware}
\r 
 270 procedure sleep(i:integer);
\r 
 278   tv.tv_sec := i div 1000;
\r 
 279   tv.tv_usec := (i mod 1000) * 1000;
\r 
 280   select(0,nil,nil,nil,@tv);
\r 
 285 destructor tlcomponent.destroy;
\r 
 287   disconnecttasks(self);
\r 
 291 procedure tlcomponent.releasetaskhandler(wparam,lparam:longint);
\r 
 297 procedure tlcomponent.release;
\r 
 299   addtask(releasetaskhandler,self,0,0);
\r 
 302 procedure tlasio.release;
\r 
 304   asinreleaseflag := true;
\r 
 308 procedure tlasio.doreceiveloop;
\r 
 310   if recvq.size = 0 then exit;
\r 
 311   if assigned(ondataavailable) then ondataavailable(self,0);
\r 
 312   if not (wsonoreceiveloop in componentoptions) then
\r 
 313   if recvq.size > 0 then tltask.create(self.doreceiveloop,self,0,0);
\r 
 316 function tlasio.receivestr;
\r 
 318   setlength(result,recvq.size);
\r 
 319   receive(@result[1],length(result));
\r 
 322 function tlasio.receive(Buf:Pointer;BufSize:integer):integer;
\r 
 328   if recvq.size < i then i := recvq.size;
\r 
 330   while (a < i) do begin
\r 
 331     b := recvq.get(p,i-a);
\r 
 333     inc(taddrint(buf),b);
\r 
 338   if wsonoreceiveloop in componentoptions then begin
\r 
 339     if recvq.size = 0 then eventcore.rmasterset(fdhandlein,false);
\r 
 343 constructor tlasio.create;
\r 
 345   inherited create(AOwner);
\r 
 346   if not assigned(eventcore) then raise exception.create('no event core');
\r 
 347   sendq := tfifo.create;
\r 
 348   recvq := tfifo.create;
\r 
 352   sendflushmaxwrite := 16384;
\r 
 355 destructor tlasio.destroy;
\r 
 357   destroying := true;
\r 
 358   if state <> wsclosed then close;
\r 
 364 procedure tlasio.close;
\r 
 369 procedure tlasio.abort;
\r 
 374 procedure tlasio.fdcleanup;
\r 
 376   if fdhandlein <> -1 then begin
\r 
 377     eventcore.rmasterclr(fdhandlein); //fd_clr(fdhandlein,fdsrmaster)
\r 
 379   if fdhandleout <> -1 then begin
\r 
 380     eventcore.wmasterclr(fdhandleout);//fd_clr(fdhandleout,fdswmaster)
\r 
 382   if fdhandlein=fdhandleout then begin
\r 
 383     if fdhandlein <> -1 then begin
\r 
 384       myfdclose(fdhandlein);
\r 
 387     if fdhandlein <> -1 then begin
\r 
 388       myfdclose(fdhandlein);
\r 
 390     if fdhandleout <> -1 then begin
\r 
 391       myfdclose(fdhandleout);
\r 
 398 procedure tlasio.internalclose(error:word);
\r 
 400   if (state<>wsclosed) and (state<>wsinvalidstate) then begin
\r 
 401     // -2 is a special indication that we should just exist silently
\r 
 402     // (used for connect failure handling when socket creation fails)
\r 
 403     if (fdhandlein = -2) and (fdhandleout = -2) then exit;
\r 
 404     if (fdhandlein < 0) or (fdhandleout < 0) then raise exception.create('internalclose called with invalid fd handles');
\r 
 405     eventcore.rmasterclr(fdhandlein);//fd_clr(fdhandlein,fdsrmaster);
\r 
 406     eventcore.wmasterclr(fdhandleout);//fd_clr(fdhandleout,fdswmaster);
\r 
 408     if closehandles then begin
\r 
 409       {$ifndef mswindows}
\r 
 410         //anyone remember why this is here? --plugwash
\r 
 411         fcntl(fdhandlein,F_SETFL,0);
\r 
 413       myfdclose(fdhandlein);
\r 
 414       if fdhandleout <> fdhandlein then begin
\r 
 415         {$ifndef mswindows}
\r 
 416           fcntl(fdhandleout,F_SETFL,0);
\r 
 418         myfdclose(fdhandleout);
\r 
 420       eventcore.setfdreverse(fdhandlein,nil);
\r 
 421       eventcore.setfdreverse(fdhandleout,nil);
\r 
 428     if assigned(onsessionclosed) then if not destroying then onsessionclosed(self,error);
\r 
 430   if assigned(sendq) then sendq.del(maxlongint);
\r 
 434 {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
\r 
 435 { All exceptions *MUST* be handled. If an exception is not handled, the     }
\r 
 436 { application will most likely be shut down !                               }
\r 
 437 procedure tlasio.HandleBackGroundException(E: Exception);
\r 
 439   CanAbort : Boolean;
\r 
 442   { First call the error event handler, if any }
\r 
 443   if Assigned(OnBgException) then begin
\r 
 445       OnBgException(Self, E, CanAbort);
\r 
 449   { Then abort the socket }
\r 
 450   if CanAbort then begin
\r 
 458 procedure tlasio.sendstr(const str : tbufferstring);
\r 
 460   putstringinsendbuffer(str);
\r 
 464 procedure tlasio.putstringinsendbuffer(const newstring : tbufferstring);
\r 
 466   if newstring <> '' then putdatainsendbuffer(@newstring[1],length(newstring));
\r 
 469 function tlasio.send(data:pointer;len:integer):integer;
\r 
 471   if state <> wsconnected then begin
\r 
 475   if len < 0 then len := 0;
\r 
 477   putdatainsendbuffer(data,len);
\r 
 482 procedure tlasio.putdatainsendbuffer(data:pointer;len:integer);
\r 
 484   sendq.add(data,len);
\r 
 487 function tlasio.sendflush : integer;
\r 
 491 //  fdstestr : fdset;
\r 
 492 //  fdstestw : fdset;
\r 
 494   if state <> wsconnected then begin
\r 
 498   datasentcalled := false;
\r 
 500   lensent := sendflushmaxwrite;
\r 
 501   if (lensent <= 0) then lensent := sendq.size;
\r 
 503   lensent := sendq.get(data,lensent);
\r 
 504   if assigned(data) then result := myfdwrite(fdhandleout,data^,lensent) else result := 0;
\r 
 506   if result = -1 then lensent := 0 else lensent := result;
\r 
 509   if (result = -1) then sendflushlasterror := getlasterror else sendflushlasterror := 0;
\r 
 512   //sendq := copy(sendq,lensent+1,length(sendq)-lensent);
\r 
 513   sendq.del(lensent);
\r 
 515   //fd_clr(fdhandleout,fdsw); // this prevents the socket being closed by a write
\r 
 516                             // that sends nothing because a previous socket has
\r 
 517                             // slready flushed this socket when the message loop
\r 
 519 //  if sendq.size > 0 then begin
\r 
 520     eventcore.wmasterset(fdhandleout);//fd_set(fdhandleout,fdswmaster);
\r 
 522 //    wmasterclr(fdhandleout);//fd_clr(fdhandleout,fdswmaster);
\r 
 524   if result > 0 then begin
\r 
 525     if assigned(onsenddata) then onsenddata(self,result);
\r 
 526 //    if sendq.size=0 then if assigned(ondatasent) then begin
\r 
 527 //      tltask.create(self.dodatasent,self,0,0);
\r 
 528 //      //begin test code
\r 
 529 //      fd_zero(fdstestr);
\r 
 530 //      fd_zero(fdstestw);
\r 
 531 //      fd_set(fdhandlein,fdstestr);
\r 
 532 //      fd_set(fdhandleout,fdstestw);
\r 
 533 //      select(maxs,@fdstestr,@fdstestw,nil,0);
\r 
 534 //      writeln(fd_isset(fdhandlein,fdstestr),' ',fd_isset(fdhandleout,fdstestw));
\r 
 538     writtenthiscycle := true;
\r 
 542 procedure tlasio.dupnowatch(invalue:longint);
\r 
 544   {  debugout('invalue='+inttostr(invalue));}
\r 
 546   if state<> wsclosed then close;
\r 
 547   fdhandlein := invalue;
\r 
 548   fdhandleout := invalue;
\r 
 549   eventcore.setfdreverse(fdhandlein,self);
\r 
 550   {$ifndef mswindows}
\r 
 551     fcntl(fdhandlein,F_SETFL,OPEN_NONBLOCK);
\r 
 553   state := wsconnected;
\r 
 558 procedure tlasio.dup(invalue:longint);
\r 
 560   dupnowatch(invalue);
\r 
 561   eventcore.rmasterset(fdhandlein,false);//fd_set(fdhandlein,fdsrmaster);
\r 
 562   eventcore.wmasterclr(fdhandleout);//fd_clr(fdhandleout,fdswmaster);
\r 
 566 procedure tlasio.handlefdtrigger(readtrigger,writetrigger:boolean);
\r 
 568   sendflushresult : integer;
\r 
 569   tempbuf:array[0..receivebufsize-1] of byte;
\r 
 572   if (state=wsconnected) and writetrigger then begin
\r 
 573     //writeln('write trigger');
\r 
 575     if (sendq.size >0) then begin
\r 
 577       sendflushresult := sendflush;
\r 
 578       if (sendflushresult <= 0) and (not writtenthiscycle) then begin
\r 
 579         if sendflushresult=0 then begin // linuxerror := 0;
\r 
 584           if sendflushlasterror=WSAEWOULDBLOCK then begin
\r 
 585             //the asynchronous nature of windows messages means we sometimes
\r 
 586             //get here with the buffer full
\r 
 587             //so do nothing in that case
\r 
 591             internalclose({$ifdef mswindows}sendflushlasterror{$else}linuxerror{$endif});
\r 
 597       //everything is sent fire off ondatasent event
\r 
 598       if fdhandleout >= 0 then eventcore.wmasterclr(fdhandleout);//fd_clr(fdhandleout,fdswmaster);
\r 
 599       if assigned(ondatasent) then begin
\r 
 600         if not datasentcalled then begin
\r 
 601           tltask.create(self.dodatasent,self,0,0);
\r 
 602           datasentcalled := true;
\r 
 607     if assigned(onfdwrite) then onfdwrite(self,0);
\r 
 609   writtenthiscycle := false;
\r 
 610   if (state =wsconnected) and readtrigger then begin
\r 
 611     if recvq.size=0 then begin
\r 
 613       if (a <= 0) or (a > sizeof(tempbuf)) then a := sizeof(tempbuf);
\r 
 614       numread := myfdread(fdhandlein,tempbuf,a);
\r 
 615       if (numread=0) and (not mustrefreshfds) then begin
\r 
 616         {if i remember correctly numread=0 is caused by eof
\r 
 617         if this isn't dealt with then you get a cpu eating infinite loop
\r 
 618         however if onsessionconnected has called processmessages that could
\r 
 619         cause us to drop to here with an empty recvq and nothing left to read
\r 
 620         and we don't want that to cause the socket to close}
\r 
 623       end else if (numread=-1) then begin
\r 
 625           //sometimes on windows we get stale messages due to the inherent delays
\r 
 626           //in the windows message queue
\r 
 627           if WSAGetLastError = wsaewouldblock then begin
\r 
 633           internalclose({$ifdef mswindows}wsagetlasterror{$else}linuxerror{$endif});
\r 
 635       end else if numread > 0 then recvq.add(@tempbuf,numread);
\r 
 638     if recvq.size > 0 then begin
\r 
 639       if wsonoreceiveloop in componentoptions then eventcore.rmasterclr(fdhandlein); //fd_clr(fdhandlein,fdsrmaster);
\r 
 640       if assigned(ondataavailable) then ondataAvailable(self,0);
\r 
 641       if not (wsonoreceiveloop in componentoptions) then if recvq.size > 0 then
\r 
 642       tltask.create(self.doreceiveloop,self,0,0);
\r 
 644     //until (numread = 0) or (currentsocket.state<>wsconnected);
\r 
 645 {    debugout('inner loop complete');}
\r 
 649 procedure tlasio.flush;
\r 
 651 type fdset = tfdset;
\r 
 657   fd_set(fdhandleout,fds);
\r 
 658   while sendq.size>0 do begin
\r 
 659     select(fdhandleout+1,nil,@fds,nil,nil);
\r 
 660     if sendflush <= 0 then exit;
\r 
 664 procedure tlasio.dodatasent(wparam,lparam:longint);
\r 
 666   if assigned(ondatasent) then ondatasent(self,lparam);
\r 
 669 procedure tlasio.deletebuffereddata;
\r 
 671   sendq.del(maxlongint);
\r 
 674 procedure tlasio.sinkdata(sender:tobject;error:word);
\r 
 676   tlasio(sender).recvq.del(maxlongint);
\r 
 679 {$ifndef mswindows}
\r 
 680   procedure tltimer.resettimes;
\r 
 682     gettimemonotonic(nextts);
\r 
 683     {if not initialevent then} tv_add(nextts,interval);
\r 
 687 {procedure tltimer.setinitialevent(newvalue : boolean);
\r 
 689   if newvalue <> finitialevent then begin
\r 
 690     finitialevent := newvalue;
\r 
 691     if assigned(timerwrapperinterface) then begin
\r 
 692       timerwrapperinterface.setinitialevent(wrappedtimer,newvalue);
\r 
 699 procedure tltimer.setontimer(newvalue:tnotifyevent);
\r 
 701   if @newvalue <> @fontimer then begin
\r 
 702     fontimer := newvalue;
\r 
 703     if assigned(timerwrapperinterface) then begin
\r 
 704       timerwrapperinterface.setontimer(wrappedtimer,newvalue);
\r 
 713 procedure tltimer.setenabled(newvalue : boolean);
\r 
 715   if newvalue <> fenabled then begin
\r 
 716     fenabled := newvalue;
\r 
 717     if assigned(timerwrapperinterface) then begin
\r 
 718       timerwrapperinterface.setenabled(wrappedtimer,newvalue);
\r 
 721         raise exception.create('non wrapper timers are not permitted on windows');
\r 
 729 procedure tltimer.setinterval(newvalue:integer);
\r 
 731   if newvalue <> finterval then begin
\r 
 732     finterval := newvalue;
\r 
 733     if assigned(timerwrapperinterface) then begin
\r 
 734       timerwrapperinterface.setinterval(wrappedtimer,newvalue);
\r 
 737         raise exception.create('non wrapper timers are not permitted on windows');
\r 
 749 constructor tltimer.create;
\r 
 751   inherited create(AOwner);
\r 
 752   if assigned(timerwrapperinterface) then begin
\r 
 753     wrappedtimer := timerwrapperinterface.createwrappedtimer;
\r 
 757     nexttimer := firsttimer;
\r 
 760     if assigned(nexttimer) then nexttimer.prevtimer := self;
\r 
 761     firsttimer := self;
\r 
 767 destructor tltimer.destroy;
\r 
 769   if assigned(timerwrapperinterface) then begin
\r 
 772     if prevtimer <> nil then begin
\r 
 773       prevtimer.nexttimer := nexttimer;
\r 
 775       firsttimer := nexttimer;
\r 
 777     if nexttimer <> nil then begin
\r 
 778       nexttimer.prevtimer := prevtimer;
\r 
 785 constructor tltask.create(ahandler:ttaskevent;aobj:tobject;awparam,alparam:longint);
\r 
 788   if assigned(onaddtask) then onaddtask(ahandler,aobj,awparam,alparam);
\r 
 789   handler   := ahandler;
\r 
 793   {nexttask  := firsttask;
\r 
 794   firsttask := self;}
\r 
 795   if assigned(lasttask) then begin
\r 
 796     lasttask.nexttask := self;
\r 
 801   //ahandler(wparam,lparam);
\r 
 804 procedure addtask(ahandler:ttaskevent;aobj:tobject;awparam,alparam:longint);
\r 
 807   tltask.create(ahandler,aobj,awparam,alparam);
\r 
 811   procedure prepsigpipe;{$ifndef ver1_0}inline;
\r 
 814     starthandlesignal(sigpipe);
\r 
 815     if not assigned(signalloopback) then begin
\r 
 816       signalloopback := tlloopback.create(nil);
\r 
 817       signalloopback.ondataAvailable := signalloopback.sinkdata;
\r 
 824 procedure processtasks;//inline;
\r 
 826   currenttask:tltask;
\r 
 829   while assigned(firsttask) do begin
\r 
 830     currenttask := firsttask;
\r 
 831     firsttask := firsttask.nexttask;
\r 
 832     if not assigned(firsttask) then lasttask := nil;
\r 
 834     if assigned(currenttask.handler) then currenttask.handler(currenttask.wparam,currenttask.lparam);
\r 
 837   currenttask := nil;
\r 
 843 procedure disconnecttasks(aobj:tobject);
\r 
 845   currenttasklocal : tltask ;
\r 
 848   currenttasklocal := firsttask; //main list of tasks
\r 
 850   // note i don't bother to destroy the links here as that will happen when
\r 
 851   // the list of tasks is processed anyway
\r 
 852   while assigned(currenttasklocal) do begin
\r 
 853     if currenttasklocal.obj = aobj then begin
\r 
 854       currenttasklocal.obj := nil;
\r 
 855       currenttasklocal.handler := nil;
\r 
 857     currenttasklocal := currenttasklocal.nexttask;
\r 
 862 procedure processmessages;
\r 
 864   eventcore.processmessages;
\r 
 866 procedure messageloop;
\r 
 868   eventcore.messageloop;
\r 
 871 procedure exitmessageloop;
\r 
 873   eventcore.exitmessageloop;
\r 
 876 function tlasio.RealSend(Data : Pointer; Len : Integer) : Integer;
\r 
 878   result := myfdwrite(fdhandleout,data^,len);
\r 
 879   if (result > 0) and assigned(onsenddata) then onsenddata(self,result);
\r 
 880   eventcore.wmasterset(fdhandleout);
\r 
 882 {$ifndef mswindows}
\r 
 883   procedure tlasio.myfdclose(fd : integer);
\r 
 887   function tlasio.myfdwrite(fd: LongInt;const buf;size: LongInt):LongInt;
\r 
 889     result := fdwrite(fd,buf,size);
\r 
 892   function tlasio.myfdread(fd: LongInt;var buf;size: LongInt):LongInt;
\r 
 894     result := fdread(fd,buf,size);
\r 
 906     signalloopback := nil;
\r