I want to define a sequential transition for my spring state machine. My state transitions are A -> B -> C. My business case is as below
I have single event E for transition to B from A and there is no event for B to C. I just want to define that if at all I am in B state I should perform the necessary actions and then move to state C without any event.
Below is the code snippet that I am using
transitions.withExternal().source(A).target(B).event(E)
transitions.withExternal().source(B).target(C)
The above transitions are taking state machine to C from A but the below onStateChanged method is first detecting change from B to C and then A to B.
Are the above transitions are correct or do we need other way to define those transitions
#OnStateChanged
public void anyStateChange() {}
I went through Spring Statemachine documentation but did not find clear answers for some scenarios. I will greatly appreciate if some one can clarify my questions.
Scenario1: How to retry errors related to action failures? Lets say I have the following states S1, S2 and S3 and when we transition from S1 to S2 I want to perform action A2. If action A2 fails I want to retry it with some time intervals. Is that possible using Spring StateMachine?
Consider AWS state machine Step Functions for example. All work in the step functions States are done using Task. And Task can be configured for retry.
transitions
.withExternal()
.source(States.S1)
.target(States.S2)
.event(Events.E1)
.action(action());
Scenario 2: Lets say Statemachine has states S1, S2 and S3. The current state is S2. If the server goes down on startup will the Statemachine execution pick up from where it left off or we will have to do it all over again?
Scenario 3: When a Guard returns false (possibly because of error condition) and prevents a transition what happens next?
How to retry a failed action?
There are two types of actions in Spring State Machine - transition actions and state actions. In scenario 1 you're talking about transition action.
When you specify a transition action, you can also specify an error handler if the action fails. This is clearly documented in the spring state machine documentation.
.withExternal()
.source(States.S1)
.target(States.S2)
.event(Events.E1)
.action(action(), errorAction());
In your errorAction() method you can implement your logic.
Possible options are:
transition to an earlier state and go the same path
transition to a specific state (e.g. retry state) where you can have your retry logic (e.g. Task/Executor that retries the action N times, and transition to other states (e.g. action success => go normal flow; action failed after N retries => transition to a failure terminal state)
There's also the official Tasks example, that demonstrates recovery/retry logic (source code).
I apologize for the cumbersome wording of the title question, but can't think of any other way to ask it concisely. I'm aware that this is quite different from, yet analogous to, the pass-by-value vs reference dichotomy. I am wondering which of the following snippets of code would behave identically:
Declarations:
Port (source : in STD_LOGIC);
...
signal destination : STD_LOGIC := '0';
#1 - Sets the "value" on clock
process (clk) begin
if falling_edge(clk) then
if source = '1' then
destination <= '1';
else
destination <= '0';
end if;
#2 - Sets a "reference" universally
-- Top-Level
destination <= source;
#3 - Sets the (value? reference?) on clock
process (clk) begin
if falling_edge(clk) then
destination <= source;
Snippet #1 will change the value of the destination to match the source every falling edge of the clock cycle. In #2, the destination will become 'connected' to the source, and changes in the source will be followed by the destination regardless of the clock cycle. I am unsure about #3; does it behave like #1 or #2, taking the value of the source and putting it in the destination, or linking the two together as in the top-level assignment?
#1
This process has a sensitivity list. When there is an event (a change in value) on any signal in a sensitivity list, the process starts executing. This process has one signal in the sensitivity list - clk - so when there is a change on that signal, the process starts executing. If that change were a rising edge, then the condition in the if statement evaluates to FALSE and so no more lines of code are executed and the process suspends (goes to sleep). If that change were a falling edge, however, then the condition in the if statement evaluates to TRUE and so, depending on the value of source, one of two lines of code is executed that assigns a value to destination. Here's the really important bit:
When a line of code containing a signal assignment (<=) is executed in VHDL (and the effect is to change the value of the target signal - the signal on the LHS of the assignment), an event is placed on the what I shall call the event queue.
The event queue is the simulator's ToDo list and events placed on it will be actioned at some future time. If there is no explicit delay specified then that event with be actioned on the next simulation cycle (or delta cycle). Bear with...
So, assuming that the effect of executing the lines containing signal assignments is to change the value of destination, then the effect of executing those lines is to place an event on the event queue. The process then suspends.
Once all the processes have suspended, the simulator takes a look at its event queue and moves the simulation time forward to the time of the next event on the queue. In this case, that event will have been scheduled for the next simulation cycle, so the simulator time advances one simulation cycle and the events for that cycle are actioned. If any sensitivity list contains a signal that has been caused to change by actioning one of those events, then the whole cycle starts again: processes get executed, lines of code containing signal assignments get executed, new events get place on the event queue for some future time...
#3
From the point of view of this discussion, case #3 is exactly the same as case #1. A falling edge on clk will cause a line of code containing a signal assignment to be executed and, if a change of value on the target signal (destination) is required then an event will get placed on the event queue to action that change on the next simulation cycle.
#2
Case #2 clearly does not depend on clk, so is different. However, #2 is an example of a concurrent signal assignment. This is effectively and implicit process: you get an implicit sensitivity list, which contains any signal on the RHS of the signal assignment. In this case, the implicit sensitivity list contains one signal - source. So, if there is an event on source then the (implicit) process starts executing resulting in a line of code containing a signal assignment being executed and hence resulting in an event being placed on the event queue (which will be scheduled for the next simulation cycle).
So, to answer your question: "do VHDL signal assignments set destination value or reference?"
The value that is placed on the event queue (ie the value to which the target signal is to be driven) is the value that was evaluated at the time the line of code containing the signal assignment was executed.
So, in case #3, the value to assign to destination which is placed on the event queue is the value that source signal had at the time the line of code containing the signal assignment was executed. If you want to cal that 'pass by copy' then do so, but I wouldn't take that analogy very far.
Note: The LRM - the VHDL standard - uses the terminology Projected Output Waveform for what I call the event queue.
(
SynthDef(\testEvt,{
arg out, gate = 1;
var sint = Blip.ar(440) * Linen.kr(gate,doneAction:2,releaseTime:0.8);
Out.ar(out, Pan2.ar(sint, 0));
}).add();
Synth(\testEvt)
(instrument: \testEvt, freq:220, sustain: inf).play;
(instrument: \testEvt,freq:220).play;
)
Executing the first and the second line after the SynthDef would create a synth which playes forever, whereas the third line's synth plays for 0.8 seconds as per default value the generated event.
The problem is that I don't use 'sustain' anywhere in my SynthDef and it uses automatically just because there is a Linen.
The same this doesn't happen for freq: both the events play at 440 and not at 220, and that's just because the SynthDef doesn't use 'freq' as an argument. So why sustain doesn't follow the same rule ?
Also, is there a way to reference synths created by an event ? So that, when they have sustain: inf as argument, I can free them on a later time.
(instrument: \testEvt, freq:220, sustain: inf).play;
and
(instrument: \testEvt,freq:220).play;
are events. Events handle a lot of things for you. One thing they do is calculate when to set a gate to 0. (Remember that gate is one of the arguments in your SynthDef.) In the first example, because you sustain for infinite duration, the gate never goes to zero. In the second example, it uses the default duration and sets the gate to zero after that duration has passed. You can find out what key words are used in event environment variables by looking at the Event.sc source file. If you search for sustain, you'll find out the other keywords it uses for timing. One of these is dur. Try this:
(instrument: \testEvt, dur:3).play
Freq is also a keyword for events, but since you have no freq argument, it can't effect your synthDef. If you want to set the freq, you'll need to make a change:
SynthDef(\testEvt,{
arg out, gate = 1, freq = 440;
var sint = Blip.ar(freq) * Linen.kr(gate,doneAction:2,releaseTime:0.8);
Out.ar(out, Pan2.ar(sint, 0));
}).add();
For contrast between events and controlling a synth directly, try:
a = Synth.new(\testEvt, [\out, 0, \gate, 1])
You can add in any other arguments you want, like freq or sustain, but they have no effect because those aren't arguments to your synthdef. And, unlike Event, synth doesn't do any calculations on your behalf. When you want the note to end, set the gate to 0 yourself:
a.set(\gate, 0)
It's good to be aware of event environment variables because they're also used by Pbinds and by using them, you can effect other things. If you had a synthdef that used sustain as an argument for something else, you could be surprised by it changing your durations.
Regarding your last sub-question,
Also, is there a way to reference synths created by an event ? So that, when they have sustain: inf as argument, I can free them on a later time.
Yes, by "indexing" the Event by \id key. This actually returns an array of node ids because an Event with \strum can fire up more than one node/synth. Also, the \id value is nil while the event is not playing. But this indexing method is fairly unnecessary for what you want, because...
You can end the (associated) synth by ending the Event early with release, just like for the Synth itself. What this does is basically gate-out its internal synth. (In your example, this release call transitions to the release point of the ASR envelope generated by Linen, by lowering gate to 0.). And, of course, use a variable to save the "reference" to the synth and/or event, if don't plan to release it right away in a program (which would produce no sound with a gated envelope).
Basically
fork { var x = Synth(\testEvt); 2.wait; x.release }
does the same as
fork { var e = (instrument: \testEvt, sustain: inf).play; 2.wait; e.release }
except there's one layer of indirection in the latter case for the release. The first example is also equivalent to
fork { var x = Synth(\testEvt); 2.wait; x.set(\gate, 0); }
which does the work of release explicitly. Event also supports set and it passes the value to the corresponding Synth control (if the latter was properly added on the server.)
Now the complicated method you asked about (retrieving node ids for the event and sending them messages) is possible too... although hardly necessary:
fork { var e = (instrument: \testEvt, sustain: inf).play; 2.wait;
e[\id].do({ arg n; s.sendMsg("/n_set", n, "gate", 0); }) }
By the way, you can't use wait outside of a Routine, that's why fork was needed in the above examples. Interactively, in the editor, you can "wait manually", of course, before calling release on either the Synth or the Event.
As a somewhat subtle point of how envelope gating works, it doesn't actually start playing (technically begin transitioning to the endpoint of the first [attack] envelope segment) until you set gate to 1. I.e. you can delay the (envelope) start as in:
fork { x = Synth(\testEvt, [\gate, 0]); 3.wait; x.set(\gate, 1); 2.wait; x.release }
Beware that the default Event.play doesn't generate this 0 to 1 gate transition though, i.e. you can't rely on it to fire your synth's envelope if you set the initial gate value to zero in your SynthDef.
Also, I'm assuming that by "free" you mean "stop playing" rather than "free their memory on the server". There's no need to manually free those (event) synths in the latter sense since they have doneAction:2 in the envelope, which does that for you once they are released and the final segment of the envelope finishes playing. If you somehow want to kill the synth right away (like Ctrl+. does) instead of triggering its fade-out you can replace the message sent in the inner function of the "complicated" example (above) with s.sendMsg("/n_free", n). Or much more simply
fork { var e = (instrument: \testEvt, sustain: inf).play; 2.wait; e.free }
Also, if you wonder about \strum, an example is:
e = (instrument: \testEvt, sustain: inf, strum: 1, out: #[0, 0]).play
Now e[\id] is an array of two nodes. Event is a bit cheeky in that it will only create multiple nodes for arrays passed to actual Synth controls rather than random fields, so "strumming" \freq (or its precursors like \degree etc.) only creates multiple nodes if your SynthDesc has a freq control.
Alas the "complicated" method is almost useless when it comes to playing Pbinds (patterns). This is because Pbind.play returns and EventStreamPlayer... that alas makes a private copy of the prototype event being played and plays that private copy, which is inaccessible to the caller context (unless you hack EventStreamPlayer.prNext). Confusingly EventStreamPlayer has an accessible event variable, but that's only the "prototype", not the private copy event being played... So if p is an instance of an EventStreamPlayer then p.event[\id] is always nil (or whatever you set it to beforehand) even while playing. Since one seldom plays Events individually and much more often patterns...
Simply as a hacking exercise tough, it turns out there is an even more convoluted way to access the ids of nodes that EventStreamPlayer fires... This relies on overriding the default Event play which thankfully can be extended outside class inheritance because the default is conveniently saved in a class dictionary...
(p = Pbind(\instrument, \testEvt, \sustain, Pseq([1, 2]), \play, {
arg tempo, srv;
var rv;
"playhack".postln;
rv = Event.parentEvents[\default][\play].value(tempo, srv);
~id.postln;
rv;
}).play)
In general however, patterns are clearly not designed to be used this way, i.e. by hacking "a layer below" to get to the node ids. As "proof", while the above works well enough with Pbind (which uses the default Event type \note) it doesn't work reliably with Pmono which doesn't set the Event \id on its first note (Event type \monoNote) but only on subsequent notes (which generate a different Event type, \monoSet). Pmono keeps an internal copy of the node id, but this is completely inaccessible on the first mono note; it only copies it to the Events on the subsequent notes for some reason (bug perhaps, but could be "by design"). Also, if you use Pdef which extends Event with type \phrase... the above hack doesn't work all, i.e. \id is never set by type \phrase; perhaps you can get to the underlying sub-events generated somehow... I haven't bothered to investigate further.
The SC documentation (in the pattern guide) even says at one point
Remember that streams made from patterns don't expose their internals. That means you can't adjust the parameters of an effect synth directly, because you have no way to find out what its node ID is.
That's not entirely correct given the above hack, but it is true in some contexts.
The API method InitializeCriticalSectionAndSpinCount allows you to set a spin count so when EnterCriticalSection is called, it loops using a spinlock to try to acquire the resource some number of times. Only if all the attempts fail does the thread transition to kernel mode to enter a wait state.
If the 'normal' InitializeCriticalSection() is called instead, is there a 'default' spin count set? (Or is it 0, no spin?)
Quoting from this article:
SpinCount ... This field defaults to zero, but can be set to a different value with the InitializeCriticalSectionAndSpinCount API
So the default is no spin.