We are in the process of transitioning this specification from a GitHub README into something a bit more palatable. The official-lookin' version is developed in the official-lookin
branch's index.html
file, which you can see on GitHub, or in its rendered glory at this long URL.
Right now, we've transferred over most of the concepts and text, but none of the algorithms or APIs. We'll be iterating on the APIs a bit more here, in Markdown format, until we feel confident in them. In the meantime, please check out the rendered spec for all of the interesting stage-setting text.
By the way, this transition is being tracked as #62.
FixedBuffer
class is used by buferred channels if it's instantiated with an argument of type number. FixedBuffer
implements general Buffer
interface:
// Buffer interface is used for buffers that can be passed
// into channels, they allow configurable bufferring of
// the channel based on the data it will be handling.
interface Buffer {
// If this buffer is empty this should return `true`,
// otherwise it should return `false`.
boolean isEmpty();
// If this buffer is full this should return `true`
// otherwise it should return `false`.
boolean isFull();
// If this buffer is empty this should throw exception
// otherwise it should return data chunk from the buffer
// and adjust internal state as appropriate.
any take();
// If this `buffer` is full this should throw an exception
// otherwise given `data` should be put into a buffer.
put(any data);
}
- Set
this.[[size]]
tosize
. - Set
this.[[buffer]]
to[]
.
- Return
this.[[buffer]].length === 0
.
- Return
this.[[buffer]].length === this.[[size]]
.
- If
this.isFull()
, throw Error - Add
chunk
to a buffer by calling:this.[[buffer]].unshift(chunk)
.
- If
this.isEmpty()
, throw Error. - Return
this.[[buffer]].pop()
.
Port is a class that both InputPort
and OutputPort
derive from.
// Port interface is implemented by both input and output ports
// of the channel.
interface Port {
// Closes this channel. Note that queued and bufferred values
// still can be taken off the closed channel. If there are
// queued takes on the channel (this implies buffer and put
// queue is empty) then those takes are resolved with `undefined`.
void close();
}
- Set
this.[[channel]]
tochannel
. - Return
this
- Let
channel
bethis.[[channel]]
- Let
result
be result of calling[[close]]
method ofchannel
. - Return
result
.
Objects implementing Operation
interface are used to represent "take" and "put" operations on the channel. They can have a state that is either complete or pending. State can be checked via isPending
method. If operation is complete it's result can be accessed via valueOf
method. If operation is pending calling valueOf
throws an error. Operation derives from Promise
and it's then
method can be used to access it's result.
interface Operation : Promise {
// If operation is pending returns `true` otherwise
// returns `false`.
boolean isPending();
// If operation is pending throws an error, otherwise
// returns result of the operation.
any valueOf();
}
- Let
choice
bethis.[[select]].[[choice]]
. - Let
result
be `true. - If
this
ischoice
, Setresult
tofalse
. - Return
result
.
- If
this.isPending()
istrue
throw an Error. - Return
this.[[result]]
.
- Let
result
betrue
. - If
this.[[select]].[[choice]]
is notvoid 0
, Setresult
tofalse
. - Return
result
.
- If
this.[[isActive]]()
isfalse
throw an Error. - Set
this.[[select]].[[choice]]
tothis
. - Resolve
this
promise with aresult
as avalue
. - Set
this.[[result]]
toresult
.
An output port represents end of the channel into which data can be put to make it available to the input port of the same channel.
[NamedConstructor=OutputPort(Channel channel)]
interface OutputPort : Port {
// If this channel is closed operation is ignored and
// promise resolved with `undefined` is returned
// (Promise.resolve(void 0)).
// If `value` put on a channel is `undefined` it is ignored
// same as it is in JSON.
// Channels can be bufferred or unbuffered. Putting values
// onto buffered channel returns promises resolved with
// `true` (`Promise.resolve(true)`) until buffer is full.
// If this channel is unbuffered or buffer is full puts values
// are queued. In such case returned value a promise that will
// be resolved with `true` once put value will make it into
// a buffer or be taken of the channel.
Operation <boolean> put(any value);
}
- Call the
[[Call]]
internal method ofPort
, withthis
asthisArgument
andchannel
as an argument.
- Return
Port.prototype
.
- Let
channel
bethis.[[channel]]
- Return
channel.[[put]](this, data, void 0)
.
An input port represents end of the channel from which data can be taken, that was put there from the output port of the same channel.
[NamedConstructor=InputPort(Channel channel)]
interface InputPort : Port {
// If this channel has no queued "puts" and has being
// already closed returns promise resolved with `undefined`
// think of it as reading undefined property.
//
// If channel has no queued or bufferred "puts" then
// "take" is enqueued and promise is returned that
// will be resolved with value whenever it's "put"
// on a channel.
//
// If this channel has bufferred data it's taken off
// the buffer and promise resolved with it is returned.
// If channel has queued "put" data it will be moved
// over to buffer.
//
// If channel is unbuffered and it has queued "put" data
// promise resolved with "put" data is returned, also
// causing pending put promise to resolve.
Operation <any> take();
}
- Call the
[[Call]]
internal method ofPort
, withthis
asthisArgument
andchannel
as an argument.
- Return
Port.prototype
.
- Let
channel
bethis.[[channel]]
- Return
channel.[[take]](this, void 0)
.
[NamedConstructor=Channel(float n),
NamedConstructor=Channel(Buffer buffer)]
interface Channel {
attribute InputPort input;
attribute OutputPort output;
}
The constructor can be passed optional argument that implements
Buffer
interface. If such argument is passed than resulting
channel is bufferred and given buffer
is used for bufferring
data. If argument is a number, then buffered channel is created
with fixed size buffer of given size. Bufferred channels allow
seperation of data handling (delegating that to buffer) from data
transfer. Bufferred channels won't block (return pre-resolved
promises) when putting data on them until buffer is full, which
gives more freedom to a producer and consumer to have they're own
work schedules.
Data still can be put on the channel even if buffer is full or if channel is unbuferred it's just in such case put operation is going to be enqueued until it can be completed.
- If
buffer
is instance ofBuffer
, Letbuffer
bebuffer
. - If
buffer
is type of number, Letbuffer
benew FixedBuffer(buffer)
. - If
bufffer
isundefined
Letbuffer
beundefined
. - Set
this.[[buffer]]
tobuffer
. - Set
this.[[puts]]
to[]
. - Set
this.[[takes]]
to[]
. - Set
input.[[closed]]
tofalse
. - Set
this.[[in]]
tonew InputPort(this)
. - Set
this.[[out]]
tonew OutputPort(this)
.
- Return
this.[[in]]
.
- Return
this.[[out]]
.
- If
this.[[closed]]
isfalse
, - Set
this.[[closed]]
totrue
. - While
this.[[takes]].length > 0
:- Let
take
bethis.[[takes]].pop()
, - If
take.[[isActive]]()
istrue
, Calltake.[[complete]](void 0)
.
- Let
- Return
void 0
.
- If
port
isn't instance ofOutputPort
throwTypeError
. - Let
puts
bethis.[[puts]]
. - Let
takes
bethis.[[takes]]
. - Let
buffer
bethis.[[buffer]]
. - Let
put
be a newly-created pending operation. - If
select
isvoid 0
,- Set
put.[[select]]
toput
.
- Set
- If
select
is instance ofSelect
,- Set
put.[[select]]
toselect
.
- Set
- If
put.[[isActive]]()
istrue
,- If
this.[[closed]]
istrue
, callput.[[complete]](void 0)
. - If
this.[[closed]]
isfalse
and- If
data
isvoid 0
,- call
put.[[complete]](true)
.
- call
- If
data
is notvoid 0
,- If
buffer
isvoid 0
,- Let
take
betakes.pop()
. - While
take
is object &take.[[isActive]]()
isfalse
,- Set
take
totake.pop()
.
- Set
- If
take
is object &take.[[isActive]]()
istrue
,- Call
put.[[complete]](true)
. - Call
take.[[complete]](data)
.
- Call
- If
take
isvoid 0
,- Set
put.[[value]]
todata
. - Call
puts.unshift(put)
.
- Set
- Let
- If
buffer
is instance ofBuffer
,- If
buffer.isFull()
istrue
,- Set
put.[[value]]
todata
. - Call
puts.unshift(put)
.
- Set
- If
buffer.isFull()
is `false,- Call
buffer.put(data)
. - Call
put.[[complete]](true)
. - If
buffer.isEmpty()
isfalse
,- Let
take
betakes.pop()
. - While
take
is object &&take.[[isActive]]()
isfalse
- Set
take
totake.pop()
.
- Set
- If
take
is object &take.[[isActive]]()
istrue
,- Call
take.[[complete]](buffer.take())
.
- Call
- Let
- Call
- If
- If
- If
- If
- Return
put
.
- If
port
isn't instance ofInputPort
throwTypeError
. - Let
puts
bethis.[[puts]]
. - Let
takes
bethis.[[takes]]
. - Let
buffer
bethis.[[buffer]]
. - Let
take
be a newly-created pending operation. - If
select
isvoid 0
,- Set
put.[[select]]
toput
.
- Set
- If
select
is instance ofSelect
,- Set
put.[[select]]
toselect
.
- Set
- If
take.[[isActive]]()
istrue
,- If
buffer
is notvoid 0
,- Let
isEmpty
bebuffer.isEmpty()
. - If
isEmpty
isfalse
- Let
data
bebuffer.take()
. - Call
take.[[complete]](data)
. - If
buffer.isFull()
isfalse
,- Let
put
beputs.pop()
. - While
buffer.isFull()
isfalse
andput
is object- If
put.[[isActive]]()
istrue
,- Call
put.[[complete]](true)
. - Call
buffer.put(put.[[value]])
.
- Call
- set
put
toputs.pop()
.
- If
- Let
- Let
- If
isEmpty
istrue
,- If
this.[[closed]]
istrue
,- Call
take.[[complete]](void 0)
.
- Call
- If
this.[[closed]]
is `false,- Call
takes.unshift(take)
.
- Call
- If
- Let
- If
buffer
isvoid 0
,- Let
put
beputs.pop()
. - While
put
is object andput.[[isActive()]]
isfalse
,- Set
put
toputs.pop()
.
- Set
- If
put
is object,- Call
put.[[complete]](true)
. - Call
take.[[complete]](put.[[value]])
.
- Call
- If
put
isvoid 0
,- If
this.[[closed]]
istrue
,- Call
take.[[complete]](void 0)
- Call
- If
this.[[closed]]
isfalse
,- Call
takes.unshift(take)
.
- Call
- If
- Let
- If
- Return
take
Select allows to make a single choice between several channel operations (put
/ take
). Choice is made in favor of operation that completes first. If more than one operation is ready to be complete at the same time choice is made in favor of the operation which was requested first.
[NamedConstructor=Select()]
interface Select {
Operation <any> take(InputPort input);
Operation <boolean> put(OutputPort output, any value);
}
- Let
this.[[choice]]
beundefined
. - Return
this
.
- Let
channel
beport.[[channel]]
- Return
channel.[[put]](port, data, this)
.
- Let
channel
beport.[[channel]]
- Return
channel.[[take]](port, this)
.