forked from dotnet/orleans
-
Notifications
You must be signed in to change notification settings - Fork 0
/
GrainTypeManager.cs
335 lines (287 loc) · 14.3 KB
/
GrainTypeManager.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
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
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Net;
using System.Reflection;
using Orleans.CodeGeneration;
using Orleans.GrainDirectory;
using Orleans.Runtime.Providers;
using Orleans.Serialization;
namespace Orleans.Runtime
{
internal class GrainTypeManager
{
private IDictionary<string, GrainTypeData> grainTypes;
private Dictionary<SiloAddress, GrainInterfaceMap> grainInterfaceMapsBySilo;
private Dictionary<int, IList<SiloAddress>> supportedSilosByTypeCode;
private readonly Logger logger = LogManager.GetLogger("GrainTypeManager");
private readonly GrainInterfaceMap grainInterfaceMap;
private readonly Dictionary<int, InvokerData> invokers = new Dictionary<int, InvokerData>();
private readonly SiloAssemblyLoader loader;
private static readonly object lockable = new object();
private readonly PlacementStrategy defaultPlacementStrategy;
internal IReadOnlyDictionary<SiloAddress, GrainInterfaceMap> GrainInterfaceMapsBySilo
{
get { return grainInterfaceMapsBySilo; }
}
public static GrainTypeManager Instance { get; private set; }
public IEnumerable<KeyValuePair<string, GrainTypeData>> GrainClassTypeData { get { return grainTypes; } }
public GrainInterfaceMap ClusterGrainInterfaceMap { get; private set; }
public static void Stop()
{
Instance = null;
}
public GrainTypeManager(SiloInitializationParameters silo, SiloAssemblyLoader loader, DefaultPlacementStrategy defaultPlacementStrategy)
: this(silo.SiloAddress.Endpoint.Address.Equals(IPAddress.Loopback), loader, defaultPlacementStrategy)
{
}
public GrainTypeManager(bool localTestMode, SiloAssemblyLoader loader, DefaultPlacementStrategy defaultPlacementStrategy)
{
this.defaultPlacementStrategy = defaultPlacementStrategy.PlacementStrategy;
this.loader = loader;
grainInterfaceMap = new GrainInterfaceMap(localTestMode, this.defaultPlacementStrategy);
ClusterGrainInterfaceMap = grainInterfaceMap;
grainInterfaceMapsBySilo = new Dictionary<SiloAddress, GrainInterfaceMap>();
lock (lockable)
{
if (Instance != null)
throw new InvalidOperationException("An attempt to create a second insance of GrainTypeManager.");
Instance = this;
}
}
public void Start(bool strict = true)
{
// loading application assemblies now occurs in four phases.
// 1. We scan the file system for assemblies meeting pre-determined criteria, specified in SiloAssemblyLoader.LoadApplicationAssemblies (called by the constructor).
// 2. We load those assemblies into memory. In the official distribution of Orleans, this is usually 4 assemblies.
// Generate code for newly loaded assemblies.
CodeGeneratorManager.GenerateAndCacheCodeForAllAssemblies();
// (no more assemblies should be loaded into memory, so now is a good time to log all types registered with the serialization manager)
SerializationManager.LogRegisteredTypes();
// 3. We scan types in memory for GrainTypeData objects that describe grain classes and their corresponding grain state classes.
InitializeGrainClassData(loader, strict);
// 4. We scan types in memory for grain method invoker objects.
InitializeInvokerMap(loader, strict);
InitializeInterfaceMap();
}
public Dictionary<string, string> GetGrainInterfaceToClassMap()
{
return grainInterfaceMap.GetPrimaryImplementations();
}
internal bool TryGetPrimaryImplementation(string grainInterface, out string grainClass)
{
return grainInterfaceMap.TryGetPrimaryImplementation(grainInterface, out grainClass);
}
internal GrainTypeData this[string className]
{
get
{
string msg;
lock (this)
{
string grainType;
if (grainInterfaceMap.TryGetPrimaryImplementation(className, out grainType))
return grainTypes[grainType];
if (grainTypes.ContainsKey(className))
return grainTypes[className];
if (TypeUtils.IsGenericClass(className))
{
var templateName = TypeUtils.GetRawClassName(className);
if (grainInterfaceMap.TryGetPrimaryImplementation(templateName, out grainType))
templateName = grainType;
if (grainTypes.ContainsKey(templateName))
{
// Found the generic template class
try
{
// Instantiate the specific type from generic template
var genericGrainTypeData = (GenericGrainTypeData)grainTypes[templateName];
Type[] typeArgs = TypeUtils.GenericTypeArgsFromClassName(className);
var concreteTypeData = genericGrainTypeData.MakeGenericType(typeArgs);
// Add to lookup tables for next time
var grainClassName = concreteTypeData.GrainClass;
grainTypes.Add(grainClassName, concreteTypeData);
return concreteTypeData;
}
catch (Exception ex)
{
msg = "Cannot instantiate generic class " + className;
logger.Error(ErrorCode.Runtime_Error_100092, msg, ex);
throw new KeyNotFoundException(msg, ex);
}
}
}
}
msg = "Cannot find GrainTypeData for class " + className;
logger.Error(ErrorCode.Runtime_Error_100093, msg);
throw new TypeLoadException(msg);
}
}
internal void GetTypeInfo(int typeCode, out string grainClass, out PlacementStrategy placement, out MultiClusterRegistrationStrategy activationStrategy, string genericArguments = null)
{
if (!ClusterGrainInterfaceMap.TryGetTypeInfo(typeCode, out grainClass, out placement, out activationStrategy, genericArguments))
throw new OrleansException(String.Format("Unexpected: Cannot find an implementation class for grain interface {0}", typeCode));
}
internal void SetInterfaceMapsBySilo(Dictionary<SiloAddress, GrainInterfaceMap> value)
{
grainInterfaceMapsBySilo = value;
RebuildFullGrainInterfaceMap();
}
internal IList<SiloAddress> GetSupportedSilos(int typeCode)
{
return supportedSilosByTypeCode[typeCode];
}
private void InitializeGrainClassData(SiloAssemblyLoader loader, bool strict)
{
grainTypes = loader.GetGrainClassTypes(strict);
}
private void InitializeInvokerMap(SiloAssemblyLoader loader, bool strict)
{
IEnumerable<KeyValuePair<int, Type>> types = loader.GetGrainMethodInvokerTypes(strict);
foreach (var i in types)
{
int ifaceId = i.Key;
Type type = i.Value;
AddInvokerClass(ifaceId, type);
}
}
private void InitializeInterfaceMap()
{
foreach (GrainTypeData grainType in grainTypes.Values)
AddToGrainInterfaceToClassMap(grainType.Type, grainType.RemoteInterfaceTypes, grainType.IsStatelessWorker);
}
private void AddToGrainInterfaceToClassMap(Type grainClass, IEnumerable<Type> grainInterfaces, bool isUnordered)
{
var grainTypeInfo = grainClass.GetTypeInfo();
var grainClassCompleteName = TypeUtils.GetFullName(grainTypeInfo);
var isGenericGrainClass = grainTypeInfo.ContainsGenericParameters;
var grainClassTypeCode = GrainInterfaceUtils.GetGrainClassTypeCode(grainClass);
var placement = GrainTypeData.GetPlacementStrategy(grainClass, this.defaultPlacementStrategy);
var registrationStrategy = GrainTypeData.GetMultiClusterRegistrationStrategy(grainClass);
foreach (var iface in grainInterfaces)
{
var ifaceCompleteName = TypeUtils.GetFullName(iface);
var ifaceName = TypeUtils.GetRawClassName(ifaceCompleteName);
var isPrimaryImplementor = IsPrimaryImplementor(grainClass, iface);
var ifaceId = GrainInterfaceUtils.GetGrainInterfaceId(iface);
grainInterfaceMap.AddEntry(ifaceId, iface, grainClassTypeCode, ifaceName, grainClassCompleteName,
grainTypeInfo.Assembly.CodeBase, isGenericGrainClass, placement, registrationStrategy, isPrimaryImplementor);
}
if (isUnordered)
grainInterfaceMap.AddToUnorderedList(grainClassTypeCode);
}
private static bool IsPrimaryImplementor(Type grainClass, Type iface)
{
// If the class name exactly matches the interface name, it is considered the primary (default)
// implementation of the interface, e.g. IFooGrain -> FooGrain
return (iface.Name.Substring(1) == grainClass.Name);
}
public bool TryGetData(string name, out GrainTypeData result)
{
return grainTypes.TryGetValue(name, out result);
}
internal GrainInterfaceMap GetTypeCodeMap()
{
// the map is immutable at this point
return grainInterfaceMap;
}
private void AddInvokerClass(int interfaceId, Type invoker)
{
lock (invokers)
{
if (!invokers.ContainsKey(interfaceId))
invokers.Add(interfaceId, new InvokerData(invoker));
}
}
/// <summary>
/// Returns a list of all graintypes in the system.
/// </summary>
/// <returns></returns>
internal string[] GetGrainTypeList()
{
return grainTypes.Keys.ToArray();
}
internal IGrainMethodInvoker GetInvoker(int interfaceId, string genericGrainType = null)
{
try
{
InvokerData invokerData;
if (invokers.TryGetValue(interfaceId, out invokerData))
return invokerData.GetInvoker(genericGrainType);
}
catch (Exception ex)
{
throw new OrleansException(String.Format("Error finding invoker for interface ID: {0} (0x{0, 8:X8}). {1}", interfaceId, ex), ex);
}
Type type;
var interfaceName = grainInterfaceMap.TryGetServiceInterface(interfaceId, out type) ?
type.FullName : "*unavailable*";
throw new OrleansException(String.Format("Cannot find an invoker for interface {0} (ID={1},0x{1, 8:X8}).",
interfaceName, interfaceId));
}
private void RebuildFullGrainInterfaceMap()
{
var newClusterGrainInterfaceMap = new GrainInterfaceMap(false, defaultPlacementStrategy);
var newSupportedSilosByTypeCode = new Dictionary<int, IList<SiloAddress>>();
newClusterGrainInterfaceMap.AddMap(grainInterfaceMap);
foreach (var kvp in grainInterfaceMapsBySilo)
{
newClusterGrainInterfaceMap.AddMap(kvp.Value);
foreach (var grainType in kvp.Value.SupportedGrainTypes)
{
IList<SiloAddress> supportedSilos;
if (!newSupportedSilosByTypeCode.TryGetValue(grainType, out supportedSilos))
{
newSupportedSilosByTypeCode[grainType] = supportedSilos = new List<SiloAddress>();
}
supportedSilos.Add(kvp.Key);
}
}
ClusterGrainInterfaceMap = newClusterGrainInterfaceMap;
supportedSilosByTypeCode = newSupportedSilosByTypeCode;
}
private class InvokerData
{
private readonly Type baseInvokerType;
private IGrainMethodInvoker invoker;
private readonly Dictionary<string, IGrainMethodInvoker> cachedGenericInvokers;
private readonly object cachedGenericInvokersLockObj;
public InvokerData(Type invokerType)
{
baseInvokerType = invokerType;
if (invokerType.GetTypeInfo().IsGenericType)
{
cachedGenericInvokers = new Dictionary<string, IGrainMethodInvoker>();
cachedGenericInvokersLockObj = new object(); ;
}
}
public IGrainMethodInvoker GetInvoker(string genericGrainType = null)
{
// if the grain class is non-generic
if (cachedGenericInvokersLockObj == null)
{
return invoker ?? (invoker = (IGrainMethodInvoker)Activator.CreateInstance(baseInvokerType));
}
else
{
lock (cachedGenericInvokersLockObj)
{
if (cachedGenericInvokers.ContainsKey(genericGrainType))
return cachedGenericInvokers[genericGrainType];
}
var typeArgs = TypeUtils.GenericTypeArgsFromArgsString(genericGrainType);
var concreteType = baseInvokerType.MakeGenericType(typeArgs);
var inv = (IGrainMethodInvoker)Activator.CreateInstance(concreteType);
lock (cachedGenericInvokersLockObj)
{
if (!cachedGenericInvokers.ContainsKey(genericGrainType))
cachedGenericInvokers[genericGrainType] = inv;
}
return inv;
}
}
}
}
}