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
77 procedure releasetaskhandler(wparam,lparam:longint);
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:string; 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 : string);virtual;
\r
123 procedure putstringinsendbuffer(const newstring : string);
\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
163 tltimer=class(tlcomponent)
\r
167 wrappedtimer : tobject;
\r
170 // finitialevent : boolean ;
\r
171 fontimer : tnotifyevent ;
\r
172 fenabled : boolean ;
\r
173 finterval : integer ; {miliseconds, default 1000}
\r
175 procedure resettimes;
\r
177 // procedure setinitialevent(newvalue : boolean);
\r
178 procedure setontimer(newvalue:tnotifyevent);
\r
179 procedure setenabled(newvalue : boolean);
\r
180 procedure setinterval(newvalue : integer);
\r
182 //making theese public for now, this code should probablly be restructured later though
\r
183 prevtimer : tltimer ;
\r
184 nexttimer : tltimer ;
\r
185 nextts : ttimeval ;
\r
187 constructor create(aowner:tcomponent);override;
\r
188 destructor destroy;override;
\r
189 // property initialevent : boolean read finitialevent write setinitialevent;
\r
190 property ontimer : tnotifyevent read fontimer write setontimer;
\r
191 property enabled : boolean read fenabled write setenabled;
\r
192 property interval : integer read finterval write setinterval;
\r
196 ttaskevent=procedure(wparam,lparam:longint) of object;
\r
198 tltask=class(tobject)
\r
200 handler : ttaskevent;
\r
205 constructor create(ahandler:ttaskevent;aobj:tobject;awparam,alparam:longint);
\r
212 procedure processmessages; virtual;abstract;
\r
213 procedure messageloop; virtual;abstract;
\r
214 procedure exitmessageloop; virtual;abstract;
\r
215 procedure setfdreverse(fd : integer;reverseto : tlasio);virtual;abstract;
\r
216 procedure rmasterset(fd : integer;islistensocket : boolean); virtual;abstract;
\r
217 procedure rmasterclr(fd: integer); virtual;abstract;
\r
218 procedure wmasterset(fd : integer); virtual;abstract;
\r
219 procedure wmasterclr(fd: integer); virtual;abstract;
\r
222 eventcore : teventcore;
\r
224 procedure processmessages;
\r
225 procedure messageloop;
\r
226 procedure exitmessageloop;
\r
229 firsttimer : tltimer ;
\r
230 firsttask , lasttask , currenttask : tltask ;
\r
232 numread : integer ;
\r
233 mustrefreshfds : boolean ;
\r
234 { lcoretestcount:integer;}
\r
236 asinreleaseflag:boolean;
\r
239 procedure disconnecttasks(aobj:tobject);
\r
240 procedure addtask(ahandler:ttaskevent;aobj:tobject;awparam,alparam:longint);
\r
242 tonaddtask = procedure(ahandler:ttaskevent;aobj:tobject;awparam,alparam:longint);
\r
244 onaddtask : tonaddtask;
\r
247 procedure sleep(i:integer);
\r
249 procedure prepsigpipe;{$ifndef ver1_0}inline;{$endif}
\r
255 uses {sockets,}lloopback,lsignal;
\r
258 uses windows,winsock;
\r
261 {$include unixstuff.inc}
\r
263 {$include ltimevalstuff.inc}
\r
266 {!!! added sleep call -beware}
\r
267 procedure sleep(i:integer);
\r
274 tv.tv_sec := i div 1000;
\r
275 tv.tv_usec := (i mod 1000) * 1000;
\r
276 select(0,nil,nil,nil,@tv);
\r
280 destructor tlcomponent.destroy;
\r
282 disconnecttasks(self);
\r
286 procedure tlcomponent.releasetaskhandler(wparam,lparam:longint);
292 procedure tlcomponent.release;
\r
294 addtask(releasetaskhandler,self,0,0);
297 procedure tlasio.release;
\r
299 asinreleaseflag := true;
\r
303 procedure tlasio.doreceiveloop;
\r
305 if recvq.size = 0 then exit;
\r
306 if assigned(ondataavailable) then ondataavailable(self,0);
\r
307 if not (wsonoreceiveloop in componentoptions) then
\r
308 if recvq.size > 0 then tltask.create(self.doreceiveloop,self,0,0);
\r
311 function tlasio.receivestr;
\r
313 setlength(result,recvq.size);
\r
314 receive(@result[1],length(result));
\r
317 function tlasio.receive(Buf:Pointer;BufSize:integer):integer;
\r
323 if recvq.size < i then i := recvq.size;
\r
325 while (a < i) do begin
\r
326 b := recvq.get(p,i-a);
\r
328 inc(taddrint(buf),b);
\r
333 if wsonoreceiveloop in componentoptions then begin
\r
334 if recvq.size = 0 then eventcore.rmasterset(fdhandlein,false);
\r
338 constructor tlasio.create;
\r
340 inherited create(AOwner);
\r
341 if not assigned(eventcore) then raise exception.create('no event core');
\r
342 sendq := tfifo.create;
\r
343 recvq := tfifo.create;
\r
349 destructor tlasio.destroy;
\r
351 destroying := true;
\r
352 if state <> wsclosed then close;
\r
358 procedure tlasio.close;
\r
363 procedure tlasio.abort;
\r
368 procedure tlasio.fdcleanup;
\r
370 if fdhandlein <> -1 then begin
\r
371 eventcore.rmasterclr(fdhandlein); //fd_clr(fdhandlein,fdsrmaster)
\r
373 if fdhandleout <> -1 then begin
\r
374 eventcore.wmasterclr(fdhandleout);//fd_clr(fdhandleout,fdswmaster)
\r
376 if fdhandlein=fdhandleout then begin
\r
377 if fdhandlein <> -1 then begin
\r
378 myfdclose(fdhandlein);
\r
381 if fdhandlein <> -1 then begin
\r
382 myfdclose(fdhandlein);
\r
384 if fdhandleout <> -1 then begin
\r
385 myfdclose(fdhandleout);
\r
392 procedure tlasio.internalclose(error:word);
\r
394 if (state<>wsclosed) and (state<>wsinvalidstate) then begin
\r
395 // -2 is a special indication that we should just exist silently
\r
396 // (used for connect failure handling when socket creation fails)
\r
397 if (fdhandlein = -2) and (fdhandleout = -2) then exit;
\r
398 if (fdhandlein < 0) or (fdhandleout < 0) then raise exception.create('internalclose called with invalid fd handles');
\r
399 eventcore.rmasterclr(fdhandlein);//fd_clr(fdhandlein,fdsrmaster);
\r
400 eventcore.wmasterclr(fdhandleout);//fd_clr(fdhandleout,fdswmaster);
\r
402 if closehandles then begin
\r
404 //anyone remember why this is here? --plugwash
\r
405 fcntl(fdhandlein,F_SETFL,0);
\r
407 myfdclose(fdhandlein);
\r
408 if fdhandleout <> fdhandlein then begin
\r
410 fcntl(fdhandleout,F_SETFL,0);
\r
412 myfdclose(fdhandleout);
\r
414 eventcore.setfdreverse(fdhandlein,nil);
\r
415 eventcore.setfdreverse(fdhandleout,nil);
\r
422 if assigned(onsessionclosed) then if not destroying then onsessionclosed(self,error);
\r
424 if assigned(sendq) then sendq.del(maxlongint);
\r
428 {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
\r
429 { All exceptions *MUST* be handled. If an exception is not handled, the }
\r
430 { application will most likely be shut down ! }
\r
431 procedure tlasio.HandleBackGroundException(E: Exception);
\r
433 CanAbort : Boolean;
\r
436 { First call the error event handler, if any }
\r
437 if Assigned(OnBgException) then begin
\r
439 OnBgException(Self, E, CanAbort);
\r
443 { Then abort the socket }
\r
444 if CanAbort then begin
\r
452 procedure tlasio.sendstr(const str : string);
\r
454 putstringinsendbuffer(str);
\r
458 procedure tlasio.putstringinsendbuffer(const newstring : string);
\r
460 if newstring <> '' then putdatainsendbuffer(@newstring[1],length(newstring));
\r
463 function tlasio.send(data:pointer;len:integer):integer;
\r
465 if state <> wsconnected then begin
\r
469 if len < 0 then len := 0;
\r
471 putdatainsendbuffer(data,len);
\r
476 procedure tlasio.putdatainsendbuffer(data:pointer;len:integer);
\r
478 sendq.add(data,len);
\r
481 function tlasio.sendflush : integer;
\r
485 // fdstestr : fdset;
\r
486 // fdstestw : fdset;
\r
488 if state <> wsconnected then exit;
\r
490 lensent := sendq.get(data,packetbasesize*2);
\r
491 if assigned(data) then result := myfdwrite(fdhandleout,data^,lensent) else result := 0;
\r
493 if result = -1 then lensent := 0 else lensent := result;
\r
495 //sendq := copy(sendq,lensent+1,length(sendq)-lensent);
\r
496 sendq.del(lensent);
\r
498 //fd_clr(fdhandleout,fdsw); // this prevents the socket being closed by a write
\r
499 // that sends nothing because a previous socket has
\r
500 // slready flushed this socket when the message loop
\r
502 // if sendq.size > 0 then begin
\r
503 eventcore.wmasterset(fdhandleout);//fd_set(fdhandleout,fdswmaster);
\r
505 // wmasterclr(fdhandleout);//fd_clr(fdhandleout,fdswmaster);
\r
507 if result > 0 then begin
\r
508 if assigned(onsenddata) then onsenddata(self,result);
\r
509 // if sendq.size=0 then if assigned(ondatasent) then begin
\r
510 // tltask.create(self.dodatasent,self,0,0);
\r
511 // //begin test code
\r
512 // fd_zero(fdstestr);
\r
513 // fd_zero(fdstestw);
\r
514 // fd_set(fdhandlein,fdstestr);
\r
515 // fd_set(fdhandleout,fdstestw);
\r
516 // select(maxs,@fdstestr,@fdstestw,nil,0);
\r
517 // writeln(fd_isset(fdhandlein,fdstestr),' ',fd_isset(fdhandleout,fdstestw));
\r
521 writtenthiscycle := true;
\r
525 procedure tlasio.dupnowatch(invalue:longint);
\r
527 { debugout('invalue='+inttostr(invalue));}
\r
529 if state<> wsclosed then close;
\r
530 fdhandlein := invalue;
\r
531 fdhandleout := invalue;
\r
532 eventcore.setfdreverse(fdhandlein,self);
\r
534 fcntl(fdhandlein,F_SETFL,OPEN_NONBLOCK);
\r
536 state := wsconnected;
\r
541 procedure tlasio.dup(invalue:longint);
\r
543 dupnowatch(invalue);
\r
544 eventcore.rmasterset(fdhandlein,false);//fd_set(fdhandlein,fdsrmaster);
\r
545 eventcore.wmasterclr(fdhandleout);//fd_clr(fdhandleout,fdswmaster);
\r
549 procedure tlasio.handlefdtrigger(readtrigger,writetrigger:boolean);
\r
551 sendflushresult : integer;
\r
552 tempbuf:array[0..receivebufsize-1] of byte;
\r
555 if (state=wsconnected) and writetrigger then begin
\r
556 //writeln('write trigger');
\r
558 if (sendq.size >0) then begin
\r
560 sendflushresult := sendflush;
\r
561 if (sendflushresult <= 0) and (not writtenthiscycle) then begin
\r
562 if sendflushresult=0 then begin // linuxerror := 0;
\r
567 if getlasterror=WSAEWOULDBLOCK then begin
\r
568 //the asynchronous nature of windows messages means we sometimes
\r
569 //get here with the buffer full
\r
570 //so do nothing in that case
\r
574 internalclose({$ifdef win32}getlasterror{$else}linuxerror{$endif});
\r
580 //everything is sent fire off ondatasent event
\r
581 if fdhandleout >= 0 then eventcore.wmasterclr(fdhandleout);//fd_clr(fdhandleout,fdswmaster);
\r
582 if assigned(ondatasent) then tltask.create(self.dodatasent,self,0,0);
\r
584 if assigned(onfdwrite) then onfdwrite(self,0);
\r
586 writtenthiscycle := false;
\r
587 if (state =wsconnected) and readtrigger then begin
\r
588 if recvq.size=0 then begin
\r
590 if (a <= 0) or (a > sizeof(tempbuf)) then a := sizeof(tempbuf);
\r
591 numread := myfdread(fdhandlein,tempbuf,a);
\r
592 if (numread=0) and (not mustrefreshfds) then begin
\r
593 {if i remember correctly numread=0 is caused by eof
\r
594 if this isn't dealt with then you get a cpu eating infinite loop
\r
595 however if onsessionconencted has called processmessages that could
\r
596 cause us to drop to here with an empty recvq and nothing left to read
\r
597 and we don't want that to cause the socket to close}
\r
600 end else if (numread=-1) then begin
\r
602 //sometimes on windows we get stale messages due to the inherent delays
\r
603 //in the windows message queue
\r
604 if WSAGetLastError = wsaewouldblock then begin
\r
610 internalclose({$ifdef win32}wsagetlasterror{$else}linuxerror{$endif});
\r
612 end else if numread > 0 then recvq.add(@tempbuf,numread);
\r
615 if recvq.size > 0 then begin
\r
616 if wsonoreceiveloop in componentoptions then eventcore.rmasterclr(fdhandlein); //fd_clr(fdhandlein,fdsrmaster);
\r
617 if assigned(ondataavailable) then ondataAvailable(self,0);
\r
618 if not (wsonoreceiveloop in componentoptions) then if recvq.size > 0 then
\r
619 tltask.create(self.doreceiveloop,self,0,0);
\r
621 //until (numread = 0) or (currentsocket.state<>wsconnected);
\r
622 { debugout('inner loop complete');}
\r
626 procedure tlasio.flush;
\r
628 type fdset = tfdset;
\r
634 fd_set(fdhandleout,fds);
\r
635 while sendq.size>0 do begin
\r
636 select(fdhandleout+1,nil,@fds,nil,nil);
\r
637 if sendflush <= 0 then exit;
\r
641 procedure tlasio.dodatasent(wparam,lparam:longint);
\r
643 if assigned(ondatasent) then ondatasent(self,lparam);
\r
646 procedure tlasio.deletebuffereddata;
\r
648 sendq.del(maxlongint);
\r
651 procedure tlasio.sinkdata(sender:tobject;error:word);
\r
653 tlasio(sender).recvq.del(maxlongint);
\r
657 procedure tltimer.resettimes;
\r
659 gettimeofday(nextts);
\r
660 {if not initialevent then} tv_add(nextts,interval);
\r
664 {procedure tltimer.setinitialevent(newvalue : boolean);
\r
666 if newvalue <> finitialevent then begin
\r
667 finitialevent := newvalue;
\r
668 if assigned(timerwrapperinterface) then begin
\r
669 timerwrapperinterface.setinitialevent(wrappedtimer,newvalue);
\r
676 procedure tltimer.setontimer(newvalue:tnotifyevent);
\r
678 if @newvalue <> @fontimer then begin
\r
679 fontimer := newvalue;
\r
680 if assigned(timerwrapperinterface) then begin
\r
681 timerwrapperinterface.setontimer(wrappedtimer,newvalue);
\r
690 procedure tltimer.setenabled(newvalue : boolean);
\r
692 if newvalue <> fenabled then begin
\r
693 fenabled := newvalue;
\r
694 if assigned(timerwrapperinterface) then begin
\r
695 timerwrapperinterface.setenabled(wrappedtimer,newvalue);
\r
698 raise exception.create('non wrapper timers are not permitted on windows');
\r
706 procedure tltimer.setinterval(newvalue:integer);
\r
708 if newvalue <> finterval then begin
\r
709 finterval := newvalue;
\r
710 if assigned(timerwrapperinterface) then begin
\r
711 timerwrapperinterface.setinterval(wrappedtimer,newvalue);
\r
714 raise exception.create('non wrapper timers are not permitted on windows');
\r
726 constructor tltimer.create;
\r
728 inherited create(AOwner);
\r
729 if assigned(timerwrapperinterface) then begin
\r
730 wrappedtimer := timerwrapperinterface.createwrappedtimer;
\r
734 nexttimer := firsttimer;
\r
737 if assigned(nexttimer) then nexttimer.prevtimer := self;
\r
738 firsttimer := self;
\r
744 destructor tltimer.destroy;
\r
746 if assigned(timerwrapperinterface) then begin
\r
749 if prevtimer <> nil then begin
\r
750 prevtimer.nexttimer := nexttimer;
\r
752 firsttimer := nexttimer;
\r
754 if nexttimer <> nil then begin
\r
755 nexttimer.prevtimer := prevtimer;
\r
762 constructor tltask.create(ahandler:ttaskevent;aobj:tobject;awparam,alparam:longint);
\r
765 if assigned(onaddtask) then onaddtask(ahandler,aobj,awparam,alparam);
\r
766 handler := ahandler;
\r
770 {nexttask := firsttask;
\r
771 firsttask := self;}
\r
772 if assigned(lasttask) then begin
\r
773 lasttask.nexttask := self;
\r
778 //ahandler(wparam,lparam);
\r
781 procedure addtask(ahandler:ttaskevent;aobj:tobject;awparam,alparam:longint);
\r
784 tltask.create(ahandler,aobj,awparam,alparam);
\r
788 procedure prepsigpipe;{$ifndef ver1_0}inline;
\r
791 starthandlesignal(sigpipe);
\r
792 if not assigned(signalloopback) then begin
\r
793 signalloopback := tlloopback.create(nil);
\r
794 signalloopback.ondataAvailable := signalloopback.sinkdata;
\r
801 procedure processtasks;//inline;
\r
803 temptask : tltask ;
\r
807 if not assigned(currenttask) then begin
\r
808 currenttask := firsttask;
\r
812 while assigned(currenttask) do begin
\r
814 if assigned(currenttask.handler) then currenttask.handler(currenttask.wparam,currenttask.lparam);
\r
815 if assigned(currenttask) then begin
\r
816 temptask := currenttask;
\r
817 currenttask := currenttask.nexttask;
\r
820 //writeln('processed a task');
\r
828 procedure disconnecttasks(aobj:tobject);
\r
830 currenttasklocal : tltask ;
\r
833 for counter := 0 to 1 do begin
\r
834 if counter = 0 then begin
\r
835 currenttasklocal := firsttask; //main list of tasks
\r
837 currenttasklocal := currenttask; //needed in case called from a task
\r
839 // note i don't bother to sestroy the links here as that will happen when
\r
840 // the list of tasks is processed anyway
\r
841 while assigned(currenttasklocal) do begin
\r
842 if currenttasklocal.obj = aobj then begin
\r
843 currenttasklocal.obj := nil;
\r
844 currenttasklocal.handler := nil;
\r
846 currenttasklocal := currenttasklocal.nexttask;
\r
852 procedure processmessages;
\r
854 eventcore.processmessages;
\r
856 procedure messageloop;
\r
858 eventcore.messageloop;
\r
861 procedure exitmessageloop;
\r
863 eventcore.exitmessageloop;
\r
866 function tlasio.RealSend(Data : Pointer; Len : Integer) : Integer;
\r
868 result := myfdwrite(fdhandleout,data^,len);
\r
869 if (result > 0) and assigned(onsenddata) then onsenddata(self,result);
\r
870 eventcore.wmasterset(fdhandleout);
\r
873 procedure tlasio.myfdclose(fd : integer);
\r
877 function tlasio.myfdwrite(fd: LongInt;const buf;size: LongInt):LongInt;
\r
879 result := fdwrite(fd,buf,size);
\r
882 function tlasio.myfdread(fd: LongInt;var buf;size: LongInt):LongInt;
\r
884 result := fdread(fd,buf,size);
\r
896 signalloopback := nil;
\r