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
267 tv.tv_sec := i div 1000;
\r
268 tv.tv_usec := (i mod 1000) * 1000;
\r
269 select(0,nil,nil,nil,@tv);
\r
273 destructor tlcomponent.destroy;
\r
275 disconnecttasks(self);
\r
279 procedure tlcomponent.releasetaskhandler(wparam,lparam:longint);
\r
285 procedure tlcomponent.release;
\r
287 addtask(releasetaskhandler,self,0,0);
\r
290 procedure tlasio.release;
\r
292 asinreleaseflag := true;
\r
296 procedure tlasio.doreceiveloop;
\r
298 if recvq.size = 0 then exit;
\r
299 if assigned(ondataavailable) then ondataavailable(self,0);
\r
300 if not (wsonoreceiveloop in componentoptions) then
\r
301 if recvq.size > 0 then tltask.create(self.doreceiveloop,self,0,0);
\r
304 function tlasio.receivestr;
\r
306 setlength(result,recvq.size);
\r
307 receive(@result[1],length(result));
\r
310 function tlasio.receive(Buf:Pointer;BufSize:integer):integer;
\r
316 if recvq.size < i then i := recvq.size;
\r
318 while (a < i) do begin
\r
319 b := recvq.get(p,i-a);
\r
321 inc(taddrint(buf),b);
\r
326 if wsonoreceiveloop in componentoptions then begin
\r
327 if recvq.size = 0 then eventcore.rmasterset(fdhandlein,false);
\r
331 constructor tlasio.create;
\r
333 inherited create(AOwner);
\r
334 if not assigned(eventcore) then raise exception.create('no event core');
\r
335 sendq := tfifo.create;
\r
336 recvq := tfifo.create;
\r
342 destructor tlasio.destroy;
\r
344 destroying := true;
\r
345 if state <> wsclosed then close;
\r
351 procedure tlasio.close;
\r
356 procedure tlasio.abort;
\r
361 procedure tlasio.fdcleanup;
\r
363 if fdhandlein <> -1 then begin
\r
364 eventcore.rmasterclr(fdhandlein); //fd_clr(fdhandlein,fdsrmaster)
\r
366 if fdhandleout <> -1 then begin
\r
367 eventcore.wmasterclr(fdhandleout);//fd_clr(fdhandleout,fdswmaster)
\r
369 if fdhandlein=fdhandleout then begin
\r
370 if fdhandlein <> -1 then begin
\r
371 myfdclose(fdhandlein);
\r
374 if fdhandlein <> -1 then begin
\r
375 myfdclose(fdhandlein);
\r
377 if fdhandleout <> -1 then begin
\r
378 myfdclose(fdhandleout);
\r
385 procedure tlasio.internalclose(error:word);
\r
387 if (state<>wsclosed) and (state<>wsinvalidstate) then begin
\r
388 // -2 is a special indication that we should just exist silently
\r
389 // (used for connect failure handling when socket creation fails)
\r
390 if (fdhandlein = -2) and (fdhandleout = -2) then exit;
\r
391 if (fdhandlein < 0) or (fdhandleout < 0) then raise exception.create('internalclose called with invalid fd handles');
\r
392 eventcore.rmasterclr(fdhandlein);//fd_clr(fdhandlein,fdsrmaster);
\r
393 eventcore.wmasterclr(fdhandleout);//fd_clr(fdhandleout,fdswmaster);
\r
395 if closehandles then begin
\r
397 //anyone remember why this is here? --plugwash
\r
398 fcntl(fdhandlein,F_SETFL,0);
\r
400 myfdclose(fdhandlein);
\r
401 if fdhandleout <> fdhandlein then begin
\r
403 fcntl(fdhandleout,F_SETFL,0);
\r
405 myfdclose(fdhandleout);
\r
407 eventcore.setfdreverse(fdhandlein,nil);
\r
408 eventcore.setfdreverse(fdhandleout,nil);
\r
415 if assigned(onsessionclosed) then if not destroying then onsessionclosed(self,error);
\r
417 if assigned(sendq) then sendq.del(maxlongint);
\r
421 {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
\r
422 { All exceptions *MUST* be handled. If an exception is not handled, the }
\r
423 { application will most likely be shut down ! }
\r
424 procedure tlasio.HandleBackGroundException(E: Exception);
\r
426 CanAbort : Boolean;
\r
429 { First call the error event handler, if any }
\r
430 if Assigned(OnBgException) then begin
\r
432 OnBgException(Self, E, CanAbort);
\r
436 { Then abort the socket }
\r
437 if CanAbort then begin
\r
445 procedure tlasio.sendstr(const str : tbufferstring);
\r
447 putstringinsendbuffer(str);
\r
451 procedure tlasio.putstringinsendbuffer(const newstring : tbufferstring);
\r
453 if newstring <> '' then putdatainsendbuffer(@newstring[1],length(newstring));
\r
456 function tlasio.send(data:pointer;len:integer):integer;
\r
458 if state <> wsconnected then begin
\r
462 if len < 0 then len := 0;
\r
464 putdatainsendbuffer(data,len);
\r
469 procedure tlasio.putdatainsendbuffer(data:pointer;len:integer);
\r
471 sendq.add(data,len);
\r
474 function tlasio.sendflush : integer;
\r
478 // fdstestr : fdset;
\r
479 // fdstestw : fdset;
\r
481 if state <> wsconnected then begin
\r
486 lensent := sendq.get(data,packetbasesize*2);
\r
487 if assigned(data) then result := myfdwrite(fdhandleout,data^,lensent) else result := 0;
\r
489 if result = -1 then lensent := 0 else lensent := result;
\r
491 //sendq := copy(sendq,lensent+1,length(sendq)-lensent);
\r
492 sendq.del(lensent);
\r
494 //fd_clr(fdhandleout,fdsw); // this prevents the socket being closed by a write
\r
495 // that sends nothing because a previous socket has
\r
496 // slready flushed this socket when the message loop
\r
498 // if sendq.size > 0 then begin
\r
499 eventcore.wmasterset(fdhandleout);//fd_set(fdhandleout,fdswmaster);
\r
501 // wmasterclr(fdhandleout);//fd_clr(fdhandleout,fdswmaster);
\r
503 if result > 0 then begin
\r
504 if assigned(onsenddata) then onsenddata(self,result);
\r
505 // if sendq.size=0 then if assigned(ondatasent) then begin
\r
506 // tltask.create(self.dodatasent,self,0,0);
\r
507 // //begin test code
\r
508 // fd_zero(fdstestr);
\r
509 // fd_zero(fdstestw);
\r
510 // fd_set(fdhandlein,fdstestr);
\r
511 // fd_set(fdhandleout,fdstestw);
\r
512 // select(maxs,@fdstestr,@fdstestw,nil,0);
\r
513 // writeln(fd_isset(fdhandlein,fdstestr),' ',fd_isset(fdhandleout,fdstestw));
\r
517 writtenthiscycle := true;
\r
521 procedure tlasio.dupnowatch(invalue:longint);
\r
523 { debugout('invalue='+inttostr(invalue));}
\r
525 if state<> wsclosed then close;
\r
526 fdhandlein := invalue;
\r
527 fdhandleout := invalue;
\r
528 eventcore.setfdreverse(fdhandlein,self);
\r
530 fcntl(fdhandlein,F_SETFL,OPEN_NONBLOCK);
\r
532 state := wsconnected;
\r
537 procedure tlasio.dup(invalue:longint);
\r
539 dupnowatch(invalue);
\r
540 eventcore.rmasterset(fdhandlein,false);//fd_set(fdhandlein,fdsrmaster);
\r
541 eventcore.wmasterclr(fdhandleout);//fd_clr(fdhandleout,fdswmaster);
\r
545 procedure tlasio.handlefdtrigger(readtrigger,writetrigger:boolean);
\r
547 sendflushresult : integer;
\r
548 tempbuf:array[0..receivebufsize-1] of byte;
\r
551 if (state=wsconnected) and writetrigger then begin
\r
552 //writeln('write trigger');
\r
554 if (sendq.size >0) then begin
\r
556 sendflushresult := sendflush;
\r
557 if (sendflushresult <= 0) and (not writtenthiscycle) then begin
\r
558 if sendflushresult=0 then begin // linuxerror := 0;
\r
563 if getlasterror=WSAEWOULDBLOCK then begin
\r
564 //the asynchronous nature of windows messages means we sometimes
\r
565 //get here with the buffer full
\r
566 //so do nothing in that case
\r
570 internalclose({$ifdef win32}getlasterror{$else}linuxerror{$endif});
\r
576 //everything is sent fire off ondatasent event
\r
577 if fdhandleout >= 0 then eventcore.wmasterclr(fdhandleout);//fd_clr(fdhandleout,fdswmaster);
\r
578 if assigned(ondatasent) then tltask.create(self.dodatasent,self,0,0);
\r
580 if assigned(onfdwrite) then onfdwrite(self,0);
\r
582 writtenthiscycle := false;
\r
583 if (state =wsconnected) and readtrigger then begin
\r
584 if recvq.size=0 then begin
\r
586 if (a <= 0) or (a > sizeof(tempbuf)) then a := sizeof(tempbuf);
\r
587 numread := myfdread(fdhandlein,tempbuf,a);
\r
588 if (numread=0) and (not mustrefreshfds) then begin
\r
589 {if i remember correctly numread=0 is caused by eof
\r
590 if this isn't dealt with then you get a cpu eating infinite loop
\r
591 however if onsessionconencted has called processmessages that could
\r
592 cause us to drop to here with an empty recvq and nothing left to read
\r
593 and we don't want that to cause the socket to close}
\r
596 end else if (numread=-1) then begin
\r
598 //sometimes on windows we get stale messages due to the inherent delays
\r
599 //in the windows message queue
\r
600 if WSAGetLastError = wsaewouldblock then begin
\r
606 internalclose({$ifdef win32}wsagetlasterror{$else}linuxerror{$endif});
\r
608 end else if numread > 0 then recvq.add(@tempbuf,numread);
\r
611 if recvq.size > 0 then begin
\r
612 if wsonoreceiveloop in componentoptions then eventcore.rmasterclr(fdhandlein); //fd_clr(fdhandlein,fdsrmaster);
\r
613 if assigned(ondataavailable) then ondataAvailable(self,0);
\r
614 if not (wsonoreceiveloop in componentoptions) then if recvq.size > 0 then
\r
615 tltask.create(self.doreceiveloop,self,0,0);
\r
617 //until (numread = 0) or (currentsocket.state<>wsconnected);
\r
618 { debugout('inner loop complete');}
\r
622 procedure tlasio.flush;
\r
624 type fdset = tfdset;
\r
630 fd_set(fdhandleout,fds);
\r
631 while sendq.size>0 do begin
\r
632 select(fdhandleout+1,nil,@fds,nil,nil);
\r
633 if sendflush <= 0 then exit;
\r
637 procedure tlasio.dodatasent(wparam,lparam:longint);
\r
639 if assigned(ondatasent) then ondatasent(self,lparam);
\r
642 procedure tlasio.deletebuffereddata;
\r
644 sendq.del(maxlongint);
\r
647 procedure tlasio.sinkdata(sender:tobject;error:word);
\r
649 tlasio(sender).recvq.del(maxlongint);
\r
653 procedure tltimer.resettimes;
\r
655 gettimeofday(nextts);
\r
656 {if not initialevent then} tv_add(nextts,interval);
\r
660 {procedure tltimer.setinitialevent(newvalue : boolean);
\r
662 if newvalue <> finitialevent then begin
\r
663 finitialevent := newvalue;
\r
664 if assigned(timerwrapperinterface) then begin
\r
665 timerwrapperinterface.setinitialevent(wrappedtimer,newvalue);
\r
672 procedure tltimer.setontimer(newvalue:tnotifyevent);
\r
674 if @newvalue <> @fontimer then begin
\r
675 fontimer := newvalue;
\r
676 if assigned(timerwrapperinterface) then begin
\r
677 timerwrapperinterface.setontimer(wrappedtimer,newvalue);
\r
686 procedure tltimer.setenabled(newvalue : boolean);
\r
688 if newvalue <> fenabled then begin
\r
689 fenabled := newvalue;
\r
690 if assigned(timerwrapperinterface) then begin
\r
691 timerwrapperinterface.setenabled(wrappedtimer,newvalue);
\r
694 raise exception.create('non wrapper timers are not permitted on windows');
\r
702 procedure tltimer.setinterval(newvalue:integer);
\r
704 if newvalue <> finterval then begin
\r
705 finterval := newvalue;
\r
706 if assigned(timerwrapperinterface) then begin
\r
707 timerwrapperinterface.setinterval(wrappedtimer,newvalue);
\r
710 raise exception.create('non wrapper timers are not permitted on windows');
\r
722 constructor tltimer.create;
\r
724 inherited create(AOwner);
\r
725 if assigned(timerwrapperinterface) then begin
\r
726 wrappedtimer := timerwrapperinterface.createwrappedtimer;
\r
730 nexttimer := firsttimer;
\r
733 if assigned(nexttimer) then nexttimer.prevtimer := self;
\r
734 firsttimer := self;
\r
740 destructor tltimer.destroy;
\r
742 if assigned(timerwrapperinterface) then begin
\r
745 if prevtimer <> nil then begin
\r
746 prevtimer.nexttimer := nexttimer;
\r
748 firsttimer := nexttimer;
\r
750 if nexttimer <> nil then begin
\r
751 nexttimer.prevtimer := prevtimer;
\r
758 constructor tltask.create(ahandler:ttaskevent;aobj:tobject;awparam,alparam:longint);
\r
761 if assigned(onaddtask) then onaddtask(ahandler,aobj,awparam,alparam);
\r
762 handler := ahandler;
\r
766 {nexttask := firsttask;
\r
767 firsttask := self;}
\r
768 if assigned(lasttask) then begin
\r
769 lasttask.nexttask := self;
\r
774 //ahandler(wparam,lparam);
\r
777 procedure addtask(ahandler:ttaskevent;aobj:tobject;awparam,alparam:longint);
\r
780 tltask.create(ahandler,aobj,awparam,alparam);
\r
784 procedure prepsigpipe;{$ifndef ver1_0}inline;
\r
787 starthandlesignal(sigpipe);
\r
788 if not assigned(signalloopback) then begin
\r
789 signalloopback := tlloopback.create(nil);
\r
790 signalloopback.ondataAvailable := signalloopback.sinkdata;
\r
797 procedure processtasks;//inline;
\r
799 temptask : tltask ;
\r
803 if not assigned(currenttask) then begin
\r
804 currenttask := firsttask;
\r
808 while assigned(currenttask) do begin
\r
810 if assigned(currenttask.handler) then currenttask.handler(currenttask.wparam,currenttask.lparam);
\r
811 if assigned(currenttask) then begin
\r
812 temptask := currenttask;
\r
813 currenttask := currenttask.nexttask;
\r
816 //writeln('processed a task');
\r
824 procedure disconnecttasks(aobj:tobject);
\r
826 currenttasklocal : tltask ;
\r
829 for counter := 0 to 1 do begin
\r
830 if counter = 0 then begin
\r
831 currenttasklocal := firsttask; //main list of tasks
\r
833 currenttasklocal := currenttask; //needed in case called from a task
\r
835 // note i don't bother to sestroy the links here as that will happen when
\r
836 // the list of tasks is processed anyway
\r
837 while assigned(currenttasklocal) do begin
\r
838 if currenttasklocal.obj = aobj then begin
\r
839 currenttasklocal.obj := nil;
\r
840 currenttasklocal.handler := nil;
\r
842 currenttasklocal := currenttasklocal.nexttask;
\r
848 procedure processmessages;
\r
850 eventcore.processmessages;
\r
852 procedure messageloop;
\r
854 eventcore.messageloop;
\r
857 procedure exitmessageloop;
\r
859 eventcore.exitmessageloop;
\r
862 function tlasio.RealSend(Data : Pointer; Len : Integer) : Integer;
\r
864 result := myfdwrite(fdhandleout,data^,len);
\r
865 if (result > 0) and assigned(onsenddata) then onsenddata(self,result);
\r
866 eventcore.wmasterset(fdhandleout);
\r
869 procedure tlasio.myfdclose(fd : integer);
\r
873 function tlasio.myfdwrite(fd: LongInt;const buf;size: LongInt):LongInt;
\r
875 result := fdwrite(fd,buf,size);
\r
878 function tlasio.myfdread(fd: LongInt;var buf;size: LongInt):LongInt;
\r
880 result := fdread(fd,buf,size);
\r
892 signalloopback := nil;
\r