{% lepolleventer.inc included by levents.pas }
{$ifdef Linux}
{ TLEpollEventer }
const
BASE_SIZE = 100;
// bug in fpc 2.0.4-
EPOLL_CTL_ADD = 1;
EPOLL_CTL_DEL = 2;
EPOLL_CTL_MOD = 3;
EPOLLIN = $01; { The associated file is available for read(2) operations. }
EPOLLPRI = $02; { There is urgent data available for read(2) operations. }
EPOLLOUT = $04; { The associated file is available for write(2) operations. }
EPOLLERR = $08; { Error condition happened on the associated file descriptor. }
EPOLLHUP = $10; { Hang up happened on the associated file descriptor. }
EPOLLONESHOT = 1 shl 30;
EPOLLET = 1 shl 31; { Sets the Edge Triggered behaviour for the associated file descriptor. }
constructor TLEpollEventer.Create;
var
lEvent: TEpollEvent;
begin
inherited Create;
FFreeList := TFPObjectList.Create;
Inflate;
FTimeout := 0;
FEpollFD := epoll_create(BASE_SIZE);
FEpollReadFD := epoll_create(BASE_SIZE);
FEpollMasterFD := epoll_create(2);
if (FEPollFD < 0) or (FEpollReadFD < 0) or (FEpollMasterFD < 0) then
raise Exception.Create('Unable to create epoll');
lEvent.events := EPOLLIN or EPOLLOUT or EPOLLPRI or EPOLLERR or EPOLLHUP or EPOLLET;
lEvent.data.fd := FEpollFD;
if epoll_ctl(FEpollMasterFD, EPOLL_CTL_ADD, FEpollFD, @lEvent) < 0 then
raise Exception.Create('Unable to add FDs to master epoll FD');
lEvent.data.fd := FEpollReadFD;
if epoll_ctl(FEpollMasterFD, EPOLL_CTL_ADD, FEpollReadFD, @lEvent) < 0 then
raise Exception.Create('Unable to add FDs to master epoll FD');
end;
destructor TLEpollEventer.Destroy;
begin
fpClose(FEpollFD);
FFreeList.Free;
inherited Destroy;
end;
function TLEpollEventer.GetTimeout: DWord;
begin
Result := DWord(FTimeout);
end;
procedure TLEpollEventer.SetTimeout(const Value: DWord);
begin
FTimeout := cInt(Value);
end;
procedure TLEpollEventer.HandleIgnoreRead(aHandle: TLHandle);
var
lEvent: TEpollEvent;
begin
lEvent.data.ptr := aHandle;
lEvent.events := EPOLLIN or EPOLLPRI or EPOLLHUP;
if not aHandle.IgnoreRead then begin
if epoll_ctl(FEpollReadFD, EPOLL_CTL_ADD, aHandle.Handle, @lEvent) < 0 then
Bail('Error modifying handle for reads', LSocketError);
end else begin
if epoll_ctl(FEpollReadFD, EPOLL_CTL_DEL, aHandle.Handle, @lEvent) < 0 then
Bail('Error modifying handle for reads', LSocketError);
end;
end;
procedure TLEpollEventer.Inflate;
var
OldLength: Integer;
begin
OldLength := Length(FEvents);
if OldLength > 1 then
SetLength(FEvents, Sqr(OldLength))
else
SetLength(FEvents, BASE_SIZE);
SetLength(FEventsRead, Length(FEvents));
end;
function TLEpollEventer.AddHandle(aHandle: TLHandle): Boolean;
var
lEvent: TEpollEvent;
begin
Result := inherited AddHandle(aHandle);
if Result then begin
Result := False;
lEvent.events := EPOLLET or EPOLLOUT or EPOLLERR;
lEvent.data.ptr := aHandle;
if epoll_ctl(FEpollFD, EPOLL_CTL_ADD, aHandle.FHandle, @lEvent) < 0 then
Bail('Error adding handle to epoll', LSocketError);
lEvent.events := EPOLLIN or EPOLLPRI or EPOLLHUP;
if not aHandle.IgnoreRead then begin
if epoll_ctl(FEpollReadFD, EPOLL_CTL_ADD, aHandle.FHandle, @lEvent) < 0 then
Bail('Error adding handle to epoll', LSocketError);
end;
if FCount > High(FEvents) then
Inflate;
end;
end;
function Max(const a, b: Integer): Integer; inline;
begin
if a > b then
Result := a
else
Result := b;
end;
function TLEpollEventer.CallAction: Boolean;
var
i, MasterChanges, Changes, ReadChanges: Integer;
Temp, TempRead: TLHandle;
MasterEvents: array[0..1] of TEpollEvent;
begin
Result := False;
Changes := 0;
ReadChanges := 0;
MasterChanges := epoll_wait(FEpollMasterFD, @MasterEvents[0], 2, FTimeout);
if MasterChanges > 0 then begin
for i := 0 to MasterChanges-1 do
if MasterEvents[i].Data.fd = FEpollFD then
Changes := epoll_wait(FEpollFD, @FEvents[0], FCount, 0)
else
ReadChanges := epoll_wait(FEpollReadFD, @FEventsRead[0], FCount, 0);
if (Changes < 0) or (ReadChanges < 0) then
Bail('Error on epoll: ', LSocketError)
else
Result := Changes + ReadChanges > 0;
if Result then begin
FInLoop := True;
for i := 0 to Max(Changes, ReadChanges)-1 do begin
Temp := nil;
if i < Changes then begin
Temp := TLHandle(FEvents[i].data.ptr);
if (not Temp.FDispose)
and (FEvents[i].events and EPOLLOUT = EPOLLOUT) then
if Assigned(Temp.FOnWrite) and not Temp.IgnoreWrite then
Temp.FOnWrite(Temp);
if Temp.FDispose then
AddForFree(Temp);
end; // writes
if i < ReadChanges then begin
TempRead := TLHandle(FEventsRead[i].data.ptr);
if (not TempRead.FDispose)
and ((FEventsRead[i].events and EPOLLIN = EPOLLIN)
or (FEventsRead[i].events and EPOLLHUP = EPOLLHUP)
or (FEventsRead[i].events and EPOLLPRI = EPOLLPRI)) then
if Assigned(TempRead.FOnRead) and not TempRead.IgnoreRead then
TempRead.FOnRead(TempRead);
if TempRead.FDispose then
AddForFree(TempRead);
end; // reads
if i < Changes then begin
if not Assigned(Temp) then
Temp := TLHandle(FEvents[i].data.ptr);
if (not Temp.FDispose)
and (FEvents[i].events and EPOLLERR = EPOLLERR) then
if Assigned(Temp.FOnError) and not Temp.IgnoreError then
Temp.FOnError(Temp, 'Handle error: ' + LStrError(LSocketError));
if Temp.FDispose then
AddForFree(Temp);
end; // errors
end;
FInLoop := False;
if Assigned(FFreeRoot) then
FreeHandles;
end;
end else if MasterChanges < 0 then
Bail('Error on epoll: ', LSocketError);
end;
function BestEventerClass: TLEventerClass;
function GetVersion(s: string): Integer;
const
Numbers = ['0'..'9'];
var
i: Integer;
begin
s := StringReplace(s, '.', '', [rfReplaceAll]);
i := 1;
while (i <= Length(s)) and (s[i] in Numbers) do
Inc(i);
s := Copy(s, 1, i - 1);
if Length(s) < 4 then // varies OS to OS
Insert('0', s, 3); // in linux, last part can be > 10
Result := StrToInt(s);
end;
{$ifndef DISABLE_EPOLL}
var
u: TUTSName;
{$endif}
begin
Result := TLSelectEventer;
{$ifndef DISABLE_EPOLL}
if fpUname(u) = 0 then // check for 2.6+
if GetVersion(u.release) >= 2600 then
Result := TLEpollEventer;
{$endif}
end;
{$endif} // Linux
syntax highlighted by Code2HTML, v. 0.9.1