packages feed

interprocess-0.2.0.0: src/Control/Concurrent/Process/StoredMVar.c

#include "SharedObjectName.h"
#include <stdlib.h>
#include <string.h>
#ifndef NDEBUG
// a bit of printf to track important events
#include <stdio.h>
#endif

typedef struct MVar MVar;

MVar *mvar_new(size_t byteSize);
MVar *mvar_lookup(const char *name);
void  mvar_destroy(MVar *mvar);

int mvar_take   (MVar *mvar, void *localDataPtr);
int mvar_trytake(MVar *mvar, void *localDataPtr);
int mvar_put    (MVar *mvar, void *localDataPtr);
int mvar_tryput (MVar *mvar, void *localDataPtr);
int mvar_read   (MVar *mvar, void *localDataPtr);
int mvar_tryread(MVar *mvar, void *localDataPtr);
int mvar_swap   (MVar *mvar, void *inPtr, void *outPtr);
int mvar_tryswap(MVar *mvar, void *inPtr, void *outPtr);
int mvar_isempty(MVar *mvar);


#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(mingw32_HOST_OS)
#include <windows.h>
#include <assert.h>
#ifndef WAIT_OBJECT_1
#define WAIT_OBJECT_1       ((WAIT_OBJECT_0 ) + 1 )
#endif

typedef struct MVarState {
  size_t dataSize;
  int    pendingReaders;
} MVarState;


typedef struct MVar {
  /* Semaphore: readers wait on this.
    mvar_put releases 2n-1 units of semaphore, and every reader takes two units,
    the last reader fails to take a second unit and sets canTakeE.
   */
  HANDLE canReadS;
  /* Auto-reset event: takers wait on this
   */
  HANDLE canTakeE;
  /* Auto-reset event: putters wait on this
   */
  HANDLE canPutE;
  /* Mutex: protect the number of pending readers
   */
  HANDLE protectReaders;
  /* FileMapping: keep the data by this handle
   */
  HANDLE dataStoreH;
  /* Address of the data store
   */
  MVarState *storePtr;
  /* Actual data is stored next to the MVarState
   */
  void      *dataPtr;
  /* Base name of the shared memory region, used to share mvar across processes.
     Secondary objects are contstructed by appending a single char to the name.
   */
  SharedObjectName  mvarName;
} MVar;


MVar *mvar_new(size_t byteSize) {
  // allocate MVar
  MVar *mvar = malloc(sizeof(MVar));
  if (mvar == NULL) {
    return NULL;
  }
  genSharedObjectName(mvar->mvarName);

  mvar->dataStoreH = CreateFileMappingA
    ( INVALID_HANDLE_VALUE       // use paging file
    , NULL                       // default security
    , PAGE_READWRITE             // read/write access
    , (DWORD)(byteSize >> 32)        // maximum object size (high-order DWORD)
    , (DWORD)((byteSize + 64) & 0xFFFFFFFF) // maximum object size (low-order DWORD)
    , mvar->mvarName);             // name of mapping object

  if (mvar->dataStoreH == NULL) {
#ifdef NDEBUG
    printf("Could not create file mapping object (%d).\n", GetLastError());
#endif
    free(mvar);
    return NULL;
  }
  mvar->storePtr = (MVarState*)MapViewOfFile
    ( mvar->dataStoreH    // handle to map object
    , FILE_MAP_ALL_ACCESS // read/write permission
    , 0
    , 0
    , byteSize + 64);

  if (mvar->storePtr == NULL) {
#ifdef NDEBUG
	  printf("Could not map view of file (%d).\n", GetLastError());
#endif
    CloseHandle(mvar->dataStoreH);
    free(mvar);
    return NULL;
  }
  mvar->dataPtr = ((void*)(mvar->storePtr)) + 64;

  // now, create all synchronization objects
  // TODO: NULL value checking?
  SharedObjectName objName = {0};
  strcpy (objName, mvar->mvarName);
  strcat (objName, "T");
  mvar->canTakeE = CreateEventA( NULL, FALSE, FALSE, objName);
  strcpy (objName, mvar->mvarName);
  strcat (objName, "P");
  mvar->canPutE = CreateEventA( NULL, FALSE, TRUE, objName);
  strcpy (objName, mvar->mvarName);
  strcat (objName, "R");
  mvar->canReadS = CreateSemaphoreA( NULL, 0, LONG_MAX, objName);
  strcpy (objName, mvar->mvarName);
  strcat (objName, "M");
  mvar->protectReaders = CreateMutexA( NULL, FALSE, objName);

  // zero readers pending
  *(mvar->storePtr) = (struct MVarState)
    { .dataSize = byteSize
    , .pendingReaders = 0
    };

  return mvar;
}

