levents.pp 14 KB

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