levents.pp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623
  1. { lNet Events abstration
  2. CopyRight (C) 2006-2008 Ales Katona
  3. This library is Free software; you can rediStribute it and/or modify it
  4. under the terms of the GNU Library General Public License as published by
  5. the Free Software Foundation; either version 2 of the License, or (at your
  6. option) any later version.
  7. This program is diStributed in the hope that it will be useful, but WITHOUT
  8. ANY WARRANTY; withOut even the implied warranty of MERCHANTABILITY or
  9. FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
  10. for more details.
  11. You should have received a Copy of the GNU Library General Public License
  12. along with This library; if not, Write to the Free Software Foundation,
  13. Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  14. This license has been modified. See File LICENSE.ADDON for more inFormation.
  15. Should you find these sources without a LICENSE File, please contact
  16. me at [email protected]
  17. }
  18. unit lEvents;
  19. {$mode objfpc}{$H+}
  20. {$inline on}
  21. {$define nochoice} // let's presume we don't have "optimized" eventer
  22. interface
  23. uses
  24. {$ifdef Linux}
  25. {$undef nochoice} // undefine for all "Optimized" targets
  26. Linux, Contnrs, Errors,
  27. {$endif}
  28. {$ifdef BSD}
  29. {$undef nochoice}
  30. BSD, Errors,
  31. {$endif}
  32. {$i sys/osunits.inc}
  33. type
  34. TLHandle = class;
  35. TLEventer = class;
  36. TLHandleEvent = procedure (aHandle: TLHandle) of object;
  37. TLHandleErrorEvent = procedure (aHandle: TLHandle; const msg: string) of object;
  38. TLEventerErrorEvent = procedure (const msg: string; Sender: TLEventer) of object;
  39. { TLHandle }
  40. TLHandle = class(TObject)
  41. protected
  42. FHandle: THandle;
  43. FEventer: TLEventer; // "queue holder"
  44. FOnRead: TLHandleEvent;
  45. FOnWrite: TLHandleEvent;
  46. FOnError: TLHandleErrorEvent;
  47. FIgnoreWrite: Boolean; // so we can do edge-triggered
  48. FIgnoreRead: Boolean; // so we can do edge-triggered
  49. FIgnoreError: Boolean; // so we can do edge-triggered
  50. FDispose: Boolean; // will free in the after-cycle
  51. FFreeing: Boolean; // used to see if it's in the "to be freed" list
  52. FPrev: TLHandle;
  53. FNext: TLHandle;
  54. FFreeNext: TLHandle;
  55. FInternalData: Pointer;
  56. procedure SetIgnoreError(const aValue: Boolean);
  57. procedure SetIgnoreWrite(const aValue: Boolean);
  58. procedure SetIgnoreRead(const aValue: Boolean);
  59. public
  60. UserData: Pointer;
  61. constructor Create; virtual;
  62. destructor Destroy; override;
  63. procedure Free; virtual; // this is a trick
  64. property Prev: TLHandle read FPrev write FPrev;
  65. property Next: TLHandle read FNext write FNext;
  66. property FreeNext: TLHandle read FFreeNext write FFreeNext;
  67. property IgnoreWrite: Boolean read FIgnoreWrite write SetIgnoreWrite;
  68. property IgnoreRead: Boolean read FIgnoreRead write SetIgnoreRead;
  69. property IgnoreError: Boolean read FIgnoreError write SetIgnoreError;
  70. property OnRead: TLHandleEvent read FOnRead write FOnRead;
  71. property OnWrite: TLHandleEvent read FOnWrite write FOnWrite;
  72. property OnError: TLHandleErrorEvent read FOnError write FOnError;
  73. property Dispose: Boolean read FDispose write FDispose;
  74. property Handle: THandle read FHandle write FHandle;
  75. property Eventer: TLEventer read FEventer;
  76. end;
  77. { TLTimer }
  78. {
  79. TLTimer = class(TObject)
  80. protected
  81. FOnTimer: TNotifyEvent;
  82. FInterval: TDateTime;
  83. FTimeout: TDateTime;
  84. FPeriodic: Boolean;
  85. FEnabled: Boolean;
  86. FNext: TLTimer;
  87. function GetInterval: Integer;
  88. procedure SetEnabled(NewEnabled: Boolean);
  89. procedure SetInterval(NewInterval: Integer);
  90. public
  91. procedure CallAction;
  92. property Enabled: Boolean read FEnabled write SetEnabled;
  93. property Interval: Integer read GetInterval write SetInterval;
  94. property Periodic: Boolean read FPeriodic write FPeriodic;
  95. property OnTimer: TNotifyEvent read FOnTimer write FOnTimer;
  96. end;
  97. }
  98. { TLTimeoutManager }
  99. {
  100. TLSetTimeout = procedure(NewTimeout: DWord) of object;
  101. TLTimeoutManager = class
  102. protected
  103. FFirst: TLTimer;
  104. FLast: TLTimer;
  105. FTimeout: DWord;
  106. FSetTimeout: TLSetTimeout;
  107. public
  108. destructor Destroy; override;
  109. procedure AddTimer(ATimer: TLTimer);
  110. procedure RemoveTimer(ATimer: TLTimer);
  111. procedure CallAction;
  112. end;
  113. }
  114. { TLEventer }
  115. TLEventer = class
  116. protected
  117. FRoot: TLHandle;
  118. FCount: Integer;
  119. FOnError: TLEventerErrorEvent;
  120. FReferences: Integer;
  121. FFreeRoot: TLHandle; // the root of "free" list if any
  122. FFreeIter: TLHandle; // the last of "free" list if any
  123. FInLoop: Boolean;
  124. function GetCount: Integer; virtual;
  125. function GetTimeout: Integer; virtual;
  126. procedure SetTimeout(const Value: Integer); virtual;
  127. function Bail(const msg: string; const Ernum: Integer): Boolean;
  128. procedure AddForFree(aHandle: TLHandle);
  129. procedure FreeHandles;
  130. procedure HandleIgnoreError(aHandle: TLHandle); virtual;
  131. procedure HandleIgnoreWrite(aHandle: TLHandle); virtual;
  132. procedure HandleIgnoreRead(aHandle: TLHandle); virtual;
  133. function GetInternalData(aHandle: TLHandle): Pointer;
  134. procedure SetInternalData(aHandle: TLHandle; const aData: Pointer);
  135. procedure SetHandleEventer(aHandle: TLHandle);
  136. procedure InternalUnplugHandle(aHandle: TLHandle); virtual;
  137. public
  138. constructor Create; virtual;
  139. destructor Destroy; override;
  140. function AddHandle(aHandle: TLHandle): Boolean; virtual;
  141. function CallAction: Boolean; virtual;
  142. procedure RemoveHandle(aHandle: TLHandle); virtual;
  143. procedure UnplugHandle(aHandle: TLHandle);
  144. procedure UnregisterHandle(aHandle: TLHandle); virtual;
  145. procedure LoadFromEventer(aEventer: TLEventer); virtual;
  146. procedure Clear;
  147. procedure AddRef;
  148. procedure DeleteRef;
  149. property Timeout: Integer read GetTimeout write SetTimeout;
  150. property OnError: TLEventerErrorEvent read FOnError write FOnError;
  151. property Count: Integer read GetCount;
  152. end;
  153. TLEventerClass = class of TLEventer;
  154. { TLSelectEventer }
  155. TLSelectEventer = class(TLEventer)
  156. protected
  157. FTimeout: TTimeVal;
  158. FReadFDSet: TFDSet;
  159. FWriteFDSet: TFDSet;
  160. FErrorFDSet: TFDSet;
  161. function GetTimeout: Integer; override;
  162. procedure SetTimeout(const Value: Integer); override;
  163. procedure ClearSets;
  164. public
  165. constructor Create; override;
  166. function CallAction: Boolean; override;
  167. end;
  168. {$i sys/lkqueueeventerh.inc}
  169. {$i sys/lepolleventerh.inc}
  170. function BestEventerClass: TLEventerClass;
  171. implementation
  172. uses
  173. syncobjs,
  174. lCommon;
  175. var
  176. CS: TCriticalSection;
  177. { TLHandle }
  178. procedure TLHandle.SetIgnoreError(const aValue: Boolean);
  179. begin
  180. if FIgnoreError <> aValue then begin
  181. FIgnoreError := aValue;
  182. if Assigned(FEventer) then
  183. FEventer.HandleIgnoreError(Self);
  184. end;
  185. end;
  186. procedure TLHandle.SetIgnoreWrite(const aValue: Boolean);
  187. begin
  188. if FIgnoreWrite <> aValue then begin
  189. FIgnoreWrite := aValue;
  190. if Assigned(FEventer) then
  191. FEventer.HandleIgnoreWrite(Self);
  192. end;
  193. end;
  194. procedure TLHandle.SetIgnoreRead(const aValue: Boolean);
  195. begin
  196. if FIgnoreRead <> aValue then begin
  197. FIgnoreRead := aValue;
  198. if Assigned(FEventer) then
  199. FEventer.HandleIgnoreRead(Self);
  200. end;
  201. end;
  202. constructor TLHandle.Create;
  203. begin
  204. FOnRead := nil;
  205. FOnWrite := nil;
  206. FOnError := nil;
  207. UserData := nil;
  208. FEventer := nil;
  209. FPrev := nil;
  210. FNext := nil;
  211. FFreeNext := nil;
  212. FFreeing := False;
  213. FDispose := False;
  214. FIgnoreWrite := False;
  215. FIgnoreRead := False;
  216. FIgnoreError := False;
  217. end;
  218. destructor TLHandle.Destroy;
  219. begin
  220. if Assigned(FEventer) then
  221. FEventer.InternalUnplugHandle(Self);
  222. end;
  223. procedure TLHandle.Free;
  224. begin
  225. CS.Enter;
  226. if Assigned(FEventer) and FEventer.FInLoop then
  227. FEventer.AddForFree(Self)
  228. else
  229. inherited Free;
  230. CS.Leave;
  231. end;
  232. { TLTimer }
  233. {
  234. function TLTimer.GetInterval: Integer;
  235. begin
  236. Result := Round(FInterval * MSecsPerDay);
  237. end;
  238. procedure TLTimer.SetEnabled(NewEnabled: integer);
  239. begin
  240. FTimeout := Now + Interval;
  241. FEnabled := true;
  242. end;
  243. procedure TLTimer.SetInterval(const aValue: Integer);
  244. begin
  245. FInterval := AValue / MSecsPerDay;
  246. end;
  247. procedure TLTimer.CallAction;
  248. begin
  249. if FEnabled and Assigned(FOnTimer) and (Now - FStarted >= FInterval) then
  250. begin
  251. FOnTimer(Self);
  252. if not FOneShot then
  253. FStarted := Now
  254. else
  255. FEnabled := false;
  256. end;
  257. end;
  258. }
  259. { TLEventer }
  260. constructor TLEventer.Create;
  261. begin
  262. FRoot := nil;
  263. FFreeRoot := nil;
  264. FFreeIter := nil;
  265. FInLoop := False;
  266. FCount := 0;
  267. FReferences := 1;
  268. end;
  269. destructor TLEventer.Destroy;
  270. begin
  271. Clear;
  272. end;
  273. function TLEventer.GetCount: Integer;
  274. begin
  275. Result := FCount;
  276. end;
  277. function TLEventer.GetTimeout: Integer;
  278. begin
  279. Result := 0;
  280. end;
  281. procedure TLEventer.SetTimeout(const Value: Integer);
  282. begin
  283. end;
  284. function TLEventer.Bail(const msg: string; const Ernum: Integer): Boolean;
  285. begin
  286. Result := False; // always false, substitute for caller's result
  287. if Assigned(FOnError) then
  288. FOnError(msg + LStrError(Ernum), Self);
  289. end;
  290. procedure TLEventer.AddForFree(aHandle: TLHandle);
  291. begin
  292. if not aHandle.FFreeing then begin
  293. aHandle.FFreeing := True;
  294. if not Assigned(FFreeIter) then begin
  295. FFreeIter := aHandle;
  296. FFreeRoot := aHandle;
  297. end else begin
  298. FFreeIter.FreeNext := aHandle;
  299. FFreeIter := aHandle;
  300. end;
  301. end;
  302. end;
  303. procedure TLEventer.FreeHandles;
  304. var
  305. Temp, Temp2: TLHandle;
  306. begin
  307. Temp := FFreeRoot;
  308. while Assigned(Temp) do begin
  309. Temp2 := Temp.FreeNext;
  310. Temp.Free;
  311. Temp := Temp2;
  312. end;
  313. FFreeRoot := nil;
  314. FFreeIter := nil;
  315. end;
  316. procedure TLEventer.HandleIgnoreError(aHandle: TLHandle);
  317. begin
  318. end;
  319. procedure TLEventer.HandleIgnoreWrite(aHandle: TLHandle);
  320. begin
  321. end;
  322. procedure TLEventer.HandleIgnoreRead(aHandle: TLHandle);
  323. begin
  324. end;
  325. function TLEventer.GetInternalData(aHandle: TLHandle): Pointer;
  326. begin
  327. Result := aHandle.FInternalData;
  328. end;
  329. procedure TLEventer.SetInternalData(aHandle: TLHandle; const aData: Pointer);
  330. begin
  331. aHandle.FInternalData := aData;
  332. end;
  333. procedure TLEventer.SetHandleEventer(aHandle: TLHandle);
  334. begin
  335. aHandle.FEventer := Self;
  336. end;
  337. procedure TLEventer.InternalUnplugHandle(aHandle: TLHandle);
  338. begin
  339. if aHandle.FEventer = Self then begin
  340. if aHandle.FEventer.FInLoop then begin
  341. aHandle.FEventer.AddForFree(aHandle);
  342. Exit;
  343. end;
  344. aHandle.FEventer := nil; // avoid recursive AV
  345. if Assigned(aHandle.FPrev) then begin
  346. aHandle.FPrev.FNext := aHandle.FNext;
  347. if Assigned(aHandle.FNext) then
  348. aHandle.FNext.FPrev := aHandle.FPrev;
  349. end else if Assigned(aHandle.FNext) then begin
  350. aHandle.FNext.FPrev := aHandle.FPrev;
  351. if aHandle = FRoot then
  352. FRoot := aHandle.FNext;
  353. end else FRoot := nil;
  354. if FCount > 0 then
  355. Dec(FCount);
  356. end;
  357. end;
  358. function TLEventer.AddHandle(aHandle: TLHandle): Boolean;
  359. begin
  360. Result := False;
  361. if not Assigned(aHandle.FEventer) then begin
  362. if not Assigned(FRoot) then begin
  363. FRoot := aHandle;
  364. end else begin
  365. if Assigned(FRoot.FNext) then begin
  366. FRoot.FNext.FPrev := aHandle;
  367. aHandle.FNext := FRoot.FNext;
  368. end;
  369. FRoot.FNext := aHandle;
  370. aHandle.FPrev := FRoot;
  371. end;
  372. aHandle.FEventer := Self;
  373. Inc(FCount);
  374. Result := True;
  375. end;
  376. end;
  377. function TLEventer.CallAction: Boolean;
  378. begin
  379. Result := True;
  380. // override in ancestor
  381. end;
  382. procedure TLEventer.RemoveHandle(aHandle: TLHandle);
  383. begin
  384. aHandle.Free;
  385. end;
  386. procedure TLEventer.UnplugHandle(aHandle: TLHandle);
  387. begin
  388. CS.Enter;
  389. InternalUnplugHandle(aHandle);
  390. CS.Leave;
  391. end;
  392. procedure TLEventer.UnregisterHandle(aHandle: TLHandle);
  393. begin
  394. // do nothing, specific to win32 LCLEventer crap (windows is shit)
  395. end;
  396. procedure TLEventer.LoadFromEventer(aEventer: TLEventer);
  397. begin
  398. Clear;
  399. FRoot := aEventer.FRoot;
  400. FOnError := aEventer.FOnError;
  401. end;
  402. procedure TLEventer.Clear;
  403. var
  404. Temp1, Temp2: TLHandle;
  405. begin
  406. Temp1 := FRoot;
  407. Temp2 := FRoot;
  408. while Assigned(Temp2) do begin
  409. Temp1 := Temp2;
  410. Temp2 := Temp1.FNext;
  411. Temp1.Free;
  412. end;
  413. FRoot := nil;
  414. end;
  415. procedure TLEventer.AddRef;
  416. begin
  417. Inc(FReferences);
  418. end;
  419. procedure TLEventer.DeleteRef;
  420. begin
  421. if FReferences > 0 then
  422. Dec(FReferences);
  423. if FReferences = 0 then
  424. Free;
  425. end;
  426. { TLSelectEventer }
  427. constructor TLSelectEventer.Create;
  428. begin
  429. inherited Create;
  430. FTimeout.tv_sec := 0;
  431. FTimeout.tv_usec := 0;
  432. end;
  433. function TLSelectEventer.GetTimeout: Integer;
  434. begin
  435. if FTimeout.tv_sec < 0 then
  436. Result := -1
  437. else
  438. Result := (FTimeout.tv_sec * 1000) + FTimeout.tv_usec;
  439. end;
  440. procedure TLSelectEventer.SetTimeout(const Value: Integer);
  441. begin
  442. if Value >= 0 then begin
  443. FTimeout.tv_sec := Value div 1000;
  444. FTimeout.tv_usec := Value mod 1000;
  445. end else begin
  446. FTimeout.tv_sec := -1;
  447. FTimeout.tv_usec := 0;
  448. end;
  449. end;
  450. procedure TLSelectEventer.ClearSets;
  451. begin
  452. fpFD_ZERO(FReadFDSet);
  453. fpFD_ZERO(FWriteFDSet);
  454. fpFD_ZERO(FErrorFDSet);
  455. end;
  456. function TLSelectEventer.CallAction: Boolean;
  457. var
  458. Temp, Temp2: TLHandle;
  459. n: Integer;
  460. MaxHandle: THandle;
  461. TempTime: TTimeVal;
  462. begin
  463. if FInLoop then
  464. Exit;
  465. if not Assigned(FRoot) then begin
  466. Sleep(FTimeout.tv_sec * 1000 + FTimeout.tv_usec div 1000);
  467. Exit;
  468. end;
  469. FInLoop := True;
  470. Temp := FRoot;
  471. MaxHandle := 0;
  472. ClearSets;
  473. while Assigned(Temp) do begin
  474. if (not Temp.FDispose ) // handle still valid
  475. and ( (not Temp.IgnoreWrite) // check write or
  476. or (not Temp.IgnoreRead ) // check read or
  477. or (not Temp.IgnoreError)) // check for errors
  478. then begin
  479. if not Temp.IgnoreWrite then
  480. fpFD_SET(Temp.FHandle, FWriteFDSet);
  481. if not Temp.IgnoreRead then
  482. fpFD_SET(Temp.FHandle, FReadFDSet);
  483. if not Temp.IgnoreError then
  484. fpFD_SET(Temp.FHandle, FErrorFDSet);
  485. if Temp.FHandle > MaxHandle then
  486. MaxHandle := Temp.FHandle;
  487. end;
  488. Temp2 := Temp;
  489. Temp := Temp.FNext;
  490. if Temp2.FDispose then
  491. Temp2.Free;
  492. end;
  493. TempTime := FTimeout;
  494. if FTimeout.tv_sec >= 0 then
  495. n := fpSelect(MaxHandle + 1, @FReadFDSet, @FWriteFDSet, @FErrorFDSet, @TempTime)
  496. else
  497. n := fpSelect(MaxHandle + 1, @FReadFDSet, @FWriteFDSet, @FErrorFDSet, nil);
  498. if n < 0 then
  499. Bail('Error on select', LSocketError);
  500. Result := n > 0;
  501. if Result then begin
  502. Temp := FRoot;
  503. while Assigned(Temp) do begin
  504. if not Temp.FDispose and (fpFD_ISSET(Temp.FHandle, FWriteFDSet) <> 0) then
  505. if Assigned(Temp.FOnWrite) and not Temp.IgnoreWrite then
  506. Temp.FOnWrite(Temp);
  507. if not Temp.FDispose and (fpFD_ISSET(Temp.FHandle, FReadFDSet) <> 0) then
  508. if Assigned(Temp.FOnRead) and not Temp.IgnoreRead then
  509. Temp.FOnRead(Temp);
  510. if not Temp.FDispose and (fpFD_ISSET(Temp.FHandle, FErrorFDSet) <> 0) then
  511. if Assigned(Temp.FOnError) and not Temp.IgnoreError then
  512. Temp.FOnError(Temp, 'Handle error' + LStrError(LSocketError));
  513. Temp2 := Temp;
  514. Temp := Temp.FNext;
  515. if Temp2.FDispose then
  516. AddForFree(Temp2);
  517. end;
  518. end;
  519. FInLoop := False;
  520. if Assigned(FFreeRoot) then
  521. FreeHandles;
  522. end;
  523. {$i sys/lkqueueeventer.inc}
  524. {$i sys/lepolleventer.inc}
  525. {$ifdef nochoice}
  526. function BestEventerClass: TLEventerClass;
  527. begin
  528. Result := TLSelectEventer;
  529. end;
  530. {$endif}
  531. initialization
  532. CS := TCriticalSection.Create;
  533. finalization
  534. CS.Free;
  535. end.