lepolleventer.inc 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  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');
  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');
  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');
  37. end;
  38. destructor TLEpollEventer.Destroy;
  39. begin
  40. fpClose(FEpollFD);
  41. FFreeList.Free;
  42. inherited Destroy;
  43. end;
  44. function TLEpollEventer.GetTimeout: DWord;
  45. begin
  46. Result := DWord(FTimeout);
  47. end;
  48. procedure TLEpollEventer.SetTimeout(const Value: DWord);
  49. begin
  50. FTimeout := cInt(Value);
  51. end;
  52. procedure TLEpollEventer.HandleIgnoreRead(aHandle: TLHandle);
  53. var
  54. lEvent: TEpollEvent;
  55. begin
  56. lEvent.data.ptr := aHandle;
  57. lEvent.events := EPOLLIN or EPOLLPRI or EPOLLHUP;
  58. if not aHandle.IgnoreRead then begin
  59. if epoll_ctl(FEpollReadFD, EPOLL_CTL_ADD, aHandle.Handle, @lEvent) < 0 then
  60. Bail('Error modifying handle for reads', LSocketError);
  61. end else begin
  62. if epoll_ctl(FEpollReadFD, EPOLL_CTL_DEL, aHandle.Handle, @lEvent) < 0 then
  63. Bail('Error modifying handle for reads', LSocketError);
  64. end;
  65. end;
  66. procedure TLEpollEventer.Inflate;
  67. var
  68. OldLength: Integer;
  69. begin
  70. OldLength := Length(FEvents);
  71. if OldLength > 1 then
  72. SetLength(FEvents, Sqr(OldLength))
  73. else
  74. SetLength(FEvents, BASE_SIZE);
  75. SetLength(FEventsRead, Length(FEvents));
  76. end;
  77. function TLEpollEventer.AddHandle(aHandle: TLHandle): Boolean;
  78. var
  79. lEvent: TEpollEvent;
  80. begin
  81. Result := inherited AddHandle(aHandle);
  82. if Result then begin
  83. Result := False;
  84. lEvent.events := EPOLLET or EPOLLOUT or EPOLLERR;
  85. lEvent.data.ptr := aHandle;
  86. if epoll_ctl(FEpollFD, EPOLL_CTL_ADD, aHandle.FHandle, @lEvent) < 0 then
  87. Bail('Error adding handle to epoll', LSocketError);
  88. lEvent.events := EPOLLIN or EPOLLPRI or EPOLLHUP;
  89. if not aHandle.IgnoreRead then begin
  90. if epoll_ctl(FEpollReadFD, EPOLL_CTL_ADD, aHandle.FHandle, @lEvent) < 0 then
  91. Bail('Error adding handle to epoll', LSocketError);
  92. end;
  93. if FCount > High(FEvents) then
  94. Inflate;
  95. end;
  96. end;
  97. function Max(const a, b: Integer): Integer; inline;
  98. begin
  99. if a > b then
  100. Result := a
  101. else
  102. Result := b;
  103. end;
  104. function TLEpollEventer.CallAction: Boolean;
  105. var
  106. i, MasterChanges, Changes, ReadChanges: Integer;
  107. Temp, TempRead: TLHandle;
  108. MasterEvents: array[0..1] of TEpollEvent;
  109. begin
  110. Result := False;
  111. Changes := 0;
  112. ReadChanges := 0;
  113. MasterChanges := epoll_wait(FEpollMasterFD, @MasterEvents[0], 2, FTimeout);
  114. if MasterChanges > 0 then begin
  115. for i := 0 to MasterChanges-1 do
  116. if MasterEvents[i].Data.fd = FEpollFD then
  117. Changes := epoll_wait(FEpollFD, @FEvents[0], FCount, 0)
  118. else
  119. ReadChanges := epoll_wait(FEpollReadFD, @FEventsRead[0], FCount, 0);
  120. if (Changes < 0) or (ReadChanges < 0) then
  121. Bail('Error on epoll: ', LSocketError)
  122. else
  123. Result := Changes + ReadChanges > 0;
  124. if Result then begin
  125. FInLoop := True;
  126. for i := 0 to Max(Changes, ReadChanges)-1 do begin
  127. Temp := nil;
  128. if i < Changes then begin
  129. Temp := TLHandle(FEvents[i].data.ptr);
  130. if (not Temp.FDispose)
  131. and (FEvents[i].events and EPOLLOUT = EPOLLOUT) then
  132. if Assigned(Temp.FOnWrite) and not Temp.IgnoreWrite then
  133. Temp.FOnWrite(Temp);
  134. if Temp.FDispose then
  135. AddForFree(Temp);
  136. end; // writes
  137. if i < ReadChanges then begin
  138. TempRead := TLHandle(FEventsRead[i].data.ptr);
  139. if (not TempRead.FDispose)
  140. and ((FEventsRead[i].events and EPOLLIN = EPOLLIN)
  141. or (FEventsRead[i].events and EPOLLHUP = EPOLLHUP)
  142. or (FEventsRead[i].events and EPOLLPRI = EPOLLPRI)) then
  143. if Assigned(TempRead.FOnRead) and not TempRead.IgnoreRead then
  144. TempRead.FOnRead(TempRead);
  145. if TempRead.FDispose then
  146. AddForFree(TempRead);
  147. end; // reads
  148. if i < Changes then begin
  149. if not Assigned(Temp) then
  150. Temp := TLHandle(FEvents[i].data.ptr);
  151. if (not Temp.FDispose)
  152. and (FEvents[i].events and EPOLLERR = EPOLLERR) then
  153. if Assigned(Temp.FOnError) and not Temp.IgnoreError then
  154. Temp.FOnError(Temp, 'Handle error: ' + LStrError(LSocketError));
  155. if Temp.FDispose then
  156. AddForFree(Temp);
  157. end; // errors
  158. end;
  159. FInLoop := False;
  160. if Assigned(FFreeRoot) then
  161. FreeHandles;
  162. end;
  163. end else if MasterChanges < 0 then
  164. Bail('Error on epoll: ', LSocketError);
  165. end;
  166. function BestEventerClass: TLEventerClass;
  167. function GetVersion(s: string): Integer;
  168. const
  169. Numbers = ['0'..'9'];
  170. var
  171. i: Integer;
  172. begin
  173. s := StringReplace(s, '.', '', [rfReplaceAll]);
  174. i := 1;
  175. while (i <= Length(s)) and (s[i] in Numbers) do
  176. Inc(i);
  177. s := Copy(s, 1, i - 1);
  178. if Length(s) < 4 then // varies OS to OS
  179. Insert('0', s, 3); // in linux, last part can be > 10
  180. Result := StrToInt(s);
  181. end;
  182. {$ifndef DISABLE_EPOLL}
  183. var
  184. u: TUTSName;
  185. {$endif}
  186. begin
  187. Result := TLSelectEventer;
  188. {$ifndef DISABLE_EPOLL}
  189. if fpUname(u) = 0 then // check for 2.6+
  190. if GetVersion(u.release) >= 2600 then
  191. Result := TLEpollEventer;
  192. {$endif}
  193. end;
  194. {$endif} // Linux