Skip to content

Instantly share code, notes, and snippets.

@rightfold
Created July 3, 2015 20:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rightfold/9fd652c09fa07a5058a5 to your computer and use it in GitHub Desktop.
Save rightfold/9fd652c09fa07a5058a5 to your computer and use it in GitHub Desktop.
function Channel(capacity) {
this._capacity = capacity || 0;
this._elements = [];
this._senders = [];
this._receivers = [];
}
Channel.prototype.send = function(value, callback) {
if (this._elements.length === this._capacity) {
if (this._receivers.length === 0) {
this._senders.push({value: value, callback: callback});
} else {
var receiver = this._receivers.shift();
receiver(value);
callback();
}
} else {
if (this._receivers.length === 0) {
this._elements.push(value);
callback();
} else {
var receiver = this._receivers.shift();
receiver(value);
callback();
}
}
};
Channel.prototype.receive = function(callback) {
if (this._elements.length === 0) {
if (this._senders.length === 0) {
this._receivers.push(callback);
} else {
var sender = this._senders.shift();
sender.callback();
callback(sender.value);
}
} else {
var value = this._elements.shift();
callback(value);
}
};
var ChannelOperation = {
SEND: 0,
RECEIVE: 1,
};
function select(cases, defaultCase) {
// Step 1: shuffle cases to prevent starvation.
for (var i = cases.length - 1; i >= 0; --i) {
var j = Math.floor(Math.random() * (i + 1));
var x = cases[j];
cases[j] = cases[i];
cases[i] = x;
}
// Step 2: check whether any cases are ready already.
for (var i = 0; i < cases.length; ++i) {
var channel = cases[i].channel;
switch (cases[i].operation) {
case ChannelOperation.SEND:
if (channel._elements.length < channel._capacity) {
channel._elements.push(cases[i].value);
cases[i].callback();
return;
} else if (channel._receivers.length !== 0) {
var receiver = channel._receivers.shift();
receiver(cases[i].value);
cases[i].callback();
return;
}
break;
case ChannelOperation.RECEIVE:
if (channel._elements.length > 0) {
var value = channel._elements.shift();
cases[i].callback(value);
return;
} else if (channel._senders.length !== 0) {
var sender = channel._senders.shift();
sender.callback();
cases[i].callback(sender.value);
return;
}
break;
}
}
// Step 3: invoke default case if it is given.
if (defaultCase) {
defaultCase();
return;
}
// Step 4: wait until a case becomes available.
function unregisterHandlers() {
for (var i = 0; i < cases.length; ++i) {
var channel = cases[i].channel;
switch (cases[i].operation) {
case ChannelOperation.SEND:
var idx = channel._senders.indexOf(cases[i].sender);
channel._senders.splice(idx, 1);
break;
case ChannelOperation.RECEIVE:
var idx = channel._receivers.indexOf(cases[i].receiver);
channel._receivers.splice(idx, 1);
break;
}
}
}
for (var i = 0; i < cases.length; ++i) {
switch (cases[i].operation) {
case ChannelOperation.SEND:
var sender = {
value: cases[i].value,
callback: function callback(i) {
unregisterHandlers();
cases[i].callback();
}.bind(null, i),
};
cases[i].sender = sender;
cases[i].channel._senders.push(sender);
break;
case ChannelOperation.RECEIVE:
var receiver = function(i, value) {
unregisterHandlers();
cases[i].callback(value);
}.bind(null, i);
cases[i].receiver = receiver;
cases[i].channel._receivers.push(receiver);
break;
}
}
}
@nabijaczleweli
Copy link

Why not const the ChannelOperation?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment