Skip to content

Instantly share code, notes, and snippets.

@Porges
Last active May 11, 2017 06:00
Show Gist options
  • Select an option

  • Save Porges/fcd638790e6bc6365d3656e09d3d7672 to your computer and use it in GitHub Desktop.

Select an option

Save Porges/fcd638790e6bc6365d3656e09d3d7672 to your computer and use it in GitHub Desktop.
Go channel implementation
void Main()
{
Channels();
BufferedChannels();
ChannelDirections();
}
// Example from: https://gobyexample.com/channels
void Channels()
{
var c = new Chan<string>();
Task.Run(() => c.Put("ping"));
var msg = c.Get();
Console.WriteLine(msg);
}
// Example from: https://gobyexample.com/channel-buffering
void BufferedChannels()
{
var c = new BufferedChan<string>(2);
c.Put("buffered");
c.Put("channel");
Console.WriteLine(c.Get());
Console.WriteLine(c.Get());
}
// Example from: https://gobyexample.com/channel-directions
void ChannelDirections()
{
void Ping(IWriteChan<string> pings, string message)
=> pings.Put(message);
void Pong(IReadChan<string> pings, IWriteChan<string> pongs)
=> pongs.Put(pings.Get());
var thePings = new BufferedChan<string>(1);
var thePongs = new BufferedChan<string>(1);
Ping(thePings, "passed message");
Pong(thePings, thePongs);
Console.WriteLine(thePongs.Get());
}
/// A Chan is a bit like an MVar,
/// except that the writer is *also* blocked
/// until a reader is present.
class Chan<T> : IReadChan<T>, IWriteChan<T>
{
enum State { Empty, ReaderWaiting, ValueWritten };
object _lock = new object();
State _state = State.Empty;
T _value;
public void Put(T value)
{
lock (_lock)
{
// wait for reader to appear
while (_state != State.ReaderWaiting)
{
Monitor.Wait(_lock);
}
// write value
_value = value;
_state = State.ValueWritten;
Monitor.PulseAll(_lock);
}
}
public T Get()
{
lock (_lock)
{
// wait for empty
while (_state != State.Empty)
{
Monitor.Wait(_lock);
}
// ask writer to write
_state = State.ReaderWaiting;
Monitor.PulseAll(_lock);
// wait for written
do
{
Monitor.Wait(_lock);
} while (_state != State.ValueWritten);
// read value, reset state
var ret = _value;
_value = default(T);
_state = State.Empty;
Monitor.PulseAll(_lock);
return ret;
}
}
}
sealed class BufferedChan<T> : IDisposable, IReadChan<T>, IWriteChan<T>
{
BlockingCollection<T> _queue;
public BufferedChan(int bufferSize)
{
_queue = new BlockingCollection<T>(bufferSize);
}
public void Dispose() => _queue.Dispose();
public void Put(T value) => _queue.Add(value);
public T Get() => _queue.Take();
}
interface IReadChan<out T>
{
T Get();
}
interface IWriteChan<in T>
{
void Put(T value);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment