Last active
July 12, 2020 22:00
-
-
Save andrewiggins/33685dc6569747e6156af33503e77e26 to your computer and use it in GitHub Desktop.
Generated by XState Viz: https://xstate.js.org/viz
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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