-
Notifications
You must be signed in to change notification settings - Fork 139
/
AtomicActionRenderer.cs
178 lines (161 loc) · 6.32 KB
/
AtomicActionRenderer.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
using System;
using System.Collections.Generic;
using Libplanet.Action;
using Libplanet.Blocks;
using Libplanet.Tx;
namespace Libplanet.Blockchain.Renderers
{
/// <summary>
/// A middleware to make action render events to satisfy transactions' atomicity.
/// <para>Decorates an <see cref="IActionRenderer{T}"/> instance and filters out render events
/// made by unsuccessful transactions (i.e., transactions with one or more exception-throwing
/// actions).</para>
/// </summary>
/// <remarks>The wrapped <see cref="ActionRenderer"/> will not receive any
/// <see cref="IActionRenderer{T}.RenderActionError"/> and
/// <see cref="IActionRenderer{T}.UnrenderActionError"/> events except for block actions,
/// which do not belong to any transactions.
/// </remarks>
/// <typeparam name="T">An <see cref="IAction"/> type. It should match to
/// <see cref="BlockChain{T}"/>'s type parameter.</typeparam>
public sealed class AtomicActionRenderer<T> : IActionRenderer<T>
where T : IAction, new()
{
private readonly List<(IAction, IActionContext, IAccountStateDelta)> _eventBuffer;
private TxId? _lastTxId;
private bool _errored;
/// <summary>
/// Creates a new <see cref="AtomicActionRenderer{T}"/> instance decorating the given
/// <paramref name="actionRenderer"/>.
/// </summary>
/// <param name="actionRenderer">The inner action renderer which has the <em>actual</em>
/// implementations and expects to receive no <see cref="RenderActionError"/>/<see
/// cref="UnrenderActionError"/> events.</param>
public AtomicActionRenderer(IActionRenderer<T> actionRenderer)
{
ActionRenderer = actionRenderer;
_lastTxId = null;
_eventBuffer = new List<(IAction, IActionContext, IAccountStateDelta)>();
_errored = false;
}
/// <summary>
/// The inner action renderer which has the <em>actual</em> implementations and expects to
/// receive no <see cref="RenderActionError"/>/<see cref="UnrenderActionError"/> events.
/// </summary>
public IActionRenderer<T> ActionRenderer { get; }
/// <inheritdoc cref="IRenderer{T}.RenderBlock(Block{T}, Block{T})"/>
public void RenderBlock(Block<T> oldTip, Block<T> newTip)
{
FlushBuffer(null, ActionRenderer.UnrenderAction);
ActionRenderer.RenderBlock(oldTip, newTip);
}
/// <inheritdoc cref="IActionRenderer{T}.RenderBlockEnd(Block{T}, Block{T})"/>
public void RenderBlockEnd(Block<T> oldTip, Block<T> newTip)
{
FlushBuffer(null, ActionRenderer.RenderAction);
ActionRenderer.RenderBlockEnd(oldTip, newTip);
}
/// <inheritdoc cref="IRenderer{T}.RenderReorg(Block{T}, Block{T}, Block{T})"/>
public void RenderReorg(Block<T> oldTip, Block<T> newTip, Block<T> branchpoint)
{
ActionRenderer.RenderReorg(oldTip, newTip, branchpoint);
}
/// <inheritdoc cref="IRenderer{T}.RenderReorgEnd(Block{T}, Block{T}, Block{T})"/>
public void RenderReorgEnd(Block<T> oldTip, Block<T> newTip, Block<T> branchpoint)
{
ActionRenderer.RenderReorgEnd(oldTip, newTip, branchpoint);
}
/// <inheritdoc
/// cref="IActionRenderer{T}.RenderAction(IAction, IActionContext, IAccountStateDelta)"/>
public void RenderAction(
IAction action,
IActionContext context,
IAccountStateDelta nextStates
)
{
if (!context.TxId.Equals(_lastTxId))
{
FlushBuffer(context.TxId, ActionRenderer.RenderAction);
}
if (context.TxId is null)
{
ActionRenderer.RenderAction(action, context, nextStates);
}
else if (!_errored)
{
_eventBuffer.Add((action, context, nextStates));
}
}
/// <inheritdoc
/// cref="IActionRenderer{T}.UnrenderAction(IAction, IActionContext, IAccountStateDelta)"/>
public void UnrenderAction(
IAction action,
IActionContext context,
IAccountStateDelta nextStates
)
{
if (!context.TxId.Equals(_lastTxId))
{
FlushBuffer(context.TxId, ActionRenderer.UnrenderAction);
}
if (context.TxId is null)
{
ActionRenderer.UnrenderAction(action, context, nextStates);
}
else if (!_errored)
{
_eventBuffer.Add((action, context, nextStates));
}
}
/// <inheritdoc
/// cref="IActionRenderer{T}.RenderActionError(IAction, IActionContext, Exception)"/>
public void RenderActionError(IAction action, IActionContext context, Exception exception)
{
if (!context.TxId.Equals(_lastTxId))
{
FlushBuffer(context.TxId, ActionRenderer.RenderAction);
}
if (context.TxId is null)
{
ActionRenderer.RenderActionError(action, context, exception);
}
else
{
_errored = true;
}
}
/// <inheritdoc
/// cref="IActionRenderer{T}.UnrenderActionError(IAction, IActionContext, Exception)"/>
public void UnrenderActionError(IAction action, IActionContext context, Exception exception)
{
if (!context.TxId.Equals(_lastTxId))
{
FlushBuffer(context.TxId, ActionRenderer.UnrenderAction);
}
if (context.TxId is null)
{
ActionRenderer.RenderActionError(action, context, exception);
}
else
{
_errored = true;
}
}
private void FlushBuffer(
TxId? newTxId,
Action<IAction, IActionContext, IAccountStateDelta> render
)
{
if (!_errored)
{
foreach (var (act, ctx, delta) in _eventBuffer)
{
render(act, ctx, delta);
}
}
_lastTxId = newTxId;
_errored = false;
_eventBuffer.Clear();
}
}
}