123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623 |
- { lNet Events abstration
- CopyRight (C) 2006-2008 Ales Katona
- This library is Free software; you can rediStribute it and/or modify it
- under the terms of the GNU Library General Public License as published by
- the Free Software Foundation; either version 2 of the License, or (at your
- option) any later version.
- This program is diStributed in the hope that it will be useful, but WITHOUT
- ANY WARRANTY; withOut even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
- for more details.
- You should have received a Copy of the GNU Library General Public License
- along with This library; if not, Write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-
- This license has been modified. See File LICENSE.ADDON for more inFormation.
- Should you find these sources without a LICENSE File, please contact
- me at [email protected]
- }
- unit lEvents;
- {$mode objfpc}{$H+}
- {$inline on}
- {$define nochoice} // let's presume we don't have "optimized" eventer
- interface
- uses
- {$ifdef Linux}
- {$undef nochoice} // undefine for all "Optimized" targets
- Linux, Contnrs, Errors,
- {$endif}
- {$ifdef BSD}
- {$undef nochoice}
- BSD, Errors,
- {$endif}
- {$i sys/osunits.inc}
- type
- TLHandle = class;
- TLEventer = class;
- TLHandleEvent = procedure (aHandle: TLHandle) of object;
- TLHandleErrorEvent = procedure (aHandle: TLHandle; const msg: string) of object;
- TLEventerErrorEvent = procedure (const msg: string; Sender: TLEventer) of object;
-
- { TLHandle }
- TLHandle = class(TObject)
- protected
- FHandle: THandle;
- FEventer: TLEventer; // "queue holder"
- FOnRead: TLHandleEvent;
- FOnWrite: TLHandleEvent;
- FOnError: TLHandleErrorEvent;
- FIgnoreWrite: Boolean; // so we can do edge-triggered
- FIgnoreRead: Boolean; // so we can do edge-triggered
- FIgnoreError: Boolean; // so we can do edge-triggered
- FDispose: Boolean; // will free in the after-cycle
- FFreeing: Boolean; // used to see if it's in the "to be freed" list
- FPrev: TLHandle;
- FNext: TLHandle;
- FFreeNext: TLHandle;
- FInternalData: Pointer;
-
- procedure SetIgnoreError(const aValue: Boolean);
- procedure SetIgnoreWrite(const aValue: Boolean);
- procedure SetIgnoreRead(const aValue: Boolean);
- public
- UserData: Pointer;
- constructor Create; virtual;
- destructor Destroy; override;
- procedure Free; virtual; // this is a trick
- property Prev: TLHandle read FPrev write FPrev;
- property Next: TLHandle read FNext write FNext;
- property FreeNext: TLHandle read FFreeNext write FFreeNext;
- property IgnoreWrite: Boolean read FIgnoreWrite write SetIgnoreWrite;
- property IgnoreRead: Boolean read FIgnoreRead write SetIgnoreRead;
- property IgnoreError: Boolean read FIgnoreError write SetIgnoreError;
- property OnRead: TLHandleEvent read FOnRead write FOnRead;
- property OnWrite: TLHandleEvent read FOnWrite write FOnWrite;
- property OnError: TLHandleErrorEvent read FOnError write FOnError;
- property Dispose: Boolean read FDispose write FDispose;
- property Handle: THandle read FHandle write FHandle;
- property Eventer: TLEventer read FEventer;
- end;
- { TLTimer }
- {
- TLTimer = class(TObject)
- protected
- FOnTimer: TNotifyEvent;
- FInterval: TDateTime;
- FTimeout: TDateTime;
- FPeriodic: Boolean;
- FEnabled: Boolean;
- FNext: TLTimer;
- function GetInterval: Integer;
- procedure SetEnabled(NewEnabled: Boolean);
- procedure SetInterval(NewInterval: Integer);
- public
- procedure CallAction;
- property Enabled: Boolean read FEnabled write SetEnabled;
- property Interval: Integer read GetInterval write SetInterval;
- property Periodic: Boolean read FPeriodic write FPeriodic;
- property OnTimer: TNotifyEvent read FOnTimer write FOnTimer;
- end;
- }
- { TLTimeoutManager }
- {
- TLSetTimeout = procedure(NewTimeout: DWord) of object;
- TLTimeoutManager = class
- protected
- FFirst: TLTimer;
- FLast: TLTimer;
- FTimeout: DWord;
- FSetTimeout: TLSetTimeout;
- public
- destructor Destroy; override;
- procedure AddTimer(ATimer: TLTimer);
- procedure RemoveTimer(ATimer: TLTimer);
- procedure CallAction;
- end;
- }
- { TLEventer }
- TLEventer = class
- protected
- FRoot: TLHandle;
- FCount: Integer;
- FOnError: TLEventerErrorEvent;
- FReferences: Integer;
- FFreeRoot: TLHandle; // the root of "free" list if any
- FFreeIter: TLHandle; // the last of "free" list if any
- FInLoop: Boolean;
- function GetCount: Integer; virtual;
- function GetTimeout: Integer; virtual;
- procedure SetTimeout(const Value: Integer); virtual;
- function Bail(const msg: string; const Ernum: Integer): Boolean;
- procedure AddForFree(aHandle: TLHandle);
- procedure FreeHandles;
- procedure HandleIgnoreError(aHandle: TLHandle); virtual;
- procedure HandleIgnoreWrite(aHandle: TLHandle); virtual;
- procedure HandleIgnoreRead(aHandle: TLHandle); virtual;
- function GetInternalData(aHandle: TLHandle): Pointer;
- procedure SetInternalData(aHandle: TLHandle; const aData: Pointer);
- procedure SetHandleEventer(aHandle: TLHandle);
- procedure InternalUnplugHandle(aHandle: TLHandle); virtual;
- public
- constructor Create; virtual;
- destructor Destroy; override;
- function AddHandle(aHandle: TLHandle): Boolean; virtual;
- function CallAction: Boolean; virtual;
- procedure RemoveHandle(aHandle: TLHandle); virtual;
- procedure UnplugHandle(aHandle: TLHandle);
- procedure UnregisterHandle(aHandle: TLHandle); virtual;
- procedure LoadFromEventer(aEventer: TLEventer); virtual;
- procedure Clear;
- procedure AddRef;
- procedure DeleteRef;
- property Timeout: Integer read GetTimeout write SetTimeout;
- property OnError: TLEventerErrorEvent read FOnError write FOnError;
- property Count: Integer read GetCount;
- end;
- TLEventerClass = class of TLEventer;
-
- { TLSelectEventer }
- TLSelectEventer = class(TLEventer)
- protected
- FTimeout: TTimeVal;
- FReadFDSet: TFDSet;
- FWriteFDSet: TFDSet;
- FErrorFDSet: TFDSet;
- function GetTimeout: Integer; override;
- procedure SetTimeout(const Value: Integer); override;
- procedure ClearSets;
- public
- constructor Create; override;
- function CallAction: Boolean; override;
- end;
-
- {$i sys/lkqueueeventerh.inc}
- {$i sys/lepolleventerh.inc}
- function BestEventerClass: TLEventerClass;
-
- implementation
- uses
- syncobjs,
- lCommon;
-
- var
- CS: TCriticalSection;
-
- { TLHandle }
- procedure TLHandle.SetIgnoreError(const aValue: Boolean);
- begin
- if FIgnoreError <> aValue then begin
- FIgnoreError := aValue;
- if Assigned(FEventer) then
- FEventer.HandleIgnoreError(Self);
- end;
- end;
- procedure TLHandle.SetIgnoreWrite(const aValue: Boolean);
- begin
- if FIgnoreWrite <> aValue then begin
- FIgnoreWrite := aValue;
- if Assigned(FEventer) then
- FEventer.HandleIgnoreWrite(Self);
- end;
- end;
- procedure TLHandle.SetIgnoreRead(const aValue: Boolean);
- begin
- if FIgnoreRead <> aValue then begin
- FIgnoreRead := aValue;
- if Assigned(FEventer) then
- FEventer.HandleIgnoreRead(Self);
- end;
- end;
- constructor TLHandle.Create;
- begin
- FOnRead := nil;
- FOnWrite := nil;
- FOnError := nil;
- UserData := nil;
- FEventer := nil;
- FPrev := nil;
- FNext := nil;
- FFreeNext := nil;
- FFreeing := False;
- FDispose := False;
- FIgnoreWrite := False;
- FIgnoreRead := False;
- FIgnoreError := False;
- end;
- destructor TLHandle.Destroy;
- begin
- if Assigned(FEventer) then
- FEventer.InternalUnplugHandle(Self);
- end;
- procedure TLHandle.Free;
- begin
- CS.Enter;
- if Assigned(FEventer) and FEventer.FInLoop then
- FEventer.AddForFree(Self)
- else
- inherited Free;
- CS.Leave;
- end;
- { TLTimer }
- {
- function TLTimer.GetInterval: Integer;
- begin
- Result := Round(FInterval * MSecsPerDay);
- end;
- procedure TLTimer.SetEnabled(NewEnabled: integer);
- begin
- FTimeout := Now + Interval;
- FEnabled := true;
- end;
- procedure TLTimer.SetInterval(const aValue: Integer);
- begin
- FInterval := AValue / MSecsPerDay;
- end;
- procedure TLTimer.CallAction;
- begin
- if FEnabled and Assigned(FOnTimer) and (Now - FStarted >= FInterval) then
- begin
- FOnTimer(Self);
- if not FOneShot then
- FStarted := Now
- else
- FEnabled := false;
- end;
- end;
- }
- { TLEventer }
- constructor TLEventer.Create;
- begin
- FRoot := nil;
- FFreeRoot := nil;
- FFreeIter := nil;
- FInLoop := False;
- FCount := 0;
- FReferences := 1;
- end;
- destructor TLEventer.Destroy;
- begin
- Clear;
- end;
- function TLEventer.GetCount: Integer;
- begin
- Result := FCount;
- end;
- function TLEventer.GetTimeout: Integer;
- begin
- Result := 0;
- end;
- procedure TLEventer.SetTimeout(const Value: Integer);
- begin
- end;
- function TLEventer.Bail(const msg: string; const Ernum: Integer): Boolean;
- begin
- Result := False; // always false, substitute for caller's result
- if Assigned(FOnError) then
- FOnError(msg + LStrError(Ernum), Self);
- end;
- procedure TLEventer.AddForFree(aHandle: TLHandle);
- begin
- if not aHandle.FFreeing then begin
- aHandle.FFreeing := True;
- if not Assigned(FFreeIter) then begin
- FFreeIter := aHandle;
- FFreeRoot := aHandle;
- end else begin
- FFreeIter.FreeNext := aHandle;
- FFreeIter := aHandle;
- end;
- end;
- end;
- procedure TLEventer.FreeHandles;
- var
- Temp, Temp2: TLHandle;
- begin
- Temp := FFreeRoot;
- while Assigned(Temp) do begin
- Temp2 := Temp.FreeNext;
- Temp.Free;
- Temp := Temp2;
- end;
- FFreeRoot := nil;
- FFreeIter := nil;
- end;
- procedure TLEventer.HandleIgnoreError(aHandle: TLHandle);
- begin
- end;
- procedure TLEventer.HandleIgnoreWrite(aHandle: TLHandle);
- begin
- end;
- procedure TLEventer.HandleIgnoreRead(aHandle: TLHandle);
- begin
- end;
- function TLEventer.GetInternalData(aHandle: TLHandle): Pointer;
- begin
- Result := aHandle.FInternalData;
- end;
- procedure TLEventer.SetInternalData(aHandle: TLHandle; const aData: Pointer);
- begin
- aHandle.FInternalData := aData;
- end;
- procedure TLEventer.SetHandleEventer(aHandle: TLHandle);
- begin
- aHandle.FEventer := Self;
- end;
- procedure TLEventer.InternalUnplugHandle(aHandle: TLHandle);
- begin
- if aHandle.FEventer = Self then begin
- if aHandle.FEventer.FInLoop then begin
- aHandle.FEventer.AddForFree(aHandle);
- Exit;
- end;
- aHandle.FEventer := nil; // avoid recursive AV
- if Assigned(aHandle.FPrev) then begin
- aHandle.FPrev.FNext := aHandle.FNext;
- if Assigned(aHandle.FNext) then
- aHandle.FNext.FPrev := aHandle.FPrev;
- end else if Assigned(aHandle.FNext) then begin
- aHandle.FNext.FPrev := aHandle.FPrev;
- if aHandle = FRoot then
- FRoot := aHandle.FNext;
- end else FRoot := nil;
- if FCount > 0 then
- Dec(FCount);
- end;
- end;
- function TLEventer.AddHandle(aHandle: TLHandle): Boolean;
- begin
- Result := False;
- if not Assigned(aHandle.FEventer) then begin
- if not Assigned(FRoot) then begin
- FRoot := aHandle;
- end else begin
- if Assigned(FRoot.FNext) then begin
- FRoot.FNext.FPrev := aHandle;
- aHandle.FNext := FRoot.FNext;
- end;
- FRoot.FNext := aHandle;
- aHandle.FPrev := FRoot;
- end;
- aHandle.FEventer := Self;
- Inc(FCount);
- Result := True;
- end;
- end;
- function TLEventer.CallAction: Boolean;
- begin
- Result := True;
- // override in ancestor
- end;
- procedure TLEventer.RemoveHandle(aHandle: TLHandle);
- begin
- aHandle.Free;
- end;
- procedure TLEventer.UnplugHandle(aHandle: TLHandle);
- begin
- CS.Enter;
- InternalUnplugHandle(aHandle);
- CS.Leave;
- end;
- procedure TLEventer.UnregisterHandle(aHandle: TLHandle);
- begin
- // do nothing, specific to win32 LCLEventer crap (windows is shit)
- end;
- procedure TLEventer.LoadFromEventer(aEventer: TLEventer);
- begin
- Clear;
- FRoot := aEventer.FRoot;
- FOnError := aEventer.FOnError;
- end;
- procedure TLEventer.Clear;
- var
- Temp1, Temp2: TLHandle;
- begin
- Temp1 := FRoot;
- Temp2 := FRoot;
- while Assigned(Temp2) do begin
- Temp1 := Temp2;
- Temp2 := Temp1.FNext;
- Temp1.Free;
- end;
- FRoot := nil;
- end;
- procedure TLEventer.AddRef;
- begin
- Inc(FReferences);
- end;
- procedure TLEventer.DeleteRef;
- begin
- if FReferences > 0 then
- Dec(FReferences);
- if FReferences = 0 then
- Free;
- end;
- { TLSelectEventer }
- constructor TLSelectEventer.Create;
- begin
- inherited Create;
- FTimeout.tv_sec := 0;
- FTimeout.tv_usec := 0;
- end;
- function TLSelectEventer.GetTimeout: Integer;
- begin
- if FTimeout.tv_sec < 0 then
- Result := -1
- else
- Result := (FTimeout.tv_sec * 1000) + FTimeout.tv_usec;
- end;
- procedure TLSelectEventer.SetTimeout(const Value: Integer);
- begin
- if Value >= 0 then begin
- FTimeout.tv_sec := Value div 1000;
- FTimeout.tv_usec := Value mod 1000;
- end else begin
- FTimeout.tv_sec := -1;
- FTimeout.tv_usec := 0;
- end;
- end;
- procedure TLSelectEventer.ClearSets;
- begin
- fpFD_ZERO(FReadFDSet);
- fpFD_ZERO(FWriteFDSet);
- fpFD_ZERO(FErrorFDSet);
- end;
- function TLSelectEventer.CallAction: Boolean;
- var
- Temp, Temp2: TLHandle;
- n: Integer;
- MaxHandle: THandle;
- TempTime: TTimeVal;
- begin
- if FInLoop then
- Exit;
- if not Assigned(FRoot) then begin
- Sleep(FTimeout.tv_sec * 1000 + FTimeout.tv_usec div 1000);
- Exit;
- end;
- FInLoop := True;
- Temp := FRoot;
- MaxHandle := 0;
- ClearSets;
- while Assigned(Temp) do begin
- if (not Temp.FDispose ) // handle still valid
- and ( (not Temp.IgnoreWrite) // check write or
- or (not Temp.IgnoreRead ) // check read or
- or (not Temp.IgnoreError)) // check for errors
- then begin
- if not Temp.IgnoreWrite then
- fpFD_SET(Temp.FHandle, FWriteFDSet);
- if not Temp.IgnoreRead then
- fpFD_SET(Temp.FHandle, FReadFDSet);
- if not Temp.IgnoreError then
- fpFD_SET(Temp.FHandle, FErrorFDSet);
- if Temp.FHandle > MaxHandle then
- MaxHandle := Temp.FHandle;
- end;
- Temp2 := Temp;
- Temp := Temp.FNext;
- if Temp2.FDispose then
- Temp2.Free;
- end;
- TempTime := FTimeout;
- if FTimeout.tv_sec >= 0 then
- n := fpSelect(MaxHandle + 1, @FReadFDSet, @FWriteFDSet, @FErrorFDSet, @TempTime)
- else
- n := fpSelect(MaxHandle + 1, @FReadFDSet, @FWriteFDSet, @FErrorFDSet, nil);
-
- if n < 0 then
- Bail('Error on select', LSocketError);
- Result := n > 0;
-
- if Result then begin
- Temp := FRoot;
- while Assigned(Temp) do begin
- if not Temp.FDispose and (fpFD_ISSET(Temp.FHandle, FWriteFDSet) <> 0) then
- if Assigned(Temp.FOnWrite) and not Temp.IgnoreWrite then
- Temp.FOnWrite(Temp);
- if not Temp.FDispose and (fpFD_ISSET(Temp.FHandle, FReadFDSet) <> 0) then
- if Assigned(Temp.FOnRead) and not Temp.IgnoreRead then
- Temp.FOnRead(Temp);
- if not Temp.FDispose and (fpFD_ISSET(Temp.FHandle, FErrorFDSet) <> 0) then
- if Assigned(Temp.FOnError) and not Temp.IgnoreError then
- Temp.FOnError(Temp, 'Handle error' + LStrError(LSocketError));
- Temp2 := Temp;
- Temp := Temp.FNext;
- if Temp2.FDispose then
- AddForFree(Temp2);
- end;
- end;
- FInLoop := False;
- if Assigned(FFreeRoot) then
- FreeHandles;
- end;
- {$i sys/lkqueueeventer.inc}
- {$i sys/lepolleventer.inc}
- {$ifdef nochoice}
- function BestEventerClass: TLEventerClass;
- begin
- Result := TLSelectEventer;
- end;
- {$endif}
- initialization
- CS := TCriticalSection.Create;
- finalization
- CS.Free;
- end.
|