-
Notifications
You must be signed in to change notification settings - Fork 29
/
LockManagerBase.cs
200 lines (150 loc) · 5.9 KB
/
LockManagerBase.cs
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
/*<FILE_LICENSE>
* Azos (A to Z Application Operating System) Framework
* The A to Z Foundation (a.k.a. Azist) licenses this file to you under the MIT license.
* See the LICENSE file in the project root for more information.
</FILE_LICENSE>*/
using System;
using System.Collections.Concurrent;
using System.Threading.Tasks;
using Azos.Apps;
using Azos.Conf;
using Azos.Collections;
using Azos.Instrumentation;
namespace Azos.Sky.Locking
{
/// <summary>
/// Base for Facades used for calling locking APIs from client code
/// </summary>
public abstract class LockManagerBase : DaemonWithInstrumentation<IApplicationComponent>, ILockManagerImplementation
{
private static readonly TimeSpan INSTR_INTERVAL = TimeSpan.FromMilliseconds(3700);
public LockManagerBase(IApplication app) : base(app) { }
public LockManagerBase(IApplicationComponent director) : base(director) { }
protected override void Destructor()
{
DisposableObject.DisposeAndNull(ref m_InstrumentationEvent);
base.Destructor();
}
private ConcurrentDictionary<LockSessionID, LockSession> m_Sessions = new ConcurrentDictionary<LockSessionID,LockSession>();
private bool m_InstrumentationEnabled;
private Time.Event m_InstrumentationEvent;
private NamedInterlocked m_Stats = new NamedInterlocked();
public override string ComponentLogTopic => SysConsts.LOG_TOPIC_LOCKING;
/// <summary>
/// Implements IInstrumentable
/// </summary>
[Config(Default=false)]
[ExternalParameter(CoreConsts.EXT_PARAM_GROUP_LOCKING, CoreConsts.EXT_PARAM_GROUP_INSTRUMENTATION)]
public override bool InstrumentationEnabled
{
get { return m_InstrumentationEnabled;}
set
{
m_InstrumentationEnabled = value;
if (m_InstrumentationEvent==null)
{
if (!value) return;
m_Stats.Clear();
m_InstrumentationEvent = new Time.Event(App.EventTimer, null, e => AcceptManagerVisit(this, e.LocalizedTime), INSTR_INTERVAL);
}
else
{
if (value) return;
DisposableObject.DisposeAndNull(ref m_InstrumentationEvent);
m_Stats.Clear();
}
}
}
public LockSession this[LockSessionID sid]
{
get
{
LockSession session;
if (m_Sessions.TryGetValue(sid, out session)) return session;
return null;
}
}
#region Pub
public virtual LockSession MakeSession(string path, object shardingID, string description = null, int? maxAgeSec = null)
{
var session = new LockSession(this, path, shardingID, description, maxAgeSec);
m_Sessions[ session.ID ] = session;
return session;
}
public LockTransactionResult ExecuteLockTransaction(LockSession session, LockTransaction transaction)
{
if (!Running) return LockTransactionResult.CallFailed;
if (session==null || transaction==null)
throw new LockingException(ServerStringConsts.ARGUMENT_ERROR+GetType().Name+".ExecuteLockTransaction(session|tran==null)");
checkSessionExists(session);
if (m_InstrumentationEnabled)
m_Stats.IncrementLong(session.Path);
return DoExecuteLockTransaction(session, transaction);
}
public Task<LockTransactionResult> ExecuteLockTransactionAsync(LockSession session, LockTransaction transaction)
{
if (!Running) return Task.FromResult(LockTransactionResult.CallFailed);
if (session==null || transaction==null)
throw new LockingException(ServerStringConsts.ARGUMENT_ERROR+GetType().Name+".ExecuteLockTransactionAsync(session|tran==null)");
checkSessionExists(session);
if (m_InstrumentationEnabled)
m_Stats.IncrementLong(session.Path);
return DoExecuteLockTransactionAsync(session, transaction);
}
public bool EndLockSession(LockSession session)
{
if (!Running) return false;
if (session==null)
throw new LockingException(ServerStringConsts.ARGUMENT_ERROR+GetType().Name+".EndLockSession(session==null)");
checkSessionExists(session);
var ended = DoEndLockSession(session);
LockSession d;
m_Sessions.TryRemove(session.ID, out d);
return ended;
}
public Task<bool> EndLockSessionAsync(LockSession session)
{
if (!Running) return Task.FromResult(false);
if (session==null)
throw new LockingException(ServerStringConsts.ARGUMENT_ERROR+GetType().Name+".EndLockSessionAsync(session==null)");
checkSessionExists(session);
LockSession d;
m_Sessions.TryRemove(session.ID, out d);
return DoEndLockSessionAsync(session);
}
#endregion
#region Protected
protected abstract LockTransactionResult DoExecuteLockTransaction(LockSession session, LockTransaction transaction);
protected abstract Task<LockTransactionResult> DoExecuteLockTransactionAsync(LockSession session, LockTransaction transaction);
protected abstract bool DoEndLockSession(LockSession session);
protected abstract Task<bool> DoEndLockSessionAsync(LockSession session);
protected override void DoStart()
{
base.DoStart();
}
protected override void DoWaitForCompleteStop()
{
base.DoWaitForCompleteStop();
}
protected override void DoAcceptManagerVisit(object manager, DateTime managerNow)
{
dumpStats();
}
#endregion
#region .pvt
private void checkSessionExists(LockSession session)
{
var s = this[session.ID];
if (s==null) throw new LockingException(ServerStringConsts.LOCK_SESSION_NOT_ACTIVE_ERROR.Args(session.ID, session.Description));
}
private void dumpStats()
{
var instr = App.Instrumentation;
if (!instr.Enabled) return;
instr.Record( new Instrumentation.LockSessions( m_Sessions.Count ) );
foreach( var kvp in m_Stats.SnapshotAllLongs(0))
instr.Record( new Instrumentation.LockTransactionRequests(kvp.Key, kvp.Value) );
}
#endregion
}
}