Is there any known implementation of Haskell MVar
in C? There is an example on how to implement it in C++. But, I will like to implement it in C - let us say only MVar CInt
equivalent in C for now. Writing synchronization primitives can be tricky. So, I will like to avoid duplication of effort if someone has already done it. I didn't understand the C++ example above well enough to confidently translate it into C - it hides the algorithmic details very well from my C++-inexperienced mind :)
The reason I am thinking about writing MVar in C is because it makes it really easy for me to use the FFI binding to an external C library to get the stream of data, and use Haskell threads to grab the data (from Storable vectors to avoid marshaling the data - MVar CInt here stores how much of the Storable vectors have been filled). I need to make sure that C threads writing to Storable locations are blocked while a Haskell thread is reading the data. That is where MVar synchronization on C side helps. It is also much faster to call unsafe or even safe C function from Haskell (~15ns for unsafe, ~150ns for safe in my test), than callback into Haskell from C (~5us). If callbacks were fast, I would have had C function call back into Haskell instead, and block on Haskell MVar.
Update:
Algorithm in pseudo-code will do as well. It should be quite easy to implement it in C, given the algorithm for newEmptyMVar, takeMVar and putMVar.
MVar can be implemented in C using a struct like below:
typedef struct{
pthread_cond_t put_cond;
pthread_cond_t take_cond;
pthread_mutex_t lock;
void* value;
} mvar;
put_cond
is used by threads that put values in MVar to signal other threads that are waiting to take value from MVar. take_cond
is the analogous counterpart for take. As for scheduling, it is default scheduling.
value
is a void pointer - so, the above structure can be used to protect any type of value in an MVar - of course, C will let you write that pointer outside MVar - so, it is the program's responsibility to ensure this doesn't happen (by avoiding squirreling away value
pointer outside MVar - always access it through MVar functions).
Initializing MVar
:
mvar* newMVar(void* arg){
//create new mvar struct
mvar* var=(mvar*) malloc(sizeof(mvar));
pthread_mutex_init(&var->lock,NULL);
pthread_cond_init(&var->take_cond,NULL);
pthread_cond_init(&var->put_cond,NULL);
var->value = arg;
return (mvar*) var;
}
Empty MVar
- uses above function:
mvar* newEmptyMVar(){
return newMVar(NULL);
}
putMVar
:
void putMVar(mvar* var,void* value){
pthread_mutex_lock(&var->lock);
while(var->value != NULL)
pthread_cond_wait(&var->put_cond,&var->lock);//if MVar is full, wait until another thread takes the value - release the mutex, and wait on put_cond to become true
var->value = value;//if here, we got the signal from another thread that took MVar - MVar is empty now. OK to fill
pthread_cond_signal(&var->take_cond);//signal other threads that value is available for taking now
pthread_mutex_unlock(&var->lock);
}
takeMVar
:
void* takeMVar(mvar* var){
void* value;
pthread_mutex_lock(&var->lock);
while(var->value == NULL)
pthread_cond_wait(&var->take_cond,&var->lock);//if MVar is empty, wait until another thread fills it - release the mutex, and wait on take_cond to become true
//take the value
value = var->value;
var->value = NULL; //push NULL value to indicate MVar is empty now
pthread_cond_signal(&var->put_cond);//signal other threads that value is available for filling now
pthread_mutex_unlock(&var->lock);
return value; //return the value that was taken from MVar
}
Complete code is on github, with example which shows how to use MVar.
MVar is quite fast if there is only one thread accessing it (and heavy contention). But, under heavy contention, and multiple threads (even two), it scales very poorly. This is not a surprise because of the way pthreads work. I have found MVar in Haskell to be very good with multiple threads. This is not a surprise given how well lightweight threads and concurrency primitives are implemented in GHC.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With