libasync.inc 12 KB

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