-
-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathUtilitySignal.pas
1244 lines (1030 loc) · 41.1 KB
/
UtilitySignal.pas
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
{-------------------------------------------------------------------------------
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
-------------------------------------------------------------------------------}
{===============================================================================
UtilitySignal
Small library designed to ease setup of real-time signal handler in Linux.
It was designed primarily for use with posix timers (and possibly message
queues), but can be, of course, used for other purposes too.
I have decided to write it for two main reasons - one is to provide some
siplified interface allowing for multiple handlers of single signal,
the second is to limit number of used signals, of which count is very
limited (30 or 32 per process in Linux), by allowing multiple users
to use one signal allocated here.
At unit initialization, this library selects and allocates one unused
real-time signal and then installs an action routine that receives all
incoming invocations of that signal.
Note that this signal can be different every time the process is run. It
can also differ between processes even if they are started from the same
executable. Which means, among others, that this library cannot be used
for interprocess communication, be aware of that!
If this library is used multiple times within the same process (eg. when
loaded with a dynamic library), this signal will be different for each
instance. Because the number of available signals is limited, you should
refrain from using this unit in a library or make sure one instance is
shared across the entire process.
The installed action routine processes the incoming signal and, using
internal dispatcher object, passes processing to registered handlers. Which
handlers will be called is selected according to a code received with the
signal - only handlers (all of them) registered for the particular code
will be called.
Every handler has parameter BreakProcessing that is set to false upon
entry. If handler sets this parameter to true before exitting, then no
other handler registered for the code will be called for currently
processed signal.
WARNING - which thread will be processing any received signal is complety
undefined and is selected arbitrarily (usually, it will the
main thread, but do not count on that). You have to account for
this fact when writing the handlers!
Make sure you understand how signals work before using this library, so
reading the linux manual (signal(7)) is strongly recommended.
Version 1.0.1 (2024-08-19)
Last change 2024-08-19
©2024 František Milt
Contacts:
František Milt: [email protected]
Support:
If you find this code useful, please consider supporting its author(s) by
making a small donation using the following link(s):
https://www.paypal.me/FMilt
Changelog:
For detailed changelog and history please refer to this git repository:
github.com/TheLazyTomcat/Lib.UtilitySignal
Dependencies:
AuxClasses - github.com/TheLazyTomcat/Lib.AuxClasses
* AuxExceptions - github.com/TheLazyTomcat/Lib.AuxExceptions
InterlockedOps - github.com/TheLazyTomcat/Lib.InterlockedOps
MulticastEvent - github.com/TheLazyTomcat/Lib.MulticastEvent
Library AuxExceptions is required only when rebasing local exception classes
(see symbol UtilitySignal_UseAuxExceptions for details).
Library AuxExceptions might also be required as an indirect dependency.
Indirect dependencies:
AuxTypes - github.com/TheLazyTomcat/Lib.AuxTypes
SimpleCPUID - github.com/TheLazyTomcat/Lib.SimpleCPUID
StrRect - github.com/TheLazyTomcat/Lib.StrRect
UInt64Utils - github.com/TheLazyTomcat/Lib.UInt64Utils
WinFileInfo - github.com/TheLazyTomcat/Lib.WinFileInfo
===============================================================================}
unit UtilitySignal;
{
UtilitySignal_UseAuxExceptions
If you want library-specific exceptions to be based on more advanced classes
provided by AuxExceptions library instead of basic Exception class, and don't
want to or cannot change code in this unit, you can define global symbol
UtilitySignal_UseAuxExceptions to achieve this.
}
{$IF Defined(UtilitySignal_UseAuxExceptions)}
{$DEFINE UseAuxExceptions}
{$IFEND}
//------------------------------------------------------------------------------
{$IF Defined(LINUX) and Defined(FPC)}
{$DEFINE Linux}
{$ELSE}
{$MESSAGE FATAL 'Unsupported operating system.'}
{$IFEND}
{$IFDEF FPC}
{$MODE ObjFPC}
{$MODESWITCH ClassicProcVars+}
{$DEFINE FPC_DisableWarns}
{$MACRO ON}
{$ENDIF}
{$H+}
//------------------------------------------------------------------------------
{
SilentDispatcherFailure
When defined, any failure of acquiring internal signal dispatcher is silently
ignored (the call will exit without performing its intended function). When
not defined, then an exception of class EUSDispacherNotReeady is raised in
such situation.
Defined by default.
To disable/undefine this symbol in a project without changing this library,
define project-wide symbol UtilitySignal_SilentDispatcherFailure_OFF.
}
{$DEFINE SilentDispatcherFailure}
{$IFDEF UtilitySignal_SilentDispatcherFailure_OFF}
{$UNDEF SilentDispatcherFailure}
{$ENDIF}
interface
uses
SysUtils, BaseUnix
{$IFDEF UseAuxExceptions}, AuxExceptions{$ENDIF};
{===============================================================================
Library-specific exceptions
===============================================================================}
type
EUSException = class({$IFDEF UseAuxExceptions}EAEGeneralException{$ELSE}Exception{$ENDIF});
EUSIndexOutOfBounds = class(EUSException);
EUSInvalidValue = class(EUSException);
EUSSetupError = class(EUSException);
EUSDispacherNotReady = class(EUSException);
{===============================================================================
Public types
===============================================================================}
type
TUSSignalValue = record
case Integer of
0: (IntValue: Integer);
1: (PtrValue: Pointer);
end;
TUSSignalInfo = record
Signal: Integer; // this will always be the same (SignalNumber)
Code: Integer;
Value: TUSSignalValue;
end;
TUSHandlerCallback = procedure(const Info: TUSSignalInfo; var BreakProcessing: Boolean);
TUSHandlerEvent = procedure(const Info: TUSSignalInfo; var BreakProcessing: Boolean) of object;
{===============================================================================
--------------------------------------------------------------------------------
Procedural interface
--------------------------------------------------------------------------------
===============================================================================}
{===============================================================================
Procedural interface - declaration
===============================================================================}
{
SignalNumber
Returns number of signal that was allocated for use by this library.
}
Function SignalNumber: Integer;
{
CurrentProcessID
Returns ID of the calling process. This can be used when sending a signal
(see functions SendSignal further down).
}
Function CurrentProcessID: pid_t;
//------------------------------------------------------------------------------
{
RegisterHandler
Registers callback or event that will be called when allocated signal with
given code is received. Overloads without Code argument are registering the
handler for code SI_QUEUE (-1).
A handler can be registered only once for each code, an attempt to register
it again will silently fail (no exception will be raised).
}
procedure RegisterHandler(Code: Integer; Handler: TUSHandlerCallback);
procedure RegisterHandler(Code: Integer; Handler: TUSHandlerEvent);
procedure RegisterHandler(Handler: TUSHandlerCallback);
procedure RegisterHandler(Handler: TUSHandlerEvent);
{
UnregisterHandler
Unregisters callback or event from the given code, so it no longer is called
when allocated signal with that code arrives. Overloads without Code argument
are unregistering the handler from code SI_QUEUE (-1).
Since handler can be registered only once for each code, unregistering it
will remove it completely from processing of that code. Unregistering a
handler that is not registered will silently fail (no exception is raised).
}
procedure UnregisterHandler(Code: Integer; Handler: TUSHandlerCallback);
procedure UnregisterHandler(Code: Integer; Handler: TUSHandlerEvent);
procedure UnregisterHandler(Handler: TUSHandlerCallback);
procedure UnregisterHandler(Handler: TUSHandlerEvent);
//------------------------------------------------------------------------------
{
SendSignal
Sends selected signal to a given process with given value.
When the sending succeeds, true is returned and output parameter Error is set
to 0. When it fails, false is returned and Error contains Linux error code
that describes reason of failure.
Note that sending signals is subject to privilege checks, so it might not be
possible, depending on whan privileges the sending process have.
The signal will arrive with code set to SI_QUEUE.
WARNING - signals are quite deep subject, so do not use provided functions
without considering what are you about to do. Always read the
manual.
}
Function SendSignal(ProcessID: pid_t; Signal: Integer; Value: TUSSignalValue; out Error: Integer): Boolean; overload;
Function SendSignal(ProcessID: pid_t; Signal: Integer; Value: Integer; out Error: Integer): Boolean; overload;
Function SendSignal(ProcessID: pid_t; Signal: Integer; Value: Pointer; out Error: Integer): Boolean; overload;
Function SendSignal(ProcessID: pid_t; Signal: Integer; Value: TUSSignalValue): Boolean; overload;
Function SendSignal(ProcessID: pid_t; Signal: Integer; Value: Integer): Boolean; overload;
Function SendSignal(ProcessID: pid_t; Signal: Integer; Value: Pointer): Boolean; overload;
{
Followng overloads are sending signal back to the calling process (but not
necessarily the calling thread!) using the signal allocated for this library.
}
Function SendSignal(Value: TUSSignalValue; out Error: Integer): Boolean; overload;
Function SendSignal(Value: Integer; out Error: Integer): Boolean; overload;
Function SendSignal(Value: Pointer; out Error: Integer): Boolean; overload;
Function SendSignal(Value: TUSSignalValue): Boolean; overload;
Function SendSignal(Value: Integer): Boolean; overload;
Function SendSignal(Value: Pointer): Boolean; overload;
implementation
uses
AuxClasses, MulticastEvent, InterlockedOps;
{$IFDEF FPC_DisableWarns}
{$DEFINE FPCDWM}
{$DEFINE W5024:={$WARN 5024 OFF}} // Parameter "$1" not used
{$ENDIF}
{===============================================================================
System types, constants and externals
===============================================================================}
const
SI_QUEUE = -1;
type
sighandlerfce_t = procedure(signo: cint); cdecl;
sigactionfce_t = procedure(signo: cint; siginfo: psiginfo; context: Pointer); cdecl;
sigset_t = array[0..Pred(1024 div (8 * SizeOf(culong)))] of culong;
psigset_t = ^sigset_t;
sigaction_t = record
handler: record
case Integer of
0: (sa_handler: sighandlerfce_t);
1: (sa_sigaction: sigactionfce_t);
end;
sa_mask: sigset_t;
sa_flags: cint;
sa_restorer: Pointer;
end;
psigaction_t = ^sigaction_t;
sigval_t = record
case Integer of
0: (sigval_int: cint); // Integer value
1: (sigval_ptr: Pointer) // Pointer value
end;
//------------------------------------------------------------------------------
Function getpid: pid_t; cdecl; external;
Function errno_ptr: pcint; cdecl; external name '__errno_location';
Function sigaction(signum: cint; act: psigaction_t; oact: psigaction_t): cint; cdecl; external;
Function sigemptyset(_set: psigset_t): cint; cdecl; external;
Function allocate_rtsig(high: cint): cint; cdecl; external name '__libc_allocate_rtsig';
Function sigqueue(pid: pid_t; sig: cint; value: sigval_t): cint; cdecl; external;
{===============================================================================
--------------------------------------------------------------------------------
TUSCodeHandlers
--------------------------------------------------------------------------------
===============================================================================}
{===============================================================================
TUSCodeHandlers - class declaration
===============================================================================}
type
TUSCodeHandlers = class(TMulticastEvent)
public
Function IndexOf(const Handler: TUSHandlerCallback): Integer; reintroduce; overload;
Function IndexOf(const Handler: TUSHandlerEvent): Integer; reintroduce; overload;
Function Add(const Handler: TUSHandlerCallback): Integer; reintroduce; overload;
Function Add(const Handler: TUSHandlerEvent): Integer; reintroduce; overload;
Function Remove(const Handler: TUSHandlerCallback): Integer; reintroduce; overload;
Function Remove(const Handler: TUSHandlerEvent): Integer; reintroduce; overload;
procedure Call(const Info: TUSSignalInfo); reintroduce;
end;
{===============================================================================
TUSCodeHandlers - class implementation
===============================================================================}
{-------------------------------------------------------------------------------
TUSCodeHandlers - public methods
-------------------------------------------------------------------------------}
Function TUSCodeHandlers.IndexOf(const Handler: TUSHandlerCallback): Integer;
begin
Result := inherited IndexOf(TCallback(Handler));
end;
//------------------------------------------------------------------------------
Function TUSCodeHandlers.IndexOf(const Handler: TUSHandlerEvent): Integer;
begin
Result := inherited IndexOf(TEvent(Handler));
end;
//------------------------------------------------------------------------------
Function TUSCodeHandlers.Add(const Handler: TUSHandlerCallback): Integer;
begin
Result := inherited Add(TCallback(Handler),False);
end;
//------------------------------------------------------------------------------
Function TUSCodeHandlers.Add(const Handler: TUSHandlerEvent): Integer;
begin
Result := inherited Add(TEvent(Handler),False);
end;
//------------------------------------------------------------------------------
Function TUSCodeHandlers.Remove(const Handler: TUSHandlerCallback): Integer;
begin
Result := inherited Remove(TCallback(Handler),True);
end;
//------------------------------------------------------------------------------
Function TUSCodeHandlers.Remove(const Handler: TUSHandlerEvent): Integer;
begin
Result := inherited Remove(TEvent(Handler),True);
end;
//------------------------------------------------------------------------------
procedure TUSCodeHandlers.Call(const Info: TUSSignalInfo);
var
i: Integer;
BreakProc: Boolean;
begin
BreakProc := False;
For i := LowIndex to HighIndex do
begin
If fEntries[i].IsMethod then
TUSHandlerEvent(fEntries[i].HandlerMethod)(Info,BreakProc)
else
TUSHandlerCallback(fEntries[i].HandlerProcedure)(Info,BreakProc);
If BreakProc then
Break{for i};
end;
end;
{===============================================================================
--------------------------------------------------------------------------------
TUSDispatcher
--------------------------------------------------------------------------------
===============================================================================}
type
TUSDispatcherItem = record
Code: Integer;
Handlers: TUSCodeHandlers;
end;
{===============================================================================
TUSDispatcher - class declaration
===============================================================================}
type
TUSDispatcher = class(TCustomListObject)
protected
fThreadLock: TMultiReadExclusiveWriteSynchronizer;
fItems: array of TUSDispatcherItem;
fItemCount: Integer;
fDirectMap: array[-8..7] of Integer;
Function GetCapacity: Integer; override;
procedure SetCapacity(Value: Integer); override;
Function GetCount: Integer; override;
procedure SetCount(Value: Integer); override;
procedure Initialize; virtual;
procedure Finalize; virtual;
// list methods
Function IndexOf(Code: Integer): Integer; virtual;
Function Find(Code: Integer; out Index: Integer): Boolean; virtual;
Function Add(Code: Integer): Integer; virtual;
procedure Delete(Index: Integer); virtual;
procedure Clear; virtual;
public
constructor Create;
destructor Destroy; override;
{
Note that methods LowIndex, HighIndex, inherited CheckIndex and getters and
setters of properties Count and Capacity are not thread protected. But this
class is used only internally and is not exposed to outer world, therefore
nobody should be able to access them.
}
Function LowIndex: Integer; override;
Function HighIndex: Integer; override;
procedure RegisterHandler(Code: Integer; Handler: TUSHandlerCallback); overload; virtual;
procedure RegisterHandler(Code: Integer; Handler: TUSHandlerEvent); overload; virtual;
procedure UnregisterHandler(Code: Integer; Handler: TUSHandlerCallback); overload; virtual;
procedure UnregisterHandler(Code: Integer; Handler: TUSHandlerEvent); overload; virtual;
procedure Dispatch(const Info: TUSSignalInfo); overload; virtual; // called by signal handler
end;
{===============================================================================
TUSDispatcher - class implementation
===============================================================================}
{-------------------------------------------------------------------------------
TUSDispatcher - protected methods
-------------------------------------------------------------------------------}
Function TUSDispatcher.GetCapacity: Integer;
begin
Result := Length(fItems);
end;
//------------------------------------------------------------------------------
procedure TUSDispatcher.SetCapacity(Value: Integer);
var
i: Integer;
begin
If Value >= 0 then
begin
If Value < fItemCount then
begin
For i := Value to HighIndex do
FreeAndNil(fItems[i].Handlers);
fItemCount := Value;
end;
SetLength(fItems,Value);
end
else raise EUSInvalidValue.CreateFmt('TUSDispatcher.SetCapacity: Invalid capacity value (%d).',[Value]);
end;
//------------------------------------------------------------------------------
Function TUSDispatcher.GetCount: Integer;
begin
Result := fItemCount;
end;
//------------------------------------------------------------------------------
{$IFDEF FPCDWM}{$PUSH}W5024{$ENDIF}
procedure TUSDispatcher.SetCount(Value: Integer);
begin
// do nothing
end;
{$IFDEF FPCDWM}{$POP}{$ENDIF}
//------------------------------------------------------------------------------
procedure TUSDispatcher.Initialize;
var
i: Integer;
begin
fThreadLock := TMultiReadExclusiveWriteSynchronizer.Create;
fItems := nil;
fItemCount := 0;
For i := Low(fDirectMap) to High(fDirectMap) do
fDirectMap[i] := -1;
end;
//------------------------------------------------------------------------------
procedure TUSDispatcher.Finalize;
begin
Clear;
FreeAndNil(fThreadLock);
end;
//------------------------------------------------------------------------------
Function TUSDispatcher.IndexOf(Code: Integer): Integer;
var
i: Integer;
L,H,C: Integer; // no, this is not Large Hadron Collider :P
begin
Result := -1;
If (Code >= Low(fDirectMap)) and (Code <= High(fDirectMap)) then
Result := fDirectMap[Code]
else If fItemCount > 8 then
begin
L := LowIndex;
H := HighIndex;
while L <= H do
begin
C := (L + H) shr 1; // div 2
If Code < fItems[C].Code then
H := Pred(C)
else If Code > fItems[C].Code then
L := Succ(C)
else
begin
Result := C;
Break{while};
end;
end;
end
else
begin
For i := LowIndex to HighIndex do
If fItems[i].Code = Code then
begin
Result := i;
Break{For i};
end;
end;
end;
//------------------------------------------------------------------------------
Function TUSDispatcher.Find(Code: Integer; out Index: Integer): Boolean;
begin
Index := IndexOf(Code);
Result := CheckIndex(Index);
end;
//------------------------------------------------------------------------------
Function TUSDispatcher.Add(Code: Integer): Integer;
var
i: Integer;
begin
If not Find(Code,Result) then
begin
// find index for sorted addition
Result := LowIndex;
For i := LowIndex to HighIndex do
If fItems[i].Code > Code then
begin
Result := i;
Break{For i}
end;
Grow;
For i := HighIndex downto Result do
// yes, i + 1 is above HighIndex, but that item must exist because of Grow
fItems[i + 1] := fItems[i];
fItems[Result].Code := Code;
fItems[Result].Handlers := TUSCodeHandlers.Create;
Inc(fItemCount);
If (Code >= Low(fDirectMap)) and (Code <= High(fDirectMap)) then
fDirectMap[Code] := Result;
end;
end;
//------------------------------------------------------------------------------
procedure TUSDispatcher.Delete(Index: Integer);
var
i: Integer;
begin
If CheckIndex(Index) then
begin
For i := Low(fDirectMap) to High(fDirectMap) do
If fDirectMap[i] = Index then
begin
fDirectMap[i] := -1;
Break{For i};
end;
FreeAndNil(fItems[Index].Handlers);
For i := Index to Pred(HighIndex) do
fItems[i] := fItems[i + 1];
Dec(fItemCount);
Shrink;
end
else raise EUSIndexOutOfBounds.CreateFmt('TUSDispatcher.Delete: Index (%d) out of bounds.',[Index]);
end;
//------------------------------------------------------------------------------
procedure TUSDispatcher.Clear;
var
i: Integer;
begin
For i := Low(fDirectMap) to High(fDirectMap) do
fDirectMap[i] := -1;
For i := LowIndex to HighIndex do
FreeAndNil(fItems[i].Handlers);
SetLength(fItems,0);
fItemCount := 0;
end;
{-------------------------------------------------------------------------------
TUSDispatcher - public methods
-------------------------------------------------------------------------------}
constructor TUSDispatcher.Create;
begin
inherited Create;
initialize;
end;
//------------------------------------------------------------------------------
destructor TUSDispatcher.Destroy;
begin
Finalize;
inherited;
end;
//------------------------------------------------------------------------------
Function TUSDispatcher.LowIndex: Integer;
begin
Result := Low(fItems);
end;
//------------------------------------------------------------------------------
Function TUSDispatcher.HighIndex: Integer;
begin
Result := Pred(fItemCount);
end;
//------------------------------------------------------------------------------
procedure TUSDispatcher.RegisterHandler(Code: Integer; Handler: TUSHandlerCallback);
var
Index: Integer;
begin
fThreadLock.BeginWrite;
try
If not Find(Code,Index) then
Index := Add(Code);
fItems[Index].Handlers.Add(Handler);
finally
fThreadLock.EndWrite;
end;
end;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure TUSDispatcher.RegisterHandler(Code: Integer; Handler: TUSHandlerEvent);
var
Index: Integer;
begin
fThreadLock.BeginWrite;
try
If not Find(Code,Index) then
Index := Add(Code);
fItems[Index].Handlers.Add(Handler);
finally
fThreadLock.EndWrite;
end;
end;
//------------------------------------------------------------------------------
procedure TUSDispatcher.UnregisterHandler(Code: Integer; Handler: TUSHandlerCallback);
var
Index: Integer;
begin
fThreadLock.BeginWrite;
try
If Find(Code,Index) then
begin
fItems[Index].Handlers.Remove(Handler);
If fItems[Index].Handlers.Count <= 0 then
Delete(Index);
end;
finally
fThreadLock.EndWrite;
end;
end;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure TUSDispatcher.UnregisterHandler(Code: Integer; Handler: TUSHandlerEvent);
var
Index: Integer;
begin
fThreadLock.BeginWrite;
try
If Find(Code,Index) then
begin
fItems[Index].Handlers.Remove(Handler);
If fItems[Index].Handlers.Count <= 0 then
Delete(Index);
end;
finally
fThreadLock.EndWrite;
end;
end;
//------------------------------------------------------------------------------
procedure TUSDispatcher.Dispatch(const Info: TUSSignalInfo);
var
Index: Integer;
begin
fThreadLock.BeginRead;
try
If Find(Info.Code,Index) then
fItems[Index].Handlers.Call(Info);
finally
fThreadLock.EndRead;
end;
end;
{===============================================================================
--------------------------------------------------------------------------------
Procedural interface
--------------------------------------------------------------------------------
===============================================================================}
{===============================================================================
Procedural interface - internal global variables
===============================================================================}
var
{
GVAR_DispThrProt counter is used for thread protection of GVAR_Dispatcher
variable, specifically to protect the object destruction (so it cannot be
destroyed while some other thread is using it).
On unit initialization, its value is set to High(Integer) - a maximum value
it can hold (about two billions).
On unit finalization, its current value, whatever it is, is negated. If,
after this negation, its value becomes -High(Integer) (negative of maximum),
then it is assumed nobody is currently using the dispatcher and it can be
destroyed.
When any thread wants to use the dispatcher, it has to first acquire it. This
operation conditionally decrements the counter when it holds value above 1.
If the condition is met and the decrement took place, then the user
thread can safely access the dispatcher. Also, the dispatcher must be
released after use (see further).
If the counter was below or equel to 1, then the thread cannot acquire the
dispatcher and must not use it. Release must not be called. Depending on
defined symbols, this can lead to an exception being raised.
When thread successfully acquired the dispatcher and now is done using it, it
must release it.
If the counter is zero, then nothing is done as this should not happen.
If the counter is above 0, then it is incremented. If it is below zero,
then it gets decremented.
In any case, if value of the counter is below or equal to -High(Integer)
after the release (meaning this unit was finalized and no other thread has
the dispatcher acquired), then the calling thread must free the dispatcher.
Note that, if the counter is negative but above -High(Integer), it means
this unit was finalized but some other thread still uses the dispatcher.
Last thread releasing it will destroy it.
NOTE - In case of problems where some thread exits before incrementing the
counter, the dispatcher will not be destroyed when the program exits.
Some leaks might be reported when debugging, but in reality no memory
is leaked because the process ended anyway (just not entirely cleanly).
Therefore this eventuality is ignored.
}
GVAR_DispThrProt: Integer = 0;
GVAR_Dispatcher: TUSDispatcher = nil;
GVAR_SignalNumber: cint = 0;
{===============================================================================
Procedural interface - internal routines
===============================================================================}
procedure DispatcherThreatProtectionInit;
begin
InterlockedStore(GVAR_DispThrProt,High(Integer));
end;
//------------------------------------------------------------------------------
Function DispatcherThreatProtectionFinal: Boolean;
begin
// true = can free the dispatcher
Result := InterlockedNeg(GVAR_DispThrProt) <= -High(Integer);
end;
//------------------------------------------------------------------------------
{$IFDEF FPCDWM}{$PUSH}W5024{$ENDIF}
Function AcquireInterlockedOp(A: Integer; var IntrRes: Integer): Integer; register;
begin
// IntrRes contains value of A on entry and is not changed
If A > 1 then
Result := A - 1
else
Result := A;
end;
{$IFDEF FPCDWM}{$POP}{$ENDIF}
Function DispatcherThreatProtectionAcquire: Boolean;
begin
// true = dispatcher is assigned and can be used (also do release)
Result := InterlockedOperation(GVAR_DispThrProt,AcquireInterlockedOp) > 1;
end;
//------------------------------------------------------------------------------
Function ReleaseInterlockedOp(A: Integer; var IntrRes: Integer): Integer; register;
begin
If A > 0 then
Result := A + 1
else If A < 0 then
Result := A - 1
else
Result := 0;
IntrRes := Result;
end;
Function DispatcherThreatProtectionRelease: Boolean;
begin
// false = dispatcher must be freed now
Result := InterlockedOperation(GVAR_DispThrProt,ReleaseInterlockedOp) > -High(Integer);
end;
{===============================================================================
Procedural interface - implementation
===============================================================================}
Function SignalNumber: Integer;
begin
Result := GVAR_SignalNumber;
end;
//------------------------------------------------------------------------------
Function CurrentProcessID: pid_t;
begin
Result := getpid;
end;
//==============================================================================
procedure RegisterHandler(Code: Integer; Handler: TUSHandlerCallback);
begin
If DispatcherThreatProtectionAcquire then
try
GVAR_Dispatcher.RegisterHandler(Code,Handler);
finally
If not DispatcherThreatProtectionRelease then
FreeAndNil(GVAR_Dispatcher);
end
{$IFNDEF SilentDispatcherFailure}
else raise EUSDispacherNotReady.Create('RegisterHandler: Dispatcher not ready.');
{$ENDIF}
end;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure RegisterHandler(Code: Integer; Handler: TUSHandlerEvent);
begin
If DispatcherThreatProtectionAcquire then
try
GVAR_Dispatcher.RegisterHandler(Code,Handler);
finally
If not DispatcherThreatProtectionRelease then
FreeAndNil(GVAR_Dispatcher);
end
{$IFNDEF SilentDispatcherFailure}
else raise EUSDispacherNotReady.Create('RegisterHandler: Dispatcher not ready.');
{$ENDIF}
end;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure RegisterHandler(Handler: TUSHandlerCallback);
begin
If DispatcherThreatProtectionAcquire then
try
GVAR_Dispatcher.RegisterHandler(SI_QUEUE,Handler);
finally
If not DispatcherThreatProtectionRelease then
FreeAndNil(GVAR_Dispatcher);
end
{$IFNDEF SilentDispatcherFailure}
else raise EUSDispacherNotReady.Create('RegisterHandler: Dispatcher not ready.');
{$ENDIF}
end;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure RegisterHandler(Handler: TUSHandlerEvent);
begin
If DispatcherThreatProtectionAcquire then
try
GVAR_Dispatcher.RegisterHandler(SI_QUEUE,Handler);
finally
If not DispatcherThreatProtectionRelease then
FreeAndNil(GVAR_Dispatcher);
end
{$IFNDEF SilentDispatcherFailure}
else raise EUSDispacherNotReady.Create('RegisterHandler: Dispatcher not ready.');
{$ENDIF}
end;
//------------------------------------------------------------------------------
procedure UnregisterHandler(Code: Integer; Handler: TUSHandlerCallback);
begin
If DispatcherThreatProtectionAcquire then
try
GVAR_Dispatcher.UnregisterHandler(Code,Handler);
finally
If not DispatcherThreatProtectionRelease then
FreeAndNil(GVAR_Dispatcher);
end
{$IFNDEF SilentDispatcherFailure}
else raise EUSDispacherNotReady.Create('UnregisterHandler: Dispatcher not ready.');
{$ENDIF}
end;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure UnregisterHandler(Code: Integer; Handler: TUSHandlerEvent);
begin
If DispatcherThreatProtectionAcquire then
try
GVAR_Dispatcher.UnregisterHandler(Code,Handler);
finally
If not DispatcherThreatProtectionRelease then
FreeAndNil(GVAR_Dispatcher);
end
{$IFNDEF SilentDispatcherFailure}
else raise EUSDispacherNotReady.Create('UnregisterHandler: Dispatcher not ready.');
{$ENDIF}
end;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
procedure UnregisterHandler(Handler: TUSHandlerCallback);
begin
If DispatcherThreatProtectionAcquire then
try
GVAR_Dispatcher.UnregisterHandler(SI_QUEUE,Handler);
finally
If not DispatcherThreatProtectionRelease then
FreeAndNil(GVAR_Dispatcher);
end
{$IFNDEF SilentDispatcherFailure}