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,sockets,
\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
45 {$include unixstuff.inc}
\r
48 absolutemaxs_select = (sizeof(fdset)*8)-1;
\r
51 fdreverse:array[0..absolutemaxs_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
68 currenttimer : tltimer ;
\r
69 temptimer : tltimer ;
\r
72 gettimemonotonic(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
90 procedure processasios(var fdsr,fdsw:fdset);//inline;
\r
92 currentsocket : tlasio ;
\r
93 socketcount : integer ; // for debugging purposes :)
\r
95 currentfdword:fdword;
\r
98 //writeln('entering processasios');
\r
99 { inc(lcoretestcount);}
\r
101 //the message loop will exit if all lasios and ltimers and lsignals are destroyed
\r
102 //if (not assigned(firstasin)) and (not assigned(firsttimer)) and (not assigned(firstsignal)) then exit;
\r
105 {------- test optimised loop}
\r
107 for dw := (maxs shr fdwordshift) downto 0 do if (fdsr[dw] or fdsw[dw]) <> 0 then begin
\r
108 currentfdword := (fdsr[dw] or fdsw[dw]);
\r
109 for bt := fdwordmaxbit downto 0 do if currentfdword and (1 shl bt) <> 0 then begin
\r
111 fd := dw shl fdwordshift or bt;
\r
112 //writeln('reversing fd ',fd);
\r
113 currentsocket := fdreverse[fd];
\r
114 {if not assigned(currentsocket) then raise exception.create('currentsocket not assigned');
\r
115 if currentsocket.fdhandlein < 0 then raise exception.create('currentsocket.fdhandlein out of range');}
\r
116 {i've seen the out of range case actually happening, so it can happen. test: just close the fd - beware}
\r
117 if not assigned(currentsocket) then begin
\r
121 if currentsocket.fdhandlein < 0 then begin
\r
126 currentsocket.handlefdtrigger(fd_isset(fd,fdsr),fd_isset(fd,fdsw));
\r
128 on E: exception do begin
\r
129 currentsocket.HandleBackGroundException(e);
\r
133 if mustrefreshfds then begin
\r
134 if select(maxs+1,@fdsr,@fdsw,nil,0) <= 0 then begin
\r
144 - sockets which are released may not be freed because theyre never processed by the loop
\r
145 made new code for handling this, using asinreleaseflag
\r
147 - when/why does the mustrefreshfds select apply, check if i did it correctly?
\r
149 - what happens if calling handlefdtrigger for a socket which does not have an event
\r
151 {------- original loop}
\r
154 currentsocket := firstasin;
\r
156 while assigned(currentsocket) do begin
\r
157 if mustrefreshfds then begin
\r
158 if select(maxs,@fdsr,@fdsw,nil,0) <= 0 then begin
\r
164 if fd_isset(currentsocket.fdhandlein,fdsr) or fd_isset(currentsocket.fdhandleout,fdsw) then begin
\r
165 currentsocket.handlefdtrigger(fd_isset(currentsocket.fdhandlein,fdsr),fd_isset(currentsocket.fdhandleout,fdsw));
\r
168 on E: exception do begin
\r
169 currentsocket.HandleBackGroundException(e);
\r
172 tempsocket := currentsocket;
\r
173 currentsocket := currentsocket.nextasin;
\r
175 if tempsocket.released then begin
\r
179 { debugout('socketcount='+inttostr(socketcount));}
\r
180 //writeln('leaving processasios');
\r
183 procedure tselecteventcore.processmessages;
\r
185 fdsr , fdsw : fdset ;
\r
186 selectresult : longint ;
\r
188 mustrefreshfds := false;
\r
192 selectresult := select(maxs+1,@fdsr,@fdsw,nil,0);
\r
193 while (selectresult>0) or assigned(firsttask) do begin;
\r
197 if selectresult > 0 then begin
\r
198 processasios(fdsr,fdsw);
\r
200 selectresult := select(maxs+1,@fdsr,@fdsw,nil,0);
\r
203 mustrefreshfds := true;
\r
208 FDSR , FDSW : fdset;
\r
211 fdsrmaster , fdswmaster : fdset ;
\r
213 function getfdsrmaster : fdset; {$ifdef fpc}inline;{$endif}
\r
215 result := fdsrmaster;
\r
217 function getfdswmaster : fdset; {$ifdef fpc}inline;{$endif}
\r
219 result := fdswmaster;
\r
224 select in linux/sysV subtracts from timeout for time spent in it, but in BSD it doesn't
\r
225 enabling select_no_autotv here makes doSelect mimic the decrement behavior, in case the caller needs it
\r
226 the caller here in lcoreselect does not need it, and enabling it would have a slight perf hit.
\r
227 it is safe for this to be enabled even if the OS does it too (it will not subtract twice)
\r
228 it is currently disabled but can be enabled if needed
\r
230 {$ifndef linux}{-$define select_no_autotv}{$endif}
\r
232 Function doSelect(timeOut:PTimeVal):longint;//inline;
\r
234 localtimeval : ttimeval;
\r
235 maxslocal : integer;
\r
236 {$ifdef select_no_autotv}
\r
237 timeoutcopy,tvstart,tvend : ttimeval;
\r
241 //zeromemory(@sset,sizeof(sset));
\r
243 fdsr := getfdsrmaster;
\r
244 fdsw := getfdswmaster;
\r
246 if assigned(firsttask) then begin
\r
247 localtimeval.tv_sec := 0;
\r
248 localtimeval.tv_usec := 0;
\r
249 timeout := @localtimeval;
\r
253 mustrefreshfds := false;
\r
254 { debugout('about to call select');}
\r
256 sigprocmask(SIG_UNBLOCK,@blockset,nil);
\r
259 {$ifdef select_no_autotv}
\r
260 if assigned(timeout) then begin
\r
261 timeoutcopy.tv_sec := timeOut.tv_sec;
\r
262 timeoutcopy.tv_usec := timeOut.tv_usec;
\r
263 gettimemonotonic(tvstart);
\r
267 result := select(maxslocal+1,@FDSR,@FDSW,nil,timeout);
\r
268 if result <= 0 then begin
\r
271 if result=-1 then begin
\r
272 if linuxerror = SYS_EINTR then begin
\r
273 // we received a signal it is not a problem
\r
275 raise esocketexception.create('select returned error '+inttostr(linuxerror));
\r
278 {$ifdef select_no_autotv}
\r
279 else if (result = 0) and assigned(timeout) then begin
\r
280 //timeout reached: zero the timeval
\r
281 timeout.tv_sec := 0;
\r
282 timeout.tv_usec := 0;
\r
284 end else if assigned(timeout) then begin
\r
285 //successful result: subtract elapsed time
\r
286 gettimemonotonic(tvend);
\r
287 tv_subtract(tvend,tvstart);
\r
288 tv_subtract(timeoutcopy,tvend);
\r
289 timeout.tv_sec := timeoutcopy.tv_sec;
\r
290 timeout.tv_usec := timeoutcopy.tv_usec;
\r
291 if (timeout.tv_sec < 0) then begin
\r
292 timeout.tv_sec := 0;
\r
293 timeout.tv_usec := 0;
\r
295 {$endif} //select_no_autotv
\r
299 sigprocmask(SIG_BLOCK,@blockset,nil);
\r
301 { debugout('select complete');}
\r
304 procedure tselecteventcore.exitmessageloop;
\r
306 exitloopflag := true
\r
311 procedure tselecteventcore.messageloop;
\r
313 tv ,tvnow : ttimeval ;
\r
314 currenttimer : tltimer ;
\r
315 selectresult:integer;
\r
320 {currentsocket := firstasin;
\r
321 if not assigned(currentsocket) then exit; //the message loop will exit if all lsockets are destroyed
\r
324 if currentsocket.state = wsconnected then currentsocket.sendflush;
\r
325 currentsocket := currentsocket.nextasin;
\r
326 until not assigned(currentsocket);}
\r
331 //the message loop will exit if all lasios and ltimers and lsignals are destroyed
\r
333 //currenttask := nil;
\r
335 //if assigned(firsttimer) then begin
\r
336 // tv.tv_sec := maxlongint;
\r
337 tv := tv_invalidtimebig;
\r
338 currenttimer := firsttimer;
\r
339 while assigned(currenttimer) do begin
\r
340 if tv_compare(tv,currenttimer.nextts) and currenttimer.enabled then tv := currenttimer.nextts;
\r
341 currenttimer := currenttimer.nexttimer;
\r
345 if tv_compare(tv,tv_invalidtimebig) then begin
\r
346 //writeln('no timers active');
\r
347 if exitloopflag then break;
\r
349 selectresult := doselect(nil);
\r
352 gettimemonotonic(tvnow);
\r
353 tv_subtract(tv,tvnow);
\r
355 //writeln('timers active');
\r
356 if tv.tv_sec < 0 then begin
\r
358 tv.tv_usec := 0; {0.1 sec}
\r
360 if exitloopflag then break;
\r
362 selectresult := doselect(@tv);
\r
366 if selectresult > 0 then processasios(fdsr,fdsw);
\r
367 {!!!only call processasios if select has asio events -beware}
\r
369 {artificial delay to throttle the number of processasios per second possible and reduce cpu usage}
\r
374 procedure tselecteventcore.rmasterset(fd : integer;islistensocket : boolean);
\r
376 //writeln('rmasterset called with fd ',fd);
\r
377 if fd > absolutemaxs then raise esocketexception.create('file descriptor out of range');
\r
378 if fd > maxs then maxs := fd;
\r
379 if fd_isset(fd,fdsrmaster) then exit;
\r
380 fd_set(fd,fdsrmaster);
\r
384 procedure tselecteventcore.rmasterclr(fd: integer);
\r
386 //writeln('rmasterclr called with fd ',fd);
\r
387 if not fd_isset(fd,fdsrmaster) then exit;
\r
388 fd_clr(fd,fdsrmaster);
\r
393 procedure tselecteventcore.wmasterset(fd : integer);
\r
395 //writeln('wmasterset called with fd ',fd);
\r
396 if fd > absolutemaxs then raise esocketexception.create('file descriptor out of range');
\r
397 if fd > maxs then maxs := fd;
\r
399 if fd_isset(fd,fdswmaster) then exit;
\r
400 fd_set(fd,fdswmaster);
\r
404 procedure tselecteventcore.wmasterclr(fd: integer);
\r
406 //writeln('wmasterclr called with fd ',fd);
\r
407 if not fd_isset(fd,fdswmaster) then exit;
\r
408 fd_clr(fd,fdswmaster);
\r
411 procedure tselecteventcore.setfdreverse(fd : integer;reverseto : tlasio);
\r
413 fdreverse[fd] := reverseto;
\r
419 procedure lcoreinit;
\r
421 if inited then exit;
\r
423 eventcore := tselecteventcore.create;
\r
425 absolutemaxs := absolutemaxs_select;
\r
428 fd_zero(fdsrmaster);
\r
429 fd_zero(fdswmaster);
\r