-
Notifications
You must be signed in to change notification settings - Fork 1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Reduce FSM<TState, TData>
allocations
#6145
Changes from all commits
b5d7d98
b06a0d7
3227a6a
1b72186
9aad12b
cb2b110
33cb3cb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -736,7 +736,7 @@ namespace Akka.Actor | |
public override int GetHashCode() { } | ||
public override string ToString() { } | ||
} | ||
public sealed class Event<TD> : Akka.Actor.INoSerializationVerificationNeeded | ||
public struct Event<TD> : Akka.Actor.INoSerializationVerificationNeeded | ||
{ | ||
public Event(object fsmEvent, TD stateData) { } | ||
public object FsmEvent { get; } | ||
|
@@ -773,10 +773,10 @@ namespace Akka.Actor | |
{ | ||
public static Akka.Actor.FSMBase.StateTimeout Instance { get; } | ||
} | ||
public class State<TS, TD> : System.IEquatable<Akka.Actor.FSMBase.State<TS, TD>> | ||
public sealed class State<TS, TD> : System.IEquatable<Akka.Actor.FSMBase.State<TS, TD>> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
{ | ||
public State(TS stateName, TD stateData, System.Nullable<System.TimeSpan> timeout = null, Akka.Actor.FSMBase.Reason stopReason = null, System.Collections.Generic.IReadOnlyList<object> replies = null, bool notifies = True) { } | ||
public System.Collections.Generic.IReadOnlyList<object> Replies { get; set; } | ||
public System.Collections.Generic.IReadOnlyList<object> Replies { get; } | ||
public TD StateData { get; } | ||
public TS StateName { get; } | ||
public Akka.Actor.FSMBase.Reason StopReason { get; } | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -63,7 +63,7 @@ public override bool Equals(object obj) | |
{ | ||
if (ReferenceEquals(null, obj)) return false; | ||
if (ReferenceEquals(this, obj)) return true; | ||
return obj is CurrentState<TS> && Equals((CurrentState<TS>)obj); | ||
return obj is CurrentState<TS> state && Equals(state); | ||
} | ||
|
||
|
||
|
@@ -129,7 +129,7 @@ public override bool Equals(object obj) | |
{ | ||
if (ReferenceEquals(null, obj)) return false; | ||
if (ReferenceEquals(this, obj)) return true; | ||
return obj is Transition<TS> && Equals((Transition<TS>)obj); | ||
return obj is Transition<TS> transition && Equals(transition); | ||
} | ||
|
||
|
||
|
@@ -206,7 +206,7 @@ public abstract class Reason { } | |
/// </summary> | ||
public sealed class Normal : Reason | ||
{ | ||
internal Normal() { } | ||
private Normal() { } | ||
|
||
/// <summary> | ||
/// Singleton instance of Normal | ||
|
@@ -220,7 +220,7 @@ internal Normal() { } | |
/// </summary> | ||
public sealed class Shutdown : Reason | ||
{ | ||
internal Shutdown() { } | ||
private Shutdown() { } | ||
|
||
/// <summary> | ||
/// Singleton instance of Shutdown | ||
|
@@ -258,7 +258,7 @@ public Failure(object cause) | |
/// </summary> | ||
public sealed class StateTimeout | ||
{ | ||
internal StateTimeout() { } | ||
private StateTimeout() { } | ||
|
||
/// <summary> | ||
/// Singleton instance of StateTimeout | ||
|
@@ -422,7 +422,7 @@ public override string ToString() | |
/// </summary> | ||
/// <typeparam name="TS">The name of the state</typeparam> | ||
/// <typeparam name="TD">The data of the state</typeparam> | ||
public class State<TS, TD> : IEquatable<State<TS, TD>> | ||
public sealed class State<TS, TD> : IEquatable<State<TS, TD>> | ||
{ | ||
/// <summary> | ||
/// Initializes a new instance of the State | ||
|
@@ -435,7 +435,7 @@ public class State<TS, TD> : IEquatable<State<TS, TD>> | |
/// <param name="notifies">TBD</param> | ||
public State(TS stateName, TD stateData, TimeSpan? timeout = null, Reason stopReason = null, IReadOnlyList<object> replies = null, bool notifies = true) | ||
{ | ||
Replies = replies ?? new List<object>(); | ||
Replies = replies ?? Array.Empty<object>(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Eliminated unnecessary |
||
StopReason = stopReason; | ||
Timeout = timeout; | ||
StateData = stateData; | ||
|
@@ -444,32 +444,33 @@ public State(TS stateName, TD stateData, TimeSpan? timeout = null, Reason stopRe | |
} | ||
|
||
/// <summary> | ||
/// TBD | ||
/// The name of this state | ||
/// </summary> | ||
public TS StateName { get; } | ||
|
||
/// <summary> | ||
/// TBD | ||
/// The data belonging to this sate | ||
/// </summary> | ||
public TD StateData { get; } | ||
|
||
/// <summary> | ||
/// TBD | ||
/// Optional. The state timeout. | ||
/// </summary> | ||
public TimeSpan? Timeout { get; } | ||
|
||
/// <summary> | ||
/// TBD | ||
/// Optional - the reason why we're stopping. | ||
/// </summary> | ||
public Reason StopReason { get; } | ||
|
||
/// <summary> | ||
/// TBD | ||
/// Optional - the set of replies to send to subscribers. | ||
/// </summary> | ||
public IReadOnlyList<object> Replies { get; protected set; } | ||
public IReadOnlyList<object> Replies { get; } | ||
|
||
/// <summary> | ||
/// TBD | ||
/// INTERNAL API. Indicates whether or not we're sending transition notifications | ||
/// for this state change. | ||
/// </summary> | ||
internal bool Notifies { get; } | ||
|
||
|
@@ -482,9 +483,24 @@ public State(TS stateName, TD stateData, TimeSpan? timeout = null, Reason stopRe | |
/// <returns>TBD</returns> | ||
internal State<TS, TD> Copy(TimeSpan? timeout, Reason stopReason = null, IReadOnlyList<object> replies = null) | ||
{ | ||
// otherwise, return a new copy. | ||
return new State<TS, TD>(StateName, StateData, timeout, stopReason ?? StopReason, replies ?? Replies, Notifies); | ||
} | ||
|
||
/// <summary> | ||
/// Remove all of the unwanted bits we don't need during a Stay() operation. | ||
/// </summary> | ||
/// <remarks> | ||
/// This is a performance optimization designed to | ||
/// </remarks> | ||
/// <returns>Returns a "clean" copy of a state object the first time it's called. Idempotent after.</returns> | ||
internal State<TS, TD> SpecialCopyForStay() | ||
{ | ||
if(Replies.Count > 0 || StopReason != null || Timeout != null || Notifies) | ||
return new State<TS, TD>(StateName, StateData, notifies:false); | ||
return this; | ||
} | ||
|
||
/// <summary> | ||
/// Modify the state transition descriptor to include a state timeout for the | ||
/// next state. This timeout overrides any default timeout set for the next state. | ||
|
@@ -539,15 +555,16 @@ internal State<TS, TD> WithStopReason(Reason reason) | |
/// </summary> | ||
internal State<TS, TD> WithNotification(bool notifies) | ||
{ | ||
// don't bother allocating even a stack type if the notifies value is identical. | ||
if (Notifies == notifies) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don't return a new copy of this object if the current |
||
return this; | ||
return new State<TS, TD>(StateName, StateData, Timeout, StopReason, Replies, notifies); | ||
} | ||
|
||
|
||
public override string ToString() | ||
{ | ||
return $"{StateName}, {StateData}"; | ||
} | ||
|
||
|
||
public bool Equals(State<TS, TD> other) | ||
{ | ||
|
@@ -585,26 +602,26 @@ public override int GetHashCode() | |
/// which allows pattern matching to extract both state and data. | ||
/// </summary> | ||
/// <typeparam name="TD">The state data for this event</typeparam> | ||
public sealed class Event<TD> : INoSerializationVerificationNeeded | ||
public readonly struct Event<TD> : INoSerializationVerificationNeeded | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We allocate one of these for every message we process, so changing to a |
||
{ | ||
/// <summary> | ||
/// Initializes a new instance of the Event | ||
/// </summary> | ||
/// <param name="fsmEvent">TBD</param> | ||
/// <param name="stateData">TBD</param> | ||
/// <param name="fsmEvent">The message received by the FSM.</param> | ||
/// <param name="stateData">The current state data of the FSM.</param> | ||
public Event(object fsmEvent, TD stateData) | ||
{ | ||
StateData = stateData; | ||
FsmEvent = fsmEvent; | ||
} | ||
|
||
/// <summary> | ||
/// TBD | ||
/// The message received by the FSM. | ||
/// </summary> | ||
public object FsmEvent { get; } | ||
|
||
/// <summary> | ||
/// TBD | ||
/// The current state data of the FSM. | ||
/// </summary> | ||
public TD StateData { get; } | ||
|
||
|
@@ -750,7 +767,8 @@ public State<TState, TData> GoTo(TState nextStateName, TData stateData) | |
/// <returns>Descriptor for staying in the current state.</returns> | ||
public State<TState, TData> Stay() | ||
{ | ||
return GoTo(_currentState.StateName).WithNotification(false); | ||
// don't want to allocate a new state if we don't have to | ||
return _currentState.SpecialCopyForStay(); | ||
} | ||
|
||
/// <summary> | ||
|
@@ -940,7 +958,7 @@ public TState StateName | |
{ | ||
get | ||
{ | ||
if (_currentState != null) | ||
if (_currentState != null) | ||
return _currentState.StateName; | ||
throw new IllegalStateException("You must call StartWith before calling StateName."); | ||
} | ||
|
@@ -1175,7 +1193,7 @@ private void ProcessEvent(Event<TData> fsmEvent, object source) | |
var stateFunc = _stateFunctions[_currentState.StateName]; | ||
var oldState = _currentState; | ||
|
||
State<TState, TData> nextState = null; | ||
State<TState, TData> nextState = default; | ||
|
||
if (stateFunc != null) | ||
{ | ||
|
@@ -1195,7 +1213,7 @@ private void ProcessEvent(Event<TData> fsmEvent, object source) | |
} | ||
} | ||
|
||
private string GetSourceString(object source) | ||
private static string GetSourceString(object source) | ||
{ | ||
if (source is string s) | ||
return s; | ||
|
@@ -1249,7 +1267,7 @@ private void MakeTransition(State<TState, TData> nextState) | |
_nextState = nextState; | ||
HandleTransition(_currentState.StateName, nextState.StateName); | ||
Listeners.Gossip(new Transition<TState>(Self, _currentState.StateName, nextState.StateName)); | ||
_nextState = null; | ||
_nextState = default(State<TState, TData>); | ||
} | ||
_currentState = nextState; | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Technically a binary breaking change maybe, but it's source compatible.