levents.pp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586
  1. { lNet Events abstration
  2. CopyRight (C) 2006-2007 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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,
  27. {$endif}
  28. {$ifdef BSD}
  29. {$undef nochoice}
  30. BSD,
  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 GetTimeout: Integer; virtual;
  125. procedure SetTimeout(const Value: Integer); virtual;
  126. function Bail(const msg: string; const Ernum: Integer): Boolean;
  127. procedure AddForFree(aHandle: TLHandle);
  128. procedure FreeHandles;
  129. procedure HandleIgnoreError(aHandle: TLHandle); virtual;
  130. procedure HandleIgnoreWrite(aHandle: TLHandle); virtual;
  131. procedure HandleIgnoreRead(aHandle: TLHandle); virtual;
  132. function GetInternalData(aHandle: TLHandle): Pointer;
  133. procedure SetInternalData(aHandle: TLHandle; const aData: Pointer);
  134. procedure SetHandleEventer(aHandle: TLHandle);
  135. public
  136. constructor Create; virtual;
  137. destructor Destroy; override;
  138. function AddHandle(aHandle: TLHandle): Boolean; virtual;
  139. function CallAction: Boolean; virtual;
  140. procedure RemoveHandle(aHandle: TLHandle); virtual;
  141. procedure UnplugHandle(aHandle: TLHandle); virtual;
  142. procedure UnregisterHandle(aHandle: TLHandle); virtual;
  143. procedure LoadFromEventer(aEventer: TLEventer); virtual;
  144. procedure Clear;
  145. procedure AddRef;
  146. procedure DeleteRef;
  147. property Timeout: Integer read GetTimeout write SetTimeout;
  148. property OnError: TLEventerErrorEvent read FOnError write FOnError;
  149. property Count: Integer read FCount;
  150. end;
  151. TLEventerClass = class of TLEventer;
  152. { TLSelectEventer }
  153. TLSelectEventer = class(TLEventer)
  154. protected
  155. FTimeout: TTimeVal;
  156. FReadFDSet: TFDSet;
  157. FWriteFDSet: TFDSet;
  158. FErrorFDSet: TFDSet;
  159. function GetTimeout: Integer; override;
  160. procedure SetTimeout(const Value: Integer); override;
  161. procedure ClearSets;
  162. public
  163. constructor Create; override;
  164. function CallAction: Boolean; override;
  165. end;
  166. {$i sys/lkqueueeventerh.inc}
  167. {$i sys/lepolleventerh.inc}
  168. function BestEventerClass: TLEventerClass;
  169. implementation
  170. uses
  171. lCommon;
  172. { TLHandle }
  173. procedure TLHandle.SetIgnoreError(const aValue: Boolean);
  174. begin
  175. if FIgnoreError <> aValue then begin
  176. FIgnoreError := aValue;
  177. if Assigned(FEventer) then
  178. FEventer.HandleIgnoreError(Self);
  179. end;
  180. end;
  181. procedure TLHandle.SetIgnoreWrite(const aValue: Boolean);
  182. begin
  183. if FIgnoreWrite <> aValue then begin
  184. FIgnoreWrite := aValue;
  185. if Assigned(FEventer) then
  186. FEventer.HandleIgnoreWrite(Self);
  187. end;
  188. end;
  189. procedure TLHandle.SetIgnoreRead(const aValue: Boolean);
  190. begin
  191. if FIgnoreRead <> aValue then begin
  192. FIgnoreRead := aValue;
  193. if Assigned(FEventer) then
  194. FEventer.HandleIgnoreRead(Self);
  195. end;
  196. end;
  197. constructor TLHandle.Create;
  198. begin
  199. FOnRead := nil;
  200. FOnWrite := nil;
  201. FOnError := nil;
  202. UserData := nil;
  203. FEventer := nil;
  204. FPrev := nil;
  205. FNext := nil;
  206. FFreeNext := nil;
  207. FFreeing := False;
  208. FDispose := False;
  209. FIgnoreWrite := False;
  210. FIgnoreRead := False;
  211. FIgnoreError := False;
  212. end;
  213. destructor TLHandle.Destroy;
  214. begin
  215. if Assigned(FEventer) then
  216. FEventer.UnplugHandle(Self);
  217. end;
  218. procedure TLHandle.Free;
  219. begin
  220. if Assigned(FEventer) and FEventer.FInLoop then
  221. FEventer.AddForFree(Self)
  222. else
  223. inherited Free;
  224. end;
  225. { TLTimer }
  226. {
  227. function TLTimer.GetInterval: Integer;
  228. begin
  229. Result := Round(FInterval * MSecsPerDay);
  230. end;
  231. procedure TLTimer.SetEnabled(NewEnabled: integer);
  232. begin
  233. FTimeout := Now + Interval;
  234. FEnabled := true;
  235. end;
  236. procedure TLTimer.SetInterval(const aValue: Integer);
  237. begin
  238. FInterval := AValue / MSecsPerDay;
  239. end;
  240. procedure TLTimer.CallAction;
  241. begin
  242. if FEnabled and Assigned(FOnTimer) and (Now - FStarted >= FInterval) then
  243. begin
  244. FOnTimer(Self);
  245. if not FOneShot then
  246. FStarted := Now
  247. else
  248. FEnabled := false;
  249. end;
  250. end;
  251. }
  252. { TLEventer }
  253. constructor TLEventer.Create;
  254. begin
  255. FRoot := nil;
  256. FFreeRoot := nil;
  257. FFreeIter := nil;
  258. FInLoop := False;
  259. FCount := 0;
  260. FReferences := 1;
  261. end;
  262. destructor TLEventer.Destroy;
  263. begin
  264. Clear;
  265. end;
  266. function TLEventer.GetTimeout: Integer;
  267. begin
  268. Result := 0;
  269. end;
  270. procedure TLEventer.SetTimeout(const Value: Integer);
  271. begin
  272. end;
  273. function TLEventer.Bail(const msg: string; const Ernum: Integer): Boolean;
  274. begin
  275. Result := False; // always false, substitute for caller's result
  276. if Assigned(FOnError) then
  277. FOnError(msg + LStrError(Ernum), Self);
  278. end;
  279. procedure TLEventer.AddForFree(aHandle: TLHandle);
  280. begin
  281. if not aHandle.FFreeing then begin
  282. aHandle.FFreeing := True;
  283. if not Assigned(FFreeIter) then begin
  284. FFreeIter := aHandle;
  285. FFreeRoot := aHandle;
  286. end else begin
  287. FFreeIter.FreeNext := aHandle;
  288. FFreeIter := aHandle;
  289. end;
  290. end;
  291. end;
  292. procedure TLEventer.FreeHandles;
  293. var
  294. Temp, Temp2: TLHandle;
  295. begin
  296. Temp := FFreeRoot;
  297. while Assigned(Temp) do begin
  298. Temp2 := Temp.FreeNext;
  299. Temp.Free;
  300. Temp := Temp2;
  301. end;
  302. FFreeRoot := nil;
  303. FFreeIter := nil;
  304. end;
  305. procedure TLEventer.HandleIgnoreError(aHandle: TLHandle);
  306. begin
  307. end;
  308. procedure TLEventer.HandleIgnoreWrite(aHandle: TLHandle);
  309. begin
  310. end;
  311. procedure TLEventer.HandleIgnoreRead(aHandle: TLHandle);
  312. begin
  313. end;
  314. function TLEventer.GetInternalData(aHandle: TLHandle): Pointer;
  315. begin
  316. Result := aHandle.FInternalData;
  317. end;
  318. procedure TLEventer.SetInternalData(aHandle: TLHandle; const aData: Pointer);
  319. begin
  320. aHandle.FInternalData := aData;
  321. end;
  322. procedure TLEventer.SetHandleEventer(aHandle: TLHandle);
  323. begin
  324. aHandle.FEventer := Self;
  325. end;
  326. function TLEventer.AddHandle(aHandle: TLHandle): Boolean;
  327. begin
  328. Result := False;
  329. if not Assigned(aHandle.FEventer) then begin
  330. if not Assigned(FRoot) then begin
  331. FRoot := aHandle;
  332. end else begin
  333. if Assigned(FRoot.FNext) then begin
  334. FRoot.FNext.FPrev := aHandle;
  335. aHandle.FNext := FRoot.FNext;
  336. end;
  337. FRoot.FNext := aHandle;
  338. aHandle.FPrev := FRoot;
  339. end;
  340. aHandle.FEventer := Self;
  341. Inc(FCount);
  342. Result := True;
  343. end;
  344. end;
  345. function TLEventer.CallAction: Boolean;
  346. begin
  347. Result := True;
  348. // override in ancestor
  349. end;
  350. procedure TLEventer.RemoveHandle(aHandle: TLHandle);
  351. begin
  352. aHandle.Free;
  353. end;
  354. procedure TLEventer.UnplugHandle(aHandle: TLHandle);
  355. begin
  356. if aHandle.FEventer = Self then begin
  357. aHandle.FEventer := nil; // avoid recursive AV
  358. if Assigned(aHandle.FPrev) then begin
  359. aHandle.FPrev.FNext := aHandle.FNext;
  360. if Assigned(aHandle.FNext) then
  361. aHandle.FNext.FPrev := aHandle.FPrev;
  362. end else if Assigned(aHandle.FNext) then begin
  363. aHandle.FNext.FPrev := aHandle.FPrev;
  364. if aHandle = FRoot then
  365. FRoot := aHandle.FNext;
  366. end else FRoot := nil;
  367. if FCount > 0 then
  368. Dec(FCount);
  369. end;
  370. end;
  371. procedure TLEventer.UnregisterHandle(aHandle: TLHandle);
  372. begin
  373. // do nothing, specific to win32 LCLEventer crap (windows is shit)
  374. end;
  375. procedure TLEventer.LoadFromEventer(aEventer: TLEventer);
  376. begin
  377. Clear;
  378. FRoot := aEventer.FRoot;
  379. FOnError := aEventer.FOnError;
  380. end;
  381. procedure TLEventer.Clear;
  382. var
  383. Temp1, Temp2: TLHandle;
  384. begin
  385. Temp1 := FRoot;
  386. Temp2 := FRoot;
  387. while Assigned(Temp2) do begin
  388. Temp1 := Temp2;
  389. Temp2 := Temp1.FNext;
  390. Temp1.Free;
  391. end;
  392. FRoot := nil;
  393. end;
  394. procedure TLEventer.AddRef;
  395. begin
  396. Inc(FReferences);
  397. end;
  398. procedure TLEventer.DeleteRef;
  399. begin
  400. if FReferences > 0 then
  401. Dec(FReferences);
  402. if FReferences = 0 then
  403. Free;
  404. end;
  405. { TLSelectEventer }
  406. constructor TLSelectEventer.Create;
  407. begin
  408. inherited Create;
  409. FTimeout.tv_sec := 0;
  410. FTimeout.tv_usec := 0;
  411. end;
  412. function TLSelectEventer.GetTimeout: Integer;
  413. begin
  414. if FTimeout.tv_sec < 0 then
  415. Result := -1
  416. else
  417. Result := (FTimeout.tv_sec * 1000) + FTimeout.tv_usec;
  418. end;
  419. procedure TLSelectEventer.SetTimeout(const Value: Integer);
  420. begin
  421. if Value >= 0 then begin
  422. FTimeout.tv_sec := Value div 1000;
  423. FTimeout.tv_usec := Value mod 1000;
  424. end else begin
  425. FTimeout.tv_sec := -1;
  426. FTimeout.tv_usec := 0;
  427. end;
  428. end;
  429. procedure TLSelectEventer.ClearSets;
  430. begin
  431. fpFD_ZERO(FReadFDSet);
  432. fpFD_ZERO(FWriteFDSet);
  433. fpFD_ZERO(FErrorFDSet);
  434. end;
  435. function TLSelectEventer.CallAction: Boolean;
  436. var
  437. Temp, Temp2: TLHandle;
  438. MaxHandle, n: Integer;
  439. TempTime: TTimeVal;
  440. begin
  441. if FInLoop then
  442. Exit;
  443. if not Assigned(FRoot) then begin
  444. Sleep(FTimeout.tv_sec * 1000 + FTimeout.tv_usec div 1000);
  445. Exit;
  446. end;
  447. FInLoop := True;
  448. Temp := FRoot;
  449. MaxHandle := 0;
  450. ClearSets;
  451. while Assigned(Temp) do begin
  452. if (not Temp.FDispose ) // handle still valid
  453. and ( (not Temp.IgnoreWrite) // check write or
  454. or (not Temp.IgnoreRead ) // check read or
  455. or (not Temp.IgnoreError)) // check for errors
  456. then begin
  457. if not Temp.IgnoreWrite then
  458. fpFD_SET(Temp.FHandle, FWriteFDSet);
  459. if not Temp.IgnoreRead then
  460. fpFD_SET(Temp.FHandle, FReadFDSet);
  461. if not Temp.IgnoreError then
  462. fpFD_SET(Temp.FHandle, FErrorFDSet);
  463. if Temp.FHandle > MaxHandle then
  464. MaxHandle := Temp.FHandle;
  465. end;
  466. Temp2 := Temp;
  467. Temp := Temp.FNext;
  468. if Temp2.FDispose then
  469. Temp2.Free;
  470. end;
  471. TempTime := FTimeout;
  472. if FTimeout.tv_sec >= 0 then
  473. n := fpSelect(MaxHandle + 1, @FReadFDSet, @FWriteFDSet, @FErrorFDSet, @TempTime)
  474. else
  475. n := fpSelect(MaxHandle + 1, @FReadFDSet, @FWriteFDSet, @FErrorFDSet, nil);
  476. if n < 0 then
  477. Bail('Error on select', LSocketError);
  478. Result := n > 0;
  479. if Result then begin
  480. Temp := FRoot;
  481. while Assigned(Temp) do begin
  482. if not Temp.FDispose and (fpFD_ISSET(Temp.FHandle, FWriteFDSet) <> 0) then
  483. if Assigned(Temp.FOnWrite) and not Temp.IgnoreWrite then
  484. Temp.FOnWrite(Temp);
  485. if not Temp.FDispose and (fpFD_ISSET(Temp.FHandle, FReadFDSet) <> 0) then
  486. if Assigned(Temp.FOnRead) and not Temp.IgnoreRead then
  487. Temp.FOnRead(Temp);
  488. if not Temp.FDispose and (fpFD_ISSET(Temp.FHandle, FErrorFDSet) <> 0) then
  489. if Assigned(Temp.FOnError) and not Temp.IgnoreError then
  490. Temp.FOnError(Temp, 'Handle error' + LStrError(LSocketError));
  491. Temp2 := Temp;
  492. Temp := Temp.FNext;
  493. if Temp2.FDispose then
  494. AddForFree(Temp2);
  495. end;
  496. end;
  497. FInLoop := False;
  498. if Assigned(FFreeRoot) then
  499. FreeHandles;
  500. end;
  501. {$i sys/lkqueueeventer.inc}
  502. {$i sys/lepolleventer.inc}
  503. {$ifdef nochoice}
  504. function BestEventerClass: TLEventerClass;
  505. begin
  506. Result := TLSelectEventer;
  507. end;
  508. {$endif}
  509. end.