MVar *mvar_lookup(const char *name) {
  // allocate MVar
  MVar *mvar = malloc(sizeof(MVar));
  if (mvar == NULL) {
    return NULL;
  }
  strcpy(mvar->mvarName, name);

  mvar->dataStoreH = OpenFileMappingA( FILE_MAP_ALL_ACCESS, FALSE, mvar->mvarName);

  if (mvar->dataStoreH == NULL) {
#ifdef NDEBUG
    printf("Could not open file mapping object (%d).\n", GetLastError());
#endif
    free(mvar);
    return NULL;
  }
  mvar->storePtr = (MVarState*)MapViewOfFile
    ( mvar->dataStoreH    // handle to map object
    , FILE_MAP_READ // read/write permission
    , 0
    , 0
    , 64);

  if (mvar->storePtr == NULL) {
#ifdef NDEBUG
	  printf("Could not map view of file (%d).\n", GetLastError());
#endif
    CloseHandle(mvar->dataStoreH);
    free(mvar);
    return NULL;
  }
  size_t storeSize = mvar->storePtr->dataSize + 64;
  UnmapViewOfFile(mvar->storePtr);
  mvar->storePtr = (MVarState*)MapViewOfFile
    ( mvar->dataStoreH    // handle to map object
    , FILE_MAP_ALL_ACCESS // read/write permission
    , 0
    , 0
    , storeSize);

  if (mvar->storePtr == NULL) {
#ifdef NDEBUG
	  printf("Could not map view of file (%d).\n", GetLastError());
#endif
    CloseHandle(mvar->dataStoreH);
    free(mvar);
    return NULL;
  }
  mvar->dataPtr = (void*)(mvar->storePtr) + 64;

  // now, create all synchronization objects
  // TODO: NULL value checking?
  SharedObjectName objName = {0};
  strcpy (objName, mvar->mvarName);
  strcat (objName, "T");
  mvar->canTakeE = OpenEventA( EVENT_MODIFY_STATE | SYNCHRONIZE, FALSE, objName);
  strcpy (objName, mvar->mvarName);
  strcat (objName, "P");
  mvar->canPutE  = OpenEventA( EVENT_MODIFY_STATE | SYNCHRONIZE, FALSE, objName);
  strcpy (objName, mvar->mvarName);
  strcat (objName, "R");
  mvar->canReadS = OpenSemaphoreA( SEMAPHORE_MODIFY_STATE | SYNCHRONIZE, FALSE, objName);
  strcpy (objName, mvar->mvarName);
  strcat (objName, "M");
  mvar->protectReaders = OpenMutexA( SYNCHRONIZE, FALSE, objName);

  return mvar;
}

void  mvar_destroy(MVar *mvar) {
  UnmapViewOfFile(mvar->storePtr);
  CloseHandle(mvar->canTakeE);
  CloseHandle(mvar->canPutE);
  CloseHandle(mvar->canReadS);
  CloseHandle(mvar->protectReaders);
  CloseHandle(mvar->dataStoreH);
  free(mvar);
}

int mvar_take   (MVar *mvar, void *localDataPtr) {
  DWORD r = WaitForSingleObject(mvar->canTakeE, INFINITE);
  if (r != WAIT_OBJECT_0) {
#ifdef NDEBUG
    printf("WaitForSingleObject canTakeE error: return %d; error code %d.\n", r, GetLastError());
#endif
    return 1;
  } else {
    memcpy(localDataPtr, mvar->dataPtr, mvar->storePtr->dataSize);
    SetEvent(mvar->canPutE);
    return 0;
  }
}

