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,
\r 
  33     classes,pgtypes,bfifo;
\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 
  44     packetbasesize = 1432;
\r 
  45     receivebufsize=packetbasesize*8;
\r 
  48     absoloutemaxs:integer=0;
\r 
  52       sigset= array[0..31] of longint;
\r 
  55     ESocketException   = class(Exception);
\r 
  56     TBgExceptionEvent  = procedure (Sender : TObject;
\r 
  58                                   var CanClose : Boolean) of object;
\r 
  60     // note : tsocketstate is defined in the same way as it is in François PIETTE's twsocket
\r 
  61     // however tlsocket currently only uses wsClosed wsConnecting wsconnected and wsListening
\r 
  62     TSocketState       = (wsInvalidState,
\r 
  64                         wsConnecting, wsConnected,
\r 
  65                         wsAccepting,  wsListening,
\r 
  68     TWSocketOption       = (wsoNoReceiveLoop, wsoTcpNoDelay);
\r 
  69     TWSocketOptions      = set of TWSocketOption;
\r 
  71     TSocketevent     = procedure(Sender: TObject; Error: word) of object;
\r 
  72     //Tdataavailevent  = procedure(data : string);
\r 
  73     TSendData          = procedure (Sender: TObject; BytesSent: Integer) of object;
\r 
  75     tlcomponent = class(tcomponent)
\r 
  78       procedure release; virtual;
\r 
  79       destructor destroy; override;
\r 
  82     tlasio = class(tlcomponent)
\r 
  84       state              : tsocketstate      ;
\r 
  85       ComponentOptions   : TWSocketOptions;
\r 
  86       fdhandlein         : Longint           ;  {file discriptor}
\r 
  87       fdhandleout        : Longint           ;  {file discriptor}
\r 
  89       onsessionclosed    : tsocketevent      ;
\r 
  90       ondataAvailable    : tsocketevent      ;
\r 
  91       onsessionAvailable : tsocketevent      ;
\r 
  93       onsessionconnected : tsocketevent      ;
\r 
  94       onsenddata         : tsenddata      ;
\r 
  95       ondatasent         : tsocketevent      ;
\r 
  96       //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       function receivestr:string; virtual;
\r 
 113       procedure internalclose(error:word); virtual;
\r 
 114       constructor Create(AOwner: TComponent); override;
\r 
 116       destructor destroy; override;
\r 
 117       procedure fdcleanup;
\r 
 118       procedure HandleBackGroundException(E: Exception);
\r 
 119       procedure handlefdtrigger(readtrigger,writetrigger:boolean); virtual;
\r 
 120       procedure dup(invalue:longint);
\r 
 122       function sendflush : integer;
\r 
 123       procedure sendstr(const str : string);virtual;
\r 
 124       procedure putstringinsendbuffer(const newstring : string);
\r 
 125       function send(data:pointer;len:integer):integer;virtual;
\r 
 126       procedure putdatainsendbuffer(data:pointer;len:integer); virtual;
\r 
 127       procedure deletebuffereddata;
\r 
 129       //procedure messageloop;
\r 
 130       function Receive(Buf:Pointer;BufSize:integer):integer; virtual;
\r 
 131       procedure flush;virtual;
\r 
 132       procedure dodatasent(wparam,lparam:longint);
\r 
 133       procedure doreceiveloop(wparam,lparam:longint);
\r 
 134       procedure sinkdata(sender:tobject;error:word);
\r 
 136       procedure release; override; {test -beware}
\r 
 138       function RealSend(Data : Pointer; Len : Integer) : Integer; //added for bewarehttpd
\r 
 140       procedure myfdclose(fd : integer); virtual;{$ifdef win32}abstract;{$endif}
\r 
 141       function myfdwrite(fd: LongInt;const buf;size: LongInt):LongInt; virtual;{$ifdef win32}abstract;{$endif}
\r 
 142       function myfdread(fd: LongInt;var buf;size: LongInt):LongInt; virtual;{$ifdef win32}abstract;{$endif}
\r 
 144       procedure dupnowatch(invalue:longint);
\r 
 146     ttimerwrapperinterface=class(tlcomponent)
\r 
 148       function createwrappedtimer : tobject;virtual;abstract;
\r 
 149 //      procedure setinitialevent(wrappedtimer : tobject;newvalue : boolean);virtual;abstract;
\r 
 150       procedure setontimer(wrappedtimer : tobject;newvalue:tnotifyevent);virtual;abstract;
\r 
 151       procedure setenabled(wrappedtimer : tobject;newvalue : boolean);virtual;abstract;
\r 
 152       procedure setinterval(wrappedtimer : tobject;newvalue : integer);virtual;abstract;
\r 
 156     timerwrapperinterface : ttimerwrapperinterface;
\r 
 164     tltimer=class(tlcomponent)
\r 
 168       wrappedtimer : tobject;
\r 
 171 //      finitialevent       : boolean           ;
\r 
 172       fontimer            : tnotifyevent      ;
\r 
 173       fenabled            : boolean           ;
\r 
 174       finterval           : integer          ; {miliseconds, default 1000}
\r 
 176         procedure resettimes;
\r 
 178 //      procedure setinitialevent(newvalue : boolean);
\r 
 179       procedure setontimer(newvalue:tnotifyevent);
\r 
 180       procedure setenabled(newvalue : boolean);
\r 
 181       procedure setinterval(newvalue : integer);
\r 
 183       //making theese public for now, this code should probablly be restructured later though
\r 
 184       prevtimer          : tltimer           ;
\r 
 185       nexttimer          : tltimer           ;
\r 
 186       nextts             : ttimeval          ;
\r 
 188       constructor create(aowner:tcomponent);override;
\r 
 189       destructor destroy;override;
\r 
 190 //      property initialevent : boolean read finitialevent write setinitialevent;
\r 
 191       property ontimer : tnotifyevent read fontimer write setontimer;
\r 
 192       property enabled : boolean read fenabled write setenabled;
\r 
 193       property interval : integer read finterval write setinterval;
\r 
 197     ttaskevent=procedure(wparam,lparam:longint) of object;
\r 
 199     tltask=class(tobject)
\r 
 201       handler  : ttaskevent;
\r 
 206       constructor create(ahandler:ttaskevent;aobj:tobject;awparam,alparam:longint);
\r 
 213       procedure processmessages; virtual;abstract;
\r 
 214       procedure messageloop; virtual;abstract;
\r 
 215       procedure exitmessageloop; virtual;abstract;
\r 
 216       procedure setfdreverse(fd : integer;reverseto : tlasio);virtual;abstract;
\r 
 217       procedure rmasterset(fd : integer;islistensocket : boolean);  virtual;abstract;
\r 
 218       procedure rmasterclr(fd: integer);  virtual;abstract;
\r 
 219       procedure wmasterset(fd : integer); virtual;abstract;
\r 
 220       procedure wmasterclr(fd: integer);  virtual;abstract;
\r 
 223     eventcore : teventcore;
\r 
 225 procedure processmessages;
\r 
 226 procedure messageloop;
\r 
 227 procedure exitmessageloop;
\r 
 230   firstasin                             : tlasio     ;
\r 
 231   firsttimer                            : tltimer    ;
\r 
 232   firsttask  , lasttask   , currenttask : tltask     ;
\r 
 234   numread                               : integer    ;
\r 
 235   mustrefreshfds                        : boolean    ;
\r 
 236 {  lcoretestcount:integer;}
\r 
 238   asinreleaseflag:boolean;
\r 
 241 procedure disconnecttasks(aobj:tobject);
\r 
 242 procedure addtask(ahandler:ttaskevent;aobj:tobject;awparam,alparam:longint);
\r 
 244   tonaddtask = procedure(ahandler:ttaskevent;aobj:tobject;awparam,alparam:longint);
\r 
 246   onaddtask : tonaddtask;
\r 
 249 procedure sleep(i:integer);
\r 
 251   procedure prepsigpipe;{$ifndef ver1_0}inline;{$endif}
\r 
 257   uses {sockets,}lloopback,lsignal;
\r 
 260   uses windows,winsock;
\r 
 263   {$include unixstuff.inc}
\r 
 265 {$include ltimevalstuff.inc}
\r 
 268 {!!! added sleep call -beware}
\r 
 269 procedure sleep(i:integer);
\r 
 276     tv.tv_sec := i div 1000;
\r 
 277     tv.tv_usec := (i mod 1000) * 1000;
\r 
 278     select(0,nil,nil,nil,@tv);
\r 
 282 destructor tlcomponent.destroy;
\r 
 284   disconnecttasks(self);
\r 
 291 procedure tlcomponent.release;
\r 
 296 procedure tlasio.release;
\r 
 298   asinreleaseflag := true;
\r 
 302 procedure tlasio.doreceiveloop;
\r 
 304   if recvq.size = 0 then exit;
\r 
 305   if assigned(ondataavailable) then ondataavailable(self,0);
\r 
 306   if not (wsonoreceiveloop in componentoptions) then
\r 
 307   if recvq.size > 0 then tltask.create(self.doreceiveloop,self,0,0);
\r 
 310 function tlasio.receivestr;
\r 
 312   setlength(result,recvq.size);
\r 
 313   receive(@result[1],length(result));
\r 
 316 function tlasio.receive(Buf:Pointer;BufSize:integer):integer;
\r 
 322   if recvq.size < i then i := recvq.size;
\r 
 324   while (a < i) do begin
\r 
 325     b := recvq.get(p,i-a);
\r 
 327     inc(taddrint(buf),b);
\r 
 332   if wsonoreceiveloop in componentoptions then begin
\r 
 333     if recvq.size = 0 then eventcore.rmasterset(fdhandlein,false);
\r 
 337 constructor tlasio.create;
\r 
 339   inherited create(AOwner);
\r 
 340   if not assigned(eventcore) then raise exception.create('no event core');
\r 
 341   sendq := tfifo.create;
\r 
 342   recvq := tfifo.create;
\r 
 346   nextasin := firstasin;
\r 
 348   if assigned(nextasin) then nextasin.prevasin := self;
\r 
 354 destructor tlasio.destroy;
\r 
 356   destroying := true;
\r 
 357   if state <> wsclosed then close;
\r 
 358   if prevasin <> nil then begin
\r 
 359     prevasin.nextasin := nextasin;
\r 
 361     firstasin := nextasin;
\r 
 363   if nextasin <> nil then begin
\r 
 364     nextasin.prevasin := prevasin;
\r 
 371 procedure tlasio.close;
\r 
 376 procedure tlasio.abort;
\r 
 381 procedure tlasio.fdcleanup;
\r 
 383   if fdhandlein <> -1 then begin
\r 
 384     eventcore.rmasterclr(fdhandlein); //fd_clr(fdhandlein,fdsrmaster)
\r 
 386   if fdhandleout <> -1 then begin
\r 
 387     eventcore.wmasterclr(fdhandleout);//fd_clr(fdhandleout,fdswmaster)
\r 
 389   if fdhandlein=fdhandleout then begin
\r 
 390     if fdhandlein <> -1 then begin
\r 
 391       myfdclose(fdhandlein);
\r 
 394     if fdhandlein <> -1 then begin
\r 
 395       myfdclose(fdhandlein);
\r 
 397     if fdhandleout <> -1 then begin
\r 
 398       myfdclose(fdhandleout);
\r 
 405 procedure tlasio.internalclose(error:word);
\r 
 407   if (state<>wsclosed) and (state<>wsinvalidstate) then begin
\r 
 408     if (fdhandlein < 0) or (fdhandleout < 0) then raise exception.create('internalclose called with invalid fd handles');
\r 
 409     eventcore.rmasterclr(fdhandlein);//fd_clr(fdhandlein,fdsrmaster);
\r 
 410     eventcore.wmasterclr(fdhandleout);//fd_clr(fdhandleout,fdswmaster);
\r 
 412     if closehandles then begin
\r 
 414         //anyone remember why this is here? --plugwash
\r 
 415         fcntl(fdhandlein,F_SETFL,0);
\r 
 417       myfdclose(fdhandlein);
\r 
 418       if fdhandleout <> fdhandlein then begin
\r 
 420           fcntl(fdhandleout,F_SETFL,0);
\r 
 422         myfdclose(fdhandleout);
\r 
 424       eventcore.setfdreverse(fdhandlein,nil);
\r 
 425       eventcore.setfdreverse(fdhandleout,nil);
\r 
 432     if assigned(onsessionclosed) then if not destroying then onsessionclosed(self,error);
\r 
 434   if assigned(sendq) then sendq.del(maxlongint);
\r 
 438 {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
\r 
 439 { All exceptions *MUST* be handled. If an exception is not handled, the     }
\r 
 440 { application will most likely be shut down !                               }
\r 
 441 procedure tlasio.HandleBackGroundException(E: Exception);
\r 
 443   CanAbort : Boolean;
\r 
 446   { First call the error event handler, if any }
\r 
 447   if Assigned(OnBgException) then begin
\r 
 449       OnBgException(Self, E, CanAbort);
\r 
 453   { Then abort the socket }
\r 
 454   if CanAbort then begin
\r 
 462 procedure tlasio.sendstr(const str : string);
\r 
 464   putstringinsendbuffer(str);
\r 
 468 procedure tlasio.putstringinsendbuffer(const newstring : string);
\r 
 470   if newstring <> '' then putdatainsendbuffer(@newstring[1],length(newstring));
\r 
 473 function tlasio.send(data:pointer;len:integer):integer;
\r 
 475   if state <> wsconnected then begin
\r 
 479   if len < 0 then len := 0;
\r 
 481   putdatainsendbuffer(data,len);
\r 
 486 procedure tlasio.putdatainsendbuffer(data:pointer;len:integer);
\r 
 488   sendq.add(data,len);
\r 
 491 function tlasio.sendflush : integer;
\r 
 495 //  fdstestr : fdset;
\r 
 496 //  fdstestw : fdset;
\r 
 498   if state <> wsconnected then exit;
\r 
 500   lensent := sendq.get(data,packetbasesize*2);
\r 
 501   if assigned(data) then result := myfdwrite(fdhandleout,data^,lensent) else result := 0;
\r 
 503   if result = -1 then lensent := 0 else lensent := result;
\r 
 505   //sendq := copy(sendq,lensent+1,length(sendq)-lensent);
\r 
 506   sendq.del(lensent);
\r 
 508   //fd_clr(fdhandleout,fdsw); // this prevents the socket being closed by a write
\r 
 509                             // that sends nothing because a previous socket has
\r 
 510                             // slready flushed this socket when the message loop
\r 
 512 //  if sendq.size > 0 then begin
\r 
 513     eventcore.wmasterset(fdhandleout);//fd_set(fdhandleout,fdswmaster);
\r 
 515 //    wmasterclr(fdhandleout);//fd_clr(fdhandleout,fdswmaster);
\r 
 517   if result > 0 then begin
\r 
 518     if assigned(onsenddata) then onsenddata(self,result);
\r 
 519 //    if sendq.size=0 then if assigned(ondatasent) then begin
\r 
 520 //      tltask.create(self.dodatasent,self,0,0);
\r 
 521 //      //begin test code
\r 
 522 //      fd_zero(fdstestr);
\r 
 523 //      fd_zero(fdstestw);
\r 
 524 //      fd_set(fdhandlein,fdstestr);
\r 
 525 //      fd_set(fdhandleout,fdstestw);
\r 
 526 //      select(maxs,@fdstestr,@fdstestw,nil,0);
\r 
 527 //      writeln(fd_isset(fdhandlein,fdstestr),' ',fd_isset(fdhandleout,fdstestw));
\r 
 531     writtenthiscycle := true;
\r 
 535 procedure tlasio.dupnowatch(invalue:longint);
\r 
 537   {  debugout('invalue='+inttostr(invalue));}
\r 
 539   if state<> wsclosed then close;
\r 
 540   fdhandlein := invalue;
\r 
 541   fdhandleout := invalue;
\r 
 542   eventcore.setfdreverse(fdhandlein,self);
\r 
 544     fcntl(fdhandlein,F_SETFL,OPEN_NONBLOCK);
\r 
 546   state := wsconnected;
\r 
 551 procedure tlasio.dup(invalue:longint);
\r 
 553   dupnowatch(invalue);
\r 
 554   eventcore.rmasterset(fdhandlein,false);//fd_set(fdhandlein,fdsrmaster);
\r 
 555   eventcore.wmasterclr(fdhandleout);//fd_clr(fdhandleout,fdswmaster);
\r 
 559 procedure tlasio.handlefdtrigger(readtrigger,writetrigger:boolean);
\r 
 561   sendflushresult : integer;
\r 
 562   tempbuf:array[0..receivebufsize-1] of byte;
\r 
 565   if (state=wsconnected) and writetrigger then begin
\r 
 566     //writeln('write trigger');
\r 
 568     if (sendq.size >0) then begin
\r 
 570       sendflushresult := sendflush;
\r 
 571       if (sendflushresult <= 0) and (not writtenthiscycle) then begin
\r 
 572         if sendflushresult=0 then begin // linuxerror := 0;
\r 
 577           if getlasterror=WSAEWOULDBLOCK then begin
\r 
 578             //the asynchronous nature of windows messages means we sometimes
\r 
 579             //get here with the buffer full
\r 
 580             //so do nothing in that case
\r 
 584             internalclose({$ifdef win32}getlasterror{$else}linuxerror{$endif});
\r 
 590       //everything is sent fire off ondatasent event
\r 
 591       if fdhandleout >= 0 then eventcore.wmasterclr(fdhandleout);//fd_clr(fdhandleout,fdswmaster);
\r 
 592       if assigned(ondatasent) then tltask.create(self.dodatasent,self,0,0);
\r 
 594     if assigned(onfdwrite) then onfdwrite(self,0);
\r 
 596   writtenthiscycle := false;
\r 
 597   if (state =wsconnected) and readtrigger then begin
\r 
 598     if recvq.size=0 then begin
\r 
 600       if (a <= 0) or (a > sizeof(tempbuf)) then a := sizeof(tempbuf);
\r 
 601       numread := myfdread(fdhandlein,tempbuf,a);
\r 
 602       if (numread=0) and (not mustrefreshfds) then begin
\r 
 603         {if i remember correctly numread=0 is caused by eof
\r 
 604         if this isn't dealt with then you get a cpu eating infinite loop
\r 
 605         however if onsessionconencted has called processmessages that could
\r 
 606         cause us to drop to here with an empty recvq and nothing left to read
\r 
 607         and we don't want that to cause the socket to close}
\r 
 610       end else if (numread=-1) then begin
\r 
 612           //sometimes on windows we get stale messages due to the inherent delays
\r 
 613           //in the windows message queue
\r 
 614           if WSAGetLastError = wsaewouldblock then begin
\r 
 620           internalclose({$ifdef win32}wsagetlasterror{$else}linuxerror{$endif});
\r 
 622       end else if numread > 0 then recvq.add(@tempbuf,numread);
\r 
 625     if recvq.size > 0 then begin
\r 
 626       if wsonoreceiveloop in componentoptions then eventcore.rmasterclr(fdhandlein); //fd_clr(fdhandlein,fdsrmaster);
\r 
 627       if assigned(ondataavailable) then ondataAvailable(self,0);
\r 
 628       if not (wsonoreceiveloop in componentoptions) then if recvq.size > 0 then
\r 
 629       tltask.create(self.doreceiveloop,self,0,0);
\r 
 631     //until (numread = 0) or (currentsocket.state<>wsconnected);
\r 
 632 {    debugout('inner loop complete');}
\r 
 636 procedure tlasio.flush;
\r 
 638 type fdset = tfdset;
\r 
 644   fd_set(fdhandleout,fds);
\r 
 645   while sendq.size>0 do begin
\r 
 646     select(fdhandleout+1,nil,@fds,nil,nil);
\r 
 647     if sendflush <= 0 then exit;
\r 
 651 procedure tlasio.dodatasent(wparam,lparam:longint);
\r 
 653   if assigned(ondatasent) then ondatasent(self,lparam);
\r 
 656 procedure tlasio.deletebuffereddata;
\r 
 658   sendq.del(maxlongint);
\r 
 661 procedure tlasio.sinkdata(sender:tobject;error:word);
\r 
 663   tlasio(sender).recvq.del(maxlongint);
\r 
 667   procedure tltimer.resettimes;
\r 
 669     gettimeofday(nextts);
\r 
 670     {if not initialevent then} tv_add(nextts,interval);
\r 
 674 {procedure tltimer.setinitialevent(newvalue : boolean);
\r 
 676   if newvalue <> finitialevent then begin
\r 
 677     finitialevent := newvalue;
\r 
 678     if assigned(timerwrapperinterface) then begin
\r 
 679       timerwrapperinterface.setinitialevent(wrappedtimer,newvalue);
\r 
 686 procedure tltimer.setontimer(newvalue:tnotifyevent);
\r 
 688   if @newvalue <> @fontimer then begin
\r 
 689     fontimer := newvalue;
\r 
 690     if assigned(timerwrapperinterface) then begin
\r 
 691       timerwrapperinterface.setontimer(wrappedtimer,newvalue);
\r 
 700 procedure tltimer.setenabled(newvalue : boolean);
\r 
 702   if newvalue <> fenabled then begin
\r 
 703     fenabled := newvalue;
\r 
 704     if assigned(timerwrapperinterface) then begin
\r 
 705       timerwrapperinterface.setenabled(wrappedtimer,newvalue);
\r 
 708         raise exception.create('non wrapper timers are not permitted on windows');
\r 
 716 procedure tltimer.setinterval(newvalue:integer);
\r 
 718   if newvalue <> finterval then begin
\r 
 719     finterval := newvalue;
\r 
 720     if assigned(timerwrapperinterface) then begin
\r 
 721       timerwrapperinterface.setinterval(wrappedtimer,newvalue);
\r 
 724         raise exception.create('non wrapper timers are not permitted on windows');
\r 
 736 constructor tltimer.create;
\r 
 738   inherited create(AOwner);
\r 
 739   if assigned(timerwrapperinterface) then begin
\r 
 740     wrappedtimer := timerwrapperinterface.createwrappedtimer;
\r 
 744     nexttimer := firsttimer;
\r 
 747     if assigned(nexttimer) then nexttimer.prevtimer := self;
\r 
 748     firsttimer := self;
\r 
 756 destructor tltimer.destroy;
\r 
 758   if assigned(timerwrapperinterface) then begin
\r 
 761     if prevtimer <> nil then begin
\r 
 762       prevtimer.nexttimer := nexttimer;
\r 
 764       firsttimer := nexttimer;
\r 
 766     if nexttimer <> nil then begin
\r 
 767       nexttimer.prevtimer := prevtimer;
\r 
 774 constructor tltask.create(ahandler:ttaskevent;aobj:tobject;awparam,alparam:longint);
\r 
 777   if assigned(onaddtask) then onaddtask(ahandler,aobj,awparam,alparam);
\r 
 778   handler   := ahandler;
\r 
 782   {nexttask  := firsttask;
\r 
 783   firsttask := self;}
\r 
 784   if assigned(lasttask) then begin
\r 
 785     lasttask.nexttask := self;
\r 
 790   //ahandler(wparam,lparam);
\r 
 793 procedure addtask(ahandler:ttaskevent;aobj:tobject;awparam,alparam:longint);
\r 
 796   tltask.create(ahandler,aobj,awparam,alparam);
\r 
 800   procedure prepsigpipe;{$ifndef ver1_0}inline;
\r 
 803     starthandlesignal(sigpipe);
\r 
 804     if not assigned(signalloopback) then begin
\r 
 805       signalloopback := tlloopback.create(nil);
\r 
 806       signalloopback.ondataAvailable := signalloopback.sinkdata;
\r 
 813 procedure processtasks;//inline;
\r 
 815   temptask                : tltask   ;
\r 
 819   if not assigned(currenttask) then begin
\r 
 820     currenttask := firsttask;
\r 
 824   while assigned(currenttask) do begin
\r 
 826     if assigned(currenttask.handler) then currenttask.handler(currenttask.wparam,currenttask.lparam);
\r 
 827     if assigned(currenttask) then begin
\r 
 828       temptask := currenttask;
\r 
 829       currenttask := currenttask.nexttask;
\r 
 832     //writeln('processed a task');
\r 
 840 procedure disconnecttasks(aobj:tobject);
\r 
 842   currenttasklocal : tltask ;
\r 
 845   for counter := 0 to 1 do begin
\r 
 846     if counter = 0 then begin
\r 
 847       currenttasklocal := firsttask; //main list of tasks
\r 
 849       currenttasklocal := currenttask; //needed in case called from a task
\r 
 851     // note i don't bother to sestroy the links here as that will happen when
\r 
 852     // the list of tasks is processed anyway
\r 
 853     while assigned(currenttasklocal) do begin
\r 
 854       if currenttasklocal.obj = aobj then begin
\r 
 855         currenttasklocal.obj := nil;
\r 
 856         currenttasklocal.handler := nil;
\r 
 858       currenttasklocal := currenttasklocal.nexttask;
\r 
 864 procedure processmessages;
\r 
 866   eventcore.processmessages;
\r 
 868 procedure messageloop;
\r 
 870   eventcore.messageloop;
\r 
 873 procedure exitmessageloop;
\r 
 875   eventcore.exitmessageloop;
\r 
 878 function tlasio.RealSend(Data : Pointer; Len : Integer) : Integer;
\r 
 880   result := myfdwrite(fdhandleout,data^,len);
\r 
 881   if (result > 0) and assigned(onsenddata) then onsenddata(self,result);
\r 
 882   eventcore.wmasterset(fdhandleout);
\r 
 885   procedure tlasio.myfdclose(fd : integer);
\r 
 889   function tlasio.myfdwrite(fd: LongInt;const buf;size: LongInt):LongInt;
\r 
 891     result := fdwrite(fd,buf,size);
\r 
 894   function tlasio.myfdread(fd: LongInt;var buf;size: LongInt):LongInt;
\r 
 896     result := fdread(fd,buf,size);
\r 
 909     signalloopback := nil;
\r