-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathStateMachine.Link.cs
More file actions
140 lines (126 loc) · 4.97 KB
/
StateMachine.Link.cs
File metadata and controls
140 lines (126 loc) · 4.97 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
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
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
namespace BAStudio.StatePattern
{
public partial class StateMachine<T>
{
List<StateMachine<T>> peers;
int peerDispatchingDepth;
/// <summary>
/// Peer machines linked to this machine. Symmetric: if A sees B, B sees A.
/// Null if no peers are linked.
/// </summary>
public IReadOnlyList<StateMachine<T>> Peers => peers?.AsReadOnly();
/// <summary>
/// Create a symmetric peer link. Static because the relationship is symmetric
/// -- neither machine is privileged.
/// </summary>
public static void Link(StateMachine<T> a, StateMachine<T> b)
{
if (a == null) throw new ArgumentNullException(nameof(a));
if (b == null) throw new ArgumentNullException(nameof(b));
if (a == b)
throw new InvalidOperationException("A machine cannot link to itself.");
if (a.peerDispatchingDepth > 0 || b.peerDispatchingDepth > 0)
throw new InvalidOperationException(
"Cannot Link/Unlink while peer event dispatch is in progress.");
a.peers ??= new List<StateMachine<T>>();
b.peers ??= new List<StateMachine<T>>();
if (a.peers.Contains(b))
throw new InvalidOperationException("Machines are already linked.");
a.peers.Add(b);
b.peers.Add(a);
}
/// <summary>
/// Remove a symmetric peer link. Does NOT exit either machine's state --
/// peers don't own each other's lifecycle.
/// </summary>
public static void Unlink(StateMachine<T> a, StateMachine<T> b)
{
if (a == null) throw new ArgumentNullException(nameof(a));
if (b == null) throw new ArgumentNullException(nameof(b));
if (a.peerDispatchingDepth > 0 || b.peerDispatchingDepth > 0)
throw new InvalidOperationException(
"Cannot Link/Unlink while peer event dispatch is in progress.");
bool removedFromA = a.peers != null && a.peers.Remove(b);
bool removedFromB = b.peers != null && b.peers.Remove(a);
if (!removedFromA || !removedFromB)
throw new InvalidOperationException("Machines are not linked.");
if (a.peers.Count == 0) a.peers = null;
if (b.peers.Count == 0) b.peers = null;
}
int peerEventForwardingDepth;
const int MaxPeerEventForwardingDepth = 8;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
void SendEventToPeers<E>(E ev)
{
if (peers == null) return;
if (peerEventForwardingDepth >= MaxPeerEventForwardingDepth)
{
LogFormat("Peer event forwarding depth limit reached ({0}).",
MaxPeerEventForwardingDepth);
return;
}
peerDispatchingDepth++;
peerEventForwardingDepth++;
try
{
var forwarded = new ForwardedEvent<E>(this, ev);
for (int i = 0; i < peers.Count; i++)
peers[i].ReceiveFromPeer(this, forwarded);
}
finally
{
peerEventForwardingDepth--;
peerDispatchingDepth--;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
void NotifyPeersOfStateChange(IState<T> from, IState<T> to)
{
if (peers == null) return;
if (peerEventForwardingDepth >= MaxPeerEventForwardingDepth)
{
LogFormat("Peer event forwarding depth limit reached ({0}).",
MaxPeerEventForwardingDepth);
return;
}
peerDispatchingDepth++;
peerEventForwardingDepth++;
try
{
var ev = new PeerStateChangedEvent(this, from, to);
for (int i = 0; i < peers.Count; i++)
peers[i].ReceiveFromPeer(this, ev);
}
finally
{
peerEventForwardingDepth--;
peerDispatchingDepth--;
}
}
/// <summary>
/// Receive from a peer. Delivers to own states only. NO re-broadcast.
/// </summary>
internal void ReceiveFromPeer<E>(StateMachine<T> source, E ev)
{
if (peerEventForwardingDepth >= MaxPeerEventForwardingDepth)
{
LogFormat("Peer event forwarding depth limit reached on receiver ({0}).",
MaxPeerEventForwardingDepth);
return;
}
peerEventForwardingDepth++;
try
{
SendEventToCurrentState(ev);
SendEventToPopupStates(ev);
}
finally
{
peerEventForwardingDepth--;
}
}
}
}