Skip to content

Instantly share code, notes, and snippets.

@andrewiggins
Last active July 12, 2020 22:00
Show Gist options
  • Save andrewiggins/33685dc6569747e6156af33503e77e26 to your computer and use it in GitHub Desktop.
Save andrewiggins/33685dc6569747e6156af33503e77e26 to your computer and use it in GitHub Desktop.
Generated by XState Viz: https://xstate.js.org/viz
var minHoldTime = 2500; // milliseconds
var checkDelay = 500 // milliseconds, defaults to 500ms or minHoldTime/2 if minHoldTime < 500
var minWaitTime = 1; // seconds
var maxWaitTime = 3; // seconds
var waitTimeout = 10; // seconds
// FYI, if using full XState, could use the 'invoke' property to call functions
// that return Promises that resolve or fail, and change state based on that.
// Since this machine generally has two transitions out of the interesting
// states, we could model the lock checking as success/failure to signal which
// transition to take.
//
// ~~However, if we want to model timeout in this machine, then the interesting
// states have more than 2 exits - success, failure, and timeout?.~~
//
// Actually, maybe we could model multiple states. Look into if the
// onDone/onError can inspect the event and then return which state to
// transition to?
const getWaitTime = () => Math.floor((Math.random() * maxWaitTime) + minWaitTime) * 1000;
Machine({
id: 'Lock',
strict: true,
initial: 'acquiring',
context: {
wait_time: 0,
total_wait_time: 0,
total_held_time: 0
},
states: {
// Read lock and either keep waiting or write & hold
acquiring: {
entry: 'attemptAcquire',
on: {
'': {
target: 'timed_out',
cond: 'timeoutExceeded'
},
HOLD: 'holding',
WAIT: 'waiting'
}
},
// Wait random time before attempting to acquire again
waiting: {
entry: [
'resetTotalHeldTime',
'setWaitTime',
],
exit: 'updateTotalWaitTime',
after: {
WAIT_DELAY: 'acquiring'
}
},
// Wait deterministic time before reading lock
holding: {
entry: 'resetTotalWaitTime',
exit: 'updateTotalHeldTime',
after: {
HOLD_DELAY: 'checking',
}
},
// read lock to see if we still have it
// and either keep holding,
// assume lock is acquired,
// or go back to waiting if it isn't
checking: {
entry: 'checkHold',
on: {
// Can't auto transition here.
// We need to do one final checkHold first,
// and then transition if we still have it.
ACQUIRED: {
target: 'acquired',
cond: 'minHoldTime'
},
HOLD: 'holding',
WAIT: 'waiting',
}
},
// done!
acquired: {
type: 'final'
},
timed_out: {
type: 'final'
}
}
}, {
guards: {
minHoldTime: (ctx, evt) => ctx.total_held_time >= minHoldTime,
timeoutExceeded: (ctx, evt) => ctx.total_wait_time > waitTimeout * 1000,
},
delays: {
WAIT_DELAY: (ctx, evt) => ctx.wait_time,
HOLD_DELAY: (ctx, evt) => checkDelay,
},
actions: {
resetTotalHeldTime: assign({
total_held_time: 0
}),
updateTotalHeldTime: assign({
total_held_time: (ctx, evt) => {
return ctx.total_held_time + checkDelay;
},
}),
setWaitTime: assign({
wait_time: getWaitTime,
}),
resetTotalWaitTime: assign({
total_wait_time: 0
}),
updateTotalWaitTime: assign({
wait_time: 0,
total_wait_time: (ctx, evt) => {
return ctx.total_wait_time + ctx.wait_time;
},
}),
attemptAcquire() {
console.log('attemptAcquire');
// Read lock
// if held by someone else, send WAIT
// if held by us, send HOLD
// if held by no one, write to lock and send HOLD
},
checkHold() {
console.log('checkHold');
// read lock
// if ours, send HOLD
// if not ours, send WAIT
}
}
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment