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 
  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 
  77       procedure releasetaskhandler(wparam,lparam:longint);
\r 
  79       procedure release; virtual;
\r 
  80       destructor destroy; override;
\r 
  83     tlasio = class(tlcomponent)
\r 
  85       state              : tsocketstate      ;
\r 
  86       ComponentOptions   : TWSocketOptions;
\r 
  87       fdhandlein         : Longint           ;  {file discriptor}
\r 
  88       fdhandleout        : Longint           ;  {file discriptor}
\r 
  90       onsessionclosed    : tsocketevent      ;
\r 
  91       ondataAvailable    : tsocketevent      ;
\r 
  92       onsessionAvailable : tsocketevent      ;
\r 
  94       onsessionconnected : tsocketevent      ;
\r 
  95       onsenddata         : tsenddata      ;
\r 
  96       ondatasent         : tsocketevent      ;
\r 
  97       //connected          : boolean         ;
\r 
 100       OnBgException      : TBgExceptionEvent ;
\r 
 101       //connectread        : boolean           ;
\r 
 103       closehandles       : boolean           ;
\r 
 104       writtenthiscycle   : boolean           ;
\r 
 105       onfdwrite           : procedure (Sender: TObject; Error: word) of object; //added for bewarehttpd
\r 
 107       destroying:boolean;
\r 
 108       recvbufsize:integer;
\r 
 109       function receivestr:tbufferstring; virtual;
\r 
 112       procedure internalclose(error:word); virtual;
\r 
 113       constructor Create(AOwner: TComponent); override;
\r 
 115       destructor destroy; override;
\r 
 116       procedure fdcleanup;
\r 
 117       procedure HandleBackGroundException(E: Exception);
\r 
 118       procedure handlefdtrigger(readtrigger,writetrigger:boolean); virtual;
\r 
 119       procedure dup(invalue:longint);
\r 
 121       function sendflush : integer;
\r 
 122       procedure sendstr(const str : tbufferstring);virtual;
\r 
 123       procedure putstringinsendbuffer(const newstring : tbufferstring);
\r 
 124       function send(data:pointer;len:integer):integer;virtual;
\r 
 125       procedure putdatainsendbuffer(data:pointer;len:integer); virtual;
\r 
 126       procedure deletebuffereddata;
\r 
 128       //procedure messageloop;
\r 
 129       function Receive(Buf:Pointer;BufSize:integer):integer; virtual;
\r 
 130       procedure flush;virtual;
\r 
 131       procedure dodatasent(wparam,lparam:longint);
\r 
 132       procedure doreceiveloop(wparam,lparam:longint);
\r 
 133       procedure sinkdata(sender:tobject;error:word);
\r 
 135       procedure release; override; {test -beware}
\r 
 137       function RealSend(Data : Pointer; Len : Integer) : Integer; //added for bewarehttpd
\r 
 139       procedure myfdclose(fd : integer); virtual;{$ifdef win32}abstract;{$endif}
\r 
 140       function myfdwrite(fd: LongInt;const buf;size: LongInt):LongInt; virtual;{$ifdef win32}abstract;{$endif}
\r 
 141       function myfdread(fd: LongInt;var buf;size: LongInt):LongInt; virtual;{$ifdef win32}abstract;{$endif}
\r 
 143       procedure dupnowatch(invalue:longint);
\r 
 145     ttimerwrapperinterface=class(tlcomponent)
\r 
 147       function createwrappedtimer : tobject;virtual;abstract;
\r 
 148 //      procedure setinitialevent(wrappedtimer : tobject;newvalue : boolean);virtual;abstract;
\r 
 149       procedure setontimer(wrappedtimer : tobject;newvalue:tnotifyevent);virtual;abstract;
\r 
 150       procedure setenabled(wrappedtimer : tobject;newvalue : boolean);virtual;abstract;
\r 
 151       procedure setinterval(wrappedtimer : tobject;newvalue : integer);virtual;abstract;
\r 
 155     timerwrapperinterface : ttimerwrapperinterface;
\r 
 157     tltimer=class(tlcomponent)
\r 
 161       wrappedtimer : tobject;
\r 
 164 //      finitialevent       : boolean           ;
\r 
 165       fontimer            : tnotifyevent      ;
\r 
 166       fenabled            : boolean           ;
\r 
 167       finterval           : integer          ; {miliseconds, default 1000}
\r 
 169         procedure resettimes;
\r 
 171 //      procedure setinitialevent(newvalue : boolean);
\r 
 172       procedure setontimer(newvalue:tnotifyevent);
\r 
 173       procedure setenabled(newvalue : boolean);
\r 
 174       procedure setinterval(newvalue : integer);
\r 
 176       //making theese public for now, this code should probablly be restructured later though
\r 
 177       prevtimer          : tltimer           ;
\r 
 178       nexttimer          : tltimer           ;
\r 
 179       nextts             : ttimeval          ;
\r 
 181       constructor create(aowner:tcomponent);override;
\r 
 182       destructor destroy;override;
\r 
 183 //      property initialevent : boolean read finitialevent write setinitialevent;
\r 
 184       property ontimer : tnotifyevent read fontimer write setontimer;
\r 
 185       property enabled : boolean read fenabled write setenabled;
\r 
 186       property interval : integer read finterval write setinterval;
\r 
 190     ttaskevent=procedure(wparam,lparam:longint) of object;
\r 
 192     tltask=class(tobject)
\r 
 194       handler  : ttaskevent;
\r 
 199       constructor create(ahandler:ttaskevent;aobj:tobject;awparam,alparam:longint);
\r 
 206       procedure processmessages; virtual;abstract;
\r 
 207       procedure messageloop; virtual;abstract;
\r 
 208       procedure exitmessageloop; virtual;abstract;
\r 
 209       procedure setfdreverse(fd : integer;reverseto : tlasio);virtual;abstract;
\r 
 210       procedure rmasterset(fd : integer;islistensocket : boolean);  virtual;abstract;
\r 
 211       procedure rmasterclr(fd: integer);  virtual;abstract;
\r 
 212       procedure wmasterset(fd : integer); virtual;abstract;
\r 
 213       procedure wmasterclr(fd: integer);  virtual;abstract;
\r 
 216     eventcore : teventcore;
\r 
 218 procedure processmessages;
\r 
 219 procedure messageloop;
\r 
 220 procedure exitmessageloop;
\r 
 223   firsttimer                            : tltimer    ;
\r 
 224   firsttask  , lasttask   , currenttask : tltask     ;
\r 
 226   numread                               : integer    ;
\r 
 227   mustrefreshfds                        : boolean    ;
\r 
 228 {  lcoretestcount:integer;}
\r 
 230   asinreleaseflag:boolean;
\r 
 233 procedure disconnecttasks(aobj:tobject);
\r 
 234 procedure addtask(ahandler:ttaskevent;aobj:tobject;awparam,alparam:longint);
\r 
 236   tonaddtask = procedure(ahandler:ttaskevent;aobj:tobject;awparam,alparam:longint);
\r 
 238   onaddtask : tonaddtask;
\r 
 241 procedure sleep(i:integer);
\r 
 243   procedure prepsigpipe;{$ifndef ver1_0}inline;{$endif}
\r 
 249   uses {sockets,}lloopback,lsignal;
\r 
 252   uses windows,winsock;
\r 
 255   {$include unixstuff.inc}
\r 
 259 {!!! added sleep call -beware}
\r 
 260 procedure sleep(i:integer);
\r 
 268   tv.tv_sec := i div 1000;
\r 
 269   tv.tv_usec := (i mod 1000) * 1000;
\r 
 270   select(0,nil,nil,nil,@tv);
\r 
 275 destructor tlcomponent.destroy;
\r 
 277   disconnecttasks(self);
\r 
 281 procedure tlcomponent.releasetaskhandler(wparam,lparam:longint);
\r 
 287 procedure tlcomponent.release;
\r 
 289   addtask(releasetaskhandler,self,0,0);
\r 
 292 procedure tlasio.release;
\r 
 294   asinreleaseflag := true;
\r 
 298 procedure tlasio.doreceiveloop;
\r 
 300   if recvq.size = 0 then exit;
\r 
 301   if assigned(ondataavailable) then ondataavailable(self,0);
\r 
 302   if not (wsonoreceiveloop in componentoptions) then
\r 
 303   if recvq.size > 0 then tltask.create(self.doreceiveloop,self,0,0);
\r 
 306 function tlasio.receivestr;
\r 
 308   setlength(result,recvq.size);
\r 
 309   receive(@result[1],length(result));
\r 
 312 function tlasio.receive(Buf:Pointer;BufSize:integer):integer;
\r 
 318   if recvq.size < i then i := recvq.size;
\r 
 320   while (a < i) do begin
\r 
 321     b := recvq.get(p,i-a);
\r 
 323     inc(taddrint(buf),b);
\r 
 328   if wsonoreceiveloop in componentoptions then begin
\r 
 329     if recvq.size = 0 then eventcore.rmasterset(fdhandlein,false);
\r 
 333 constructor tlasio.create;
\r 
 335   inherited create(AOwner);
\r 
 336   if not assigned(eventcore) then raise exception.create('no event core');
\r 
 337   sendq := tfifo.create;
\r 
 338   recvq := tfifo.create;
\r 
 344 destructor tlasio.destroy;
\r 
 346   destroying := true;
\r 
 347   if state <> wsclosed then close;
\r 
 353 procedure tlasio.close;
\r 
 358 procedure tlasio.abort;
\r 
 363 procedure tlasio.fdcleanup;
\r 
 365   if fdhandlein <> -1 then begin
\r 
 366     eventcore.rmasterclr(fdhandlein); //fd_clr(fdhandlein,fdsrmaster)
\r 
 368   if fdhandleout <> -1 then begin
\r 
 369     eventcore.wmasterclr(fdhandleout);//fd_clr(fdhandleout,fdswmaster)
\r 
 371   if fdhandlein=fdhandleout then begin
\r 
 372     if fdhandlein <> -1 then begin
\r 
 373       myfdclose(fdhandlein);
\r 
 376     if fdhandlein <> -1 then begin
\r 
 377       myfdclose(fdhandlein);
\r 
 379     if fdhandleout <> -1 then begin
\r 
 380       myfdclose(fdhandleout);
\r 
 387 procedure tlasio.internalclose(error:word);
\r 
 389   if (state<>wsclosed) and (state<>wsinvalidstate) then begin
\r 
 390     // -2 is a special indication that we should just exist silently
\r 
 391     // (used for connect failure handling when socket creation fails)
\r 
 392     if (fdhandlein = -2) and (fdhandleout = -2) then exit;
\r 
 393     if (fdhandlein < 0) or (fdhandleout < 0) then raise exception.create('internalclose called with invalid fd handles');
\r 
 394     eventcore.rmasterclr(fdhandlein);//fd_clr(fdhandlein,fdsrmaster);
\r 
 395     eventcore.wmasterclr(fdhandleout);//fd_clr(fdhandleout,fdswmaster);
\r 
 397     if closehandles then begin
\r 
 399         //anyone remember why this is here? --plugwash
\r 
 400         fcntl(fdhandlein,F_SETFL,0);
\r 
 402       myfdclose(fdhandlein);
\r 
 403       if fdhandleout <> fdhandlein then begin
\r 
 405           fcntl(fdhandleout,F_SETFL,0);
\r 
 407         myfdclose(fdhandleout);
\r 
 409       eventcore.setfdreverse(fdhandlein,nil);
\r 
 410       eventcore.setfdreverse(fdhandleout,nil);
\r 
 417     if assigned(onsessionclosed) then if not destroying then onsessionclosed(self,error);
\r 
 419   if assigned(sendq) then sendq.del(maxlongint);
