blob: cb10a8e5c0fe71eef2b8636704d7101e10654568 [file] [log] [blame]
//Copyright 2009 Princeton University
//Written by Christian Bienia
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#ifdef ENABLE_PTHREADS
#include <pthread.h>
#endif //ENABLE_PTHREADS
#ifdef ENABLE_DMALLOC
#include <dmalloc.h>
#endif //ENABLE_DMALLOC
#include "mbuffer.h"
#ifdef ENABLE_PTHREADS
//Use spin locks instead of mutexes (this file only)
#define ENABLE_SPIN_LOCKS
#ifdef ENABLE_SPIN_LOCKS
typedef pthread_spinlock_t pthread_lock_t;
#define PTHREAD_LOCK_INIT(l) pthread_spin_init(l, PTHREAD_PROCESS_PRIVATE)
#define PTHREAD_LOCK_DESTROY(l) pthread_spin_destroy(l)
#define PTHREAD_LOCK(l) pthread_spin_lock(l)
#define PTHREAD_UNLOCK(l) pthread_spin_unlock(l)
#else
typedef pthread_mutex_t pthread_lock_t;
#define PTHREAD_LOCK_INIT(l) pthread_mutex_init(l, NULL)
#define PTHREAD_LOCK_DESTROY(l) pthread_mutex_destroy(l)
#define PTHREAD_LOCK(l) pthread_mutex_lock(l)
#define PTHREAD_UNLOCK(l) pthread_mutex_unlock(l)
#endif //ENABLE_SPIN_LOCKS
//Array with global locks to use. We use many mutexes to achieve some concurrency.
//FIXME: Merge into mbuffer mcb
//FIXME: Update documentation with latest changes
//Unfortunately mutexes cannot be stored inside the memory buffers because of
//concurrent free operations
pthread_lock_t *locks = NULL;
//Number of locks to use. More locks means higher potential concurrency, assuming lock usage is reasonably balanced.
//We use a prime number for that value to get a simple but effective hash function
#define NUMBER_OF_LOCKS 1021
//A very simple hash function to map memory addresses to a lock id for the locks array
static inline int lock_hash(void *p) {
return (int)(((unsigned long long int)p) % NUMBER_OF_LOCKS);
}
#endif //ENABLE_PTHREADS
//Initialize memory buffer subsystem
int mbuffer_system_init() {
#ifdef ENABLE_PTHREADS
int i;
assert(locks==NULL);
locks = malloc(NUMBER_OF_LOCKS * sizeof(pthread_lock_t));
if(locks==NULL) return -1;
for(i=0; i<NUMBER_OF_LOCKS; i++) {
if(PTHREAD_LOCK_INIT(&locks[i]) != 0) {
int j;
for(j=0; j<i; j++) {
PTHREAD_LOCK_DESTROY(&locks[i]);
}
free((void *)locks);
locks=NULL;
return -1;
}
}
#endif
return 0;
}
//Shutdown memory buffer subsystem
int mbuffer_system_destroy() {
#ifdef ENABLE_PTHREADS
int i, rv;
rv=0;
for(i=0; i<NUMBER_OF_LOCKS; i++) {
rv+=PTHREAD_LOCK_DESTROY(&locks[i]);
}
free((void *)locks);
locks=NULL;
return rv ? -1 : 0;
#else
return 0;
#endif
}
//Initialize a memory buffer
int mbuffer_create(mbuffer_t *m, size_t size) {
void *ptr;
assert(m!=NULL);
assert(size > 0);
//FIXME: Merge both mallocs to one
ptr = malloc(size);
if(ptr==NULL) return -1;
m->mcb = (mcb_t *)malloc(sizeof(mcb_t));
if(m->mcb==NULL) {
free(ptr);
ptr=NULL;
return -1;
}
m->ptr = ptr;
m->n = size;
m->mcb->i = 1;
m->mcb->ptr = ptr;
#ifdef ENABLE_MBUFFER_CHECK
m->check_flag=MBUFFER_CHECK_MAGIC;
#endif
return 0;
}
//Make a shallow copy of a memory buffer
mbuffer_t *mbuffer_clone(mbuffer_t *m) {
mbuffer_t *temp;
assert(m!=NULL);
#ifdef ENABLE_MBUFFER_CHECK
assert(m->check_flag==MBUFFER_CHECK_MAGIC);
#endif
temp = malloc(sizeof(mbuffer_t));
if(temp==NULL) return NULL;
//Update reference counter
#ifdef ENABLE_PTHREADS
PTHREAD_LOCK(&locks[lock_hash(m->mcb)]);
assert(m->mcb->i>=1);
m->mcb->i++;
PTHREAD_UNLOCK(&locks[lock_hash(m->mcb)]);
#else
assert(m->mcb->i>=1);
m->mcb->i++;
#endif //ENABLE_PTHREADS
//copy state, use joint mcb
temp->ptr = m->ptr;
temp->n = m->n;
temp->mcb = m->mcb;
#ifdef ENABLE_MBUFFER_CHECK
temp->check_flag=MBUFFER_CHECK_MAGIC;
#endif
return temp;
}
//Make a deep copy of a memory buffer
mbuffer_t *mbuffer_copy(mbuffer_t *m) {
mbuffer_t *temp;
assert(m!=NULL);
assert(m->n >= 1);
#ifdef ENABLE_MBUFFER_CHECK
assert(m->check_flag==MBUFFER_CHECK_MAGIC);
#endif
//NOTE: No need to update reference counter of master, resulting copy of buffer will be independent
temp = malloc(sizeof(mbuffer_t));
if(temp==NULL) return NULL;
if(mbuffer_create(temp, m->n)!=0) {
free(temp);
temp=NULL;
return NULL;
}
memcpy(temp->ptr, m->ptr, m->n);
#ifdef ENABLE_MBUFFER_CHECK
temp->check_flag=MBUFFER_CHECK_MAGIC;
#endif
return temp;
}
//Free a memory buffer
void mbuffer_free(mbuffer_t *m) {
unsigned int ref;
assert(m!=NULL);
#ifdef ENABLE_MBUFFER_CHECK
assert(m->check_flag==MBUFFER_CHECK_MAGIC);
#endif
//Update meta state first to avoid races
#ifdef ENABLE_PTHREADS
PTHREAD_LOCK(&locks[lock_hash(m->mcb)]);
m->mcb->i--;
ref = m->mcb->i;
PTHREAD_UNLOCK(&locks[lock_hash(m->mcb)]);
#else
m->mcb->i--;
ref = m->mcb->i;
#endif //ENABLE_PTHREADS
//NOTE: No need to synchronize access to ref counter value again because if it has hit 0 the buffer is dead
if(ref==0) {
free(m->mcb->ptr);
m->mcb->ptr=NULL;
free(m->mcb);
m->mcb=NULL;
}
#ifdef ENABLE_MBUFFER_CHECK
m->check_flag=0;
#endif
}
//Resize a memory buffer
//Returns 0 if the operation was successful
int mbuffer_realloc(mbuffer_t *m, size_t size) {
void *r;
assert(m!=NULL);
assert(size>0);
#ifdef ENABLE_MBUFFER_CHECK
assert(m->check_flag==MBUFFER_CHECK_MAGIC);
#endif
#ifdef ENABLE_PTHREADS
PTHREAD_LOCK(&locks[lock_hash(m->mcb)]);
#endif //ENABLE_PTHREADS
//We cannot resize a buffer if more than one pointer to it is in circulation
if(m->mcb->i > 1) {
#ifdef ENABLE_PTHREADS
PTHREAD_UNLOCK(&locks[lock_hash(m->mcb)]);
#endif
return -1;
}
//This must be the original mbuffer, otherwise we'd have to do something more complicated
if(m->ptr != m->mcb->ptr) {
#ifdef ENABLE_PTHREADS
PTHREAD_UNLOCK(&locks[lock_hash(m->mcb)]);
#endif
return -1;
}
r = realloc(m->ptr, size);
if(r != NULL) {
m->ptr = r;
m->n = size;
m->mcb->ptr = r;
}
#ifdef ENABLE_PTHREADS
PTHREAD_UNLOCK(&locks[lock_hash(m->mcb)]);
#endif
if(r == NULL) {
return -1;
} else {
return 0;
}
}
//Split a memory buffer m1 into two buffers m1 and m2 at the designated location
//Returns 0 if the operation was successful
int mbuffer_split(mbuffer_t *m1, mbuffer_t *m2, size_t split) {
assert(m1!=NULL);
assert(m2!=NULL);
assert(split>0);
assert(split < m1->n);
#ifdef ENABLE_MBUFFER_CHECK
assert(m1->check_flag==MBUFFER_CHECK_MAGIC);
assert(m2->check_flag!=MBUFFER_CHECK_MAGIC);
#endif
//Update reference counter
#ifdef ENABLE_PTHREADS
PTHREAD_LOCK(&locks[lock_hash(m1->mcb)]);
assert(m1->mcb->i>=1);
m1->mcb->i++;
PTHREAD_UNLOCK(&locks[lock_hash(m1->mcb)]);
#else
assert(m1->mcb->i>=1);
m1->mcb->i++;
#endif //ENABLE_PTHREADS
//split buffer
m2->ptr = m1->ptr+split;
m2->n = m1->n-split;
m2->mcb = m1->mcb;
m1->n = split;
#ifdef ENABLE_MBUFFER_CHECK
m2->check_flag=MBUFFER_CHECK_MAGIC;
#endif
return 0;
}