Thread-safe message queue without mutex in C
With a size of 2, your 'queue' can hold only one item, so it is not really a queue.  If
you increase the size to 8 (say) you now really have a queue, but your tests don't
work.

You seem to define send and recv as follows:


send indicates the 'slot' holding the most recently entered data (queue not empty).
recv indicates the 'slot' before the oldest data in the queue (queue not empty).
if send == recv  the queue is empty.  


So with both send and recv at zero, when the first data is written it goes to slot 1 and send is incremented to 1; when the 2nd data is written it goes to slot 2 and send is incremented to 2.  If you now read from the queue, in recv you use the send counter to extract the data instead of using recv + 1.  Hence the queue fails.

A more logical arrangement might be as follows with the counters renamed in
and out (naming them the same as the access functions is confusing):


in indicates the next 'slot' to be written by send (queue not empty).
out indicates the 'slot' containing the next data to be read by recv (queue not empty).
if in == out the queue is empty.  


So with both send and recv at zero, when the first data is sent, it goes into slot 0 (to which in and out already point) and in is incremented to 1.  The first read now reads from out at 0 - which is correct.



On thread safety, it is very difficult to see/analyse such things.  You would be
better to assume that it isn't (it certainly wouldn't be if there were more
than 2 threads) and use a mutex.  As your wait loops need to be rewritten
(they just waste CPU cycles needlessly) and you are using Posix threads, use a
Posix mutex and a "condition variable" to synchronise.

Here's an example, but there are many more on the web: http://www.ibm.com/developerworks/library/l-posix3/



Some minor coding points:


Add const to pointer parameters that do not change.
Return a value from create
Don't cast the return from malloc.  Casting is necessary in C++ not C
(where void* can be assigned to any pointer).  In C, adding a cast can cause
problems if you don't have the header for malloc included, as the compiler
will assume that malloc returns an int.  If int and pointer have
different sizes this is a problem.
null is not really needed.  You can use either 0 or NULL.
the _t type suffix is apparently reserved by Posix.