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
24 baseunix,unix,unixutil,
\r
29 exitloopflag : boolean ; {if set by app, exit mainloop}
\r
31 function getfdsrmaster : fdset; {$ifdef useinline}inline;{$endif}
\r
32 function getfdswmaster : fdset; {$ifdef useinline}inline;{$endif}
\r
34 procedure lcoreinit;
\r
39 classes,pgtypes,bfifo,
\r
44 {$include unixstuff.inc}
\r
45 {$include ltimevalstuff.inc}
\r
48 absoloutemaxs_select = (sizeof(fdset)*8)-1;
\r
51 fdreverse:array[0..absoloutemaxs_select] of tlasio;
\r
53 tselecteventcore=class(teventcore)
\r
55 procedure processmessages; override;
\r
56 procedure messageloop; override;
\r
57 procedure exitmessageloop;override;
\r
58 procedure setfdreverse(fd : integer;reverseto : tlasio); override;
\r
59 procedure rmasterset(fd : integer;islistensocket : boolean); override;
\r
60 procedure rmasterclr(fd: integer); override;
\r
61 procedure wmasterset(fd : integer); override;
\r
62 procedure wmasterclr(fd: integer); override;
\r
65 procedure processtimers;inline;
\r
67 tv ,tvnow : ttimeval ;
\r
68 currenttimer : tltimer ;
\r
69 temptimer : tltimer ;
\r
72 gettimeofday(tvnow);
\r
73 currenttimer := firsttimer;
\r
74 while assigned(currenttimer) do begin
\r
75 //writeln(currenttimer.enabled);
\r
76 if tv_compare(tvnow,ttimeval(currenttimer.nextts)) and currenttimer.enabled then begin
\r
77 //if assigned(currenttimer.ontimer) then begin
\r
78 // if currenttimer.enabled then if currenttimer.initialevent or currenttimer.initialdone then currenttimer.ontimer(currenttimer);
\r
79 // currenttimer.initialdone := true;
\r
81 if assigned(currenttimer.ontimer) then currenttimer.ontimer(currenttimer);
\r
82 currenttimer.nextts := timeval(tvnow);
\r
83 tv_add(ttimeval(currenttimer.nextts),currenttimer.interval);
\r
85 temptimer := currenttimer;
\r
86 currenttimer := currenttimer.nexttimer;
\r
87 if temptimer.released then temptimer.free;
\r
91 procedure processasios(var fdsr,fdsw:fdset);//inline;
\r
93 currentsocket : tlasio ;
\r
94 tempsocket : tlasio ;
\r
95 socketcount : integer ; // for debugging perposes :)
\r
98 { inc(lcoretestcount);}
\r
100 //the message loop will exit if all lasio's and ltimer's and lsignal's are destroyed
\r
101 //if (not assigned(firstasin)) and (not assigned(firsttimer)) and (not assigned(firstsignal)) then exit;
\r
104 {------- test optimised loop}
\r
106 for dw := (maxs shr 5) downto 0 do if (fdsr[dw] or fdsw[dw]) <> 0 then begin
\r
107 for bt := 0 to 31 do if (fdsr[dw] or fdsw[dw]) and (1 shl bt) <> 0 then begin
\r
109 currentsocket := fdreverse[dw shl 5 or bt];
\r
110 {if not assigned(currentsocket) then raise exception.create('currentsocket not assigned');
\r
111 if currentsocket.fdhandlein < 0 then raise exception.create('currentsocket.fdhandlein out of range');}
\r
112 {i've seen the out of range case actually happening, so it can happen. test: just close the fd - beware}
\r
113 if not assigned(currentsocket) then begin
\r
114 fdclose(dw shl 5 or bt);
\r
117 if currentsocket.fdhandlein < 0 then begin
\r
118 fdclose(dw shl 5 or bt);
\r
122 currentsocket.handlefdtrigger(fd_isset(currentsocket.fdhandlein,fdsr),fd_isset(currentsocket.fdhandleout,fdsw));
\r
124 on E: exception do begin
\r
125 currentsocket.HandleBackGroundException(e);
\r
129 if mustrefreshfds then begin
\r
130 if select(maxs+1,@fdsr,@fdsw,nil,0) <= 0 then begin
\r
138 if asinreleaseflag then begin
\r
139 asinreleaseflag := false;
\r
140 currentsocket := firstasin;
\r
141 while assigned(currentsocket) do begin
\r
142 tempsocket := currentsocket;
\r
143 currentsocket := currentsocket.nextasin;
\r
144 if tempsocket.released then begin
\r
151 - sockets which are released may not be freed because theyre never processed by the loop
\r
152 made new code for handling this, using asinreleaseflag
\r
154 - when/why does the mustrefreshfds select apply, sheck if i did it correctly?
\r
156 - what happens if calling handlefdtrigger for a socket which does not have an event
\r
158 {------- original loop}
\r
161 currentsocket := firstasin;
\r
163 while assigned(currentsocket) do begin
\r
164 if mustrefreshfds then begin
\r
165 if select(maxs,@fdsr,@fdsw,nil,0) <= 0 then begin
\r
171 if fd_isset(currentsocket.fdhandlein,fdsr) or fd_isset(currentsocket.fdhandleout,fdsw) then begin
\r
172 currentsocket.handlefdtrigger(fd_isset(currentsocket.fdhandlein,fdsr),fd_isset(currentsocket.fdhandleout,fdsw));
\r
175 on E: exception do begin
\r
176 currentsocket.HandleBackGroundException(e);
\r
179 tempsocket := currentsocket;
\r
180 currentsocket := currentsocket.nextasin;
\r
182 if tempsocket.released then begin
\r
186 { debugout('socketcount='+inttostr(socketcount));}
\r
189 procedure tselecteventcore.processmessages;
\r
191 fdsr , fdsw : fdset ;
\r
192 selectresult : longint ;
\r
194 mustrefreshfds := false;
\r
198 selectresult := select(maxs+1,@fdsr,@fdsw,nil,0);
\r
199 while (selectresult>0) or assigned(firsttask) or assigned(currenttask) do begin;
\r
203 if selectresult > 0 then begin
\r
204 processasios(fdsr,fdsw);
\r
206 selectresult := select(maxs+1,@fdsr,@fdsw,nil,0);
\r
209 mustrefreshfds := true;
\r
214 FDSR , FDSW : fdset;
\r
217 fdsrmaster , fdswmaster : fdset ;
\r
219 function getfdsrmaster : fdset; {$ifdef fpc}inline;{$endif}
\r
221 result := fdsrmaster;
\r
223 function getfdswmaster : fdset; {$ifdef fpc}inline;{$endif}
\r
225 result := fdswmaster;
\r
229 Function doSelect(timeOut:PTimeVal):longint;//inline;
\r
231 localtimeval : ttimeval;
\r
232 maxslocal : integer;
\r
235 //zeromemory(@sset,sizeof(sset));
\r
237 fdsr := getfdsrmaster;
\r
238 fdsw := getfdswmaster;
\r
240 if assigned(firsttask) then begin
\r
241 localtimeval.tv_sec := 0;
\r
242 localtimeval.tv_usec := 0;
\r
243 timeout := @localtimeval;
\r
247 mustrefreshfds := false;
\r
248 { debugout('about to call select');}
\r
250 sigprocmask(SIG_UNBLOCK,@blockset,nil);
\r
252 result := select(maxslocal+1,@FDSR,@FDSW,nil,timeout);
\r
253 if result <= 0 then begin
\r
256 if result=-1 then begin
\r
257 if linuxerror = SYS_EINTR then begin
\r
258 // we received a signal it's not a problem
\r
260 raise esocketexception.create('select returned error '+inttostr(linuxerror));
\r
265 sigprocmask(SIG_BLOCK,@blockset,nil);
\r
267 { debugout('select complete');}
\r
270 procedure tselecteventcore.exitmessageloop;
\r
272 exitloopflag := true
\r
277 procedure tselecteventcore.messageloop;
\r
279 tv ,tvnow : ttimeval ;
\r
280 currenttimer : tltimer ;
\r
281 selectresult:integer;
\r
286 {currentsocket := firstasin;
\r
287 if not assigned(currentsocket) then exit; //the message loop will exit if all lsockets are destroyed
\r
290 if currentsocket.state = wsconnected then currentsocket.sendflush;
\r
291 currentsocket := currentsocket.nextasin;
\r
292 until not assigned(currentsocket);}
\r
297 //the message loop will exit if all lasio's and ltimer's and lsignal's are destroyed
\r
298 if (not assigned(firstasin)) and (not assigned(firsttimer)) {$ifndef nosignal} and (not assigned(firstsignal)){$endif} then exit;
\r
301 currentsocket := firstasin;
\r
302 if not assigned(currentsocket) then exit; //the message loop will exit if all lsockets are destroyed
\r
305 if (not currentsocket.released) and (currentsocket.state<>wsclosed) then fd_set(currentsocket.fdhandlein,fdsr);
\r
306 if (not currentsocket.released) and (currentsocket.state=wsconnecting) then fd_set(currentsocket.fdhandleout,fdsw);
\r
307 if currentsocket is tlsocket then begin
\r
308 if (not currentsocket.released) and (currentsocket.state=wsconnected) and(tlsocket(currentsocket).sendq <> '') then fd_set(currentsocket.fdhandleout,fdsw);
\r
310 tempsocket := currentsocket;
\r
311 currentsocket := currentsocket.nextasin;
\r
312 if tempsocket.released then begin
\r
315 until not assigned(currentsocket);
\r
318 //currenttask := nil;
\r
320 //if assigned(firsttimer) then begin
\r
321 // tv.tv_sec := maxlongint;
\r
322 tv := tv_invalidtimebig;
\r
323 currenttimer := firsttimer;
\r
324 while assigned(currenttimer) do begin
\r
325 if tv_compare(tv,currenttimer.nextts) and currenttimer.enabled then tv := currenttimer.nextts;
\r
326 currenttimer := currenttimer.nexttimer;
\r
330 if tv_compare(tv,tv_invalidtimebig) then begin
\r
331 //writeln('no timers active');
\r
332 if exitloopflag then break;
\r
334 selectresult := doselect(nil);
\r
337 gettimeofday(tvnow);
\r
338 tv_substract(tv,tvnow);
\r
340 //writeln('timers active');
\r
341 if tv.tv_sec < 0 then begin
\r
343 tv.tv_usec := 0; {0.1 sec}
\r
345 if exitloopflag then break;
\r
347 selectresult := doselect(@tv);
\r
351 if selectresult > 0 then processasios(fdsr,fdsw);
\r
352 {!!!only call processasios if select has asio events -beware}
\r
354 {artificial delay to throttle the number of processasios per second possible and reduce cpu usage}
\r
359 procedure tselecteventcore.rmasterset(fd : integer;islistensocket : boolean);
\r
361 if fd > absoloutemaxs then raise esocketexception.create('file discriptor out of range');
\r
362 if fd > maxs then maxs := fd;
\r
363 if fd_isset(fd,fdsrmaster) then exit;
\r
364 fd_set(fd,fdsrmaster);
\r
368 procedure tselecteventcore.rmasterclr(fd: integer);
\r
370 if not fd_isset(fd,fdsrmaster) then exit;
\r
371 fd_clr(fd,fdsrmaster);
\r
376 procedure tselecteventcore.wmasterset(fd : integer);
\r
378 if fd > absoloutemaxs then raise esocketexception.create('file discriptor out of range');
\r
379 if fd > maxs then maxs := fd;
\r
381 if fd_isset(fd,fdswmaster) then exit;
\r
382 fd_set(fd,fdswmaster);
\r
386 procedure tselecteventcore.wmasterclr(fd: integer);
\r
388 if not fd_isset(fd,fdswmaster) then exit;
\r
389 fd_clr(fd,fdswmaster);
\r
392 procedure tselecteventcore.setfdreverse(fd : integer;reverseto : tlasio);
\r
394 fdreverse[fd] := reverseto;
\r
400 procedure lcoreinit;
\r
402 if inited then exit;
\r
404 eventcore := tselecteventcore.create;
\r
406 absoloutemaxs := absoloutemaxs_select;
\r
409 fd_zero(fdsrmaster);
\r
410 fd_zero(fdswmaster);
\r