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
578 //the asynchronous nature of windows messages means we sometimes
579 //get here with the buffer full
580 //so do nothing in that case
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