ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/runtime/IBEvents.pas
(Generate patch)

Comparing ibx/trunk/runtime/IBEvents.pas (file contents):
Revision 33 by tony, Sat Jul 18 12:30:52 2015 UTC vs.
Revision 45 by tony, Tue Dec 6 10:33:46 2016 UTC

# Line 44 | Line 44
44  
45   unit IBEvents;
46  
47 < {$Mode Delphi}
47 > {$mode objfpc}{$H+}
48  
49   interface
50  
# Line 54 | Line 54 | uses
54   {$ELSE}
55    unix,
56   {$ENDIF}
57 <  Classes, IBHeader, IBExternals, IB, IBDatabase;
57 >  Classes, IBExternals, IB, IBDatabase;
58  
59   const
60    MaxEvents = 15;
# Line 68 | Line 68 | type
68  
69    TIBEvents = class(TComponent)
70    private
71    FIBLoaded: Boolean;
71      FBase: TIBBase;
72 +    FEventIntf: IEvents;
73      FEvents: TStrings;
74      FOnEventAlert: TEventAlert;
75    FEventHandler: TObject;
75      FRegistered: boolean;
76      FDeferredRegister: boolean;
77 +    procedure EventHandler(Sender: IEvents);
78 +    procedure ProcessEvents;
79      procedure EventChange(sender: TObject);
80      function GetDatabase: TIBDatabase;
80    function GetDatabaseHandle: TISC_DB_HANDLE;
81      procedure SetDatabase( value: TIBDatabase);
82      procedure ValidateDatabase( Database: TIBDatabase);
83      procedure DoBeforeDatabaseDisconnect(Sender: TObject);
# Line 92 | Line 92 | type
92      destructor Destroy; override;
93      procedure RegisterEvents;
94      procedure UnRegisterEvents;
95    property DatabaseHandle: TISC_DB_HANDLE read GetDatabaseHandle;
95      property DeferredRegister: boolean read FDeferredRegister write FDeferredRegister;
96 +    property EventIntf: IEvents read FEventIntf;
97    published
98      property Database: TIBDatabase read GetDatabase write SetDatabase;
99      property Events: TStrings read FEvents write SetEvents;
# Line 104 | Line 104 | type
104  
105   implementation
106  
107 < uses
108 <  IBIntf, syncobjs, SysUtils;
109 <
110 < type
111 <
112 <  TEventHandlerStates = (
113 <    stIdle,           {Events not monitored}
114 <    stHasEvb,         {Event Block Allocated but not queued}
115 <    stQueued,         {Waiting for Event}
116 <    stSignalled       {Event Callback signalled Event}
117 <   );
118 <
119 <  { TEventHandler }
120 <
121 <  TEventHandler = class(TThread)
122 <  private
123 <    FOwner: TIBEvents;
124 <    FCriticalSection: TCriticalSection;   {protects race conditions in stQueued state}
125 <    {$IFDEF WINDOWS}
126 <    {Make direct use of Windows API as TEventObject don't seem to work under
127 <     Windows!}
128 <    FEventHandler: THandle;
129 <    {$ELSE}
130 <    FEventWaiting: TEventObject;
131 <    {$ENDIF}
132 <    FState: TEventHandlerStates;
133 <    FEventBuffer: PChar;
134 <    FEventBufferLen: integer;
135 <    FEventID: ISC_LONG;
136 <    FRegisteredState: Boolean;
137 <    FResultBuffer: PChar;
138 <    FEvents: TStringList;
139 <    FSignalFired: boolean;
140 <    procedure QueueEvents;
141 <    procedure CancelEvents;
142 <    procedure HandleEventSignalled(length: short; updated: PChar);
143 <    procedure DoEventSignalled;
144 <  protected
145 <    procedure Execute; override;
146 <  public
147 <    constructor Create(Owner: TIBEvents);
148 <    destructor Destroy; override;
149 <    procedure Terminate;
150 <    procedure RegisterEvents(Events: TStrings);
151 <    procedure UnregisterEvents;
152 <  end;
153 <
154 < {This procedure is used for the event call back - note the cdecl }
155 <
156 < procedure IBEventCallback( ptr: pointer; length: short; updated: PChar); cdecl;
157 < begin
158 <  if (ptr = nil) or (length = 0) or (updated = nil) then
159 <    Exit;
160 <  { Handle events asynchronously in second thread }
161 <  TEventHandler(ptr).HandleEventSignalled(length,updated);
162 < end;
163 <
164 <
165 <
166 < { TEventHandler }
167 <
168 < procedure TEventHandler.QueueEvents;
169 < var
170 <  callback: pointer;
171 <  DBH: TISC_DB_HANDLE;
172 < begin
173 <  if FState <> stHasEvb then
174 <    Exit;
175 <  FCriticalSection.Enter;
176 <  try
177 <    callback := @IBEventCallback;
178 <    DBH := FOwner.DatabaseHandle;
179 <    if (isc_que_events( StatusVector, @DBH, @FEventID, FEventBufferLen,
180 <                     FEventBuffer, TISC_CALLBACK(callback), PVoid(Self)) <> 0) then
181 <      IBDatabaseError;
182 <    FState := stQueued
183 <  finally
184 <    FCriticalSection.Leave
185 <  end;
186 < end;
187 <
188 < procedure TEventHandler.CancelEvents;
189 < var
190 <  DBH: TISC_DB_HANDLE;
191 < begin
192 <  if FState in [stQueued,stSignalled] then
193 <  begin
194 <    FCriticalSection.Enter;
195 <    try
196 <      DBH := FOwner.DatabaseHandle;
197 <      if (isc_Cancel_events( StatusVector, @DBH, @FEventID) <> 0) then
198 <          IBDatabaseError;
199 <      FState := stHasEvb;
200 <    finally
201 <      FCriticalSection.Leave
202 <    end;
203 <  end;
204 <
205 <  if FState = stHasEvb then
206 <  begin
207 <    isc_free( FEventBuffer);
208 <    FEventBuffer := nil;
209 <    isc_free( FResultBuffer);
210 <    FResultBuffer := nil;
211 <    FState := stIdle
212 <  end;
213 <  FSignalFired := false
214 < end;
215 <
216 < procedure TEventHandler.HandleEventSignalled(length: short; updated: PChar);
217 < begin
218 <  FCriticalSection.Enter;
219 <  try
220 <    if FState <> stQueued then
221 <      Exit;
222 <    Move(Updated[0], FResultBuffer[0], Length);
223 <    FState := stSignalled;
224 <    {$IFDEF WINDOWS}
225 <    SetEVent(FEventHandler);
226 <    {$ELSE}
227 <    FEventWaiting.SetEvent;
228 <    {$ENDIF}
229 <  finally
230 <    FCriticalSection.Leave
231 <  end;
232 < end;
233 <
234 < procedure TEventHandler.DoEventSignalled;
235 < var
236 <  i: integer;
237 <  CancelAlerts: boolean;
238 <  Status: array[0..19] of ISC_LONG; {Note in 64 implementation the ibase.h implementation
239 <                                     is different from Interbase 6.0 API documentatoin}
240 < begin
241 <    if FState <> stSignalled then
242 <      Exit;
243 <    isc_event_counts( @Status, FEventBufferLen, FEventBuffer, FResultBuffer);
244 <    CancelAlerts := false;
245 <    if not FSignalFired then
246 <      FSignalFired := true   {Ignore first time}
247 <    else
248 <    if assigned(FOwner.FOnEventAlert)  then
249 <    begin
250 <      for i := 0 to FEvents.Count - 1 do
251 <      begin
252 <        try
253 <        if (Status[i] <> 0) and not CancelAlerts then
254 <            FOwner.FOnEventAlert( self, FEvents[i], Status[i], CancelAlerts);
255 <        except
256 <            FOwner.FBase.HandleException(Self)
257 <        end;
258 <      end;
259 <    end;
260 <    FState := stHasEvb;
261 <  if  CancelAlerts then
262 <      CancelEvents
263 <    else
264 <      QueueEvents
265 < end;
266 <
267 < procedure TEventHandler.Execute;
268 < begin
269 <  while not Terminated do
270 <  begin
271 <    {$IFDEF WINDOWS}
272 <    WaitForSingleObject(FEventHandler,INFINITE);
273 <    {$ELSE}
274 <    FEventWaiting.WaitFor(INFINITE);
275 <    {$ENDIF}
276 <
277 <    if not Terminated and (FState = stSignalled) then
278 <      Synchronize(DoEventSignalled)
279 <  end;
280 < end;
281 <
282 <
283 <
284 < constructor TEventHandler.Create(Owner: TIBEvents);
285 < var
286 <  PSa : PSecurityAttributes;
287 < {$IFDEF WINDOWS}
288 <  Sd : TSecurityDescriptor;
289 <  Sa : TSecurityAttributes;
290 < begin
291 <  InitializeSecurityDescriptor(@Sd,SECURITY_DESCRIPTOR_REVISION);
292 <  SetSecurityDescriptorDacl(@Sd,true,nil,false);
293 <  Sa.nLength := SizeOf(Sa);
294 <  Sa.lpSecurityDescriptor := @Sd;
295 <  Sa.bInheritHandle := true;
296 <  PSa := @Sa;
297 < {$ELSE}
298 < begin
299 <  PSa:= nil;
300 < {$ENDIF}
301 <  inherited Create(true);
302 <  FOwner := Owner;
303 <  FState := stIdle;
304 <  FCriticalSection := TCriticalSection.Create;
305 <  {$IFDEF WINDOWS}
306 <  FEventHandler := CreateEvent(PSa,false,true,nil);
307 <  {$ELSE}
308 <  FEventWaiting := TEventObject.Create(PSa,false,true,FOwner.Name+'.Events');
309 <  {$ENDIF}
310 <  FEvents := TStringList.Create;
311 <  FreeOnTerminate := true;
312 <  Resume
313 < end;
314 <
315 < destructor TEventHandler.Destroy;
316 < begin
317 <  if assigned(FCriticalSection) then FCriticalSection.Free;
318 <  {$IFDEF WINDOWS}
319 <  CloseHandle(FEventHandler);
320 <  {$ELSE}
321 <  if assigned(FEventWaiting) then FEventWaiting.Free;
322 <  {$ENDIF}
323 <  if assigned(FEvents) then FEvents.Free;
324 <  inherited Destroy;
325 < end;
326 <
327 < procedure TEventHandler.Terminate;
328 < begin
329 <  inherited Terminate;
330 <  {$IFDEF WINDOWS}
331 <  SetEvent(FEventHandler);
332 <  {$ELSE}
333 <  FEventWaiting.SetEvent;
334 <  {$ENDIF}
335 <  CancelEvents;
336 < end;
337 <
338 < procedure TEventHandler.RegisterEvents(Events: TStrings);
339 < var
340 <  i: integer;
341 <  EventNames: array of PChar;
342 < begin
343 <  UnregisterEvents;
344 <
345 <  if Events.Count = 0 then
346 <    exit;
347 <
348 <  setlength(EventNames,MaxEvents);
349 <  try
350 <    for i := 0 to Events.Count-1 do
351 <      EventNames[i] := PChar(Events[i]);
352 <    FEvents.Assign(Events);
353 <    FEventBufferlen := isc_event_block(@FEventBuffer,@FResultBuffer,
354 <                        Events.Count,
355 <                        EventNames[0],EventNames[1],EventNames[2],
356 <                        EventNames[3],EventNames[4],EventNames[5],
357 <                        EventNames[6],EventNames[7],EventNames[8],
358 <                        EventNames[9],EventNames[10],EventNames[11],
359 <                        EventNames[12],EventNames[13],EventNames[14]
360 <                        );
361 <    FState := stHasEvb;
362 <    FRegisteredState := true;
363 <    QueueEvents
364 <  finally
365 <    SetLength(EventNames,0)
366 <  end;
367 < end;
368 <
369 < procedure TEventHandler.UnregisterEvents;
370 < begin
371 <  if FRegisteredState then
372 <  begin
373 <    CancelEvents;
374 <    FRegisteredState := false;
375 <  end;
376 < end;
107 > uses SysUtils, FBMessages;
108  
109   { TIBEvents }
110  
# Line 388 | Line 119 | end;
119   constructor TIBEvents.Create( AOwner: TComponent);
120   begin
121    inherited Create( AOwner);
391  FIBLoaded := False;
392  CheckIBLoaded;
393  FIBLoaded := True;
122    FBase := TIBBase.Create(Self);
123 <  FBase.BeforeDatabaseDisconnect := DoBeforeDatabaseDisconnect;
124 <  FBase.AfterDatabaseConnect := DoAfterDatabaseConnect;
123 >  FBase.BeforeDatabaseDisconnect := @DoBeforeDatabaseDisconnect;
124 >  FBase.AfterDatabaseConnect := @DoAfterDatabaseConnect;
125    FEvents := TStringList.Create;
126    with TStringList( FEvents) do
127    begin
128 <    OnChange := EventChange;
128 >    OnChange := @EventChange;
129      Duplicates := dupIgnore;
130    end;
403  FEventHandler := TEventHandler.Create(self)
131   end;
132  
133   destructor TIBEvents.Destroy;
134   begin
135 <  if FIBLoaded then
136 <  begin
137 <    UnregisterEvents;
138 <    SetDatabase(nil);
139 <    TStringList(FEvents).OnChange := nil;
413 <    FBase.Free;
414 <    FEvents.Free;
415 <  end;
416 <  if assigned(FEventHandler) then
417 <    TEventHandler(FEventHandler).Terminate;
418 <  FEventHandler := nil;
419 <  inherited Destroy;
135 >  UnregisterEvents;
136 >  SetDatabase(nil);
137 >  TStringList(FEvents).OnChange := nil;
138 >  FBase.Free;
139 >  FEvents.Free;
140   end;
141  
142 + procedure TIBEvents.EventHandler(Sender: IEvents);
143 + begin
144 +  TThread.Synchronize(nil,@ProcessEvents);
145 + end;
146  
147 + procedure TIBEvents.ProcessEvents;
148 + var EventCounts: TEventCounts;
149 +    CancelAlerts: Boolean;
150 +    i: integer;
151 + begin
152 +  if (csDestroying in ComponentState) or (FEventIntf = nil) then Exit;
153 +  EventCounts := FEventIntf.ExtractEventCounts;
154 +  if assigned(FOnEventAlert) then
155 +  begin
156 +    CancelAlerts := false;
157 +    for i := 0 to Length(EventCounts) -1 do
158 +    begin
159 +      OnEventAlert(self,EventCounts[i].EventName,EventCounts[i].Count,CancelAlerts);
160 +      if CancelAlerts then break;
161 +    end;
162 +  end;
163 +  if CancelAlerts then
164 +    UnRegisterEvents
165 +  else
166 +    FEventIntf.AsyncWaitForEvent(@EventHandler);
167 + end;
168  
169   procedure TIBEvents.EventChange( sender: TObject);
170   begin
# Line 431 | Line 176 | begin
176    begin
177      TStringList(Events).OnChange := nil;
178      Events.Delete( MaxEvents);
179 <    TStringList(Events).OnChange := EventChange;
179 >    TStringList(Events).OnChange := @EventChange;
180      IBError(ibxeMaximumEvents, [nil]);
181    end;
182 <  if Registered then
183 <    TEventHandler(FEventHandler).RegisterEvents(Events);
182 >  if Registered  and (FEventIntf <> nil) then
183 >  begin
184 >    FEventIntf.SetEvents(Events);
185 >    FEventIntf.AsyncWaitForEvent(@EventHandler);
186 >  end;
187   end;
188  
189   procedure TIBEvents.Notification( AComponent: TComponent;
# Line 451 | Line 199 | end;
199  
200   procedure TIBEvents.RegisterEvents;
201   begin
202 +  if FRegistered then Exit;
203    ValidateDatabase( Database);
204    if csDesigning in ComponentState then FRegistered := true
205    else
# Line 459 | Line 208 | begin
208        FDeferredRegister := true
209      else
210      begin
211 <      TEventHandler(FEventHandler).RegisterEvents(Events);
211 >      FEventIntf := Database.Attachment.GetEventHandler(Events);
212 >      FEventIntf.AsyncWaitForEvent(@EventHandler);
213        FRegistered := true;
214      end;
215    end;
# Line 487 | Line 237 | begin
237    Result := FBase.Database
238   end;
239  
240 < procedure TIBEvents.SetRegistered( value: Boolean);
240 > procedure TIBEvents.SetRegistered(value: boolean);
241   begin
242    FDeferredRegister := false;
243    if not assigned(FBase) or (FBase.Database = nil) then
# Line 499 | Line 249 | begin
249    if value then RegisterEvents else UnregisterEvents;
250   end;
251  
252 < procedure TIBEvents.UnregisterEvents;
252 > procedure TIBEvents.UnRegisterEvents;
253   begin
254    FDeferredRegister := false;
255    if not FRegistered then
# Line 508 | Line 258 | begin
258      FRegistered := false
259    else
260    begin
261 <    TEventHandler(FEventHandler).UnRegisterEvents;
261 >    FEventIntf := nil;
262      FRegistered := false;
263    end;
264   end;
# Line 524 | Line 274 | begin
274      Registered := true
275   end;
276  
527 function TIBEvents.GetDatabaseHandle: TISC_DB_HANDLE;
528 begin
529  ValidateDatabase(FBase.Database);
530  Result := FBase.Database.Handle;
531 end;
532
277  
278   end.

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines