ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/runtime/nongui/sv5ipc.inc
Revision: 322
Committed: Thu Feb 25 12:12:20 2021 UTC (3 years, 8 months ago) by tony
File size: 22072 byte(s)
Log Message:
sv5ipc tidy up

File Contents

# User Rev Content
1 tony 319 {Used by IBIPC and implements System V IPC}
2 tony 209
3 tony 319 uses IBMessages, ipc, Errors, baseunix;
4    
5 tony 209 const
6     IPCFileName: string = 'FB.SQL.MONITOR1_0';
7     cNumberOfSemaphores = 10;
8     cMutexSemaphore = 0;
9     cMonitorCounter = 1;
10     cReadReadyEventSemaphore = 2;
11     cReadFinishedEventSemaphore = 4;
12     cDataAvailableEventSemaphore = 6;
13     cWriterBusyEventSemaphore = 8;
14 tony 320 cDefaultTimeout = 10; {seconds }
15 tony 209
16     {$IF FPC_FULLVERSION = 30000 }
17     {Fix regression in FPC 3.0.0 ipc.pp unit. Expected to be fixed in fpc 3.0.2}
18     {$IF defined(darwin) }
19     SEM_GETNCNT = 3; { Return the value of sempid (READ) }
20     SEM_GETPID = 4; { Return the value of semval (READ) }
21     SEM_GETVAL = 5; { Return semvals into arg.array (READ) }
22     SEM_GETALL = 6; { Return the value of semzcnt (READ) }
23     SEM_GETZCNT = 7; { Set the value of semval to arg.val (ALTER) }
24     SEM_SETVAL = 8; { Set semvals from arg.array (ALTER) }
25     SEM_SETALL = 9;
26     {$ENDIF}
27     {$ENDIF}
28    
29     function GetLastErrno: cint;
30     begin
31     Result := fpgetErrno
32     end;
33    
34     type
35     {Interprocess Communication Objects. All platform dependent IPC is abstracted
36     into this set of objects }
37    
38     { TIpcCommon }
39    
40 tony 319 TIpcCommon = class(TInterfacedObject)
41 tony 209 public
42 tony 319 function GetSa: PSecurityAttributes;
43 tony 209 property Sa : PSecurityAttributes read GetSa;
44     end;
45    
46     { TSharedMemory }
47    
48     {
49     The shared memory segment is used for interprocess communication and
50     holds both a message buffer and a number of shared variables. Shared
51     memory is allocated to each shared variable using the Allocate function.
52     An underlying assumption is that each process using the shared memory
53     calls "Allocate" in the same order and for the same memory sizes.
54    
55     Linux:
56    
57     The Linux implementation uses Linux shared memory. IPC_PRIVATE is used
58     to allocate the memory and the resulting memory id is written to a
59     well known file. By default this is in the current user's home directory,
60     but this can be over-ridden to specify a globally unique filename.
61    
62     Access to the shared memory is restricted to the current user/group.
63     Note that the Linux semaphore set is also created with the shared memory.
64     }
65    
66 tony 321 ISharedMemory = interface
67     ['{db77bdd4-233a-4c9c-9212-dd7945e2e57c}']
68 tony 320 function IsInitialiser: boolean;
69 tony 321 function Allocate(Size: integer): PByte;
70     function GetLastAllocationSize: integer;
71 tony 322 function sem_op(SemNum, op: integer; flags: cshort = 0): cint;
72     function sem_timedop(SemNum, op: integer; timeout_secs: integer; flags: cshort = 0): cint;
73     function GetSemValue(SemNum: integer): cint;
74     procedure SemInit(SemNum, AValue: cint);
75 tony 321 property LastAllocationSize: integer read GetLastAllocationSize;
76 tony 320 end;
77    
78 tony 319 TSharedMemory = class(TIpcCommon,ISharedMemory)
79 tony 209 private
80 tony 320 FSharedMemoryID: cint;
81 tony 322 FSemaphoreSetID: cint;
82 tony 319 FBuffer: PByte;
83 tony 209 FLastAllocationSize: integer;
84     FUnused: integer;
85 tony 319 FBufptr: PByte;
86     FIPCFileName: AnsiString;
87 tony 320 FInitialiser: boolean;
88 tony 209 procedure DropSharedMemory;
89     procedure GetSharedMemory(MemSize: integer);
90     public
91     constructor Create(MemSize: integer);
92     destructor Destroy; override;
93 tony 319 function Allocate(Size: integer): PByte;
94     function GetLastAllocationSize: integer;
95 tony 320 function IsInitialiser: boolean;
96 tony 322 function sem_op(SemNum, op: integer; flags: cshort = 0): cint;
97     function sem_timedop(SemNum, op: integer; timeout_secs: integer; flags: cshort = 0): cint;
98     function GetSemValue(SemNum: integer): cint;
99     procedure SemInit(SemNum, AValue: cint);
100 tony 319 property LastAllocationSize: integer read GetLastAllocationSize;
101 tony 209 end;
102    
103     {TMutex}
104    
105 tony 319 TMutex = class(TIpcCommon,IMutex)
106 tony 209 private
107 tony 322 FSharedMemory: ISharedMemory;
108 tony 209 FMutexSemaphore: cint;
109     FLockCount: integer;
110     public
111 tony 322 constructor Create(SemNumber: cint; sm: ISharedMemory);
112 tony 209 procedure Lock;
113     procedure Unlock;
114     end;
115    
116     { TSingleLockGate }
117    
118     {
119     A single lock gate is either open or closed. When open, any thread can pass
120     through it while, when closed, all threads are blocked as they try to pass
121     through the gate. When the gate is opened, all blocked threads are resumed.
122    
123     There is an implementation assumption that only one writer thread at
124     a time (i.e. the thread which locks or unlocks the gate) can have access to
125     it at any one time. I.e. an external Mutex prevents race conditions.
126    
127     Linux:
128    
129     In the Linux implementation, the gate is implemented by a semaphore
130     and a share memory integer used as a bi-state variable. When the gate
131     is open, the bi-state variable is non-zero. It is set to zero when closed.
132     Another shared memory integer is used to count the number of waiting
133     threads, and a second semaphore is used to protect access to this.
134    
135     The event semaphore is initialised to zero. When a thread passes through the gate
136     it checks the state. If open, the thread continues. If closed then it
137     increments the count of waiting threads and then decrements the semaphore
138     and hence enters an indefinite wait state.
139    
140     When the gate is locked, the state is set to zero. When unlocked, the state
141     is set to one and the semaphore incremented by the number of waiting threads,
142     which itself is then zeroed.
143    
144     Always initialised to the Unlocked state
145     }
146    
147 tony 319 TSingleLockGate = class(TIpcCommon,ISingleLockGate)
148 tony 209 private
149 tony 321 FSharedMemory: ISharedMemory;
150 tony 209 FSemaphore: cint;
151     FMutex: cint;
152     FSignalledState: PInteger;
153     FWaitingThreads: PInteger;
154     function GetWaitingThreads: integer;
155     public
156 tony 321 constructor Create(SemNum: cint; sm: ISharedMemory);
157 tony 209 property WaitingThreads: integer read GetWaitingThreads;
158     public
159     procedure PassthroughGate;
160     procedure Unlock;
161     procedure Lock;
162     end;
163    
164     { TMultilockGate }
165    
166     { This type of Gate is used where several reader threads must pass
167     through the gate before it can be opened for a writer thread.
168    
169     The reader threads register their interest by each locking the gate.
170     The writer thread then waits on the locked gate until all the reader
171     threads have separately unlocked the gate.
172    
173     There is an underlying assumption of a single writer. A Mutex must
174     be used to control access to the gate from the writer side if this
175     assumption is invalid.
176    
177     Linux:
178    
179     The Linux implementation uses a single semaphore to implement the gate,
180     which is initialised to 1 (unlocked), and a count of the number of
181     threads that have locked the gate (LockCount). A mutex semaphore
182     protects access to the LockCount. When the gate is locked, the lockcount
183     is incremented and, if the LockCount was originally zero, the semaphore is
184     set to zero (Gate Closed).
185    
186     Unlocking the gate, is the reverse. The LockCount is decremented and, if it
187     reaches zero, the semaphore is set to one (Gate Opened).
188    
189     When a writer passes through the gate, it checks the LockCount, if zero it
190     proceeds to pass through the gate. Otherwise it decrements and waits on the
191     semaphore. When the writer resumes, it increments the semaphore in order
192     to return it to its unlocked state. The wait is a timed wait, as there is
193     a risk that a reader thread may terminate while the gate is locked. If the
194     LockCount is non-zero, it is decremented and the writer returns to wait on
195     the gate.
196    
197     Always initialised to the Unlocked state
198     }
199    
200 tony 319 TMultilockGate = class(TIpcCommon,IMultiLockGate)
201 tony 209 private
202 tony 321 FSharedMemory: ISharedMemory;
203 tony 209 FOnGateTimeout: TNotifyEvent;
204     FSemaphore: cint;
205     FMutex: cint;
206     FLockCount: PInteger;
207     function GetLockCount: integer;
208     public
209 tony 321 constructor Create(SemNum: cint; sm: ISharedMemory);
210 tony 209 procedure Lock;
211     procedure Unlock;
212     procedure PassthroughGate;
213 tony 319 function GetOnGateTimeout: TNotifyEvent;
214     procedure SetOnGateTimeout(AValue: TNotifyEvent);
215 tony 209 property LockCount: integer read GetLockCount;
216 tony 319 property OnGateTimeout: TNotifyEvent read GetOnGateTimeout write SetOnGateTimeout;
217 tony 209 end;
218    
219 tony 319 { TIPCInterface }
220 tony 209
221 tony 319 TIPCInterface = class(TIpcCommon,IIPCInterface)
222 tony 209 private
223     FMaxBufferSize: integer;
224 tony 321 FSharedMemory: ISharedMemory;
225 tony 319 FWriteLock: IMutex;
226     FBuffer: PByte;
227 tony 209 FTraceDataType,
228     FBufferSize: PInteger;
229     FTimeStamp: PDateTime;
230 tony 319 FMsgNumber: PInteger;
231     FReadReadyEvent: IMultiLockGate;
232     FReadFinishedEvent: IMultiLockGate;
233     FDataAvailableEvent: ISingleLockGate;
234     FWriterBusyEvent: ISingleLockGate;
235 tony 209 public
236     constructor Create;
237     procedure IncMonitorCount;
238     procedure DecMonitorCount;
239     procedure SendTrace(TraceObject: TTraceObject);
240     procedure ReceiveTrace(TraceObject: TTraceObject);
241 tony 319 function GetDataAvailableEvent: ISingleLockGate;
242     function GetWriterBusyEvent: ISingleLockGate;
243     function GetReadReadyEvent: IMultiLockGate;
244     function GetReadFinishedEvent: IMultiLockGate;
245     function GetWriteLock: IMutex;
246     function GetMonitorCount: integer;
247     function GetMaxBufferSize: integer;
248     property DataAvailableEvent: ISingleLockGate read GetDataAvailableEvent;
249     property WriterBusyEvent: ISingleLockGate read GetWriterBusyEvent;
250     property ReadReadyEvent: IMultiLockGate read GetReadReadyEvent;
251     property ReadFinishedEvent: IMultiLockGate read GetReadFinishedEvent;
252     property WriteLock: IMutex read GetWriteLock;
253 tony 209 property MonitorCount: integer read GetMonitorCount;
254 tony 319 property MaxBufferSize: integer read GetMaxBufferSize;
255 tony 209 end;
256    
257     { TSharedMemory }
258    
259     procedure TSharedMemory.GetSharedMemory(MemSize: integer);
260     var F: cint;
261     begin
262 tony 319 if GetEnvironmentVariable('FBSQL_IPCFILENAME') <> '' then
263     FIPCFileName := GetEnvironmentVariable('FBSQL_IPCFILENAME')
264     else
265     FIPCFileName := GetTempDir(true) + IPCFileName + '.' + GetEnvironmentVariable('USER');
266    
267 tony 209 {Get the Shared Memory and Semaphore IDs from the Global File if it exists
268     or create them and the file otherwise }
269    
270     repeat
271 tony 319 F := fpOpen(FIPCFileName, O_WrOnly or O_Creat or O_Excl);
272 tony 209 if F < 0 then
273     begin
274     if fpgetErrno = ESysEEXIST {EEXIST} then
275     begin
276     { looks like it already exists}
277     Sleep(100);
278 tony 319 F := fpOpen(FIPCFileName,O_RdOnly);
279 tony 209 if (F < 0) and (fpgetErrno = ESysENOENT {ENOENT}) then
280     {probably just got deleted }
281     else
282     if F < 0 then
283     IBError(ibxeCannotCreateSharedResource,['Error accessing IPC File - ' +
284     StrError(fpgetErrno)]);
285     end
286     else
287     IBError(ibxeCannotCreateSharedResource,['Error creating IPC File - ' +
288     StrError(fpgetErrno)]);
289     end
290     else
291     FInitialiser := true
292     until F >= 0;
293    
294     if FInitialiser then
295     begin
296     FSharedMemoryID := shmget(IPC_PRIVATE,MemSize, IPC_CREAT or
297     S_IRUSR or S_IWUSR or S_IRGRP or S_IWGRP);
298     if FSharedMemoryID < 0 then
299     IBError(ibxeCannotCreateSharedResource,['Cannot create shared memory segment - ' +
300     StrError(fpgetErrno)]);
301    
302     FSemaphoreSetID := semget(IPC_PRIVATE, cNumberOfSemaphores,IPC_CREAT or
303     S_IRUSR or S_IWUSR or S_IRGRP or S_IWGRP);
304     if FSemaphoreSetID < 0 then
305     IBError(ibxeCannotCreateSharedResource,['Cannot create shared semaphore set - ' +
306     StrError(fpgetErrno)]);
307    
308     fpWrite(F,FSharedMemoryID,sizeof(FSharedMemoryID));
309     fpWrite(F,FSemaphoreSetID,sizeof(FSemaphoreSetID));
310     end
311     else
312     begin
313     fpRead(F,FSharedMemoryID,sizeof(FSharedMemoryID));
314     fpRead(F,FSemaphoreSetID,sizeof(FSemaphoreSetID));
315     if GetSemValue(cMonitorCounter) = 0 then
316     begin
317     FInitialiser := true;
318     //writeln('Opened file and is initialiser');
319     end
320     end;
321     fpClose(F);
322     end;
323    
324     procedure TSharedMemory.DropSharedMemory;
325     var ds: TShmid_ds;
326     arg: tsemun;
327     begin
328     if shmctl(FSharedMemoryID,IPC_STAT,@ds) < 0 then
329     IBError(ibxeSV5APIError,['Error getting shared memory info' + strError(fpgetErrno)]);
330     if ds.shm_nattch = 0 then { we are the last one out - so, turn off the lights }
331     begin
332     shmctl(FSharedMemoryID,IPC_RMID,nil);
333     semctl(FSemaphoreSetID,0,IPC_RMID,arg);
334 tony 319 DeleteFile(FIPCFileName);
335 tony 209 end;
336     end;
337    
338     constructor TSharedMemory.Create(MemSize: integer);
339     begin
340     inherited Create;
341     FInitialiser := false;
342     GetSharedMemory(MemSize);
343     FBuffer := shmat(FSharedMemoryID,nil,0);
344     if PtrInt(FBuffer) = -1 then
345     IBError(ibxeCannotCreateSharedResource,[StrError(Errno)]);
346     FBufPtr := FBuffer;
347     FUnused := MemSize
348     end;
349    
350     destructor TSharedMemory.Destroy;
351     begin
352     shmdt(FBuffer);
353     DropSharedMemory;
354     inherited Destroy;
355     end;
356    
357 tony 319 function TSharedMemory.Allocate(Size: integer): PByte;
358 tony 209 begin
359     if Size > FUnused then
360     IBError(ibxeCannotCreateSharedResource, ['Not enough shared memory']);
361     Result := FBufPtr;
362    
363     if Size = 0 then
364     begin
365     FLastAllocationSize := FUnused;
366     FUnused := 0
367     end
368     else
369     begin
370     FLastAllocationSize := Size;
371     Dec(FUnused,Size);
372     end;
373     Inc(FBufPtr,Size)
374     end;
375    
376 tony 319 function TSharedMemory.GetLastAllocationSize: integer;
377     begin
378     Result := FLastAllocationSize;
379     end;
380    
381 tony 320 function TSharedMemory.IsInitialiser: boolean;
382     begin
383     Result := FInitialiser;
384     end;
385    
386 tony 322 function TSharedMemory.sem_op(SemNum, op: integer; flags: cshort): cint;
387 tony 209 var sembuf: TSEMbuf;
388     begin
389     sembuf.sem_num := SemNum;
390     sembuf.sem_op:= op;
391     sembuf.sem_flg := flags or SEM_UNDO;
392     Result := semop(FSemaphoreSetID,@sembuf,1);
393     end;
394    
395 tony 322 function TSharedMemory.sem_timedop(SemNum, op: integer; timeout_secs: integer;
396 tony 209 flags: cshort): cint;
397     var sembuf: TSEMbuf;
398     timeout: TimeSpec;
399     begin
400     sembuf.sem_num := SemNum;
401     sembuf.sem_op:= op;
402     sembuf.sem_flg := flags or SEM_UNDO;
403     timeout.tv_sec := timeout_secs;
404     timeout.tv_nsec := 0;
405 tony 319 {$IF declared(semtimedop)}
406 tony 209 Result := semtimedop(FSemaphoreSetID,@sembuf,1,@timeout);
407     {$ELSE}
408     Result := semop(FSemaphoreSetID,@sembuf,1); {May hang on race condition}
409 tony 319 {$IFEND}
410 tony 209 end;
411    
412 tony 322 function TSharedMemory.GetSemValue(SemNum: integer): cint;
413 tony 209 var args :TSEMun;
414     begin
415     Result := semctl(FSemaphoreSetID,SemNum,SEM_GETVAL,args);
416     if Result < 0 then
417     IBError(ibxeSV5APIError,['GetSemValue: '+strError(GetLastErrno)]);
418     end;
419    
420 tony 322 procedure TSharedMemory.SemInit(SemNum, AValue: cint);
421 tony 209 var args :TSEMun;
422     begin
423     //writeln('Initialising ',SemNum,' to ',AValue);
424     args.val := AValue;
425     if semctl(FSemaphoreSetID,SemNum,SEM_SETVAL,args) < 0 then
426     IBError(ibxeCannotCreateSharedResource,['Unable to initialise Semaphone ' +
427     IntToStr(SemNum) + '- ' + StrError(GetLastErrno)]);
428    
429     end;
430    
431 tony 322 { TIpcCommon }
432    
433     function TIpcCommon.GetSa: PSecurityAttributes;
434     begin
435     Result := nil
436     end;
437    
438 tony 209 { TMutex }
439    
440 tony 322 constructor TMutex.Create(SemNumber: cint; sm: ISharedMemory);
441 tony 209 begin
442     inherited Create;
443 tony 322 FSharedMemory := sm;
444 tony 209 FMutexSemaphore := SemNumber;
445 tony 322 if FSharedMemory.IsInitialiser then
446     FSharedMemory.SemInit(FMutexSemaphore,1)
447 tony 209 end;
448    
449     { Obtain ownership of the Mutex and prevent other threads from accessing protected resource }
450    
451     procedure TMutex.Lock;
452     begin
453     //writeln('Lock: Entering Mutex ',FMutexSemaphore,' LockCount=',FLockCount,' State = ',GetSemValue(FMutexSemaphore));
454     if FLockCount = 0 then
455 tony 322 FSharedMemory.sem_op(FMutexSemaphore,-1);
456 tony 209 Inc(FLockCount);
457     //writeln('Lock: Mutex Exit');
458     end;
459    
460     {Give up ownership of the Mutex and allow other threads access }
461    
462     procedure TMutex.Unlock;
463     begin
464     //writeln('UnLock: Entering Mutex, LockCount=',FLockCount);
465     if FLockCount = 0 then Exit;
466     Dec(FLockCount);
467     if FLockCount = 0 then
468 tony 322 FSharedMemory.sem_op(FMutexSemaphore,1);
469 tony 209 //writeln('UnLock: Mutex Exit',' State = ',GetSemValue(FMutexSemaphore));
470     end;
471    
472     { TSingleLockGate }
473    
474     function TSingleLockGate.GetWaitingThreads: integer;
475     begin
476     Result := FWaitingThreads^
477     end;
478    
479 tony 321 constructor TSingleLockGate.Create(SemNum: cint; sm: ISharedMemory);
480 tony 209 begin
481     inherited Create;
482 tony 319 FSharedMemory := sm;
483     FSignalledState := PInteger(FSharedMemory.Allocate(sizeof(FSignalledState)));
484     FWaitingThreads := PInteger(FSharedMemory.Allocate(sizeof(FWaitingThreads)));
485 tony 209 FSemaphore := SemNum;
486     FMutex := SemNum + 1;
487 tony 320 if FSharedMemory.IsInitialiser then
488 tony 209 begin
489     FSignalledState^ := 1;
490     FWaitingThreads^ := 0;
491 tony 322 FSharedMemory.SemInit(FSemaphore,0);
492     FSharedMemory.SemInit(FMutex,1);
493 tony 209 end;
494     end;
495    
496     procedure TSingleLockGate.PassthroughGate;
497     begin
498     if FSignalledState^ = 0 then
499     begin
500 tony 322 FSharedMemory.sem_op(FMutex,-1,0); //Acquire Mutex
501 tony 209 Inc(FWaitingThreads^);
502 tony 322 FSharedMemory.sem_op(FMutex,1,0); //Release Mutex
503 tony 209 //writeln(ClassName + ': Wait State Entered ',FSemaphore,' = ',GetSemValue(FSemaphore));
504 tony 322 FSharedMemory.sem_op(FSemaphore,-1,0); //Enter Wait
505 tony 209 //writeln(ClassName + ': Wait State Ends ',FSemaphore);
506     end;
507     end;
508    
509     procedure TSingleLockGate.Unlock;
510     begin
511     if FSignalledState^ = 0 then
512     begin
513     FSignalledState^ := 1;
514 tony 322 FSharedMemory.sem_op(FMutex,-1,0); //Acquire Mutex
515 tony 319 {$IFDEF DEBUG}writeln(ClassName + ': Unlocking' ,FSemaphore);{$ENDIF}
516 tony 322 FSharedMemory.sem_op(FSemaphore,FWaitingThreads^,0);
517 tony 209 FWaitingThreads^ := 0;
518 tony 322 FSharedMemory.sem_op(FMutex,1,0); //Release Mutex
519 tony 209 end;
520     end;
521    
522     procedure TSingleLockGate.Lock;
523     begin
524     if FSignalledState^ = 1 then
525     begin
526 tony 319 {$IFDEF DEBUG}writeln(ClassName + ': Locking Gate ',FSemaphore);{$ENDIF}
527 tony 322 FSharedMemory.SemInit(FSemaphore,0);
528 tony 209 FSignalledState^ := 0;
529     end;
530     end;
531    
532     { TMultilockGate }
533    
534 tony 321 constructor TMultilockGate.Create(SemNum: cint; sm: ISharedMemory);
535 tony 209 begin
536     inherited Create;
537     FSemaphore := SemNum;
538     FMutex := SemNum + 1;
539 tony 319 FSharedMemory := sm;
540     FLockCount := PInteger(FSharedMemory.Allocate(sizeof(FLockCount)));
541 tony 320 if FSharedMemory.IsInitialiser then
542 tony 209 begin
543     FLockCount^ := 0;
544 tony 322 FSharedMemory.SemInit(FSemaphore,1);
545     FSharedMemory.SemInit(FMutex,1);
546 tony 209 end;
547     end;
548    
549     function TMultilockGate.GetLockCount: integer;
550     begin
551     Result := FLockCount^
552     end;
553    
554 tony 319 function TMultilockGate.GetOnGateTimeout: TNotifyEvent;
555     begin
556     Result := FOnGateTimeout;
557     end;
558    
559     procedure TMultilockGate.SetOnGateTimeout(AValue: TNotifyEvent);
560     begin
561     FOnGateTimeout := AValue;
562     end;
563    
564 tony 209 procedure TMultilockGate.Lock;
565     begin
566 tony 322 FSharedMemory.sem_op(FMutex,-1,0); //Acquire Mutex
567 tony 209 if FLockCount^ = 0 then
568     begin
569 tony 319 {$IFDEF DEBUG}writeln(ClassName,': Locking ',FSemaphore);{$ENDIF}
570 tony 322 FSharedMemory.SemInit(FSemaphore,0);
571 tony 209 end;
572     Inc(FLockCount^);
573 tony 322 FSharedMemory.sem_op(FMutex,1,0); //Release Mutex
574 tony 209 end;
575    
576     procedure TMultilockGate.Unlock;
577     begin
578 tony 322 FSharedMemory.sem_op(FMutex,-1,0); //Acquire Mutex
579 tony 209 Dec(FLockCount^);
580     if FLockCount^ <= 0 then
581     begin
582 tony 319 {$IFDEF DEBUG}writeln(ClassName,': UnLocking ',FSemaphore);{$ENDIF}
583 tony 322 FSharedMemory.SemInit(FSemaphore,1);
584 tony 209 FLockCount^ := 0
585     end;
586 tony 322 FSharedMemory.sem_op(FMutex,1,0); //Release Mutex
587 tony 209 end;
588    
589     procedure TMultilockGate.PassthroughGate;
590     begin
591     if FLockCount^ = 0 then
592     Exit;
593 tony 319 {$IFDEF DEBUG}writeln(ClassName,': Waiting on ',FSemaphore);{$ENDIF}
594 tony 322 while FSharedMemory.sem_timedop(FSemaphore,-1,cDefaultTimeout) < 0 do
595 tony 209 {looks like we lost a reader}
596     begin
597 tony 319 {$IFDEF DEBUG}writeln(ClassName,': reader lost timeout');{$ENDIF}
598 tony 209 if FLockCount^ > 0 then
599     begin
600     UnLock;
601     if assigned(FOnGateTimeout) then
602     OnGateTimeout(self)
603     end
604     end;
605 tony 322 FSharedMemory.sem_op(FSemaphore,1);
606 tony 319 {$IFDEF DEBUG}writeln(ClassName,': Wait done on ',FSemaphore);{$ENDIF}
607 tony 209 end;
608    
609    
610 tony 319 { TIPCInterface }
611 tony 209
612 tony 319 function TIPCInterface.GetMonitorCount: integer;
613 tony 209 begin
614 tony 322 Result := FSharedMemory.GetSemValue(cMonitorCounter)
615 tony 209 end;
616    
617 tony 319 function TIPCInterface.GetMaxBufferSize: integer;
618     begin
619     Result := FMaxBufferSize;
620     end;
621    
622     constructor TIPCInterface.Create;
623     begin
624 tony 209 inherited Create;
625     FSharedMemory := TSharedMemory.Create(cMonitorHookSize);
626    
627 tony 322 FWriteLock := TMutex.Create(cMutexSemaphore,FSharedMemory);
628 tony 209
629 tony 319 FDataAvailableEvent := TSingleLockGate.Create(cDataAvailableEventSemaphore,FSharedMemory);
630     FWriterBusyEvent := TSingleLockGate.Create(cWriterBusyEventSemaphore,FSharedMemory);
631     FReadReadyEvent := TMultiLockGate.Create(cReadReadyEventSemaphore,FSharedMemory);
632     FReadFinishedEvent := TMultiLockGate.Create(cReadFinishedEventSemaphore,FSharedMemory);
633 tony 209
634 tony 320 if FSharedMemory.IsInitialiser then
635 tony 322 FSharedMemory.SemInit(cMonitorCounter,0);
636 tony 209 FTraceDataType := PInteger(FSharedMemory.Allocate(sizeof(Integer)));
637     FTimeStamp := PDateTime(FSharedMemory.Allocate(sizeof(TDateTime)));
638     FBufferSize := PInteger(FSharedMemory.Allocate(sizeof(Integer)));
639 tony 319 FMsgNumber := PInteger(FSharedMemory.Allocate(sizeof(Integer)));
640 tony 209 FBuffer := FSharedMemory.Allocate(0); //All remaining
641     FMaxBufferSize := FSharedMemory.LastAllocationSize;
642    
643 tony 320 if FSharedMemory.IsInitialiser then
644 tony 209 begin
645     FBufferSize^ := 0;
646 tony 319 FDataAvailableEvent.Lock;
647     FMsgNumber^ := 0;
648 tony 209 end;
649     end;
650    
651 tony 319 procedure TIPCInterface.IncMonitorCount;
652 tony 209 begin
653 tony 322 FSharedMemory.sem_op(cMonitorCounter,1);
654 tony 209 end;
655    
656 tony 319 procedure TIPCInterface.DecMonitorCount;
657 tony 209 begin
658 tony 322 FSharedMemory.sem_op(cMonitorCounter,-1,IPC_NOWAIT);
659 tony 209 end;
660    
661 tony 319 procedure TIPCInterface.SendTrace(TraceObject: TTraceObject);
662 tony 209 begin
663     FTraceDataType^ := Integer(TraceObject.FDataType);
664     FTimeStamp^ := TraceObject.FTimeStamp;
665 tony 319 if Length(TraceObject.FMsg) > MaxBufferSize then
666     FBufferSize^ := MaxBufferSize
667     else
668     FBufferSize^ := Length(TraceObject.FMsg);
669     FMsgNumber^ := TraceObject.FMsgNumber;
670 tony 209 Move(TraceObject.FMsg[1], FBuffer^, FBufferSize^);
671     end;
672    
673 tony 319 procedure TIPCInterface.ReceiveTrace(TraceObject: TTraceObject);
674 tony 209 begin
675 tony 319 SetString(TraceObject.FMsg, PAnsiChar(FBuffer), FBufferSize^);
676 tony 209 TraceObject.FDataType := TTraceFlag(FTraceDataType^);
677     TraceObject.FTimeStamp := TDateTime(FTimeStamp^);
678 tony 319 TraceObject.FMsgNumber := FMsgNumber^;
679 tony 209 end;
680    
681 tony 319 function TIPCInterface.GetDataAvailableEvent: ISingleLockGate;
682     begin
683     Result := FDataAvailableEvent;
684     end;
685 tony 209
686 tony 319 function TIPCInterface.GetWriterBusyEvent: ISingleLockGate;
687     begin
688     Result := FWriterBusyEvent;
689     end;
690 tony 209
691 tony 319 function TIPCInterface.GetReadReadyEvent: IMultiLockGate;
692     begin
693     Result := FReadReadyEvent;
694     end;
695    
696     function TIPCInterface.GetReadFinishedEvent: IMultiLockGate;
697     begin
698     Result := FReadFinishedEvent;
699     end;
700    
701     function TIPCInterface.GetWriteLock: IMutex;
702     begin
703     Result := FWriteLock;
704     end;
705    
706    
707