iced_internals = require('./iced')
exports.iced = iced = iced_internals.runtime
icedlib
This class contains non-essential but convenient runtime libraries for iced programs
The timeout
connector, which allows us to compose timeouts with
existing event-based calls
_timeout = (cb, t, res, tmp) -> rv = new iced.Rendezvous tmp[0] = rv.id(true).defer(arr...) setTimeout rv.id(false).defer(), t await rv.wait defer which res[0] = which if res cb.apply(null, arr) exports.timeout = (cb, t, res) -> tmp = [] _timeout cb, t, res, tmp tmp[0]
The 'and' connector, that allows you to check only once that
all operations with a parallel await
worked...
_iand = (cb, res, tmp) -> await tmp[0] = defer ok res[0] = false unless ok cb()
this function takes as input two values: a callback, and a place to store a result. It returns a new callback.
exports.iand = (cb, res) -> tmp = [] _iand cb, res, tmp tmp[0]
The 'or' connector, that allows you to check only once that
one of several operations in a parallel await
worked
_ior = (cb, res, tmp) -> await tmp[0] = defer ok res[0] = true if ok cb() exports.ior = (cb, res) -> tmp = [] _ior cb, res, tmp tmp[0]
Pipeliner -- a class for firing a follow of network calls in a pipelined fashion, so that only so many of them are outstanding at once.
exports.Pipeliner = class Pipeliner constructor : (window, delay) -> @window = window || 1 @delay = delay || 0 @queue = [] @n_out = 0 @cb = null
This is a hack to work with the desugaring of 'defer' output by the coffee compiler. Same as in rendezvous
@[iced_internals.const.deferrals] = this
Rebind "defer" to "_defer"; We can't do this directly since the compiler would pick it up
@["defer"] = @_defer
Call this to wait in a queue until there is room in the window
waitInQueue : (cb) ->
Wait until there is room in the window.
while @n_out >= @window await (@cb = defer())
Lanuch a computation, so mark that there's one more guy outstanding.
@n_out++
Delay if that was asked for...
if @delay await setTimeout defer(), @delay cb()
Helper for this._defer, seen below..
__defer : (out, deferArgs) ->
Make a callback that this.defer can return. This callback might have to fill in slots when its fulfilled, so that's why we need to wrap the output of defer() in an anonymous wrapper.
await voidCb = defer() out[0] = (args...) -> deferArgs.assign_fn?.apply null, args voidCb()
There is now one fewer outstanding computation.
@n_out--
If some is waiting in waitInQueue above, then now is the time to release him. Use "race-free" callback technique.
if @cb tmp = @cb @cb = null tmp()
This function, Pipeliner._defer, has to return a callback to its caller. It does this with the same trick above. The helper function _defer() does the heavy lifting, returning its callback to us as the first slot in tmp[0].
_defer : (deferArgs) -> tmp = [] @__defer tmp, deferArgs tmp[0]
flush everything left in the pipe
flush : (autocb) -> while @n_out await (@cb = defer())