libasync.pp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562
  1. {
  2. $Id$
  3. libasync: Asynchronous event management
  4. Copyright (C) 2001-2002 by
  5. Areca Systems GmbH / Sebastian Guenther, [email protected]
  6. Unix implementation
  7. See the file COPYING.FPC, included in this distribution,
  8. for details about the copyright.
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  12. }
  13. unit libasync;
  14. {$MODE objfpc}
  15. interface
  16. type
  17. TAsyncData = record
  18. IsRunning, DoBreak: Boolean;
  19. HasCallbacks: Boolean; // True as long as callbacks are set
  20. FirstTimer: Pointer;
  21. FirstIOCallback: Pointer;
  22. CurIOCallback: Pointer; // current callback being processed within 'run'
  23. NextIOCallback: Pointer; // next callback to get processed within 'run'
  24. FDData: Pointer;
  25. HighestHandle: LongInt;
  26. end;
  27. {$INCLUDE libasync.inc}
  28. implementation
  29. {$ifdef VER1_0}
  30. uses Linux;
  31. {$else}
  32. Uses Unix;
  33. {$endif}
  34. const
  35. MaxHandle = SizeOf(TFDSet) * 8 - 1;
  36. type
  37. PTimerData = ^TTimerData;
  38. TTimerData = record
  39. Next: PTimerData;
  40. MSec: LongInt;
  41. NextTick: Int64;
  42. Callback: TAsyncCallback;
  43. UserData: Pointer;
  44. Periodic: Boolean;
  45. end;
  46. PIOCallbackData = ^TIOCallbackData;
  47. TIOCallbackData = record
  48. Next: PIOCallbackData;
  49. IOHandle: LongInt;
  50. ReadCallback, WriteCallback: TAsyncCallback;
  51. ReadUserData, WriteUserData: Pointer;
  52. SavedHandleFlags: LongInt;
  53. end;
  54. function InitIOCallback(Handle: TAsyncHandle; IOHandle: LongInt;
  55. ARead: Boolean; ReadCallback: TAsyncCallback; ReadUserData: Pointer;
  56. AWrite: Boolean; WriteCallback: TAsyncCallback; WriteUserData: Pointer):
  57. TAsyncResult;
  58. var
  59. Data: PIOCallbackData;
  60. i: LongInt;
  61. NeedData: Boolean;
  62. begin
  63. if IOHandle > MaxHandle then
  64. begin
  65. Result := asyncInvalidFileHandle;
  66. exit;
  67. end;
  68. NeedData := True;
  69. Data := Handle^.Data.FirstIOCallback;
  70. while Assigned(Data) do
  71. begin
  72. if Data^.IOHandle = IOHandle then
  73. begin
  74. if ARead then
  75. begin
  76. if Assigned(Data^.ReadCallback) then
  77. begin
  78. Result := asyncHandlerAlreadySet;
  79. exit;
  80. end;
  81. Data^.ReadCallback := ReadCallback;
  82. Data^.ReadUserData := ReadUserData;
  83. end;
  84. if AWrite then
  85. begin
  86. if Assigned(Data^.WriteCallback) then
  87. begin
  88. Result := asyncHandlerAlreadySet;
  89. exit;
  90. end;
  91. Data^.WriteCallback := WriteCallback;
  92. Data^.WriteUserData := WriteUserData;
  93. end;
  94. NeedData := False;
  95. break;
  96. end;
  97. Data := Data^.Next;
  98. end;
  99. if NeedData then
  100. begin
  101. New(Data);
  102. Data^.Next := Handle^.Data.FirstIOCallback;
  103. Handle^.Data.FirstIOCallback := Data;
  104. Data^.IOHandle := IOHandle;
  105. if ARead then
  106. begin
  107. Data^.ReadCallback := ReadCallback;
  108. Data^.ReadUserData := ReadUserData;
  109. end else
  110. Data^.ReadCallback := nil;
  111. if AWrite then
  112. begin
  113. Data^.WriteCallback := WriteCallback;
  114. Data^.WriteUserData := WriteUserData;
  115. end else
  116. Data^.WriteCallback := nil;
  117. if not Assigned(Handle^.Data.FDData) then
  118. begin
  119. GetMem(Handle^.Data.FDData, SizeOf(TFDSet) * 2);
  120. FD_Zero(PFDSet(Handle^.Data.FDData)[0]);
  121. FD_Zero(PFDSet(Handle^.Data.FDData)[1]);
  122. end;
  123. if IOHandle > Handle^.Data.HighestHandle then
  124. Handle^.Data.HighestHandle := IOHandle;
  125. end;
  126. Data^.SavedHandleFlags := fcntl(IOHandle, F_GetFl);
  127. fcntl(IOHandle, F_SetFl, Data^.SavedHandleFlags or Open_NonBlock);
  128. case IOHandle of
  129. StdInputHandle:
  130. i := Open_RdOnly;
  131. StdOutputHandle, StdErrorHandle:
  132. i := Open_WrOnly;
  133. else
  134. i := Data^.SavedHandleFlags and Open_Accmode;
  135. end;
  136. case i of
  137. Open_RdOnly:
  138. if ARead then
  139. FD_Set(IOHandle, PFDSet(Handle^.Data.FDData)[0]);
  140. Open_WrOnly:
  141. if AWrite then
  142. FD_Set(IOHandle, PFDSet(Handle^.Data.FDData)[1]);
  143. Open_RdWr:
  144. begin
  145. if ARead then
  146. FD_Set(IOHandle, PFDSet(Handle^.Data.FDData)[0]);
  147. if AWrite then
  148. FD_Set(IOHandle, PFDSet(Handle^.Data.FDData)[1]);
  149. end;
  150. end;
  151. Handle^.Data.HasCallbacks := True;
  152. Result := asyncOK;
  153. end;
  154. procedure CheckForCallbacks(Handle: TAsyncHandle);
  155. var
  156. Data: PIOCallbackData;
  157. begin
  158. if (Handle^.Data.HasCallbacks) and
  159. (not Assigned(Handle^.Data.FirstIOCallback)) and
  160. (not Assigned(Handle^.Data.FirstTimer)) then
  161. Handle^.Data.HasCallbacks := False;
  162. end;
  163. procedure asyncInit(Handle: TAsyncHandle); cdecl;
  164. begin
  165. Handle^.Data.HighestHandle := -1;
  166. end;
  167. procedure asyncFree(Handle: TAsyncHandle); cdecl;
  168. var
  169. Timer, NextTimer: PTimerData;
  170. IOCallback, NextIOCallback: PIOCallbackData;
  171. begin
  172. Timer := PTimerData(Handle^.Data.FirstTimer);
  173. while Assigned(Timer) do
  174. begin
  175. NextTimer := Timer^.Next;
  176. Dispose(Timer);
  177. Timer := NextTimer;
  178. end;
  179. IOCallback := PIOCallbackData(Handle^.Data.FirstIOCallback);
  180. while Assigned(IOCallback) do
  181. begin
  182. if (IOCallback^.SavedHandleFlags and Open_NonBlock) = 0 then
  183. fcntl(IOCallback^.IOHandle, F_SetFl, IOCallback^.SavedHandleFlags);
  184. NextIOCallback := IOCallback^.Next;
  185. Dispose(IOCallback);
  186. IOCallback := NextIOCallback;
  187. end;
  188. Handle^.Data.NextIOCallback := nil;
  189. if Assigned(Handle^.Data.FDData) then
  190. FreeMem(Handle^.Data.FDData);
  191. end;
  192. procedure asyncRun(Handle: TAsyncHandle); cdecl;
  193. var
  194. Timer, NextTimer: PTimerData;
  195. TimeOut, AsyncResult: Integer;
  196. CurTime, NextTick: Int64;
  197. CurReadFDSet, CurWriteFDSet: TFDSet;
  198. CurIOCallback: PIOCallbackData;
  199. begin
  200. if Handle^.Data.IsRunning then
  201. exit;
  202. Handle^.Data.DoBreak := False;
  203. Handle^.Data.IsRunning := True;
  204. // Prepare timers
  205. if Assigned(Handle^.Data.FirstTimer) then
  206. begin
  207. CurTime := asyncGetTicks;
  208. Timer := Handle^.Data.FirstTimer;
  209. while Assigned(Timer) do
  210. begin
  211. Timer^.NextTick := CurTime + Timer^.MSec;
  212. Timer := Timer^.Next;
  213. end;
  214. end;
  215. while (not Handle^.Data.DoBreak) and Handle^.Data.HasCallbacks do
  216. begin
  217. Timer := Handle^.Data.FirstTimer;
  218. if Assigned(Handle^.Data.FirstTimer) then
  219. begin
  220. // Determine when the next timer tick will happen
  221. CurTime := asyncGetTicks;
  222. NextTick := High(Int64);
  223. Timer := Handle^.Data.FirstTimer;
  224. while Assigned(Timer) do
  225. begin
  226. if Timer^.NextTick < NextTick then
  227. NextTick := Timer^.NextTick;
  228. Timer := Timer^.Next;
  229. end;
  230. TimeOut := NextTick - CurTime;
  231. if TimeOut < 0 then
  232. TimeOut := 0;
  233. end else
  234. TimeOut := -1;
  235. if Handle^.Data.HighestHandle >= 0 then
  236. begin
  237. CurReadFDSet := PFDSet(Handle^.Data.FDData)[0];
  238. CurWriteFDSet := PFDSet(Handle^.Data.FDData)[1];
  239. AsyncResult := Select(Handle^.Data.HighestHandle + 1,
  240. @CurReadFDSet, @CurWriteFDSet, nil, TimeOut);
  241. end else
  242. AsyncResult := Select(0, nil, nil, nil, TimeOut);
  243. if Assigned(Handle^.Data.FirstTimer) then
  244. begin
  245. // Check for triggered timers
  246. CurTime := asyncGetTicks;
  247. Timer := Handle^.Data.FirstTimer;
  248. while Assigned(Timer) do
  249. begin
  250. if Timer^.NextTick <= CurTime then
  251. begin
  252. Timer^.Callback(Timer^.UserData);
  253. NextTimer := Timer^.Next;
  254. if Timer^.Periodic then
  255. Inc(Timer^.NextTick, Timer^.MSec)
  256. else
  257. asyncRemoveTimer(Handle, Timer);
  258. if Handle^.Data.DoBreak then
  259. break;
  260. Timer := NextTimer;
  261. end else
  262. Timer := Timer^.Next;
  263. end;
  264. end;
  265. if (AsyncResult > 0) and not Handle^.Data.DoBreak then
  266. begin
  267. // Check for I/O events
  268. Handle^.Data.CurIOCallback := Handle^.Data.FirstIOCallback;
  269. while Assigned(Handle^.Data.CurIOCallback) do
  270. begin
  271. CurIOCallback := PIOCallbackData(Handle^.Data.CurIOCallback);
  272. Handle^.Data.NextIOCallback := CurIOCallback^.Next;
  273. if FD_IsSet(CurIOCallback^.IOHandle, CurReadFDSet) and
  274. FD_IsSet(CurIOCallback^.IOHandle, PFDSet(Handle^.Data.FDData)[0]) and
  275. Assigned(CurIOCallback^.ReadCallback) then
  276. begin
  277. CurIOCallback^.ReadCallback(CurIOCallback^.ReadUserData);
  278. if Handle^.Data.DoBreak then
  279. break;
  280. end;
  281. CurIOCallback := PIOCallbackData(Handle^.Data.CurIOCallback);
  282. if Assigned(CurIOCallback) and
  283. FD_IsSet(CurIOCallback^.IOHandle, CurWriteFDSet) and
  284. FD_IsSet(CurIOCallback^.IOHandle, PFDSet(Handle^.Data.FDData)[1]) and
  285. Assigned(CurIOCallback^.WriteCallback) then
  286. begin
  287. CurIOCallback^.WriteCallback(CurIOCallback^.WriteUserData);
  288. if Handle^.Data.DoBreak then
  289. break;
  290. end;
  291. Handle^.Data.CurIOCallback := Handle^.Data.NextIOCallback;
  292. end;
  293. end;
  294. end;
  295. Handle^.Data.CurIOCallback := nil;
  296. Handle^.Data.NextIOCallback := nil;
  297. Handle^.Data.IsRunning := False;
  298. end;
  299. procedure asyncBreak(Handle: TAsyncHandle); cdecl;
  300. begin
  301. Handle^.Data.DoBreak := True;
  302. end;
  303. function asyncIsRunning(Handle: TAsyncHandle): Boolean; cdecl;
  304. begin
  305. Result := Handle^.Data.IsRunning;
  306. end;
  307. function asyncGetTicks: Int64; cdecl;
  308. var
  309. Time: TimeVal;
  310. begin
  311. GetTimeOfDay(Time);
  312. Result := Int64(Time.Sec) * 1000 + Int64(Time.USec div 1000);
  313. end;
  314. function asyncAddTimer(
  315. Handle: TAsyncHandle;
  316. MSec: LongInt;
  317. Periodic: Boolean;
  318. Callback: TAsyncCallback;
  319. UserData: Pointer
  320. ): TAsyncTimer; cdecl;
  321. var
  322. Data: PTimerData;
  323. begin
  324. if not Assigned(Callback) then
  325. exit;
  326. New(Data);
  327. Result := Data;
  328. Data^.Next := Handle^.Data.FirstTimer;
  329. Handle^.Data.FirstTimer := Data;
  330. Data^.MSec := MSec;
  331. Data^.Periodic := Periodic;
  332. Data^.Callback := Callback;
  333. Data^.UserData := UserData;
  334. if Handle^.Data.IsRunning then
  335. Data^.NextTick := asyncGetTicks + MSec;
  336. Handle^.Data.HasCallbacks := True;
  337. end;
  338. procedure asyncRemoveTimer(
  339. Handle: TAsyncHandle;
  340. Timer: TASyncTimer); cdecl;
  341. var
  342. Data, CurData, PrevData, NextData: PTimerData;
  343. begin
  344. Data := PTimerData(Timer);
  345. CurData := Handle^.Data.FirstTimer;
  346. PrevData := nil;
  347. while Assigned(CurData) do
  348. begin
  349. NextData := CurData^.Next;
  350. if CurData = Data then
  351. begin
  352. if Assigned(PrevData) then
  353. PrevData^.Next := NextData
  354. else
  355. Handle^.Data.FirstTimer := NextData;
  356. break;
  357. end;
  358. PrevData := CurData;
  359. CurData := NextData;
  360. end;
  361. Dispose(Data);
  362. CheckForCallbacks(Handle);
  363. end;
  364. function asyncSetIOCallback(
  365. Handle: TAsyncHandle;
  366. IOHandle: LongInt;
  367. Callback: TAsyncCallback;
  368. UserData: Pointer): TAsyncResult; cdecl;
  369. begin
  370. Result := InitIOCallback(Handle, IOHandle, True, Callback, UserData,
  371. True, Callback, UserData);
  372. end;
  373. procedure asyncClearIOCallback(Handle: TAsyncHandle;
  374. IOHandle: LongInt); cdecl;
  375. var
  376. CurData, PrevData, NextData: PIOCallbackData;
  377. begin
  378. CurData := Handle^.Data.FirstIOCallback;
  379. PrevData := nil;
  380. while Assigned(CurData) do
  381. begin
  382. NextData := CurData^.Next;
  383. if CurData^.IOHandle = IOHandle then
  384. begin
  385. if Handle^.Data.CurIOCallback = CurData then
  386. Handle^.Data.CurIOCallback := nil;
  387. if Handle^.Data.NextIOCallback = CurData then
  388. Handle^.Data.NextIOCallback := NextData;
  389. FD_Clr(IOHandle, PFDSet(Handle^.Data.FDData)[0]);
  390. FD_Clr(IOHandle, PFDSet(Handle^.Data.FDData)[1]);
  391. if Assigned(PrevData) then
  392. PrevData^.Next := NextData
  393. else
  394. Handle^.Data.FirstIOCallback := NextData;
  395. Dispose(CurData);
  396. break;
  397. end;
  398. PrevData := CurData;
  399. CurData := NextData;
  400. end;
  401. CheckForCallbacks(Handle);
  402. end;
  403. function asyncSetDataAvailableCallback(
  404. Handle: TAsyncHandle;
  405. IOHandle: LongInt;
  406. Callback: TAsyncCallback;
  407. UserData: Pointer): TAsyncResult; cdecl;
  408. begin
  409. Result := InitIOCallback(Handle, IOHandle, True, Callback, UserData, False,
  410. nil, nil);
  411. end;
  412. procedure asyncClearDataAvailableCallback(Handle: TAsyncHandle;
  413. IOHandle: LongInt); cdecl;
  414. var
  415. CurData, PrevData, NextData: PIOCallbackData;
  416. begin
  417. CurData := Handle^.Data.FirstIOCallback;
  418. PrevData := nil;
  419. while Assigned(CurData) do
  420. begin
  421. NextData := CurData^.Next;
  422. if CurData^.IOHandle = IOHandle then
  423. begin
  424. if Handle^.Data.CurIOCallback = CurData then
  425. Handle^.Data.CurIOCallback := nil;
  426. if Handle^.Data.NextIOCallback = CurData then
  427. Handle^.Data.NextIOCallback := NextData;
  428. FD_Clr(IOHandle, PFDSet(Handle^.Data.FDData)[0]);
  429. if Assigned(CurData^.WriteCallback) then
  430. CurData^.ReadCallback := nil
  431. else
  432. begin
  433. if Assigned(PrevData) then
  434. PrevData^.Next := NextData
  435. else
  436. Handle^.Data.FirstIOCallback := NextData;
  437. Dispose(CurData);
  438. end;
  439. break;
  440. end;
  441. PrevData := CurData;
  442. CurData := NextData;
  443. end;
  444. CheckForCallbacks(Handle);
  445. end;
  446. function asyncSetCanWriteCallback(
  447. Handle: TAsyncHandle;
  448. IOHandle: LongInt;
  449. Callback: TAsyncCallback;
  450. UserData: Pointer): TAsyncResult; cdecl;
  451. begin
  452. Result := InitIOCallback(Handle, IOHandle, False, nil, nil, True,
  453. Callback, UserData);
  454. end;
  455. procedure asyncClearCanWriteCallback(Handle: TAsyncHandle;
  456. IOHandle: LongInt); cdecl;
  457. var
  458. CurData, PrevData, NextData: PIOCallbackData;
  459. begin
  460. CurData := Handle^.Data.FirstIOCallback;
  461. PrevData := nil;
  462. while Assigned(CurData) do
  463. begin
  464. NextData := CurData^.Next;
  465. if CurData^.IOHandle = IOHandle then
  466. begin
  467. if Handle^.Data.CurIOCallback = CurData then
  468. Handle^.Data.CurIOCallback := nil;
  469. if Handle^.Data.NextIOCallback = CurData then
  470. Handle^.Data.NextIOCallback := NextData;
  471. FD_Clr(IOHandle, PFDSet(Handle^.Data.FDData)[1]);
  472. if Assigned(CurData^.ReadCallback) then
  473. CurData^.WriteCallback := nil
  474. else
  475. begin
  476. if Assigned(PrevData) then
  477. PrevData^.Next := NextData
  478. else
  479. Handle^.Data.FirstIOCallback := NextData;
  480. Dispose(CurData);
  481. end;
  482. break;
  483. end;
  484. PrevData := CurData;
  485. CurData := NextData;
  486. end;
  487. CheckForCallbacks(Handle);
  488. end;
  489. end.
  490. {
  491. $Log$
  492. Revision 1.4 2002-09-15 15:51:09 sg
  493. * Removed debugging output code
  494. Revision 1.3 2002/09/15 15:43:30 sg
  495. * Improved error reporting
  496. Revision 1.1 2002/01/29 17:54:53 peter
  497. * splitted to base and extra
  498. }