libasync.pp 12 KB

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