int mvar_trytake(MVar *mvar, void *localDataPtr) {
  DWORD r = WaitForSingleObject(mvar->canTakeE, 0);
  switch (r) {
    case WAIT_OBJECT_0:
      memcpy(localDataPtr, mvar->dataPtr, mvar->storePtr->dataSize);
      SetEvent(mvar->canPutE);
      return 0;
    case WAIT_TIMEOUT:
      return 1;
    default:
#ifdef NDEBUG
      printf("WaitForSingleObject canTakeE error: return %d; error code %d.\n", r, GetLastError());
#endif
      return 1;
  }
}

int mvar_put    (MVar *mvar, void *localDataPtr) {
  DWORD r = WaitForSingleObject(mvar->canPutE, INFINITE);
  if (r != WAIT_OBJECT_0) {
#ifdef NDEBUG
    printf("WaitForSingleObject canPutE error: return %d; error code %d.\n", r, GetLastError());
#endif
    return 1;
  } else {
    memcpy(mvar->dataPtr, localDataPtr, mvar->storePtr->dataSize);
    // first check readers, and only then, maybe allow takers
    r = WaitForSingleObject(mvar->protectReaders, INFINITE);
    assert( r == WAIT_OBJECT_0 );
    int remRdrs = mvar->storePtr->pendingReaders;
    r = ReleaseMutex(mvar->protectReaders);
    assert( r != 0 );
    if (remRdrs == 0) {
      SetEvent(mvar->canTakeE);
    } else {
      ReleaseSemaphore(mvar->canReadS, 2 * (LONG) remRdrs - 1, NULL);
    }
    return 0;
  }
}

int mvar_tryput (MVar *mvar, void *localDataPtr) {
  DWORD r = WaitForSingleObject(mvar->canPutE, 0);
  switch (r) {
    case WAIT_OBJECT_0:
      memcpy(mvar->dataPtr, localDataPtr, mvar->storePtr->dataSize);
      // first check readers, and only then, maybe allow takers
      r = WaitForSingleObject(mvar->protectReaders, INFINITE);
      assert( r == WAIT_OBJECT_0 );
      int remRdrs = mvar->storePtr->pendingReaders;
      r = ReleaseMutex(mvar->protectReaders);
      assert( r != 0 );
      if (remRdrs == 0) {
        SetEvent(mvar->canTakeE);
      } else {
        ReleaseSemaphore(mvar->canReadS, 2 * (LONG) remRdrs - 1, NULL);
      }
      return 0;
    case WAIT_TIMEOUT:
      return 1;
    default:
#ifdef NDEBUG
      printf("WaitForSingleObject canTakeE error: return %d; error code %d.\n", r, GetLastError());
#endif
      return 1;
  }
}

int mvar_read   (MVar *mvar, void *localDataPtr) {
  DWORD r = WaitForSingleObject(mvar->protectReaders, INFINITE);
  assert( r == WAIT_OBJECT_0 );
  mvar->storePtr->pendingReaders++;
  r = ReleaseMutex(mvar->protectReaders);
  assert( r != 0 );
  DWORD signaled = WaitForMultipleObjects(2, (HANDLE*)mvar, FALSE, INFINITE);
  switch (signaled) {
    case WAIT_OBJECT_0:
    case WAIT_OBJECT_1:
      memcpy(localDataPtr, mvar->dataPtr, mvar->storePtr->dataSize);
      r = WaitForSingleObject(mvar->protectReaders, INFINITE);
      assert( r == WAIT_OBJECT_0 );
      --(mvar->storePtr->pendingReaders);
      r = ReleaseMutex(mvar->protectReaders);
      assert( r != 0 );
      if ( signaled == WAIT_OBJECT_0 ) {
        if (WaitForSingleObject(mvar->canReadS, 0) != WAIT_OBJECT_0) {
          SetEvent(mvar->canTakeE);
        }
      } else if (signaled == WAIT_OBJECT_1 ) {
        SetEvent(mvar->canTakeE);
      }
      return 0;
    default:
#ifdef NDEBUG
      printf("(mvar_read) WaitForMultipleObjects error: return %d; error code %d.\n", r, GetLastError());
#endif
      return 1;
  }
}

