-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathMaxLogic.FileMutex.pas
More file actions
115 lines (95 loc) · 2.54 KB
/
Copy pathMaxLogic.FileMutex.pas
File metadata and controls
115 lines (95 loc) · 2.54 KB
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
Unit MaxLogic.FileMutex;
Interface
Uses
{$IFDEF MSWINDOWS}
Winapi.Windows,
{$ENDIF}
sysUtils, classes;
Type
/// <summary>
/// you can use THandleStream to read/write from/to the underlying file
/// </summary>
TFileMutex = Class
Private
FFileHandle: THandle;
Public
Constructor Create;
Destructor Destroy; Override;
Function Open(Const aFileName: String; aTimeOutInMs: Integer): Boolean;
Procedure Close;
Property Handle: THandle Read FFileHandle;
End;
Implementation
Uses
{$IFDEF POSIX}
Posix.Base,
{$ENDIF}
ioUtils, diagnostics, math;
{$IFDEF POSIX}
Const
cLockExclusive = 2;
cLockNonBlocking = 4;
cLockUnlock = 8;
Function Cflock(fd: Integer; operation: Integer): Integer; Cdecl; External libc Name _PU + 'flock';
Function TryLockHandle(aHandle: THandle): Boolean;
Begin
Result := Cflock(Integer(aHandle), cLockExclusive Or cLockNonBlocking) = 0;
End;
Procedure UnlockHandle(aHandle: THandle);
Begin
Cflock(Integer(aHandle), cLockUnlock);
End;
{$ENDIF}
Function TFileMutex.Open(Const aFileName: String; aTimeOutInMs: Integer): Boolean;
Var
st: TStopWatch;
sleepMs: Integer;
Begin
Close;
st := TStopWatch.startNew;
Repeat
If TFile.Exists(aFileName) Then
FFileHandle := FileOpen(aFileName, fmOpenReadWrite OR fmShareExclusive)
else begin
// chmod is as octal value
FFileHandle := FileCreate(aFileName, fmOpenReadWrite Or fmShareExclusive, 420);
end;
{$IFDEF POSIX}
If (FFileHandle <> INVALID_HANDLE_VALUE) And Not TryLockHandle(FFileHandle) Then
Begin
FileClose(FFileHandle);
FFileHandle := INVALID_HANDLE_VALUE;
End;
{$ENDIF}
// we do not spin forever, the cpu would not like that. Rather go to sleep after some time
If (FFileHandle = INVALID_HANDLE_VALUE) And (st.ElapsedMilliseconds < aTimeOutInMs) And (st.ElapsedMilliseconds > 30) Then
Begin
sleepMs := min(5, (aTimeOutInMs - st.ElapsedMilliseconds) - 1);
If sleepMs > 0 Then
sleep(sleepMs);
End;
until (FFileHandle <> INVALID_HANDLE_VALUE) or (st.ElapsedMilliseconds >= aTimeOutInMs);
Result := (FFileHandle <> INVALID_HANDLE_VALUE);
End;
Destructor TFileMutex.Destroy;
Begin
Close;
Inherited;
End;
Constructor TFileMutex.Create;
Begin
Inherited Create;
FFileHandle:= INVALID_HANDLE_VALUE;
End;
Procedure TFileMutex.Close;
Begin
if fFileHandle <> INVALID_HANDLE_VALUE Then
Begin
{$IFDEF POSIX}
UnlockHandle(FFileHandle);
{$ENDIF}
FileClose(FFileHandle);
FFileHandle := INVALID_HANDLE_VALUE;
End;
End;
End.