4using System.Net.NetworkInformation;
6using System.Reflection;
10using System.Threading.Tasks;
29 private static readonly Dictionary<Type, ObjectInfo> objectInfo =
new Dictionary<Type, ObjectInfo>();
30 private static Dictionary<Type, IProperty> propertyTypes =
null;
31 private static ObjectInfo rootObject;
33 private readonly LinkedList<ClusterUdpClient> outgoing =
new LinkedList<ClusterUdpClient>();
34 private readonly LinkedList<ClusterUdpClient> incoming =
new LinkedList<ClusterUdpClient>();
35 private readonly IPEndPoint destination;
36 internal readonly
byte[] key;
40 private readonly Random rnd =
new Random();
41 private readonly Dictionary<IPEndPoint, object> remoteStatus;
42 internal readonly Dictionary<string, LockInfo> lockedResources =
new Dictionary<string, LockInfo>();
43 private object localStatus;
44 private Timer aliveTimer;
45 internal bool shuttingDown =
false;
46 private readonly
bool internalScheduler;
47 private ManualResetEvent shutDown =
new ManualResetEvent(
false);
58 : this(MulticastAddress, Port, Encoding.UTF8.GetBytes(SharedSecret),
Sniffers)
72 ClusterUdpClient ClusterUdpClient;
75 if (MulticastAddress.AddressFamily != AddressFamily.InterNetwork)
76 throw new ArgumentException(
"Cluster communication must be done using IPv4.", nameof(MulticastAddress));
78 if (propertyTypes is
null)
81 rootObject = GetObjectInfo(typeof(
object));
84 this.aes = Aes.Create();
85 this.aes.BlockSize = 128;
86 this.aes.KeySize = 256;
87 this.aes.Mode = CipherMode.CBC;
88 this.aes.Padding = PaddingMode.None;
91 this.destination =
new IPEndPoint(MulticastAddress, Port);
93 this.localStatus =
null;
94 this.remoteStatus =
new Dictionary<IPEndPoint, object>();
95 this.currentStatus =
new Cache<string, object>(
int.MaxValue, TimeSpan.MaxValue, TimeSpan.FromSeconds(30),
true);
96 this.currentStatus.Removed += this.CurrentStatus_Removed;
101 this.internalScheduler =
false;
106 this.internalScheduler =
true;
109 foreach (NetworkInterface Interface
in NetworkInterface.GetAllNetworkInterfaces())
111 if (Interface.OperationalStatus != OperationalStatus.Up)
114 switch (Interface.NetworkInterfaceType)
116 case NetworkInterfaceType.Loopback:
120 IPInterfaceProperties Properties = Interface.GetIPProperties();
122 foreach (UnicastIPAddressInformation UnicastAddress
in Properties.UnicastAddresses)
124 IPAddress Address = UnicastAddress.Address;
125 AddressFamily AddressFamily = Address.AddressFamily;
126 bool IsLoopback = Interface.NetworkInterfaceType == NetworkInterfaceType.Loopback;
128 if (Address.AddressFamily != MulticastAddress.AddressFamily)
137 Client =
new UdpClient(AddressFamily)
140 ExclusiveAddressUse =
true,
141 MulticastLoopback =
true,
142 EnableBroadcast =
true,
148 Client.Client.Bind(
new IPEndPoint(Address, 0));
152 this.
Information(
"Joining Multicast address " + MulticastAddress);
156 Client.JoinMulticastGroup(MulticastAddress);
164 this.
Warning(
"Address provided is not a multi-cast address.");
166 catch (NotSupportedException)
178 ClusterUdpClient =
new ClusterUdpClient(
this, Client, Address);
179 ClusterUdpClient.BeginReceive();
181 this.outgoing.AddLast(ClusterUdpClient);
187 Client =
new UdpClient(AddressFamily)
189 ExclusiveAddressUse =
false,
192 Client.Client.Bind(
new IPEndPoint(Address, Port));
194 catch (NotSupportedException)
206 ClusterUdpClient =
new ClusterUdpClient(
this, Client, Address);
207 ClusterUdpClient.BeginReceive();
209 this.incoming.AddLast(ClusterUdpClient);
210 this.endpoints =
null;
217 Client =
new UdpClient(Port, MulticastAddress.AddressFamily)
219 MulticastLoopback =
true
224 this.
Information(
"Joining Multicast address " + MulticastAddress);
228 Client.JoinMulticastGroup(MulticastAddress);
236 this.
Warning(
"Address provided is not a multi-cast address.");
238 ClusterUdpClient =
new ClusterUdpClient(
this, Client,
null);
239 ClusterUdpClient.BeginReceive();
241 this.incoming.AddLast(ClusterUdpClient);
242 this.endpoints =
null;
249 this.aliveTimer =
new Timer(this.SendAlive,
null, 0, 5000);
259 if (this.endpoints is
null)
261 List<IPEndPoint> Result =
new List<IPEndPoint>();
262 LinkedListNode<ClusterUdpClient> Loop = this.incoming.First;
264 while (!(Loop is
null))
266 Result.Add(Loop.Value.EndPoint);
270 this.endpoints = Result.ToArray();
273 return this.endpoints;
277 private IPEndPoint[] endpoints =
null;
282 [Obsolete(
"Use DisposeAsync")]
293 if (!this.shuttingDown)
295 LinkedList<string> ToRelease =
null;
297 lock (this.lockedResources)
299 foreach (LockInfo Info
in this.lockedResources.Values)
303 ToRelease ??=
new LinkedList<string>();
304 ToRelease.AddLast(Info.Resource);
308 this.lockedResources.Clear();
311 if (!(ToRelease is
null))
313 foreach (
string Resource
in ToRelease)
322 this.shuttingDown =
true;
325 this.shutDown?.WaitOne(2000);
326 this.shutDown?.Dispose();
327 this.shutDown =
null;
334 internal async Task Dispose2()
336 if (this.internalScheduler)
339 this.scheduler =
null;
341 this.aliveTimer?.Dispose();
342 this.aliveTimer =
null;
344 this.Clear(this.outgoing);
345 this.Clear(this.incoming);
348 this.currentStatus =
null;
355 await DisposableAsync.DisposeAsync();
356 else if (Sniffer is IDisposable Disposable)
357 Disposable.Dispose();
365 this.shutDown?.Set();
368 private void Clear(LinkedList<ClusterUdpClient> Clients)
370 LinkedListNode<ClusterUdpClient> Loop = Clients.First;
372 while (!(Loop is
null))
376 Loop.Value.Dispose();
389 internal static ObjectInfo GetObjectInfo(Type T)
393 if (objectInfo.TryGetValue(T, out ObjectInfo Result))
397 List<PropertyReference> Properties =
new List<PropertyReference>();
399 foreach (PropertyInfo PI
in T.GetRuntimeProperties())
401 if (PI.GetMethod is
null || PI.SetMethod is
null)
404 Type PT = PI.PropertyType;
410 Properties.Add(
new PropertyReference()
418 return new ObjectInfo()
421 Properties = Properties.ToArray()
425 internal static IProperty GetProperty(Type PT)
429 Type ElementType = PT.GetElementType();
430 return new ArrayProperty(PT, ElementType, GetProperty(ElementType));
433 TypeInfo PTI = PT.GetTypeInfo();
437 else if (PTI.IsGenericType && PT.GetGenericTypeDefinition() == typeof(Nullable<>))
439 Type ElementType = PT.GenericTypeArguments[0];
440 IProperty Element = GetProperty(ElementType);
446 else if (!PTI.IsValueType)
452 private static void Init()
454 Dictionary<Type, IProperty> PropertyTypes =
new Dictionary<Type, IProperty>();
459 if (DefaultConstructor is
null)
469 if (PropertyTypes.ContainsKey(PT))
471 Log.
Error(
"Multiple classes available for property type " + PT.FullName +
".");
483 if (propertyTypes is
null)
484 Types.OnInvalidated += (Sender, e) => Init();
486 propertyTypes = PropertyTypes;
497 rootObject.Serialize(Output, Object);
498 return Output.ToArray();
510 return rootObject.Deserialize(Input, typeof(
object));
513 internal async
void DataReceived(
bool ConstantBuffer,
byte[] Data, IPEndPoint From)
517 if (Data.Length == 0)
523 byte Command = Input.ReadByte();
528 object Object = rootObject.Deserialize(Input, typeof(
object));
531 this.
Information(
"Unacknowledged message received: " + Object.GetType().FullName);
535 await Message.MessageReceived(
this, From);
540 this.
Error(
"Non-message object received in message: " + Object?.GetType()?.FullName);
544 Guid Id = Input.ReadGuid();
545 string s = Id.ToString();
546 ulong Len = Input.ReadVarUInt64();
551 string Address =
new IPAddress(Input.ReadBinary()).ToString();
552 int Port = Input.ReadUInt16();
554 if (this.IsEndpoint(this.outgoing, Address, Port) ||
555 this.IsEndpoint(this.incoming, Address, Port))
567 if (!this.currentStatus.
TryGetValue(s, out
object Obj) ||
570 Object = rootObject.Deserialize(Input, typeof(
object));
573 this.
Information(
"Acknowledged message received: " + Object.GetType().FullName);
579 Ack = await Message.MessageReceived(
this, From);
590 this.currentStatus[s] = Ack;
594 this.
Error(
"Non-message object received in message: " + Object?.GetType()?.FullName);
601 Output.
WriteByte(Ack ? (
byte)2 : (byte)3);
605 this.
Information(
"Sending " + (Ack ?
"ACK" :
"NACK") +
" to " + From.ToString());
607 this.Transmit(
true, Output.
ToArray(), From);
613 Id = Input.ReadGuid();
617 this.
Information(Command == 2 ?
"ACK received" :
"NACK received");
620 Obj is MessageStatus MessageStatus)
622 lock (MessageStatus.Acknowledged)
624 MessageStatus.Acknowledged[From] = (
Command == 2);
629 if (MessageStatus.IsComplete(CurrentStatus))
631 this.currentStatus.
Remove(s);
632 this.scheduler.
Remove(MessageStatus.Timeout);
635 MessageStatus.Message, MessageStatus.GetResponses(CurrentStatus),
636 MessageStatus.State));
640 this.
Warning(
"Response discarded. No status found for endpoing " + s);
644 Id = Input.ReadGuid();
646 Len = Input.ReadVarUInt64();
651 string Address =
new IPAddress(Input.ReadBinary()).ToString();
652 int Port = Input.ReadUInt16();
654 if (this.IsEndpoint(this.outgoing, Address, Port) ||
655 this.IsEndpoint(this.incoming, Address, Port))
669 Object = rootObject.Deserialize(Input, typeof(
object));
672 this.
Information(
"Command received: " + Object.GetType().FullName);
678 Obj = await ClusterCommand.Execute(
this, From);
679 this.currentStatus[s] = Obj;
687 Obj =
new Exception(
"Non-command object received in command: " + Object?.GetType()?.FullName);
692 if (Obj is Exception ex)
705 rootObject.Serialize(Output, Obj);
708 this.Transmit(
true, Output.
ToArray(), From);
713 Id = Input.ReadGuid();
717 Obj is CommandStatusBase CommandStatus)
719 Object = rootObject.Deserialize(Input, typeof(
object));
722 this.
Information(
"Command Response received: " + Object.GetType().FullName);
724 CommandStatus.AddResponse(From, Object);
728 if (CommandStatus.IsComplete(CurrentStatus))
730 this.currentStatus.
Remove(s);
731 this.scheduler.
Remove(CommandStatus.Timeout);
733 await CommandStatus.RaiseResponseEvent(CurrentStatus);
739 Id = Input.ReadGuid();
743 Obj is CommandStatusBase CommandStatus2)
745 string ExceptionMessage = Input.ReadString();
746 string ExceptionType = Input.ReadString();
755 ex = (
Exception)Activator.CreateInstance(T, ExceptionMessage);
763 this.
Error(ex.Message);
765 CommandStatus2.AddError(From, ex);
769 if (CommandStatus2.IsComplete(CurrentStatus))
771 this.currentStatus.
Remove(s);
772 this.scheduler.
Remove(CommandStatus2.Timeout);
774 await CommandStatus2.RaiseResponseEvent(CurrentStatus);
780 this.
Error(
"Invalid command received.");
791 internal bool IsEcho(IPEndPoint Endpoint)
793 LinkedListNode<ClusterUdpClient> Loop = this.outgoing.First;
795 while (!(Loop is
null))
797 if (Endpoint.Equals(Loop.Value.EndPoint))
806 private bool IsEndpoint(LinkedList<ClusterUdpClient> Clients,
string Address,
int Port)
808 LinkedListNode<ClusterUdpClient> Loop = Clients.First;
810 while (!(Loop is
null))
812 if (Loop.Value.IsEndpoint(Address, Port))
824 private void Transmit(
bool ConstantBuffer,
byte[] Message, IPEndPoint Destination)
828 LinkedListNode<ClusterUdpClient> Loop = this.outgoing.First;
830 while (!(Loop is
null))
832 Loop.Value.BeginTransmit(ConstantBuffer, Message, Destination);
854 this.
Information(
"Sending unacknowledged message: " + Message.GetType().FullName);
861 rootObject.Serialize(Output, Message);
862 this.Transmit(
true, Output.
ToArray(), Destination);
868 if (this.shuttingDown)
869 await this.Dispose2();
884 EventHandlerAsync<ClusterMessageAckEventArgs> Callback,
object State)
897 EventHandlerAsync<ClusterMessageAckEventArgs> Callback,
object State)
900 this.
Information(
"Sending acknowledged message: " + Message.GetType().FullName);
903 Guid Id = Guid.NewGuid();
904 string s = Id.ToString();
905 MessageStatus Rec =
null;
912 rootObject.Serialize(Output, Message);
915 DateTime Now = DateTime.Now;
917 Rec =
new MessageStatus()
922 Destination = Destination,
925 TimeLimit = Now.AddSeconds(30)
928 this.currentStatus[s] = Rec;
930 Rec.Timeout = this.scheduler.
Add(Now.AddSeconds(2),
this.ResendAcknowledgedMessage, Rec);
932 this.Transmit(
true, Bin, Destination);
936 if (Rec.IsComplete(CurrentStatus))
938 this.currentStatus.
Remove(s);
939 this.scheduler.
Remove(Rec.Timeout);
942 Rec.Message, Rec.GetResponses(CurrentStatus), Rec.State));
949 if (this.shuttingDown)
950 await this.Dispose2();
958 private async Task ResendAcknowledgedMessage(
object P)
960 MessageStatus Rec = (MessageStatus)P;
965 DateTime Now = DateTime.Now;
967 if (Rec.IsComplete(CurrentStatus) || Now >= Rec.TimeLimit)
969 this.currentStatus.
Remove(Rec.Id.ToString());
970 this.scheduler.
Remove(Rec.Timeout);
973 Rec.Message, Rec.GetResponses(CurrentStatus), Rec.State));
981 lock (Rec.Acknowledged)
985 foreach (IPEndPoint Endpoint
in Rec.Acknowledged.Keys)
987 Output.
WriteBinary(Endpoint.Address.GetAddressBytes());
992 Output.
WriteRaw(Rec.MessageBinary, 18, Rec.MessageBinary.Length - 18);
994 Rec.Timeout = this.scheduler.
Add(Now.AddSeconds(2),
this.ResendAcknowledgedMessage, Rec);
995 this.Transmit(
true, Output.
ToArray(), Rec.Destination);
1020 TaskCompletionSource<EndpointAcknowledgement[]> Result =
new TaskCompletionSource<EndpointAcknowledgement[]>();
1024 Result.TrySetResult(e.Responses);
1025 return Task.CompletedTask;
1028 return await Result.Task;
1038 EventHandlerAsync<ClusterMessageAckEventArgs> Callback,
object State)
1051 EventHandlerAsync<ClusterMessageAckEventArgs> Callback,
object State)
1053 Guid MessageId = Guid.NewGuid();
1057 MessageID = MessageId,
1059 }, Destination, async (Sender, e) =>
1063 MessageID = MessageId
1064 }, Destination, async (sender2, e2) =>
1087 TaskCompletionSource<EndpointAcknowledgement[]> Result =
new TaskCompletionSource<EndpointAcknowledgement[]>();
1091 Result.TrySetResult(e.Responses);
1092 return Task.CompletedTask;
1095 return await Result.Task;
1098 private void SendAlive(
object _)
1100 Task.Run(() => this.SendAlive(this.destination));
1103 private async Task SendAlive(IPEndPoint Destination)
1111 this.localStatus = e.
Status;
1115 Status = this.localStatus
1118 catch (Exception ex)
1127 public event EventHandlerAsync<ClusterGetStatusEventArgs>
GetStatus =
null;
1144 lock (this.remoteStatus)
1149 foreach (KeyValuePair<IPEndPoint, object> P
in this.remoteStatus)
1169 lock (this.remoteStatus)
1171 New = !this.remoteStatus.ContainsKey(Endpoint);
1172 this.remoteStatus[Endpoint] = Status;
1177 await this.SendAlive(Endpoint);
1180 this.
Information(
"New remote endpoint: " + Endpoint.ToString());
1185 this.
Information(
"Remote endpoint status updated: " + Endpoint.ToString());
1205 lock (this.remoteStatus)
1207 Removed = this.remoteStatus.Remove(Endpoint);
1213 this.
Warning(
"Remote endpoint removed: " + Endpoint.ToString());
1234 public event EventHandlerAsync<ClusterEndpointStatusEventArgs>
EndpointStatus =
null;
1236 internal void StatusReported(
object Status, IPEndPoint RemoteEndpoint)
1240 string s = RemoteEndpoint.ToString();
1243 this.currentStatus[s] = RemoteEndpoint;
1246 internal Task EndpointShutDown(IPEndPoint RemoteEndpoint)
1251 internal void AssuredTransport(Guid MessageId,
IClusterMessage Message)
1253 this.currentStatus[MessageId.ToString()] = Message;
1256 internal async Task<bool> AssuredDelivery(Guid MessageId,
ClusterEndpoint Endpoint, IPEndPoint RemoteEndpoint)
1258 string s = MessageId.ToString();
1260 if (this.currentStatus.
TryGetValue(s, out
object Obj))
1266 this.currentStatus[s] =
false;
1267 b = await Message.MessageReceived(Endpoint, RemoteEndpoint);
1268 this.currentStatus[s] = b;
1283 if (e.
Value is IPEndPoint RemoteEndpoint)
1286 this.
Warning(
"Remote endpoint dropped: " + RemoteEndpoint.ToString());
1290 else if (e.
Value is MessageStatus MessageStatus)
1294 this.scheduler.
Remove(MessageStatus.Timeout);
1297 this.
Warning(
"Timeout: " + MessageStatus.Message.GetType().FullName);
1300 MessageStatus.Message, MessageStatus.GetResponses(
this.GetRemoteStatuses()),
1301 MessageStatus.State));
1311 public Task
Ping(EventHandlerAsync<ClusterMessageAckEventArgs> Callback,
object State)
1321 TaskCompletionSource<EndpointAcknowledgement[]> Result =
new TaskCompletionSource<EndpointAcknowledgement[]>();
1323 await this.
Ping((Sender, e) =>
1325 Result.TrySetResult(e.Responses);
1326 return Task.CompletedTask;
1329 return await Result.Task;
1345 Guid Id = Guid.NewGuid();
1346 string s = Id.ToString();
1347 CommandStatus<ResponseType> Rec =
null;
1354 rootObject.Serialize(Output, Command);
1356 byte[] Bin = Output.
ToArray();
1357 DateTime Now = DateTime.Now;
1359 Rec =
new CommandStatus<ResponseType>()
1364 CommandBinary = Bin,
1365 TimeLimit = Now.AddSeconds(30),
1366 Callback = Callback,
1370 this.currentStatus[s] = Rec;
1372 Rec.Timeout = this.scheduler.
Add(Now.AddSeconds(2),
this.ResendCommand<ResponseType>, Rec);
1374 this.Transmit(
true, Bin, this.destination);
1378 if (Rec.IsComplete(CurrentStatus))
1380 this.currentStatus.
Remove(Rec.IdStr);
1381 this.scheduler.
Remove(Rec.Timeout);
1384 Rec.Command, Rec.GetResponses(CurrentStatus), Rec.State));
1391 if (this.shuttingDown)
1392 await this.Dispose2();
1400 private async Task ResendCommand<ResponseType>(
object P)
1402 CommandStatus<ResponseType> Rec = (CommandStatus<ResponseType>)P;
1403 DateTime Now = DateTime.Now;
1409 if (Rec.IsComplete(CurrentStatus) || Now >= Rec.TimeLimit)
1411 this.currentStatus.
Remove(Rec.IdStr);
1412 this.scheduler.
Remove(Rec.Timeout);
1415 Rec.Command, Rec.GetResponses(CurrentStatus), Rec.State));
1424 lock (Rec.Responses)
1428 foreach (IPEndPoint Endpoint
in Rec.Responses.Keys)
1430 Output.
WriteBinary(Endpoint.Address.GetAddressBytes());
1435 Output.
WriteRaw(Rec.CommandBinary, 18, Rec.CommandBinary.Length - 18);
1437 Rec.Timeout = this.scheduler.
Add(Now.AddSeconds(2),
this.ResendCommand<ResponseType>, Rec);
1438 this.Transmit(
true, Output.
ToArray(),
this.destination);
1441 catch (Exception ex)
1457 TaskCompletionSource<EndpointResponse<ResponseType>[]> Result =
1458 new TaskCompletionSource<EndpointResponse<ResponseType>[]>();
1462 Result.TrySetResult(e.Responses);
1463 return Task.CompletedTask;
1466 return await Result.Task;
1477 return this.ExecuteCommand(
new Echo()
1480 }, Callback, State);
1487 public async Task<EndpointResponse<string>[]>
EchoAsync(
string Text)
1489 TaskCompletionSource<EndpointResponse<string>[]> Result =
new TaskCompletionSource<EndpointResponse<string>[]>();
1491 await this.
Echo(Text, (Sender, e) =>
1493 Result.TrySetResult(e.Responses);
1494 return Task.CompletedTask;
1497 return await Result.Task;
1507 return this.ExecuteCommand(
new Assemblies(), Callback, State);
1515 TaskCompletionSource<EndpointResponse<string[]>[]> Result =
new TaskCompletionSource<EndpointResponse<string[]>[]>();
1519 Result.TrySetResult(e.Responses);
1520 return Task.CompletedTask;
1523 return await Result.Task;
1533 public Task
Lock(
string ResourceName,
int TimeoutMilliseconds,
1534 EventHandlerAsync<ClusterResourceLockEventArgs> Callback,
object State)
1537 LockInfoRec InfoRec;
1539 lock (this.lockedResources)
1541 if (!this.lockedResources.TryGetValue(ResourceName, out Info))
1543 Info =
new LockInfo()
1545 Resource = ResourceName,
1549 this.lockedResources[ResourceName] = Info;
1552 Info.Queue.AddLast(InfoRec =
new LockInfoRec()
1555 Timeout = DateTime.Now.AddMilliseconds(TimeoutMilliseconds),
1556 Callback = Callback,
1562 return this.LockResult(
false,
null, InfoRec);
1564 return this.
Lock(Info, InfoRec);
1567 private Task
Lock(LockInfo Info, LockInfoRec InfoRec)
1571 Resource = Info.Resource
1574 IPEndPoint LockedBy =
null;
1578 if (Response.
ACK.HasValue && !Response.
ACK.Value)
1587 lock (this.lockedResources)
1592 Ok = LockedBy is
null;
1597 Info.Queue.Remove(InfoRec);
1599 else if (InfoRec.Timeout <= DateTime.Now)
1601 Info.Queue.Remove(InfoRec);
1603 if (!Info.Locked && Info.Queue.First is
null)
1604 this.lockedResources.Remove(Info.Resource);
1608 return this.LockResult(Ok, LockedBy, InfoRec);
1613 private async Task LockResult(
bool Ok, IPEndPoint LockedBy, LockInfoRec InfoRec)
1616 await this.Raise(InfoRec.Info.Resource,
true,
null, InfoRec);
1617 else if (DateTime.Now >= InfoRec.Timeout)
1618 await this.Raise(InfoRec.Info.Resource,
false, LockedBy, InfoRec);
1619 else if (!InfoRec.TimeoutScheduled)
1621 InfoRec.LockedBy = LockedBy;
1622 InfoRec.Timeout = this.scheduler.
Add(InfoRec.Timeout,
this.LockTimeout, InfoRec);
1623 InfoRec.TimeoutScheduled =
true;
1627 private Task LockTimeout(
object P)
1629 LockInfoRec InfoRec = (LockInfoRec)P;
1630 LockInfo Info = InfoRec.Info;
1632 InfoRec.TimeoutScheduled =
false;
1634 lock (this.lockedResources)
1636 Info.Queue.Remove(InfoRec);
1638 if (!Info.Locked && Info.Queue.First is
null)
1639 this.lockedResources.Remove(Info.Resource);
1642 return this.Raise(Info.Resource,
false, InfoRec.LockedBy, InfoRec);
1645 private async Task Raise(
string ResourceName,
bool LockSuccessful, IPEndPoint LockedBy,
1646 LockInfoRec InfoRec)
1650 if (InfoRec.TimeoutScheduled)
1652 this.scheduler.
Remove(InfoRec.Timeout);
1653 InfoRec.TimeoutScheduled =
false;
1656 LockInfo Info = InfoRec.Info;
1658 lock (this.lockedResources)
1660 Info.Queue.Remove(InfoRec);
1662 if (!Info.Locked && Info.Queue.First is
null)
1663 this.lockedResources.Remove(Info.Resource);
1667 LockSuccessful, LockedBy, InfoRec.State));
1669 catch (Exception ex)
1680 public async Task<ClusterResourceLockEventArgs>
LockAsync(
string ResourceName,
int TimeoutMilliseconds)
1682 TaskCompletionSource<ClusterResourceLockEventArgs> Result =
new TaskCompletionSource<ClusterResourceLockEventArgs>();
1684 await this.
Lock(ResourceName, TimeoutMilliseconds, (Sender, e) =>
1686 Result.TrySetResult(e);
1687 return Task.CompletedTask;
1690 return await Result.Task;
1701 LockInfoRec InfoRec;
1703 lock (this.lockedResources)
1705 if (!this.lockedResources.TryGetValue(ResourceName, out Info) || !Info.Locked)
1706 throw new ArgumentException(
"Resource not locked.", nameof(ResourceName));
1708 Info.Locked =
false;
1710 if (Info.Queue.First is
null)
1712 this.lockedResources.Remove(ResourceName);
1716 InfoRec = Info.Queue.First.Value;
1721 Resource = ResourceName
1724 if (!(InfoRec is
null))
1726 this.scheduler.
Add(DateTime.Now.AddMilliseconds(
this.rnd.Next(50) + 1), (P) =>
1728 this.Lock(Info, InfoRec);
1733 internal void Released(
string ResourceName)
1736 LockInfoRec InfoRec;
1738 lock (this.lockedResources)
1740 if (!this.lockedResources.TryGetValue(ResourceName, out Info) || Info.Locked)
1743 if (Info.Queue.First is
null)
1745 this.lockedResources.Remove(ResourceName);
1749 InfoRec = Info.Queue.First.Value;
1752 this.scheduler.
Add(DateTime.Now.AddMilliseconds(
this.rnd.Next(50) + 1), (P) =>
1754 this.Lock(Info, InfoRec);
Static class managing the application event log. Applications and services log events on this static ...
static void Exception(Exception Exception, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, params KeyValuePair< string, object >[] Tags)
Logs an exception. Event type will be determined by the severity of the exception.
static void Error(string Message, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, string StackTrace, params KeyValuePair< string, object >[] Tags)
Logs an error event.
static Exception UnnestException(Exception Exception)
Unnests an exception, to extract the relevant inner exception.
Event arguments for cluster endpoint events.
Represents one endpoint (or participant) in the network cluster.
Task SendMessageAcknowledged(IClusterMessage Message, EventHandlerAsync< ClusterMessageAckEventArgs > Callback, object State)
Sends a message using acknowledged service. ("at least once")
async Task< EndpointAcknowledgement[]> SendMessageAcknowledgedAsync(IClusterMessage Message, IPEndPoint Destination)
Sends a message using acknowledged service.
Task Lock(string ResourceName, int TimeoutMilliseconds, EventHandlerAsync< ClusterResourceLockEventArgs > Callback, object State)
Locks a singleton resource in the cluster.
Task Echo(string Text, EventHandlerAsync< ClusterResponseEventArgs< string > > Callback, object State)
Asks endpoints in the cluster to echo a text string back to the sender.
async Task ExecuteCommand< ResponseType >(IClusterCommand Command, EventHandlerAsync< ClusterResponseEventArgs< ResponseType > > Callback, object State)
Execute a command on the other members of the cluster, and waits for responses to be returned....
async Task< EndpointAcknowledgement[]> SendMessageAssuredAsync(IClusterMessage Message, IPEndPoint Destination)
Sends a message using assured service. ("exactly once")
Task< EndpointAcknowledgement[]> SendMessageAssuredAsync(IClusterMessage Message)
Sends a message using assured service. ("exactly once")
async Task< ClusterResourceLockEventArgs > LockAsync(string ResourceName, int TimeoutMilliseconds)
Locks a singleton resource in the cluster.
ClusterEndpoint(IPAddress MulticastAddress, int Port, string SharedSecret, params ISniffer[] Sniffers)
Represents one endpoint (or participant) in the network cluster.
EventHandlerAsync< ClusterGetStatusEventArgs > GetStatus
Event raised when current status is needed.
Task< EndpointAcknowledgement[]> SendMessageAcknowledgedAsync(IClusterMessage Message)
Sends a message using acknowledged service.
async Task< bool > RemoveRemoteStatus(IPEndPoint Endpoint)
Explicitly removes a remote cluster endpoint status object.
async Task DisposeAsync()
IDisposable.Dispose
Task SendMessageUnacknowledged(IClusterMessage Message)
Sends an unacknowledged message ("at most once")
object LocalStatus
Local status. For remote endpoint statuses, see GetRemoteStatuses
async Task Release(string ResourceName)
Releases a resource.
EndpointStatus[] GetRemoteStatuses()
Gets current remote statuses. For the current local status, see LocalStatus
async Task SendMessageAcknowledged(IClusterMessage Message, IPEndPoint Destination, EventHandlerAsync< ClusterMessageAckEventArgs > Callback, object State)
Sends a message using acknowledged service. ("at least once")
IPEndPoint[] Endpoints
IP endpoints listening on.
EventHandlerAsync< ClusterEndpointEventArgs > EndpointOffline
Event raised when an endpoint goes offline.
object Deserialize(byte[] Data)
Deserializes an object.
async Task SendMessageUnacknowledged(IClusterMessage Message, IPEndPoint Destination)
Sends an unacknowledged message ("at most once")
async Task SendMessageAssured(IClusterMessage Message, IPEndPoint Destination, EventHandlerAsync< ClusterMessageAckEventArgs > Callback, object State)
Sends a message using assured service. ("exactly once")
EventHandlerAsync< ClusterEndpointStatusEventArgs > EndpointStatus
Event raised when status has been reported by an endpoint.
EventHandlerAsync< ClusterEndpointEventArgs > EndpointOnline
Event raised when a new endpoint is available in the cluster.
byte[] Serialize(object Object)
Serializes an object.
Task GetAssemblies(EventHandlerAsync< ClusterResponseEventArgs< string[]> > Callback, object State)
Asks endpoints in the cluster to return assemblies available in their runtime environment.
async void AddRemoteStatus(IPEndPoint Endpoint, object Status)
Explicitly adds a remote endpoint status object. In normal operation, calling this method is not nece...
async Task< EndpointResponse< string >[]> EchoAsync(string Text)
Asks endpoints in the cluster to echo a text string back to the sender.
EventHandlerAsync< ClusterMessageEventArgs > OnMessageReceived
Event raised when a cluster message has been received.
void Dispose()
IDisposable.Dispose
async Task< EndpointResponse< string[]>[]> GetAssembliesAsync()
Asks endpoints in the cluster to return assemblies available in their runtime environment.
Task SendMessageAssured(IClusterMessage Message, EventHandlerAsync< ClusterMessageAckEventArgs > Callback, object State)
Sends a message using assured service. ("exactly once")
ClusterEndpoint(IPAddress MulticastAddress, int Port, byte[] SharedSecret, params ISniffer[] Sniffers)
Represents one endpoint (or participant) in the network cluster.
Task Ping(EventHandlerAsync< ClusterMessageAckEventArgs > Callback, object State)
Sends an acknowledged ping message to the other servers in the cluster.
async Task< EndpointResponse< ResponseType >[]> ExecuteCommandAsync< ResponseType >(IClusterCommand Command)
Execute a command on the other members of the cluster, and waits for responses to be returned....
async Task< EndpointAcknowledgement[]> PingAsync()
Sends an acknowledged ping message to the other servers in the cluster.
Event arguments for cluster endpoint status events.
Event arguments for cluster get status events.
object Status
Current status of service.
Event arguments for cluster message acknowledgement events.
Event arguments for cluster message events.
Event arguments for cluster resource lock events.
Event arguments for cluster message acknowledgement events.
Command to request available assembly names from cluster endpoints.
Command echoing incoming text string.
Contains information about one of the endpoints in the cluster.
bool? ACK
Acknowledgement response:
IPEndPoint Endpoint
Remote endpoint
Contains information about one of the endpoints in the cluster.
Alive message regularly sent on the network to inform members of the cluster of the status of the cur...
Delivers a message, as part of an assured message transfer
Message used to inform other members that the sending endpoint is trying to lock a resource....
Ping message that can be sent to test endpoints in cluster receive messages.
Message used to inform other members that the sending endpoint is releasing a locked resource.
Message sent when an endpoint is shut down, to let the other endpoints know the current endpoint is n...
Transports a message, as part of an assured message transfer
Abstract base class for properties
abstract Type PropertyType
Property Type
void WriteRaw(byte[] Binary)
Writes raw binary data to the output.
byte[] ToArray()
Returns the binary output.
void Dispose()
IDisposable.Dispose
void WriteVarUInt64(ulong Value)
Writes a variable-length unsigned integer to the output.
void WriteBinary(byte[] Value)
Writes binary data to the output.
void WriteGuid(Guid Value)
Writes a Guid to the output.
void WriteString(string Value)
Writes a string to the output.
void WriteByte(byte Value)
Writes a byte to the output.
void WriteUInt16(ushort Value)
Writes a 16-bit unsigned integer to the output.
Simple base class for classes implementing communication protocols.
static bool IsMulticastAddress(IPAddress Address)
Checks if an IP Address is a multi-cast address or not.
void Exception(Exception Exception)
Called to inform the viewer of an exception state.
ISniffer[] Sniffers
Registered sniffers.
bool HasSniffers
If there are sniffers registered on the object.
void Error(string Error)
Called to inform the viewer of an error state.
void Warning(string Warning)
Called to inform the viewer of a warning state.
void TransmitBinary(int Count)
Called when binary data has been transmitted.
void ReceiveBinary(int Count)
Called when binary data has been received.
void Information(string Comment)
Called to inform the viewer of something.
Implements an in-memory cache.
bool ContainsKey(KeyType Key)
Checks if a key is available in the cache.
void Dispose()
IDisposable.Dispose
bool Remove(KeyType Key)
Removes an item from the cache.
bool TryGetValue(KeyType Key, out ValueType Value)
Tries to get a value from the cache.
Event arguments for cache item removal events.
ValueType Value
Value of item that was removed.
RemovedReason Reason
Reason for removing the item.
Static class that dynamically manages types and interfaces available in the runtime environment.
static Type GetType(string FullName)
Gets a type, given its full name.
static bool TryGetModuleParameter(string Name, out object Value)
Tries to get a module parameter value.
static object[] NoParameters
Contains an empty array of parameter values.
static Type[] GetTypesImplementingInterface(string InterfaceFullName)
Gets all types implementing a given interface.
static ConstructorInfo GetDefaultConstructor(Type Type)
Gets the default constructor of a type, if one exists.
Class that can be used to schedule events in time. It uses a timer to execute tasks at the appointed ...
bool Remove(DateTime When)
Removes an event scheduled for a given point in time.
void Dispose()
IDisposable.Dispose
DateTime Add(DateTime When, Action< object > Callback, object State)
Adds an event.
Contains methods for simple hash calculations.
static byte[] ComputeSHA256Hash(byte[] Data)
Computes the SHA-256 hash of a block of binary data.
Interface for asynchronously disposable objects.
Interface for cluster commands.
Interface for cluster messages.
Abstract base class for properties
Interface for sniffers. Sniffers can be added to ICommunicationLayer classes to eavesdrop on communic...
delegate Task EventHandlerAsync(object Sender, EventArgs e)
Asynchronous version of EventArgs.
RemovedReason
Reason for removing the item.