libasync.inc 12 KB

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