\r 
 423 {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
\r 
 424 { All exceptions *MUST* be handled. If an exception is not handled, the     }
\r 
 425 { application will most likely be shut down !                               }
\r 
 426 procedure tlasio.HandleBackGroundException(E: Exception);
\r 
 428   CanAbort : Boolean;
\r 
 431   { First call the error event handler, if any }
\r 
 432   if Assigned(OnBgException) then begin
\r 
 434       OnBgException(Self, E, CanAbort);
\r 
 438   { Then abort the socket }
\r 
 439   if CanAbort then begin
\r 
 447 procedure tlasio.sendstr(const str : tbufferstring);
\r 
 449   putstringinsendbuffer(str);
\r 
 453 procedure tlasio.putstringinsendbuffer(const newstring : tbufferstring);
\r 
 455   if newstring <> '' then putdatainsendbuffer(@newstring[1],length(newstring));
\r 
 458 function tlasio.send(data:pointer;len:integer):integer;
\r 
 460   if state <> wsconnected then begin
\r 
 464   if len < 0 then len := 0;
\r 
 466   putdatainsendbuffer(data,len);
\r 
 471 procedure tlasio.putdatainsendbuffer(data:pointer;len:integer);
\r 
 473   sendq.add(data,len);
\r 
 476 function tlasio.sendflush : integer;
\r 
 480 //  fdstestr : fdset;
\r 
 481 //  fdstestw : fdset;
\r 
 483   if state <> wsconnected then begin
\r 
 488   lensent := sendq.get(data,packetbasesize*2);
\r 
 489   if assigned(data) then result := myfdwrite(fdhandleout,data^,lensent) else result := 0;
\r 
 491   if result = -1 then lensent := 0 else lensent := result;
\r 
 493   //sendq := copy(sendq,lensent+1,length(sendq)-lensent);
\r 
 494   sendq.del(lensent);
\r 
 496   //fd_clr(fdhandleout,fdsw); // this prevents the socket being closed by a write
\r 
 497                             // that sends nothing because a previous socket has
\r 
 498                             // slready flushed this socket when the message loop
\r 
 500 //  if sendq.size > 0 then begin
\r 
 501     eventcore.wmasterset(fdhandleout);//fd_set(fdhandleout,fdswmaster);
\r 
 503 //    wmasterclr(fdhandleout);//fd_clr(fdhandleout,fdswmaster);
\r 
 505   if result > 0 then begin
\r 
 506     if assigned(onsenddata) then onsenddata(self,result);
\r 
 507 //    if sendq.size=0 then if assigned(ondatasent) then begin
\r 
 508 //      tltask.create(self.dodatasent,self,0,0);
\r 
 509 //      //begin test code
\r 
 510 //      fd_zero(fdstestr);
\r 
 511 //      fd_zero(fdstestw);
\r 
 512 //      fd_set(fdhandlein,fdstestr);
\r 
 513 //      fd_set(fdhandleout,fdstestw);
\r 
 514 //      select(maxs,@fdstestr,@fdstestw,nil,0);
\r 
 515 //      writeln(fd_isset(fdhandlein,fdstestr),' ',fd_isset(fdhandleout,fdstestw));
\r 
 519     writtenthiscycle := true;
\r 
 523 procedure tlasio.dupnowatch(invalue:longint);
\r 
 525   {  debugout('invalue='+inttostr(invalue));}
\r 
 527   if state<> wsclosed then close;
\r 
 528   fdhandlein := invalue;
\r 
 529   fdhandleout := invalue;
\r 
 530   eventcore.setfdreverse(fdhandlein,self);
\r 
 532     fcntl(fdhandlein,F_SETFL,OPEN_NONBLOCK);
\r 
 534   state := wsconnected;
\r 
 539 procedure tlasio.dup(invalue:longint);
\r 
 541   dupnowatch(invalue);
\r 
 542   eventcore.rmasterset(fdhandlein,false);//fd_set(fdhandlein,fdsrmaster);
\r 
 543   eventcore.wmasterclr(fdhandleout);//fd_clr(fdhandleout,fdswmaster);
\r 
 547 procedure tlasio.handlefdtrigger(readtrigger,writetrigger:boolean);
\r 
 549   sendflushresult : integer;
\r 
 550   tempbuf:array[0..receivebufsize-1] of byte;
\r 
 553   if (state=wsconnected) and writetrigger then begin
\r 
 554     //writeln('write trigger');
\r 
 556     if (sendq.size >0) then begin
\r 
 558       sendflushresult := sendflush;
\r 
 559       if (sendflushresult <= 0) and (not writtenthiscycle) then begin
\r 
 560         if sendflushresult=0 then begin // linuxerror := 0;
\r 
 565           if getlasterror=WSAEWOULDBLOCK then begin
\r 
 566             //the asynchronous nature of windows messages means we sometimes
\r 
 567             //get here with the buffer full
\r 
 568             //so do nothing in that case
\r 
 572             internalclose({$ifdef win32}getlasterror{$else}linuxerror{$endif});
\r 
 578       //everything is sent fire off ondatasent event
\r 
 579       if fdhandleout >= 0 then eventcore.wmasterclr(fdhandleout);//fd_clr(fdhandleout,fdswmaster);
\r 
 580       if assigned(ondatasent) then tltask.create(self.dodatasent,self,0,0);
\r 
 582     if assigned(onfdwrite) then onfdwrite(self,0);
\r 
 584   writtenthiscycle := false;
\r 
 585   if (state =wsconnected) and readtrigger then begin
\r 
 586     if recvq.size=0 then begin
\r 
 588       if (a <= 0) or (a > sizeof(tempbuf)) then a := sizeof(tempbuf);
\r 
 589       numread := myfdread(fdhandlein,tempbuf,a);
\r 
 590       if (numread=0) and (not mustrefreshfds) then begin
\r 
 591         {if i remember correctly numread=0 is caused by eof
\r 
 592         if this isn't dealt with then you get a cpu eating infinite loop
\r 
 593         however if onsessionconencted has called processmessages that could
\r 
 594         cause us to drop to here with an empty recvq and nothing left to read
\r 
 595         and we don't want that to cause the socket to close}
\r 
 598       end else if (numread=-1) then begin
\r 
 600           //sometimes on windows we get stale messages due to the inherent delays
\r 
 601           //in the windows message queue
\r 
 602           if WSAGetLastError = wsaewouldblock then begin
\r 
 608           internalclose({$ifdef win32}wsagetlasterror{$else}linuxerror{$endif});
\r 
 610       end else if numread > 0 then recvq.add(@tempbuf,numread);
\r 
 613     if recvq.size > 0 then begin
\r 
 614       if wsonoreceiveloop in componentoptions then eventcore.rmasterclr(fdhandlein); //fd_clr(fdhandlein,fdsrmaster);
\r 
 615       if assigned(ondataavailable) then ondataAvailable(self,0);
\r 
 616       if not (wsonoreceiveloop in componentoptions) then if recvq.size > 0 then
\r 
 617       tltask.create(self.doreceiveloop,self,0,0);
\r 
 619     //until (numread = 0) or (currentsocket.state<>wsconnected);
\r 
 620 {    debugout('inner loop complete');}
\r 
 624 procedure tlasio.flush;
\r 
 626 type fdset = tfdset;
\r 
 632   fd_set(fdhandleout,fds);
\r 
 633   while sendq.size>0 do begin
\r 
 634     select(fdhandleout+1,nil,@fds,nil,nil);
\r 
 635     if sendflush <= 0 then exit;
\r 
 639 procedure tlasio.dodatasent(wparam,lparam:longint);
\r 
 641   if assigned(ondatasent) then ondatasent(self,lparam);
\r 
 644 procedure tlasio.deletebuffereddata;
\r 
 646   sendq.del(maxlongint);
\r 
 649 procedure tlasio.sinkdata(sender:tobject;error:word);
\r 
 651   tlasio(sender).recvq.del(maxlongint);
\r 
 655   procedure tltimer.resettimes;
\r 
 657     gettimeofday(nextts);
\r 
 658     {if not initialevent then} tv_add(nextts,interval);
\r 
 662 {procedure tltimer.setinitialevent(newvalue : boolean);
\r 
 664   if newvalue <> finitialevent then begin
\r 
 665     finitialevent := newvalue;
\r 
 666     if assigned(timerwrapperinterface) then begin
\r 
 667       timerwrapperinterface.setinitialevent(wrappedtimer,newvalue);
\r 
 674 procedure tltimer.setontimer(newvalue:tnotifyevent);
\r 
 676   if @newvalue <> @fontimer then begin
\r 
 677     fontimer := newvalue;
\r 
 678     if assigned(timerwrapperinterface) then begin
\r 
 679       timerwrapperinterface.setontimer(wrappedtimer,newvalue);
\r 
 688 procedure tltimer.setenabled(newvalue : boolean);
\r 
 690   if newvalue <> fenabled then begin
\r 
 691     fenabled := newvalue;
\r 
 692     if assigned(timerwrapperinterface) then begin
\r 
 693       timerwrapperinterface.setenabled(wrappedtimer,newvalue);
\r 
 696         raise exception.create('non wrapper timers are not permitted on windows');
\r 
 704 procedure tltimer.setinterval(newvalue:integer);
\r 
 706   if newvalue <> finterval then begin
\r 
 707     finterval := newvalue;
\r 
 708     if assigned(timerwrapperinterface) then begin
\r 
 709       timerwrapperinterface.setinterval(wrappedtimer,newvalue);
\r 
 712         raise exception.create('non wrapper timers are not permitted on windows');
\r 
 724 constructor tltimer.create;
\r 
 726   inherited create(AOwner);
\r 
 727   if assigned(timerwrapperinterface) then begin
\r 
 728     wrappedtimer := timerwrapperinterface.createwrappedtimer;
\r 
 732     nexttimer := firsttimer;
\r 
 735     if assigned(nexttimer) then nexttimer.prevtimer := self;
\r 
 736     firsttimer := self;
\r 
 742 destructor tltimer.destroy;
\r 
 744   if assigned(timerwrapperinterface) then begin
\r 
 747     if prevtimer <> nil then begin
\r 
 748       prevtimer.nexttimer := nexttimer;
\r 
 750       firsttimer := nexttimer;
\r 
 752     if nexttimer <> nil then begin
\r 
 753       nexttimer.prevtimer := prevtimer;
\r 
 760 constructor tltask.create(ahandler:ttaskevent;aobj:tobject;awparam,alparam:longint);
\r 
 763   if assigned(onaddtask) then onaddtask(ahandler,aobj,awparam,alparam);
\r 
 764   handler   := ahandler;
\r 
 768   {nexttask  := firsttask;
\r 
 769   firsttask := self;}
\r 
 770   if assigned(lasttask) then begin
\r 
 771     lasttask.nexttask := self;
\r 
 776   //ahandler(wparam,lparam);
\r 
 779 procedure addtask(ahandler:ttaskevent;aobj:tobject;awparam,alparam:longint);
\r 
 782   tltask.create(ahandler,aobj,awparam,alparam);
\r 
 786   procedure prepsigpipe;{$ifndef ver1_0}inline;
\r 
 789     starthandlesignal(sigpipe);
\r 
 790     if not assigned(signalloopback) then begin
\r 
 791       signalloopback := tlloopback.create(nil);
\r 
 792       signalloopback.ondataAvailable := signalloopback.sinkdata;
\r 
 799 procedure processtasks;//inline;
\r 
 801   temptask                : tltask   ;
\r 
 805   if not assigned(currenttask) then begin
\r 
 806     currenttask := firsttask;
\r 
 810   while assigned(currenttask) do begin
\r 
 812     if assigned(currenttask.handler) then currenttask.handler(currenttask.wparam,currenttask.lparam);
\r 
 813     if assigned(currenttask) then begin
\r 
 814       temptask := currenttask;
\r 
 815       currenttask := currenttask.nexttask;
\r 
 818     //writeln('processed a task');
\r 
 826 procedure disconnecttasks(aobj:tobject);
\r 
 828   currenttasklocal : tltask ;
\r 
 831   for counter := 0 to 1 do begin
\r 
 832     if counter = 0 then begin
\r 
 833       currenttasklocal := firsttask; //main list of tasks
\r 
 835       currenttasklocal := currenttask; //needed in case called from a task
\r 
 837     // note i don't bother to sestroy the links here as that will happen when
\r 
 838     // the list of tasks is processed anyway
\r 
 839     while assigned(currenttasklocal) do begin
\r 
 840       if currenttasklocal.obj = aobj then begin
\r 
 841         currenttasklocal.obj := nil;
\r 
 842         currenttasklocal.handler := nil;
\r 
 844       currenttasklocal := currenttasklocal.nexttask;
\r 
 850 procedure processmessages;
\r 
 852   eventcore.processmessages;
\r 
 854 procedure messageloop;
\r 
 856   eventcore.messageloop;
\r 
 859 procedure exitmessageloop;
\r 
 861   eventcore.exitmessageloop;
\r 
 864 function tlasio.RealSend(Data : Pointer; Len : Integer) : Integer;
\r 
 866   result := myfdwrite(fdhandleout,data^,len);
\r 
 867   if (result > 0) and assigned(onsenddata) then onsenddata(self,result);
\r 
 868   eventcore.wmasterset(fdhandleout);
\r 
 871   procedure tlasio.myfdclose(fd : integer);
\r 
 875   function tlasio.myfdwrite(fd: LongInt;const buf;size: LongInt):LongInt;
\r 
 877     result := fdwrite(fd,buf,size);
\r 
 880   function tlasio.myfdread(fd: LongInt;var buf;size: LongInt):LongInt;
\r 
 882     result := fdread(fd,buf,size);
\r 
 894     signalloopback := nil;
\r