libasync.pp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563
  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. WriteLn('DoBreak: ', Handle^.Data.DoBreak);
  299. Writeln('HasCallbacks: ', Handle^.Data.HasCallbacks);
  300. WriteLn('FirstCallback: ', Integer(Handle^.Data.HasCallbacks));
  301. end;
  302. procedure asyncBreak(Handle: TAsyncHandle); cdecl;
  303. begin
  304. Handle^.Data.DoBreak := True;
  305. end;
  306. function asyncIsRunning(Handle: TAsyncHandle): Boolean; cdecl;
  307. begin
  308. Result := Handle^.Data.IsRunning;
  309. end;
  310. function asyncGetTicks: Int64; cdecl;
  311. var
  312. Time: TimeVal;
  313. begin
  314. GetTimeOfDay(Time);
  315. Result := Int64(Time.Sec) * 1000 + Int64(Time.USec div 1000);
  316. end;
  317. function asyncAddTimer(
  318. Handle: TAsyncHandle;
  319. MSec: LongInt;
  320. Periodic: Boolean;
  321. Callback: TAsyncCallback;
  322. UserData: Pointer
  323. ): TAsyncTimer; cdecl;
  324. var
  325. Data: PTimerData;
  326. begin
  327. if not Assigned(Callback) then
  328. exit;
  329. New(Data);
  330. Result := Data;
  331. Data^.Next := Handle^.Data.FirstTimer;
  332. Handle^.Data.FirstTimer := Data;
  333. Data^.MSec := MSec;
  334. Data^.Periodic := Periodic;
  335. Data^.Callback := Callback;
  336. Data^.UserData := UserData;
  337. if Handle^.Data.IsRunning then
  338. Data^.NextTick := asyncGetTicks + MSec;
  339. Handle^.Data.HasCallbacks := True;
  340. end;
  341. procedure asyncRemoveTimer(
  342. Handle: TAsyncHandle;
  343. Timer: TASyncTimer); cdecl;
  344. var
  345. Data, CurData, PrevData, NextData: PTimerData;
  346. begin
  347. Data := PTimerData(Timer);
  348. CurData := Handle^.Data.FirstTimer;
  349. PrevData := nil;
  350. while Assigned(CurData) do
  351. begin
  352. NextData := CurData^.Next;
  353. if CurData = Data then
  354. begin
  355. if Assigned(PrevData) then
  356. PrevData^.Next := NextData
  357. else
  358. Handle^.Data.FirstTimer := NextData;
  359. break;
  360. end;
  361. PrevData := CurData;
  362. CurData := NextData;
  363. end;
  364. Dispose(Data);
  365. CheckForCallbacks(Handle);
  366. end;
  367. function asyncSetIOCallback(
  368. Handle: TAsyncHandle;
  369. IOHandle: LongInt;
  370. Callback: TAsyncCallback;
  371. UserData: Pointer): TAsyncResult; cdecl;
  372. begin
  373. Result := InitIOCallback(Handle, IOHandle, True, Callback, UserData,
  374. True, Callback, UserData);
  375. end;
  376. procedure asyncClearIOCallback(Handle: TAsyncHandle;
  377. IOHandle: LongInt); cdecl;
  378. var
  379. CurData, PrevData, NextData: PIOCallbackData;
  380. begin
  381. CurData := Handle^.Data.FirstIOCallback;
  382. PrevData := nil;
  383. while Assigned(CurData) do
  384. begin
  385. NextData := CurData^.Next;
  386. if CurData^.IOHandle = IOHandle then
  387. begin
  388. if Handle^.Data.CurIOCallback = CurData then
  389. Handle^.Data.CurIOCallback := nil;
  390. if Handle^.Data.NextIOCallback = CurData then
  391. Handle^.Data.NextIOCallback := NextData;
  392. FD_Clr(IOHandle, PFDSet(Handle^.Data.FDData)[0]);
  393. FD_Clr(IOHandle, PFDSet(Handle^.Data.FDData)[1]);
  394. if Assigned(PrevData) then
  395. PrevData^.Next := NextData
  396. else
  397. Handle^.Data.FirstIOCallback := NextData;
  398. Dispose(CurData);
  399. break;
  400. end;
  401. PrevData := CurData;
  402. CurData := NextData;
  403. end;
  404. CheckForCallbacks(Handle);
  405. end;
  406. function asyncSetDataAvailableCallback(
  407. Handle: TAsyncHandle;
  408. IOHandle: LongInt;
  409. Callback: TAsyncCallback;
  410. UserData: Pointer): TAsyncResult; cdecl;
  411. begin
  412. Result := InitIOCallback(Handle, IOHandle, True, Callback, UserData, False,
  413. nil, nil);
  414. end;
  415. procedure asyncClearDataAvailableCallback(Handle: TAsyncHandle;
  416. IOHandle: LongInt); cdecl;
  417. var
  418. CurData, PrevData, NextData: PIOCallbackData;
  419. begin
  420. CurData := Handle^.Data.FirstIOCallback;
  421. PrevData := nil;
  422. while Assigned(CurData) do
  423. begin
  424. NextData := CurData^.Next;
  425. if CurData^.IOHandle = IOHandle then
  426. begin
  427. if Handle^.Data.CurIOCallback = CurData then
  428. Handle^.Data.CurIOCallback := nil;
  429. if Handle^.Data.NextIOCallback = CurData then
  430. Handle^.Data.NextIOCallback := NextData;
  431. FD_Clr(IOHandle, PFDSet(Handle^.Data.FDData)[0]);
  432. if Assigned(CurData^.WriteCallback) then
  433. CurData^.ReadCallback := 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. function asyncSetCanWriteCallback(
  450. Handle: TAsyncHandle;
  451. IOHandle: LongInt;
  452. Callback: TAsyncCallback;
  453. UserData: Pointer): TAsyncResult; cdecl;
  454. begin
  455. Result := InitIOCallback(Handle, IOHandle, False, nil, nil, True,
  456. Callback, UserData);
  457. end;
  458. procedure asyncClearCanWriteCallback(Handle: TAsyncHandle;
  459. IOHandle: LongInt); cdecl;
  460. var
  461. CurData, PrevData, NextData: PIOCallbackData;
  462. begin
  463. CurData := Handle^.Data.FirstIOCallback;
  464. PrevData := nil;
  465. while Assigned(CurData) do
  466. begin
  467. NextData := CurData^.Next;
  468. if CurData^.IOHandle = IOHandle then
  469. begin
  470. if Handle^.Data.CurIOCallback = CurData then
  471. Handle^.Data.CurIOCallback := nil;
  472. if Handle^.Data.NextIOCallback = CurData then
  473. Handle^.Data.NextIOCallback := NextData;
  474. FD_Clr(IOHandle, PFDSet(Handle^.Data.FDData)[1]);
  475. if Assigned(CurData^.ReadCallback) then
  476. CurData^.WriteCallback := nil
  477. else
  478. begin
  479. if Assigned(PrevData) then
  480. PrevData^.Next := NextData
  481. else
  482. Handle^.Data.FirstIOCallback := NextData;
  483. Dispose(CurData);
  484. end;
  485. break;
  486. end;
  487. PrevData := CurData;
  488. CurData := NextData;
  489. end;
  490. CheckForCallbacks(Handle);
  491. end;
  492. end.
  493. {
  494. $Log$
  495. Revision 1.3 2002-09-15 15:43:30 sg
  496. * Improved error reporting
  497. Revision 1.1 2002/01/29 17:54:53 peter
  498. * splitted to base and extra
  499. }