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
51 {$IF CompilerVersion >= 20.0}
\r
52 { Delphi 2009/2010 widestring is the default string type }
\r
53 thostname = ansistring;
\r
54 tbufferstring = ansistring;
\r
55 tipstring = ansistring;
\r
57 { Old pre-unicode delphi, todo: need to check if ansistring is available
\r
58 in all older versions of delphi }
\r
60 tbufferstring = string;
\r
65 sigset= array[0..31] of longint;
\r
68 ESocketException = class(Exception);
\r
69 TBgExceptionEvent = procedure (Sender : TObject;
\r
71 var CanClose : Boolean) of object;
\r
73 // note : tsocketstate is defined in the same way as it is in François PIETTE's twsocket
\r
74 // however tlsocket currently only uses wsClosed wsConnecting wsconnected and wsListening
\r
75 TSocketState = (wsInvalidState,
\r
77 wsConnecting, wsConnected,
\r
78 wsAccepting, wsListening,
\r
81 TWSocketOption = (wsoNoReceiveLoop, wsoTcpNoDelay);
\r
82 TWSocketOptions = set of TWSocketOption;
\r
84 TSocketevent = procedure(Sender: TObject; Error: word) of object;
\r
85 //Tdataavailevent = procedure(data : string);
\r
86 TSendData = procedure (Sender: TObject; BytesSent: Integer) of object;
\r
88 tlcomponent = class(tcomponent)
\r
90 procedure releasetaskhandler(wparam,lparam:longint);
\r
92 procedure release; virtual;
\r
93 destructor destroy; override;
\r
96 tlasio = class(tlcomponent)
\r
98 state : tsocketstate ;
\r
99 ComponentOptions : TWSocketOptions;
\r
100 fdhandlein : Longint ; {file discriptor}
\r
101 fdhandleout : Longint ; {file discriptor}
\r
103 onsessionclosed : tsocketevent ;
\r
104 ondataAvailable : tsocketevent ;
\r
105 onsessionAvailable : tsocketevent ;
\r
107 onsessionconnected : tsocketevent ;
\r
108 onsenddata : tsenddata ;
\r
109 ondatasent : tsocketevent ;
\r
110 //connected : boolean ;
\r
113 OnBgException : TBgExceptionEvent ;
\r
114 //connectread : boolean ;
\r
116 closehandles : boolean ;
\r
117 writtenthiscycle : boolean ;
\r
118 onfdwrite : procedure (Sender: TObject; Error: word) of object; //added for bewarehttpd
\r
120 destroying:boolean;
\r
121 recvbufsize:integer;
\r
122 function receivestr:tbufferstring; virtual;
\r
125 procedure internalclose(error:word); virtual;
\r
126 constructor Create(AOwner: TComponent); override;
\r
128 destructor destroy; override;
\r
129 procedure fdcleanup;
\r
130 procedure HandleBackGroundException(E: Exception);
\r
131 procedure handlefdtrigger(readtrigger,writetrigger:boolean); virtual;
\r
132 procedure dup(invalue:longint);
\r
134 function sendflush : integer;
\r
135 procedure sendstr(const str : tbufferstring);virtual;
\r
136 procedure putstringinsendbuffer(const newstring : tbufferstring);
\r
137 function send(data:pointer;len:integer):integer;virtual;
\r
138 procedure putdatainsendbuffer(data:pointer;len:integer); virtual;
\r
139 procedure deletebuffereddata;
\r
141 //procedure messageloop;
\r
142 function Receive(Buf:Pointer;BufSize:integer):integer; virtual;
\r
143 procedure flush;virtual;
\r
144 procedure dodatasent(wparam,lparam:longint);
\r
145 procedure doreceiveloop(wparam,lparam:longint);
\r
146 procedure sinkdata(sender:tobject;error:word);
\r
148 procedure release; override; {test -beware}
\r
150 function RealSend(Data : Pointer; Len : Integer) : Integer; //added for bewarehttpd
\r
152 procedure myfdclose(fd : integer); virtual;{$ifdef win32}abstract;{$endif}
\r
153 function myfdwrite(fd: LongInt;const buf;size: LongInt):LongInt; virtual;{$ifdef win32}abstract;{$endif}
\r
154 function myfdread(fd: LongInt;var buf;size: LongInt):LongInt; virtual;{$ifdef win32}abstract;{$endif}
\r
156 procedure dupnowatch(invalue:longint);
\r
158 ttimerwrapperinterface=class(tlcomponent)
\r
160 function createwrappedtimer : tobject;virtual;abstract;
\r
161 // procedure setinitialevent(wrappedtimer : tobject;newvalue : boolean);virtual;abstract;
\r
162 procedure setontimer(wrappedtimer : tobject;newvalue:tnotifyevent);virtual;abstract;
\r
163 procedure setenabled(wrappedtimer : tobject;newvalue : boolean);virtual;abstract;
\r
164 procedure setinterval(wrappedtimer : tobject;newvalue : integer);virtual;abstract;
\r
168 timerwrapperinterface : ttimerwrapperinterface;
\r
170 tltimer=class(tlcomponent)
\r
174 wrappedtimer : tobject;
\r
177 // finitialevent : boolean ;
\r
178 fontimer : tnotifyevent ;
\r
179 fenabled : boolean ;
\r
180 finterval : integer ; {miliseconds, default 1000}
\r
182 procedure resettimes;
\r
184 // procedure setinitialevent(newvalue : boolean);
\r
185 procedure setontimer(newvalue:tnotifyevent);
\r
186 procedure setenabled(newvalue : boolean);
\r
187 procedure setinterval(newvalue : integer);
\r
189 //making theese public for now, this code should probablly be restructured later though
\r
190 prevtimer : tltimer ;
\r
191 nexttimer : tltimer ;
\r
192 nextts : ttimeval ;
\r
194 constructor create(aowner:tcomponent);override;
\r
195 destructor destroy;override;
\r
196 // property initialevent : boolean read finitialevent write setinitialevent;
\r
197 property ontimer : tnotifyevent read fontimer write setontimer;
\r
198 property enabled : boolean read fenabled write setenabled;
\r
199 property interval : integer read finterval write setinterval;
\r
203 ttaskevent=procedure(wparam,lparam:longint) of object;
\r
205 tltask=class(tobject)
\r
207 handler : ttaskevent;
\r
212 constructor create(ahandler:ttaskevent;aobj:tobject;awparam,alparam:longint);
\r
219 procedure processmessages; virtual;abstract;
\r
220 procedure messageloop; virtual;abstract;
\r
221 procedure exitmessageloop; virtual;abstract;
\r
222 procedure setfdreverse(fd : integer;reverseto : tlasio);virtual;abstract;
\r
223 procedure rmasterset(fd : integer;islistensocket : boolean); virtual;abstract;
\r
224 procedure rmasterclr(fd: integer); virtual;abstract;
\r
225 procedure wmasterset(fd : integer); virtual;abstract;
\r
226 procedure wmasterclr(fd: integer); virtual;abstract;
\r
229 eventcore : teventcore;
\r
231 procedure processmessages;
\r
232 procedure messageloop;
\r
233 procedure exitmessageloop;
\r
236 firsttimer : tltimer ;
\r
237 firsttask , lasttask , currenttask : tltask ;
\r
239 numread : integer ;
\r
240 mustrefreshfds : boolean ;
\r
241 { lcoretestcount:integer;}
\r
243 asinreleaseflag:boolean;
\r
246 procedure disconnecttasks(aobj:tobject);
\r
247 procedure addtask(ahandler:ttaskevent;aobj:tobject;awparam,alparam:longint);
\r
249 tonaddtask = procedure(ahandler:ttaskevent;aobj:tobject;awparam,alparam:longint);
\r
251 onaddtask : tonaddtask;
\r
254 procedure sleep(i:integer);
\r
256 procedure prepsigpipe;{$ifndef ver1_0}inline;{$endif}
\r
262 uses {sockets,}lloopback,lsignal;
\r
265 uses windows,winsock;
\r
268 {$include unixstuff.inc}
\r
272 {!!! added sleep call -beware}
\r
273 procedure sleep(i:integer);
\r
280 tv.tv_sec := i div 1000;
\r
281 tv.tv_usec := (i mod 1000) * 1000;
\r
282 select(0,nil,nil,nil,@tv);
\r
286 destructor tlcomponent.destroy;
\r
288 disconnecttasks(self);
\r
292 procedure tlcomponent.releasetaskhandler(wparam,lparam:longint);
\r
298 procedure tlcomponent.release;
\r
300 addtask(releasetaskhandler,self,0,0);
\r
303 procedure tlasio.release;
\r
305 asinreleaseflag := true;
\r
309 procedure tlasio.doreceiveloop;
\r
311 if recvq.size = 0 then exit;
\r
312 if assigned(ondataavailable) then ondataavailable(self,0);
\r
313 if not (wsonoreceiveloop in componentoptions) then
\r
314 if recvq.size > 0 then tltask.create(self.doreceiveloop,self,0,0);
\r
317 function tlasio.receivestr;
\r
319 setlength(result,recvq.size);
\r
320 receive(@result[1],length(result));
\r
323 function tlasio.receive(Buf:Pointer;BufSize:integer):integer;
\r
329 if recvq.size < i then i := recvq.size;
\r
331 while (a < i) do begin
\r
332 b := recvq.get(p,i-a);
\r
334 inc(taddrint(buf),b);
\r
339 if wsonoreceiveloop in componentoptions then begin
\r
340 if recvq.size = 0 then eventcore.rmasterset(fdhandlein,false);
\r
344 constructor tlasio.create;
\r
346 inherited create(AOwner);
\r
347 if not assigned(eventcore) then raise exception.create('no event core');
\r
348 sendq := tfifo.create;
\r
349 recvq := tfifo.create;
\r
355 destructor tlasio.destroy;
\r
357 destroying := true;
\r
358 if state <> wsclosed then close;
\r
364 procedure tlasio.close;
\r
369 procedure tlasio.abort;
\r
374 procedure tlasio.fdcleanup;
\r
376 if fdhandlein <> -1 then begin
\r
377 eventcore.rmasterclr(fdhandlein); //fd_clr(fdhandlein,fdsrmaster)
\r
379 if fdhandleout <> -1 then begin
\r
380 eventcore.wmasterclr(fdhandleout);//fd_clr(fdhandleout,fdswmaster)
\r
382 if fdhandlein=fdhandleout then begin
\r
383 if fdhandlein <> -1 then begin
\r
384 myfdclose(fdhandlein);
\r
387 if fdhandlein <> -1 then begin
\r
388 myfdclose(fdhandlein);
\r
390 if fdhandleout <> -1 then begin
\r
391 myfdclose(fdhandleout);
\r
398 procedure tlasio.internalclose(error:word);
\r
400 if (state<>wsclosed) and (state<>wsinvalidstate) then begin
\r
401 // -2 is a special indication that we should just exist silently
\r
402 // (used for connect failure handling when socket creation fails)
\r
403 if (fdhandlein = -2) and (fdhandleout = -2) then exit;
\r
404 if (fdhandlein < 0) or (fdhandleout < 0) then raise exception.create('internalclose called with invalid fd handles');
\r
405 eventcore.rmasterclr(fdhandlein);//fd_clr(fdhandlein,fdsrmaster);
\r
406 eventcore.wmasterclr(fdhandleout);//fd_clr(fdhandleout,fdswmaster);
\r
408 if closehandles then begin
\r
410 //anyone remember why this is here? --plugwash
\r
411 fcntl(fdhandlein,F_SETFL,0);
\r
413 myfdclose(fdhandlein);
\r
414 if fdhandleout <> fdhandlein then begin
\r
416 fcntl(fdhandleout,F_SETFL,0);
\r
418 myfdclose(fdhandleout);
\r
420 eventcore.setfdreverse(fdhandlein,nil);
\r
421 eventcore.setfdreverse(fdhandleout,nil);
\r
428 if assigned(onsessionclosed) then if not destroying then onsessionclosed(self,error);
\r
430 if assigned(sendq) then sendq.del(maxlongint);
\r
434 {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
\r
435 { All exceptions *MUST* be handled. If an exception is not handled, the }
\r
436 { application will most likely be shut down ! }
\r
437 procedure tlasio.HandleBackGroundException(E: Exception);
\r
439 CanAbort : Boolean;
\r
442 { First call the error event handler, if any }
\r
443 if Assigned(OnBgException) then begin
\r
445 OnBgException(Self, E, CanAbort);
\r
449 { Then abort the socket }
\r
450 if CanAbort then begin
\r
458 procedure tlasio.sendstr(const str : tbufferstring);
\r
460 putstringinsendbuffer(str);
\r
464 procedure tlasio.putstringinsendbuffer(const newstring : tbufferstring);
\r
466 if newstring <> '' then putdatainsendbuffer(@newstring[1],length(newstring));
\r
469 function tlasio.send(data:pointer;len:integer):integer;
\r
471 if state <> wsconnected then begin
\r
475 if len < 0 then len := 0;
\r
477 putdatainsendbuffer(data,len);
\r
482 procedure tlasio.putdatainsendbuffer(data:pointer;len:integer);
\r
484 sendq.add(data,len);
\r
487 function tlasio.sendflush : integer;
\r
491 // fdstestr : fdset;
\r
492 // fdstestw : fdset;
\r
494 if state <> wsconnected then begin
\r
499 lensent := sendq.get(data,packetbasesize*2);
\r
500 if assigned(data) then result := myfdwrite(fdhandleout,data^,lensent) else result := 0;
\r
502 if result = -1 then lensent := 0 else lensent := result;
\r
504 //sendq := copy(sendq,lensent+1,length(sendq)-lensent);
\r
505 sendq.del(lensent);
\r
507 //fd_clr(fdhandleout,fdsw); // this prevents the socket being closed by a write
\r
508 // that sends nothing because a previous socket has
\r
509 // slready flushed this socket when the message loop
\r
511 // if sendq.size > 0 then begin
\r
512 eventcore.wmasterset(fdhandleout);//fd_set(fdhandleout,fdswmaster);
\r
514 // wmasterclr(fdhandleout);//fd_clr(fdhandleout,fdswmaster);
\r
516 if result > 0 then begin
\r
517 if assigned(onsenddata) then onsenddata(self,result);
\r
518 // if sendq.size=0 then if assigned(ondatasent) then begin
\r
519 // tltask.create(self.dodatasent,self,0,0);
\r
520 // //begin test code
\r
521 // fd_zero(fdstestr);
\r
522 // fd_zero(fdstestw);
\r
523 // fd_set(fdhandlein,fdstestr);
\r
524 // fd_set(fdhandleout,fdstestw);
\r
525 // select(maxs,@fdstestr,@fdstestw,nil,0);
\r
526 // writeln(fd_isset(fdhandlein,fdstestr),' ',fd_isset(fdhandleout,fdstestw));
\r
530 writtenthiscycle := true;
\r
534 procedure tlasio.dupnowatch(invalue:longint);
\r
536 { debugout('invalue='+inttostr(invalue));}
\r
538 if state<> wsclosed then close;
\r
539 fdhandlein := invalue;
\r
540 fdhandleout := invalue;
\r
541 eventcore.setfdreverse(fdhandlein,self);
\r
543 fcntl(fdhandlein,F_SETFL,OPEN_NONBLOCK);
\r
545 state := wsconnected;
\r
550 procedure tlasio.dup(invalue:longint);
\r
552 dupnowatch(invalue);
\r
553 eventcore.rmasterset(fdhandlein,false);//fd_set(fdhandlein,fdsrmaster);
\r
554 eventcore.wmasterclr(fdhandleout);//fd_clr(fdhandleout,fdswmaster);
\r
558 procedure tlasio.handlefdtrigger(readtrigger,writetrigger:boolean);
\r
560 sendflushresult : integer;
\r
561 tempbuf:array[0..receivebufsize-1] of byte;
\r
564 if (state=wsconnected) and writetrigger then begin
\r
565 //writeln('write trigger');
\r
567 if (sendq.size >0) then begin
\r
569 sendflushresult := sendflush;
\r
570 if (sendflushresult <= 0) and (not writtenthiscycle) then begin
\r
571 if sendflushresult=0 then begin // linuxerror := 0;
\r
576 if getlasterror=WSAEWOULDBLOCK then begin
\r
577 //the asynchronous nature of windows messages means we sometimes
\r
578 //get here with the buffer full
\r
579 //so do nothing in that case
\r
583 internalclose({$ifdef win32}getlasterror{$else}linuxerror{$endif});
\r
589 //everything is sent fire off ondatasent event
\r
590 if fdhandleout >= 0 then eventcore.wmasterclr(fdhandleout);//fd_clr(fdhandleout,fdswmaster);
\r
591 if assigned(ondatasent) then tltask.create(self.dodatasent,self,0,0);
\r
593 if assigned(onfdwrite) then onfdwrite(self,0);
\r
595 writtenthiscycle := false;
\r
596 if (state =wsconnected) and readtrigger then begin
\r
597 if recvq.size=0 then begin
\r
599 if (a <= 0) or (a > sizeof(tempbuf)) then a := sizeof(tempbuf);
\r
600 numread := myfdread(fdhandlein,tempbuf,a);
\r
601 if (numread=0) and (not mustrefreshfds) then begin
\r
602 {if i remember correctly numread=0 is caused by eof
\r
603 if this isn't dealt with then you get a cpu eating infinite loop
\r
604 however if onsessionconencted has called processmessages that could
\r
605 cause us to drop to here with an empty recvq and nothing left to read
\r
606 and we don't want that to cause the socket to close}
\r
609 end else if (numread=-1) then begin
\r
611 //sometimes on windows we get stale messages due to the inherent delays
\r
612 //in the windows message queue
\r
613 if WSAGetLastError = wsaewouldblock then begin
\r
619 internalclose({$ifdef win32}wsagetlasterror{$else}linuxerror{$endif});
\r
621 end else if numread > 0 then recvq.add(@tempbuf,numread);
\r
624 if recvq.size > 0 then begin
\r
625 if wsonoreceiveloop in componentoptions then eventcore.rmasterclr(fdhandlein); //fd_clr(fdhandlein,fdsrmaster);
\r
626 if assigned(ondataavailable) then ondataAvailable(self,0);
\r
627 if not (wsonoreceiveloop in componentoptions) then if recvq.size > 0 then
\r
628 tltask.create(self.doreceiveloop,self,0,0);
\r
630 //until (numread = 0) or (currentsocket.state<>wsconnected);
\r
631 { debugout('inner loop complete');}
\r
635 procedure tlasio.flush;
\r
637 type fdset = tfdset;
\r
643 fd_set(fdhandleout,fds);
\r
644 while sendq.size>0 do begin
\r
645 select(fdhandleout+1,nil,@fds,nil,nil);
\r
646 if sendflush <= 0 then exit;
\r
650 procedure tlasio.dodatasent(wparam,lparam:longint);
\r
652 if assigned(ondatasent) then ondatasent(self,lparam);
\r
655 procedure tlasio.deletebuffereddata;
\r
657 sendq.del(maxlongint);
\r
660 procedure tlasio.sinkdata(sender:tobject;error:word);
\r
662 tlasio(sender).recvq.del(maxlongint);
\r
666 procedure tltimer.resettimes;
\r
668 gettimeofday(nextts);
\r
669 {if not initialevent then} tv_add(nextts,interval);
\r
673 {procedure tltimer.setinitialevent(newvalue : boolean);
\r
675 if newvalue <> finitialevent then begin
\r
676 finitialevent := newvalue;
\r
677 if assigned(timerwrapperinterface) then begin
\r
678 timerwrapperinterface.setinitialevent(wrappedtimer,newvalue);
\r
685 procedure tltimer.setontimer(newvalue:tnotifyevent);
\r
687 if @newvalue <> @fontimer then begin
\r
688 fontimer := newvalue;
\r
689 if assigned(timerwrapperinterface) then begin
\r
690 timerwrapperinterface.setontimer(wrappedtimer,newvalue);
\r
699 procedure tltimer.setenabled(newvalue : boolean);
\r
701 if newvalue <> fenabled then begin
\r
702 fenabled := newvalue;
\r
703 if assigned(timerwrapperinterface) then begin
\r
704 timerwrapperinterface.setenabled(wrappedtimer,newvalue);
\r
707 raise exception.create('non wrapper timers are not permitted on windows');
\r
715 procedure tltimer.setinterval(newvalue:integer);
\r
717 if newvalue <> finterval then begin
\r
718 finterval := newvalue;
\r
719 if assigned(timerwrapperinterface) then begin
\r
720 timerwrapperinterface.setinterval(wrappedtimer,newvalue);
\r
723 raise exception.create('non wrapper timers are not permitted on windows');
\r
735 constructor tltimer.create;
\r
737 inherited create(AOwner);
\r
738 if assigned(timerwrapperinterface) then begin
\r
739 wrappedtimer := timerwrapperinterface.createwrappedtimer;
\r
743 nexttimer := firsttimer;
\r
746 if assigned(nexttimer) then nexttimer.prevtimer := self;
\r
747 firsttimer := self;
\r
753 destructor tltimer.destroy;
\r
755 if assigned(timerwrapperinterface) then begin
\r
758 if prevtimer <> nil then begin
\r
759 prevtimer.nexttimer := nexttimer;
\r
761 firsttimer := nexttimer;
\r
763 if nexttimer <> nil then begin
\r
764 nexttimer.prevtimer := prevtimer;
\r
771 constructor tltask.create(ahandler:ttaskevent;aobj:tobject;awparam,alparam:longint);
\r
774 if assigned(onaddtask) then onaddtask(ahandler,aobj,awparam,alparam);
\r
775 handler := ahandler;
\r
779 {nexttask := firsttask;
\r
780 firsttask := self;}
\r
781 if assigned(lasttask) then begin
\r
782 lasttask.nexttask := self;
\r
787 //ahandler(wparam,lparam);
\r
790 procedure addtask(ahandler:ttaskevent;aobj:tobject;awparam,alparam:longint);
\r
793 tltask.create(ahandler,aobj,awparam,alparam);
\r
797 procedure prepsigpipe;{$ifndef ver1_0}inline;
\r
800 starthandlesignal(sigpipe);
\r
801 if not assigned(signalloopback) then begin
\r
802 signalloopback := tlloopback.create(nil);
\r
803 signalloopback.ondataAvailable := signalloopback.sinkdata;
\r
810 procedure processtasks;//inline;
\r
812 temptask : tltask ;
\r
816 if not assigned(currenttask) then begin
\r
817 currenttask := firsttask;
\r
821 while assigned(currenttask) do begin
\r
823 if assigned(currenttask.handler) then currenttask.handler(currenttask.wparam,currenttask.lparam);
\r
824 if assigned(currenttask) then begin
\r
825 temptask := currenttask;
\r
826 currenttask := currenttask.nexttask;
\r
829 //writeln('processed a task');
\r
837 procedure disconnecttasks(aobj:tobject);
\r
839 currenttasklocal : tltask ;
\r
842 for counter := 0 to 1 do begin
\r
843 if counter = 0 then begin
\r
844 currenttasklocal := firsttask; //main list of tasks
\r
846 currenttasklocal := currenttask; //needed in case called from a task
\r
848 // note i don't bother to sestroy the links here as that will happen when
\r
849 // the list of tasks is processed anyway
\r
850 while assigned(currenttasklocal) do begin
\r
851 if currenttasklocal.obj = aobj then begin
\r
852 currenttasklocal.obj := nil;
\r
853 currenttasklocal.handler := nil;
\r
855 currenttasklocal := currenttasklocal.nexttask;
\r
861 procedure processmessages;
\r
863 eventcore.processmessages;
\r
865 procedure messageloop;
\r
867 eventcore.messageloop;
\r
870 procedure exitmessageloop;
\r
872 eventcore.exitmessageloop;
\r
875 function tlasio.RealSend(Data : Pointer; Len : Integer) : Integer;
\r
877 result := myfdwrite(fdhandleout,data^,len);
\r
878 if (result > 0) and assigned(onsenddata) then onsenddata(self,result);
\r
879 eventcore.wmasterset(fdhandleout);
\r
882 procedure tlasio.myfdclose(fd : integer);
\r
886 function tlasio.myfdwrite(fd: LongInt;const buf;size: LongInt):LongInt;
\r
888 result := fdwrite(fd,buf,size);
\r
891 function tlasio.myfdread(fd: LongInt;var buf;size: LongInt):LongInt;
\r
893 result := fdread(fd,buf,size);
\r
905 signalloopback := nil;
\r