int mvar_tryread(MVar *mvar, void *localDataPtr) {
  DWORD r = WaitForSingleObject(mvar->canTakeE, 0);
  switch (r) {
    case WAIT_OBJECT_0:
      memcpy(localDataPtr, mvar->dataPtr, mvar->storePtr->dataSize);
      SetEvent(mvar->canTakeE);
      return 0;
    case WAIT_TIMEOUT:
      return 1;
    default:
#ifdef NDEBUG
      printf("WaitForSingleObject canTakeE error: return %d; error code %d.\n", r, GetLastError());
#endif
      return 1;
  }
}

int mvar_swap   (MVar *mvar, void *inPtr, void *outPtr) {
  DWORD r = WaitForSingleObject(mvar->canTakeE, INFINITE);
  if (r != WAIT_OBJECT_0) {
#ifdef NDEBUG
    printf("WaitForSingleObject canTakeE error: return %d; error code %d.\n", r, GetLastError());
#endif
    return 1;
  } else {
    memcpy(outPtr, mvar->dataPtr, mvar->storePtr->dataSize);
    memcpy(mvar->dataPtr, inPtr , mvar->storePtr->dataSize);
    SetEvent(mvar->canTakeE);
    return 0;
  }
}

int mvar_tryswap(MVar *mvar, void *inPtr, void *outPtr) {
  DWORD r = WaitForSingleObject(mvar->canTakeE, 0);
  switch (r) {
    case WAIT_OBJECT_0:
      memcpy(outPtr, mvar->dataPtr, mvar->storePtr->dataSize);
      memcpy(mvar->dataPtr, inPtr , mvar->storePtr->dataSize);
      SetEvent(mvar->canTakeE);
      return 0;
    case WAIT_TIMEOUT:
      return 1;
    default:
#ifdef NDEBUG
      printf("WaitForSingleObject canTakeE error: return %d; error code %d.\n", r, GetLastError());
#endif
      return 1;
  }
}

int mvar_isempty(MVar *mvar) {
  DWORD r = WaitForSingleObject(mvar->canPutE, 0);
  if (r == WAIT_TIMEOUT) {
    return 1;
  } else {
    SetEvent(mvar->canPutE);
    return 0;
  }
}



#else
#include <pthread.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>


typedef struct MVarState {
  pthread_mutex_t     mvMut;
  pthread_cond_t      canPutC;
  pthread_cond_t      canTakeC;
  size_t              dataSize;
  pthread_mutexattr_t mvMAttr;
  pthread_condattr_t  condAttr;
  int                 isFull;
  int                 pendingReaders;
  int                 totalUsers;
} MVarState;

typedef struct MVar {
  /* State is stored in the shared data area, accessed by all threads.
     It has a fixed size and kept at the beginning of a shared memory region.
   */
  MVarState *statePtr;
  /* Actual data is stored next to the MVarState
   */
  void      *dataPtr;
  /* Name of the shared memory region, used to share mvar across processes.
   */
  SharedObjectName  mvarName;
} MVar;

// ensure 64 byte alignment (maximum possible we can think of, e.g. AVX512)
size_t mvar_state_size64() {
  size_t x = sizeof(MVarState);
  size_t r = x % 64;
  return r == 0 ? x : (x + 64 - r);
}

