Good stuff, thanks for sharing. Random comments based on a quick glance through the code if I may.<p><pre><code> chan_t* chan_init(int capacity);
</code></pre>
should be "size_t capacity", since negative capacity values are invalid. This also eliminated the need for the first check in the function.<p><pre><code> chan->buffered
</code></pre>
is duplicate in meaning to chan->queue being non-NULL.<p>Why chan's mutexes and pcond are malloc'ed and not included in chan's struct? They are also not freed when channel is disposed, unless I'm missing something. Mutexes are also not destroyed when buffered chan is disposed, but they <i>are</i> created for it.<p>(Edit)<p>Also, my biggest nitpick would be that chan_t structure should really not be in chan.h header since it's meant to be transparent to the app and the API operates exclusively with pointers to chan_t. Then, if it is made private (read - internal to the implementation), then you can divorce buffered and unbuffered channels into two separate structs, allocate required one and then multiplex between them in calls that treat buffered and unbuffered channels differently. I.e.<p><pre><code> struct chan_t
{
// Shared properties
pthread_mutex_t m_mu;
pthread_cond_t m_cond;
int closed;
// Type
int buffered;
};
struct chan_unbuffered_t
{
struct chan_t base;
pthread_mutex_t r_mu;
pthread_mutex_t w_mu;
int readers;
blocking_pipe_t* pipe;
};
...
</code></pre>
or better yet, just include a handful of function pointers into chan_t for each of the API methods that need more than the shared part of chan_t struct, initialize them on allocation and put type-specific code in these functions. It will make for a more compact, concise and localized code.<p>(Edit 2)<p>Here's what I mean with function pointers - <a href="https://gist.github.com/anonymous/5f97d8db71776b188820" rel="nofollow">https://gist.github.com/anonymous/5f97d8db71776b188820</a> - basically poor man's inheritance and virtual methods :)
Libtask[1] by Russ Cox (one of the creators of Go) may provide some inspiration. It implements channels but uses coroutines instead of threading. As a consequence it has some cool stuff like networking and it does all of the hard parts of saving/restoring registers for task switching.<p>[1]: <a href="http://swtch.com/libtask/" rel="nofollow">http://swtch.com/libtask/</a>
Quick question: isn't channel's raison d'etre the fact that they need not be backed by system threads, that they are considerably cheaper and one can fire tens of thousands of them without worrying too much about performance. In other words aren't they primarily intended as a tool for solving a concurrency problem rather than a parallelism problem.<p>Although, as far as I recall Go does map a bundle of them to a thread and can thus handle parallelism as well.<p>Just to clarify, this is not a complaint, I am trying to get a better idea of these abstractions.<p>@tylertreat<p>> I believe you're confusing goroutines with channels. Goroutines are lightweight threads of execution. Channels are the pipes that connect goroutines.<p>That is indeed correct. I was thinking more in the line of fibres that meld the concepts of a coroutine and a channel into a single abstraction. So the idea is Chan connects threads rather than coroutines.
One of our students[0] at Monkey Project Organization[1] through Google Summer of Code[2], wrote a complete co-routine implementation for the Duda I/O[3] Web Services stack.<p>The feature is called "dthread" or "duda-thread" which aims to expose Channels and generic co-routines functionalities, you can check the following relevant links:<p>a. The co-routine implementation:<p><a href="https://github.com/monkey/duda/blob/master/src/duda_dthread.c" rel="nofollow">https://github.com/monkey/duda/blob/master/src/duda_dthread....</a><p>it does not need mutex or anything similar, short code but a lot of thinking behind it.<p>b. Example: web service to calculate Fibonacci using channels:<p><a href="https://github.com/monkey/duda-examples/blob/master/080_dthread_fibonacci/main.c" rel="nofollow">https://github.com/monkey/duda-examples/blob/master/080_dthr...</a><p>the whole work will be available on our next stable release, if someone want to give it a try, just drop us a line.<p>[0] <a href="http://blog-swpd.rhcloud.com" rel="nofollow">http://blog-swpd.rhcloud.com</a><p>[1] <a href="http://monkey-project.com" rel="nofollow">http://monkey-project.com</a><p>[2] <a href="http://www.google-melange.com" rel="nofollow">http://www.google-melange.com</a><p>[3] <a href="http://duda.io" rel="nofollow">http://duda.io</a>
The circle is now complete...
Plan 9 from user space implementation of channels.
<a href="http://swtch.com/usr/local/plan9/include/thread.h" rel="nofollow">http://swtch.com/usr/local/plan9/include/thread.h</a>
Here is some interesting reading:
<a href="http://golang.org/src/pkg/runtime/chan.goc" rel="nofollow">http://golang.org/src/pkg/runtime/chan.goc</a> Go's implementation
<a href="https://docs.google.com/document/d/1yIAYmbvL3JxOKOjuCyon7JhW4cSv1wy5hC0ApeGMV9s/pub" rel="nofollow">https://docs.google.com/document/d/1yIAYmbvL3JxOKOjuCyon7JhW...</a> Dmitry's "Go channels on steroids" post
I think you need to add<p><pre><code> pthread_cond_destroy(&chan->m_cond);
</code></pre>
After <a href="https://github.com/tylertreat/chan/blob/master/src/chan.c#L136" rel="nofollow">https://github.com/tylertreat/chan/blob/master/src/chan.c#L1...</a>
> #include <pthread.h><p>That is not "pure C". "pthread.h" (POSIX threads) is not part of the C language standard, it is a system-specific header commonly found on UNIX systems. Windows has a different threading system for example.
Pretty nifty. I like the names of the functions chan_can_recv() and chan_can_send(). Made me think think of the old cooking show "Yan Can Cook."