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 absolutemaxs: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 descriptor}
\r
88 fdhandleout : Longint ; {file descriptor}
\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 mswindows}abstract;{$endif}
\r
140 function myfdwrite(fd: LongInt;const buf;size: LongInt):LongInt; virtual;{$ifdef mswindows}abstract;{$endif}
\r
141 function myfdread(fd: LongInt;var buf;size: LongInt):LongInt; virtual;{$ifdef mswindows}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 ; {milliseconds, default 1000}
\r
168 {$ifndef mswindows}
\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 these public for now, this code should probably 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
254 {$ifndef mswindows}
\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
398 {$ifndef mswindows}
\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
404 {$ifndef mswindows}
\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
531 {$ifndef mswindows}
\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 mswindows}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 onsessionconnected 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 mswindows}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
654 {$ifndef mswindows}
\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 destroy 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
870 {$ifndef mswindows}
\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