lepolleventer.inc 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. {% lepolleventer.inc included by levents.pas }
  2. {$ifdef Linux}
  3. { TLEpollEventer }
  4. const
  5. BASE_SIZE = 100;
  6. // bug in fpc 2.0.4-
  7. EPOLL_CTL_ADD = 1;
  8. EPOLL_CTL_DEL = 2;
  9. EPOLL_CTL_MOD = 3;
  10. EPOLLIN = $01; { The associated file is available for read(2) operations. }
  11. EPOLLPRI = $02; { There is urgent data available for read(2) operations. }
  12. EPOLLOUT = $04; { The associated file is available for write(2) operations. }
  13. EPOLLERR = $08; { Error condition happened on the associated file descriptor. }
  14. EPOLLHUP = $10; { Hang up happened on the associated file descriptor. }
  15. EPOLLONESHOT = 1 shl 30;
  16. EPOLLET = 1 shl 31; { Sets the Edge Triggered behaviour for the associated file descriptor. }
  17. constructor TLEpollEventer.Create;
  18. var
  19. lEvent: TEpollEvent;
  20. begin
  21. inherited Create;
  22. FFreeList := TFPObjectList.Create;
  23. Inflate;
  24. FTimeout := 0;
  25. FEpollFD := epoll_create(BASE_SIZE);
  26. FEpollReadFD := epoll_create(BASE_SIZE);
  27. FEpollMasterFD := epoll_create(2);
  28. if (FEPollFD < 0) or (FEpollReadFD < 0) or (FEpollMasterFD < 0) then
  29. raise Exception.Create('Unable to create epoll: ' + StrError(fpgeterrno));
  30. lEvent.events := EPOLLIN or EPOLLOUT or EPOLLPRI or EPOLLERR or EPOLLHUP or EPOLLET;
  31. lEvent.data.fd := FEpollFD;
  32. if epoll_ctl(FEpollMasterFD, EPOLL_CTL_ADD, FEpollFD, @lEvent) < 0 then
  33. raise Exception.Create('Unable to add FDs to master epoll FD: ' + StrError(fpGetErrno));
  34. lEvent.data.fd := FEpollReadFD;
  35. if epoll_ctl(FEpollMasterFD, EPOLL_CTL_ADD, FEpollReadFD, @lEvent) < 0 then
  36. raise Exception.Create('Unable to add FDs to master epoll FD: ' + StrError(fpGetErrno));
  37. end;
  38. destructor TLEpollEventer.Destroy;
  39. begin
  40. fpClose(FEpollFD);
  41. FFreeList.Free;
  42. inherited Destroy;
  43. end;
  44. function TLEpollEventer.GetTimeout: Integer;
  45. begin
  46. Result := FTimeout;
  47. end;
  48. procedure TLEpollEventer.SetTimeout(const Value: Integer);
  49. begin
  50. if Value >= 0 then
  51. FTimeout := Value
  52. else
  53. FTimeout := -1;
  54. end;
  55. procedure TLEpollEventer.HandleIgnoreRead(aHandle: TLHandle);
  56. var
  57. lEvent: TEpollEvent;
  58. begin
  59. lEvent.data.ptr := aHandle;
  60. lEvent.events := EPOLLIN or EPOLLPRI or EPOLLHUP;
  61. if not aHandle.IgnoreRead then begin
  62. if epoll_ctl(FEpollReadFD, EPOLL_CTL_ADD, aHandle.Handle, @lEvent) < 0 then
  63. Bail('Error modifying handle for reads', LSocketError);
  64. end else begin
  65. if epoll_ctl(FEpollReadFD, EPOLL_CTL_DEL, aHandle.Handle, @lEvent) < 0 then
  66. Bail('Error modifying handle for reads', LSocketError);
  67. end;
  68. end;
  69. procedure TLEpollEventer.Inflate;
  70. var
  71. OldLength: Integer;
  72. begin
  73. OldLength := Length(FEvents);
  74. if OldLength > 1 then
  75. SetLength(FEvents, Sqr(OldLength))
  76. else
  77. SetLength(FEvents, BASE_SIZE);
  78. SetLength(FEventsRead, Length(FEvents));
  79. end;
  80. function TLEpollEventer.AddHandle(aHandle: TLHandle): Boolean;
  81. var
  82. lEvent: TEpollEvent;
  83. begin
  84. Result := inherited AddHandle(aHandle);
  85. if Result then begin
  86. Result := False;
  87. lEvent.events := EPOLLET or EPOLLOUT or EPOLLERR;
  88. lEvent.data.ptr := aHandle;
  89. if epoll_ctl(FEpollFD, EPOLL_CTL_ADD, aHandle.FHandle, @lEvent) < 0 then
  90. Bail('Error adding handle to epoll', LSocketError);
  91. lEvent.events := EPOLLIN or EPOLLPRI or EPOLLHUP;
  92. if not aHandle.IgnoreRead then begin
  93. if epoll_ctl(FEpollReadFD, EPOLL_CTL_ADD, aHandle.FHandle, @lEvent) < 0 then
  94. Bail('Error adding handle to epoll', LSocketError);
  95. end;
  96. if FCount > High(FEvents) then
  97. Inflate;
  98. end;
  99. end;
  100. function Max(const a, b: Integer): Integer; inline;
  101. begin
  102. if a > b then
  103. Result := a
  104. else
  105. Result := b;
  106. end;
  107. function TLEpollEventer.CallAction: Boolean;
  108. var
  109. i, MasterChanges, Changes, ReadChanges: Integer;
  110. Temp, TempRead: TLHandle;
  111. MasterEvents: array[0..1] of TEpollEvent;
  112. begin
  113. Result := False;
  114. if FInLoop then
  115. Exit;
  116. Changes := 0;
  117. ReadChanges := 0;
  118. MasterChanges := epoll_wait(FEpollMasterFD, @MasterEvents[0], 2, FTimeout);
  119. if MasterChanges > 0 then begin
  120. for i := 0 to MasterChanges - 1 do
  121. if MasterEvents[i].Data.fd = FEpollFD then
  122. Changes := epoll_wait(FEpollFD, @FEvents[0], FCount, 0)
  123. else
  124. ReadChanges := epoll_wait(FEpollReadFD, @FEventsRead[0], FCount, 0);
  125. if (Changes < 0) or (ReadChanges < 0) then
  126. Bail('Error on epoll', LSocketError)
  127. else
  128. Result := Changes + ReadChanges > 0;
  129. if Result then begin
  130. FInLoop := True;
  131. for i := 0 to Max(Changes, ReadChanges) - 1 do begin
  132. Temp := nil;
  133. if i < Changes then begin
  134. Temp := TLHandle(FEvents[i].data.ptr);
  135. if (not Temp.FDispose)
  136. and (FEvents[i].events and EPOLLOUT = EPOLLOUT) then
  137. if Assigned(Temp.FOnWrite) and not Temp.IgnoreWrite then
  138. Temp.FOnWrite(Temp);
  139. if Temp.FDispose then
  140. AddForFree(Temp);
  141. end; // writes
  142. if i < ReadChanges then begin
  143. TempRead := TLHandle(FEventsRead[i].data.ptr);
  144. if (not TempRead.FDispose)
  145. and ((FEventsRead[i].events and EPOLLIN = EPOLLIN)
  146. or (FEventsRead[i].events and EPOLLHUP = EPOLLHUP)
  147. or (FEventsRead[i].events and EPOLLPRI = EPOLLPRI)) then
  148. if Assigned(TempRead.FOnRead) and not TempRead.IgnoreRead then
  149. TempRead.FOnRead(TempRead);
  150. if TempRead.FDispose then
  151. AddForFree(TempRead);
  152. end; // reads
  153. if i < Changes then begin
  154. if not Assigned(Temp) then
  155. Temp := TLHandle(FEvents[i].data.ptr);
  156. if (not Temp.FDispose)
  157. and (FEvents[i].events and EPOLLERR = EPOLLERR) then
  158. if Assigned(Temp.FOnError) and not Temp.IgnoreError then
  159. Temp.FOnError(Temp, 'Handle error' + LStrError(LSocketError));
  160. if Temp.FDispose then
  161. AddForFree(Temp);
  162. end; // errors
  163. end;
  164. FInLoop := False;
  165. if Assigned(FFreeRoot) then
  166. FreeHandles;
  167. end;
  168. end else if MasterChanges < 0 then
  169. Bail('Error on epoll', LSocketError);
  170. end;
  171. function BestEventerClass: TLEventerClass;
  172. var
  173. tmp: THandle;
  174. begin
  175. {$IFNDEF FORCE_SELECT}
  176. try
  177. tmp := epoll_create(1);
  178. if tmp >= 0 then begin
  179. FpClose(tmp);
  180. Result := TLEpollEventer;
  181. end else
  182. Result := TLSelectEventer;
  183. except
  184. Result := TLSelectEventer;
  185. end;
  186. {$ELSE}
  187. Result := TLSelectEventer;
  188. {$ENDIF}
  189. end;
  190. {$endif} // Linux