123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518 |
- {
- $Id$
- libasync: Asynchronous event management
- Copyright (C) 2001 by
- Areca Systems GmbH / Sebastian Guenther, [email protected]
- Unix implementation
- See the file COPYING.FPC, included in this distribution,
- for details about the copyright.
- 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.
- }
- unit libasync;
- {$MODE objfpc}
- interface
- type
- TAsyncData = record
- IsRunning, DoBreak: Boolean;
- HasCallbacks: Boolean; // True as long as callbacks are set
- FirstTimer: Pointer;
- FirstIOCallback: Pointer;
- FDData: Pointer;
- HighestHandle: LongInt;
- end;
- {$INCLUDE libasync.inc}
- implementation
- {$ifdef VER1_0}
- uses Linux;
- {$else}
- Uses Unix;
- {$endif}
- const
- MaxHandle = SizeOf(TFDSet) * 8 - 1;
- type
- PTimerData = ^TTimerData;
- TTimerData = record
- Next: PTimerData;
- MSec: LongInt;
- NextTick: Int64;
- Callback: TAsyncCallback;
- UserData: Pointer;
- Periodic: Boolean;
- end;
- PIOCallbackData = ^TIOCallbackData;
- TIOCallbackData = record
- Next: PIOCallbackData;
- IOHandle: LongInt;
- ReadCallback, WriteCallback: TAsyncCallback;
- ReadUserData, WriteUserData: Pointer;
- SavedHandleFlags: LongInt;
- end;
- procedure InitIOCallback(Handle: TAsyncHandle; IOHandle: LongInt;
- ARead: Boolean; ReadCallback: TAsyncCallback; ReadUserData: Pointer;
- AWrite: Boolean; WriteCallback: TAsyncCallback; WriteUserData: Pointer);
- var
- Data: PIOCallbackData;
- i: LongInt;
- NeedData: Boolean;
- begin
- if IOHandle > MaxHandle then
- exit;
- NeedData := True;
- Data := Handle^.Data.FirstIOCallback;
- while Assigned(Data) do
- begin
- if Data^.IOHandle = IOHandle then
- begin
- if ARead then
- begin
- Data^.ReadCallback := ReadCallback;
- Data^.ReadUserData := ReadUserData;
- end;
- if AWrite then
- begin
- Data^.WriteCallback := WriteCallback;
- Data^.WriteUserData := WriteUserData;
- end;
- NeedData := False;
- break;
- end;
- Data := Data^.Next;
- end;
- if NeedData then
- begin
- New(Data);
- Data^.Next := Handle^.Data.FirstIOCallback;
- Handle^.Data.FirstIOCallback := Data;
- Data^.IOHandle := IOHandle;
- if ARead then
- begin
- Data^.ReadCallback := ReadCallback;
- Data^.ReadUserData := ReadUserData;
- end else
- Data^.ReadCallback := nil;
- if AWrite then
- begin
- Data^.WriteCallback := WriteCallback;
- Data^.WriteUserData := WriteUserData;
- end else
- Data^.WriteCallback := nil;
- if not Assigned(Handle^.Data.FDData) then
- begin
- GetMem(Handle^.Data.FDData, SizeOf(TFDSet) * 2);
- FD_Zero(PFDSet(Handle^.Data.FDData)[0]);
- FD_Zero(PFDSet(Handle^.Data.FDData)[1]);
- end;
- if IOHandle > Handle^.Data.HighestHandle then
- Handle^.Data.HighestHandle := IOHandle;
- end;
- Data^.SavedHandleFlags := fcntl(IOHandle, F_GetFl);
- fcntl(IOHandle, F_SetFl, Data^.SavedHandleFlags or Open_NonBlock);
- case IOHandle of
- StdInputHandle:
- i := Open_RdOnly;
- StdOutputHandle, StdErrorHandle:
- i := Open_WrOnly;
- else
- i := Data^.SavedHandleFlags and Open_Accmode;
- end;
- case i of
- Open_RdOnly:
- if ARead then
- FD_Set(IOHandle, PFDSet(Handle^.Data.FDData)[0]);
- Open_WrOnly:
- if AWrite then
- FD_Set(IOHandle, PFDSet(Handle^.Data.FDData)[1]);
- Open_RdWr:
- begin
- if ARead then
- FD_Set(IOHandle, PFDSet(Handle^.Data.FDData)[0]);
- if AWrite then
- FD_Set(IOHandle, PFDSet(Handle^.Data.FDData)[1]);
- end;
- end;
- Handle^.Data.HasCallbacks := True;
- end;
- procedure CheckForCallbacks(Handle: TAsyncHandle);
- var
- Data: PIOCallbackData;
- begin
- if (Handle^.Data.HasCallbacks) and
- (not Assigned(Handle^.Data.FirstIOCallback)) and
- (not Assigned(Handle^.Data.FirstTimer)) then
- Handle^.Data.HasCallbacks := False;
- end;
- procedure asyncInit(Handle: TAsyncHandle); cdecl;
- begin
- Handle^.Data.HighestHandle := -1;
- end;
- procedure asyncFree(Handle: TAsyncHandle); cdecl;
- var
- Timer, NextTimer: PTimerData;
- IOCallback, NextIOCallback: PIOCallbackData;
- begin
- Timer := PTimerData(Handle^.Data.FirstTimer);
- while Assigned(Timer) do
- begin
- NextTimer := Timer^.Next;
- Dispose(Timer);
- Timer := NextTimer;
- end;
- IOCallback := PIOCallbackData(Handle^.Data.FirstIOCallback);
- while Assigned(IOCallback) do
- begin
- if (IOCallback^.SavedHandleFlags and Open_NonBlock) = 0 then
- fcntl(IOCallback^.IOHandle, F_SetFl, IOCallback^.SavedHandleFlags);
- NextIOCallback := IOCallback^.Next;
- Dispose(IOCallback);
- IOCallback := NextIOCallback;
- end;
- if Assigned(Handle^.Data.FDData) then
- FreeMem(Handle^.Data.FDData);
- end;
- procedure asyncRun(Handle: TAsyncHandle); cdecl;
- var
- Timer, NextTimer: PTimerData;
- TimeOut, AsyncResult: Integer;
- CurTime, NextTick: Int64;
- CurReadFDSet, CurWriteFDSet: TFDSet;
- IOCallback: PIOCallbackData;
- begin
- if Handle^.Data.IsRunning then
- exit;
- Handle^.Data.DoBreak := False;
- Handle^.Data.IsRunning := True;
- // Prepare timers
- if Assigned(Handle^.Data.FirstTimer) then
- begin
- CurTime := asyncGetTicks;
- Timer := Handle^.Data.FirstTimer;
- while Assigned(Timer) do
- begin
- Timer^.NextTick := CurTime + Timer^.MSec;
- Timer := Timer^.Next;
- end;
- end;
- while (not Handle^.Data.DoBreak) and Handle^.Data.HasCallbacks do
- begin
- Timer := Handle^.Data.FirstTimer;
- if Assigned(Handle^.Data.FirstTimer) then
- begin
- // Determine when the next timer tick will happen
- CurTime := asyncGetTicks;
- NextTick := High(Int64);
- Timer := Handle^.Data.FirstTimer;
- while Assigned(Timer) do
- begin
- if Timer^.NextTick < NextTick then
- NextTick := Timer^.NextTick;
- Timer := Timer^.Next;
- end;
- TimeOut := NextTick - CurTime;
- if TimeOut < 0 then
- TimeOut := 0;
- end else
- TimeOut := -1;
- if Handle^.Data.HighestHandle >= 0 then
- begin
- CurReadFDSet := PFDSet(Handle^.Data.FDData)[0];
- CurWriteFDSet := PFDSet(Handle^.Data.FDData)[1];
- AsyncResult := Select(Handle^.Data.HighestHandle + 1,
- @CurReadFDSet, @CurWriteFDSet, nil, TimeOut);
- end else
- AsyncResult := Select(0, nil, nil, nil, TimeOut);
- if Assigned(Handle^.Data.FirstTimer) then
- begin
- // Check for triggered timers
- CurTime := asyncGetTicks;
- Timer := Handle^.Data.FirstTimer;
- while Assigned(Timer) do
- begin
- if Timer^.NextTick <= CurTime then
- begin
- Timer^.Callback(Timer^.UserData);
- NextTimer := Timer^.Next;
- if Timer^.Periodic then
- Inc(Timer^.NextTick, Timer^.MSec)
- else
- asyncRemoveTimer(Handle, Timer);
- if Handle^.Data.DoBreak then
- break;
- Timer := NextTimer;
- end else
- Timer := Timer^.Next;
- end;
- end;
- if (AsyncResult > 0) and not Handle^.Data.DoBreak then
- begin
- // Check for I/O events
- IOCallback := Handle^.Data.FirstIOCallback;
- while Assigned(IOCallback) do
- begin
- if FD_IsSet(IOCallback^.IOHandle, CurReadFDSet) and
- FD_IsSet(IOCallback^.IOHandle, PFDSet(Handle^.Data.FDData)[0]) then
- begin
- IOCallback^.ReadCallback(IOCallback^.ReadUserData);
- if Handle^.Data.DoBreak then
- break;
- end;
- if FD_IsSet(IOCallback^.IOHandle, CurWriteFDSet) and
- FD_IsSet(IOCallback^.IOHandle, PFDSet(Handle^.Data.FDData)[1]) then
- begin
- IOCallback^.WriteCallback(IOCallback^.WriteUserData);
- if Handle^.Data.DoBreak then
- break;
- end;
- IOCallback := IOCallback^.Next;
- end;
- end;
- end;
- Handle^.Data.IsRunning := False;
- end;
- procedure asyncBreak(Handle: TAsyncHandle); cdecl;
- begin
- Handle^.Data.DoBreak := True;
- end;
- function asyncIsRunning(Handle: TAsyncHandle): Boolean; cdecl;
- begin
- Result := Handle^.Data.IsRunning;
- end;
- function asyncGetTicks: Int64; cdecl;
- var
- Time: TimeVal;
- begin
- GetTimeOfDay(Time);
- Result := Int64(Time.Sec) * 1000 + Int64(Time.USec div 1000);
- end;
- function asyncAddTimer(
- Handle: TAsyncHandle;
- MSec: LongInt;
- Periodic: Boolean;
- Callback: TAsyncCallback;
- UserData: Pointer
- ): TAsyncTimer; cdecl;
- var
- Data: PTimerData;
- begin
- if not Assigned(Callback) then
- exit;
- New(Data);
- Result := Data;
- Data^.Next := Handle^.Data.FirstTimer;
- Handle^.Data.FirstTimer := Data;
- Data^.MSec := MSec;
- Data^.Periodic := Periodic;
- Data^.Callback := Callback;
- Data^.UserData := UserData;
- if Handle^.Data.IsRunning then
- Data^.NextTick := asyncGetTicks + MSec;
- Handle^.Data.HasCallbacks := True;
- end;
- procedure asyncRemoveTimer(
- Handle: TAsyncHandle;
- Timer: TASyncTimer); cdecl;
- var
- Data, CurData, PrevData, NextData: PTimerData;
- begin
- Data := PTimerData(Timer);
- CurData := Handle^.Data.FirstTimer;
- PrevData := nil;
- while Assigned(CurData) do
- begin
- NextData := CurData^.Next;
- if CurData = Data then
- begin
- if Assigned(PrevData) then
- PrevData^.Next := NextData
- else
- Handle^.Data.FirstTimer := NextData;
- break;
- end;
- PrevData := CurData;
- CurData := NextData;
- end;
- Dispose(Data);
- CheckForCallbacks(Handle);
- end;
- procedure asyncSetIOCallback(
- Handle: TAsyncHandle;
- IOHandle: LongInt;
- Callback: TAsyncCallback;
- UserData: Pointer); cdecl;
- begin
- InitIOCallback(Handle, IOHandle, True, Callback, UserData,
- True, Callback, UserData);
- end;
- procedure asyncClearIOCallback(Handle: TAsyncHandle;
- IOHandle: LongInt); cdecl;
- var
- CurData, PrevData, NextData: PIOCallbackData;
- begin
- CurData := Handle^.Data.FirstIOCallback;
- PrevData := nil;
- while Assigned(CurData) do
- begin
- NextData := CurData^.Next;
- if CurData^.IOHandle = IOHandle then
- begin
- FD_Clr(IOHandle, PFDSet(Handle^.Data.FDData)[0]);
- FD_Clr(IOHandle, PFDSet(Handle^.Data.FDData)[1]);
- if Assigned(PrevData) then
- PrevData^.Next := NextData
- else
- Handle^.Data.FirstIOCallback := NextData;
- Dispose(CurData);
- break;
- end;
- PrevData := CurData;
- CurData := NextData;
- end;
- CheckForCallbacks(Handle);
- end;
- procedure asyncSetDataAvailableCallback(
- Handle: TAsyncHandle;
- IOHandle: LongInt;
- Callback: TAsyncCallback;
- UserData: Pointer); cdecl;
- begin
- InitIOCallback(Handle, IOHandle, True, Callback, UserData, False, nil, nil);
- end;
- procedure asyncClearDataAvailableCallback(Handle: TAsyncHandle;
- IOHandle: LongInt); cdecl;
- var
- CurData, PrevData, NextData: PIOCallbackData;
- begin
- CurData := Handle^.Data.FirstIOCallback;
- PrevData := nil;
- while Assigned(CurData) do
- begin
- NextData := CurData^.Next;
- if CurData^.IOHandle = IOHandle then
- begin
- FD_Clr(IOHandle, PFDSet(Handle^.Data.FDData)[0]);
- if Assigned(CurData^.WriteCallback) then
- CurData^.ReadCallback := nil
- else
- begin
- if Assigned(PrevData) then
- PrevData^.Next := NextData
- else
- Handle^.Data.FirstIOCallback := NextData;
- Dispose(CurData);
- end;
- break;
- end;
- PrevData := CurData;
- CurData := NextData;
- end;
- CheckForCallbacks(Handle);
- end;
- procedure asyncSetCanWriteCallback(
- Handle: TAsyncHandle;
- IOHandle: LongInt;
- Callback: TAsyncCallback;
- UserData: Pointer); cdecl;
- begin
- InitIOCallback(Handle, IOHandle, False, nil, nil, True, Callback, UserData);
- end;
- procedure asyncClearCanWriteCallback(Handle: TAsyncHandle;
- IOHandle: LongInt); cdecl;
- var
- CurData, PrevData, NextData: PIOCallbackData;
- begin
- CurData := Handle^.Data.FirstIOCallback;
- PrevData := nil;
- while Assigned(CurData) do
- begin
- NextData := CurData^.Next;
- if CurData^.IOHandle = IOHandle then
- begin
- FD_Clr(IOHandle, PFDSet(Handle^.Data.FDData)[1]);
- if Assigned(CurData^.ReadCallback) then
- CurData^.WriteCallback := nil
- else
- begin
- if Assigned(PrevData) then
- PrevData^.Next := NextData
- else
- Handle^.Data.FirstIOCallback := NextData;
- Dispose(CurData);
- end;
- break;
- end;
- PrevData := CurData;
- CurData := NextData;
- end;
- CheckForCallbacks(Handle);
- end;
- end.
- {
- $Log$
- Revision 1.2 2002-09-07 15:42:52 peter
- * old logs removed and tabs fixed
- Revision 1.1 2002/01/29 17:54:53 peter
- * splitted to base and extra
- }
|