You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

lcoreselect.pas 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382
  1. {lsocket.pas}
  2. {io and timer code by plugwash}
  3. { Copyright (C) 2005 Bas Steendijk and Peter Green
  4. For conditions of distribution and use, see copyright notice in zlib_license.txt
  5. which is included in the package
  6. ----------------------------------------------------------------------------- }
  7. {$ifdef fpc}
  8. {$ifndef ver1_0}
  9. {$define useinline}
  10. {$endif}
  11. {$endif}
  12. unit lcoreselect;
  13. interface
  14. uses
  15. {$ifdef VER1_0}
  16. linux,
  17. {$else}
  18. baseunix,unix,unixutil,
  19. {$endif}
  20. fd_utils;
  21. var
  22. maxs : longint ;
  23. exitloopflag : boolean ; {if set by app, exit mainloop}
  24. function getfdsrmaster : fdset; {$ifdef useinline}inline;{$endif}
  25. function getfdswmaster : fdset; {$ifdef useinline}inline;{$endif}
  26. procedure lcoreinit;
  27. implementation
  28. uses
  29. lcore,sysutils,
  30. classes,pgtypes,bfifo,
  31. {$ifndef nosignal}
  32. lsignal;
  33. {$endif}
  34. {$include unixstuff.inc}
  35. {$include ltimevalstuff.inc}
  36. const
  37. absoloutemaxs_select = (sizeof(fdset)*8)-1;
  38. var
  39. fdreverse:array[0..absoloutemaxs_select] of tlasio;
  40. type
  41. tselecteventcore=class(teventcore)
  42. public
  43. procedure processmessages; override;
  44. procedure messageloop; override;
  45. procedure exitmessageloop;override;
  46. procedure setfdreverse(fd : integer;reverseto : tlasio); override;
  47. procedure rmasterset(fd : integer;islistensocket : boolean); override;
  48. procedure rmasterclr(fd: integer); override;
  49. procedure wmasterset(fd : integer); override;
  50. procedure wmasterclr(fd: integer); override;
  51. end;
  52. procedure processtimers;inline;
  53. var
  54. tv ,tvnow : ttimeval ;
  55. currenttimer : tltimer ;
  56. temptimer : tltimer ;
  57. begin
  58. gettimeofday(tvnow);
  59. currenttimer := firsttimer;
  60. while assigned(currenttimer) do begin
  61. //writeln(currenttimer.enabled);
  62. if tv_compare(tvnow,ttimeval(currenttimer.nextts)) and currenttimer.enabled then begin
  63. //if assigned(currenttimer.ontimer) then begin
  64. // if currenttimer.enabled then if currenttimer.initialevent or currenttimer.initialdone then currenttimer.ontimer(currenttimer);
  65. // currenttimer.initialdone := true;
  66. //end;
  67. if assigned(currenttimer.ontimer) then currenttimer.ontimer(currenttimer);
  68. currenttimer.nextts := timeval(tvnow);
  69. tv_add(ttimeval(currenttimer.nextts),currenttimer.interval);
  70. end;
  71. temptimer := currenttimer;
  72. currenttimer := currenttimer.nexttimer;
  73. end;
  74. end;
  75. procedure processasios(var fdsr,fdsw:fdset);//inline;
  76. var
  77. currentsocket : tlasio ;
  78. tempsocket : tlasio ;
  79. socketcount : integer ; // for debugging perposes :)
  80. dw,bt:integer;
  81. begin
  82. { inc(lcoretestcount);}
  83. //the message loop will exit if all lasio's and ltimer's and lsignal's are destroyed
  84. //if (not assigned(firstasin)) and (not assigned(firsttimer)) and (not assigned(firstsignal)) then exit;
  85. {------- test optimised loop}
  86. socketcount := 0;
  87. for dw := (maxs shr 5) downto 0 do if (fdsr[dw] or fdsw[dw]) <> 0 then begin
  88. for bt := 0 to 31 do if (fdsr[dw] or fdsw[dw]) and (1 shl bt) <> 0 then begin
  89. inc(socketcount);
  90. currentsocket := fdreverse[dw shl 5 or bt];
  91. {if not assigned(currentsocket) then raise exception.create('currentsocket not assigned');
  92. if currentsocket.fdhandlein < 0 then raise exception.create('currentsocket.fdhandlein out of range');}
  93. {i've seen the out of range case actually happening, so it can happen. test: just close the fd - beware}
  94. if not assigned(currentsocket) then begin
  95. fdclose(dw shl 5 or bt);
  96. continue
  97. end;
  98. if currentsocket.fdhandlein < 0 then begin
  99. fdclose(dw shl 5 or bt);
  100. continue
  101. end;
  102. try
  103. currentsocket.handlefdtrigger(fd_isset(currentsocket.fdhandlein,fdsr),fd_isset(currentsocket.fdhandleout,fdsw));
  104. except
  105. on E: exception do begin
  106. currentsocket.HandleBackGroundException(e);
  107. end;
  108. end;
  109. if mustrefreshfds then begin
  110. if select(maxs+1,@fdsr,@fdsw,nil,0) <= 0 then begin
  111. fd_zero(fdsr);
  112. fd_zero(fdsw);
  113. end;
  114. end;
  115. end;
  116. end;
  117. {
  118. !!! issues:
  119. - sockets which are released may not be freed because theyre never processed by the loop
  120. made new code for handling this, using asinreleaseflag
  121. - when/why does the mustrefreshfds select apply, sheck if i did it correctly?
  122. - what happens if calling handlefdtrigger for a socket which does not have an event
  123. }
  124. {------- original loop}
  125. (*
  126. currentsocket := firstasin;
  127. socketcount := 0;
  128. while assigned(currentsocket) do begin
  129. if mustrefreshfds then begin
  130. if select(maxs,@fdsr,@fdsw,nil,0) <= 0 then begin
  131. fd_zero(fdsr);
  132. fd_zero(fdsw);
  133. end;
  134. end;
  135. try
  136. if fd_isset(currentsocket.fdhandlein,fdsr) or fd_isset(currentsocket.fdhandleout,fdsw) then begin
  137. currentsocket.handlefdtrigger(fd_isset(currentsocket.fdhandlein,fdsr),fd_isset(currentsocket.fdhandleout,fdsw));
  138. end;
  139. except
  140. on E: exception do begin
  141. currentsocket.HandleBackGroundException(e);
  142. end;
  143. end;
  144. tempsocket := currentsocket;
  145. currentsocket := currentsocket.nextasin;
  146. inc(socketcount);
  147. if tempsocket.released then begin
  148. tempsocket.free;
  149. end;
  150. end; *)
  151. { debugout('socketcount='+inttostr(socketcount));}
  152. end;
  153. procedure tselecteventcore.processmessages;
  154. var
  155. fdsr , fdsw : fdset ;
  156. selectresult : longint ;
  157. begin
  158. mustrefreshfds := false;
  159. {$ifndef nosignal}
  160. prepsigpipe;
  161. {$endif}
  162. selectresult := select(maxs+1,@fdsr,@fdsw,nil,0);
  163. while (selectresult>0) or assigned(firsttask) or assigned(currenttask) do begin;
  164. processtasks;
  165. processtimers;
  166. if selectresult > 0 then begin
  167. processasios(fdsr,fdsw);
  168. end;
  169. selectresult := select(maxs+1,@fdsr,@fdsw,nil,0);
  170. end;
  171. mustrefreshfds := true;
  172. end;
  173. var
  174. FDSR , FDSW : fdset;
  175. var
  176. fdsrmaster , fdswmaster : fdset ;
  177. function getfdsrmaster : fdset; {$ifdef fpc}inline;{$endif}
  178. begin
  179. result := fdsrmaster;
  180. end;
  181. function getfdswmaster : fdset; {$ifdef fpc}inline;{$endif}
  182. begin
  183. result := fdswmaster;
  184. end;
  185. Function doSelect(timeOut:PTimeVal):longint;//inline;
  186. var
  187. localtimeval : ttimeval;
  188. maxslocal : integer;
  189. begin
  190. //unblock signals
  191. //zeromemory(@sset,sizeof(sset));
  192. //sset[0] := ;
  193. fdsr := getfdsrmaster;
  194. fdsw := getfdswmaster;
  195. if assigned(firsttask) then begin
  196. localtimeval.tv_sec := 0;
  197. localtimeval.tv_usec := 0;
  198. timeout := @localtimeval;
  199. end;
  200. maxslocal := maxs;
  201. mustrefreshfds := false;
  202. { debugout('about to call select');}
  203. {$ifndef nosignal}
  204. sigprocmask(SIG_UNBLOCK,@blockset,nil);
  205. {$endif}
  206. result := select(maxslocal+1,@FDSR,@FDSW,nil,timeout);
  207. if result <= 0 then begin
  208. fd_zero(FDSR);
  209. fd_zero(FDSW);
  210. if result=-1 then begin
  211. if linuxerror = SYS_EINTR then begin
  212. // we received a signal it's not a problem
  213. end else begin
  214. raise esocketexception.create('select returned error '+inttostr(linuxerror));
  215. end;
  216. end;
  217. end;
  218. {$ifndef nosignal}
  219. sigprocmask(SIG_BLOCK,@blockset,nil);
  220. {$endif}
  221. { debugout('select complete');}
  222. end;
  223. procedure tselecteventcore.exitmessageloop;
  224. begin
  225. exitloopflag := true
  226. end;
  227. procedure tselecteventcore.messageloop;
  228. var
  229. tv ,tvnow : ttimeval ;
  230. currenttimer : tltimer ;
  231. selectresult:integer;
  232. begin
  233. {$ifndef nosignal}
  234. prepsigpipe;
  235. {$endif}
  236. {currentsocket := firstasin;
  237. if not assigned(currentsocket) then exit; //the message loop will exit if all lsockets are destroyed
  238. repeat
  239. if currentsocket.state = wsconnected then currentsocket.sendflush;
  240. currentsocket := currentsocket.nextasin;
  241. until not assigned(currentsocket);}
  242. repeat
  243. //the message loop will exit if all lasio's and ltimer's and lsignal's are destroyed
  244. processtasks;
  245. //currenttask := nil;
  246. {beware}
  247. //if assigned(firsttimer) then begin
  248. // tv.tv_sec := maxlongint;
  249. tv := tv_invalidtimebig;
  250. currenttimer := firsttimer;
  251. while assigned(currenttimer) do begin
  252. if tv_compare(tv,currenttimer.nextts) and currenttimer.enabled then tv := currenttimer.nextts;
  253. currenttimer := currenttimer.nexttimer;
  254. end;
  255. if tv_compare(tv,tv_invalidtimebig) then begin
  256. //writeln('no timers active');
  257. if exitloopflag then break;
  258. { sleep(10);}
  259. selectresult := doselect(nil);
  260. end else begin
  261. gettimeofday(tvnow);
  262. tv_substract(tv,tvnow);
  263. //writeln('timers active');
  264. if tv.tv_sec < 0 then begin
  265. tv.tv_sec := 0;
  266. tv.tv_usec := 0; {0.1 sec}
  267. end;
  268. if exitloopflag then break;
  269. { sleep(10);}
  270. selectresult := doselect(@tv);
  271. processtimers;
  272. end;
  273. if selectresult > 0 then processasios(fdsr,fdsw);
  274. {!!!only call processasios if select has asio events -beware}
  275. {artificial delay to throttle the number of processasios per second possible and reduce cpu usage}
  276. until false;
  277. end;
  278. procedure tselecteventcore.rmasterset(fd : integer;islistensocket : boolean);
  279. begin
  280. if fd > absoloutemaxs then raise esocketexception.create('file discriptor out of range');
  281. if fd > maxs then maxs := fd;
  282. if fd_isset(fd,fdsrmaster) then exit;
  283. fd_set(fd,fdsrmaster);
  284. end;
  285. procedure tselecteventcore.rmasterclr(fd: integer);
  286. begin
  287. if not fd_isset(fd,fdsrmaster) then exit;
  288. fd_clr(fd,fdsrmaster);
  289. end;
  290. procedure tselecteventcore.wmasterset(fd : integer);
  291. begin
  292. if fd > absoloutemaxs then raise esocketexception.create('file discriptor out of range');
  293. if fd > maxs then maxs := fd;
  294. if fd_isset(fd,fdswmaster) then exit;
  295. fd_set(fd,fdswmaster);
  296. end;
  297. procedure tselecteventcore.wmasterclr(fd: integer);
  298. begin
  299. if not fd_isset(fd,fdswmaster) then exit;
  300. fd_clr(fd,fdswmaster);
  301. end;
  302. procedure tselecteventcore.setfdreverse(fd : integer;reverseto : tlasio);
  303. begin
  304. fdreverse[fd] := reverseto;
  305. end;
  306. var
  307. inited:boolean;
  308. procedure lcoreinit;
  309. begin
  310. if inited then exit;
  311. inited := true;
  312. eventcore := tselecteventcore.create;
  313. absoloutemaxs := absoloutemaxs_select;
  314. maxs := 0;
  315. fd_zero(fdsrmaster);
  316. fd_zero(fdswmaster);
  317. end;
  318. end.