MVar *mvar_new(size_t byteSize) {
  int r = 0;
  // allocate MVar
  MVar *mvar = malloc(sizeof(MVar));
  if (mvar == NULL) {
    return NULL;
  }
  genSharedObjectName(mvar->mvarName);

  // allocate shared memory for MVarState and data
  size_t dataShift = mvar_state_size64();
  size_t totalSize = dataShift + byteSize;
  int memFd = shm_open(mvar->mvarName, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
  if (memFd < 0) {
    free(mvar);
    return NULL;
  }
  r = ftruncate(memFd, totalSize);
  if (r != 0) {
    shm_unlink(mvar->mvarName);
    free(mvar);
    return NULL;
  }
  mvar->statePtr = (MVarState*) mmap( NULL, totalSize
                                    , PROT_READ | PROT_WRITE
                                    , MAP_SHARED, memFd, 0);
  if (mvar->statePtr == MAP_FAILED) {
    shm_unlink(mvar->mvarName);
    free(mvar);
    return NULL;
  }
  mvar->dataPtr = ((void*)(mvar->statePtr)) + dataShift;

  // setup state
  *(mvar->statePtr) = (struct MVarState)
    { .isFull = 0
    , .pendingReaders = 0
    , .dataSize = byteSize
    , .totalUsers = 1
    };

  // init mutex and condition variables
  r = pthread_mutexattr_init(&(mvar->statePtr->mvMAttr));
#ifndef NDEBUG
  if ( r == 0) r = pthread_mutexattr_settype(&(mvar->statePtr->mvMAttr), PTHREAD_MUTEX_ERRORCHECK);
#endif
  if ( r == 0) r = pthread_mutexattr_setpshared(&(mvar->statePtr->mvMAttr), PTHREAD_PROCESS_SHARED);
  if ( r == 0) r = pthread_mutex_init(&(mvar->statePtr->mvMut), &(mvar->statePtr->mvMAttr));
  if ( r == 0) r = pthread_condattr_init(&(mvar->statePtr->condAttr));
  if ( r == 0) r = pthread_condattr_setpshared(&(mvar->statePtr->condAttr), PTHREAD_PROCESS_SHARED);
  if ( r == 0) r = pthread_cond_init(&(mvar->statePtr->canPutC), &(mvar->statePtr->condAttr));
  if ( r == 0) r = pthread_cond_init(&(mvar->statePtr->canTakeC), &(mvar->statePtr->condAttr));
  if ( r != 0 ) {
    munmap(mvar->statePtr, totalSize);
    shm_unlink(mvar->mvarName);
    free(mvar);
    return NULL;
  }

  return mvar;
}

MVar *mvar_lookup(const char *name) {
  int memFd = shm_open(name, O_RDWR, S_IRUSR | S_IWUSR);
  if (memFd < 0) return NULL;

  // first, map only sizeof(MVarState) bytes
  // then, read the actual size and remap memory
  void *mvs0 = mmap( NULL, sizeof(MVarState)
                   , PROT_READ, MAP_SHARED, memFd, 0);
  if (mvs0 == MAP_FAILED) return NULL;
  size_t dataShift = mvar_state_size64(),
         storeSize = dataShift + ((MVarState*)mvs0)->dataSize;
  munmap(mvs0, sizeof(MVarState)); // don't really care if it is failed
  MVarState *mvs = (MVarState*) mmap( NULL, storeSize
                                    , PROT_READ | PROT_WRITE
                                    , MAP_SHARED, memFd, 0);
  if (mvs == MAP_FAILED) return NULL;
  // setup MVar struct
  MVar *mvar = malloc(sizeof(MVar));
  if (mvar == NULL) {
    munmap(mvs, storeSize);
    return NULL;
  }
  mvar->statePtr = (MVarState*)mvs;
  mvar->dataPtr  = ((void*)(mvar->statePtr)) + dataShift;
  strcpy(mvar->mvarName, name);

  // update state
  int r = 0;
  r = pthread_mutex_lock(&(mvar->statePtr->mvMut));
  if ( r != 0 || mvar->statePtr->totalUsers == 0 ) {
    munmap(mvar->statePtr, storeSize);
    free(mvar);
    return NULL;
  }
  mvar->statePtr->totalUsers++;
  r = pthread_mutex_unlock(&(mvar->statePtr->mvMut));
  if ( r != 0 ) {
    munmap(mvar->statePtr, storeSize);
    free(mvar);
    return NULL;
  }

  return mvar;
}

void mvar_destroy(MVar *mvar) {
  int usersLeft;
  size_t storeSize = mvar->statePtr->dataSize + mvar_state_size64();
  pthread_mutex_lock(&(mvar->statePtr->mvMut));
  usersLeft = --(mvar->statePtr->totalUsers);
  pthread_mutex_unlock(&(mvar->statePtr->mvMut));
  if ( usersLeft > 0 ) {
#ifndef NDEBUG
    printf( "Destroying local instance of mvar %s, %d users left.\n"
          , mvar->mvarName, usersLeft);
#endif
    munmap(mvar->statePtr, storeSize);
  } else {
#ifndef NDEBUG
    printf( "Destroying mvar %s globally (no other users left).\n", mvar->mvarName);
#endif
    pthread_cond_destroy(&(mvar->statePtr->canTakeC));
    pthread_cond_destroy(&(mvar->statePtr->canPutC));
    pthread_condattr_destroy(&(mvar->statePtr->condAttr));
    pthread_mutex_destroy(&(mvar->statePtr->mvMut));
    pthread_mutexattr_destroy(&(mvar->statePtr->mvMAttr));
    munmap(mvar->statePtr, storeSize);
    shm_unlink(mvar->mvarName);
  }
  free(mvar);
}


int mvar_take(MVar *mvar, void *localDataPtr) {
  int r = 0;
  r = pthread_mutex_lock(&(mvar->statePtr->mvMut));
  if ( r != 0 ) return r;
  while ( !(mvar->statePtr->isFull) || mvar->statePtr->pendingReaders > 0 ) {
    if ( mvar->statePtr->pendingReaders > 0 ) {
      pthread_cond_broadcast(&(mvar->statePtr->canTakeC));
    }
    r = pthread_cond_wait(&(mvar->statePtr->canTakeC), &(mvar->statePtr->mvMut));
    if ( r != 0 ) {
      pthread_mutex_unlock(&(mvar->statePtr->mvMut));
      return r;
    }
  }
  memcpy(localDataPtr, mvar->dataPtr, mvar->statePtr->dataSize);
  mvar->statePtr->isFull = 0;
  pthread_cond_signal(&(mvar->statePtr->canPutC));
  pthread_mutex_unlock(&(mvar->statePtr->mvMut));
  return 0;
}

int mvar_trytake(MVar *mvar, void *localDataPtr) {
  int r = 0;
  r = pthread_mutex_lock(&(mvar->statePtr->mvMut));
  if ( r != 0 ) return r;
  // shortcut if is empty
  if ( !(mvar->statePtr->isFull) ) {
    pthread_mutex_unlock(&(mvar->statePtr->mvMut));
    return 1;
  }
  while ( mvar->statePtr->pendingReaders > 0 ) {
    // make sure readers do not sleep
    pthread_cond_broadcast(&(mvar->statePtr->canTakeC));
    // last reader should wake me up, wait for it.
    r = pthread_cond_wait(&(mvar->statePtr->canTakeC), &(mvar->statePtr->mvMut));
    if ( r != 0 ) {
      pthread_mutex_unlock(&(mvar->statePtr->mvMut));
      return r;
    }
    // repeat emptyness check (if another trytake was faster)
    if ( !(mvar->statePtr->isFull) ) {
      pthread_mutex_unlock(&(mvar->statePtr->mvMut));
      return 1;
    }
  }
  memcpy(localDataPtr, mvar->dataPtr, mvar->statePtr->dataSize);
  mvar->statePtr->isFull = 0;
  pthread_cond_signal(&(mvar->statePtr->canPutC));
  pthread_mutex_unlock(&(mvar->statePtr->mvMut));
  return 0;
}

int mvar_put(MVar *mvar, void *localDataPtr) {
  int r = 0;
  r = pthread_mutex_lock(&(mvar->statePtr->mvMut));
  if ( r != 0 ) return r;
  while ( mvar->statePtr->isFull ) {
    r = pthread_cond_wait(&(mvar->statePtr->canPutC), &(mvar->statePtr->mvMut));
    if ( r != 0 ) {
      pthread_mutex_unlock(&(mvar->statePtr->mvMut));
      return r;
    }
  }
  memcpy(mvar->dataPtr, localDataPtr, mvar->statePtr->dataSize);
  mvar->statePtr->isFull = 1;
  pthread_cond_broadcast(&(mvar->statePtr->canTakeC));
  pthread_mutex_unlock(&(mvar->statePtr->mvMut));
  return 0;
}

int mvar_tryput(MVar *mvar, void *localDataPtr) {
  int r = 0;
  r = pthread_mutex_lock(&(mvar->statePtr->mvMut));
  if ( r != 0 ) return r;
  // shortcut if is full
  if ( mvar->statePtr->isFull ) {
    pthread_mutex_unlock(&(mvar->statePtr->mvMut));
    return 1;
  }
  memcpy(mvar->dataPtr, localDataPtr, mvar->statePtr->dataSize);
  mvar->statePtr->isFull = 1;
  pthread_cond_broadcast(&(mvar->statePtr->canTakeC));
  pthread_mutex_unlock(&(mvar->statePtr->mvMut));
  return 0;
}

int mvar_read(MVar *mvar, void *localDataPtr) {
  int r = 0;
  r = pthread_mutex_lock(&(mvar->statePtr->mvMut));
  if ( r != 0 ) return r;
  mvar->statePtr->pendingReaders++;
  while ( !(mvar->statePtr->isFull) ) {
    r = pthread_cond_wait(&(mvar->statePtr->canTakeC), &(mvar->statePtr->mvMut));
    if ( r != 0 ) {
      pthread_mutex_unlock(&(mvar->statePtr->mvMut));
      return r;
    }
  }
  memcpy(localDataPtr, mvar->dataPtr, mvar->statePtr->dataSize);
  if ( (--(mvar->statePtr->pendingReaders)) == 0 ) {
    pthread_cond_signal(&(mvar->statePtr->canTakeC));
  }
  pthread_mutex_unlock(&(mvar->statePtr->mvMut));
  return 0;
}

int mvar_tryread(MVar *mvar, void *localDataPtr) {
  int r = 0;
  r = pthread_mutex_lock(&(mvar->statePtr->mvMut));
  if ( r != 0 ) return r;
  // shortcut if is empty
  if ( !(mvar->statePtr->isFull) ) {
    pthread_mutex_unlock(&(mvar->statePtr->mvMut));
    return 1;
  }
  memcpy(localDataPtr, mvar->dataPtr, mvar->statePtr->dataSize);
  pthread_mutex_unlock(&(mvar->statePtr->mvMut));
  return 0;
}

int mvar_swap(MVar *mvar, void *inPtr, void *outPtr) {
  int r = 0;
  r = pthread_mutex_lock(&(mvar->statePtr->mvMut));
  if ( r != 0 ) return r;
  while ( !(mvar->statePtr->isFull) || mvar->statePtr->pendingReaders > 0 ) {
    if ( mvar->statePtr->pendingReaders > 0 ) {
      pthread_cond_broadcast(&(mvar->statePtr->canTakeC));
    }
    r = pthread_cond_wait(&(mvar->statePtr->canTakeC), &(mvar->statePtr->mvMut));
    if ( r != 0 ) {
      pthread_mutex_unlock(&(mvar->statePtr->mvMut));
      return r;
    }
  }
  memcpy(outPtr, mvar->dataPtr, mvar->statePtr->dataSize);
  memcpy(mvar->dataPtr, inPtr , mvar->statePtr->dataSize);
  pthread_cond_signal(&(mvar->statePtr->canTakeC));
  pthread_mutex_unlock(&(mvar->statePtr->mvMut));
  return 0;
}

int mvar_tryswap(MVar *mvar, void *inPtr, void *outPtr) {
  int r = 0;
  r = pthread_mutex_lock(&(mvar->statePtr->mvMut));
  if ( r != 0 ) return r;
  // shortcut if is empty
  if ( !(mvar->statePtr->isFull) ) {
    pthread_mutex_unlock(&(mvar->statePtr->mvMut));
    return 1;
  }
  while ( mvar->statePtr->pendingReaders > 0 ) {
    // make sure readers do not sleep
    pthread_cond_broadcast(&(mvar->statePtr->canTakeC));
    // last reader should wake me up, wait for it.
    r = pthread_cond_wait(&(mvar->statePtr->canTakeC), &(mvar->statePtr->mvMut));
    if ( r != 0 ) {
      pthread_mutex_unlock(&(mvar->statePtr->mvMut));
      return r;
    }
    // repeat emptyness check (if another trytake was faster)
    if ( !(mvar->statePtr->isFull) ) {
      pthread_mutex_unlock(&(mvar->statePtr->mvMut));
      return 1;
    }
  }
  memcpy(outPtr, mvar->dataPtr, mvar->statePtr->dataSize);
  memcpy(mvar->dataPtr, inPtr , mvar->statePtr->dataSize);
  pthread_cond_signal(&(mvar->statePtr->canPutC));
  pthread_mutex_unlock(&(mvar->statePtr->mvMut));
  return 0;
}

int mvar_isempty(MVar *mvar) {
  return mvar->statePtr->isFull == 0;
}

#endif


void mvar_name(MVar *mvar, char * const name) {
  strcpy(name, mvar->mvarName);
}