/* * ring.c * * A ring buffer implementation for cutlass. This is mainly used * by the audio subsystem, in order to extract encoder-sized * chunks from a arbitrarily-sized input stream. * * Copyright (c) 2004, 2005 nash e.f. * Todd MacDermid */ #include #include #include /** * @brief Check whether the ring buffer is full. * * @detailed Check whether the ring buffer is full or not. This is essentially * like looking directly into the structure. You can do that, too. :-) * * @return 0 if the buffer is not full, anything else if it is full. * */ int cutlass_ring_full(cutlass_ring_t *ring) { if (NULL == ring) return (1); if (0 != ring->is_full) return (1); return (0); } /** * @brief Allocate a new ring buffer with size members each of size mbrsize * where the pointers are advanced by stepsz bytes. * * @detailed */ cutlass_ring_t * cutlass_ring_new(size_t mbrsize, uint32_t size) { int err = 0; cutlass_ring_t *ring = NULL; ring = (cutlass_ring_t *)calloc(1, sizeof(cutlass_ring_t)); if (NULL == ring) { cutlass_sysmsg(NULL, CUT_ERROR, "cutlass_ring_new: " "can't allocate memory for ring buffer: ", strerror(errno)); goto ring_new_death; } /// setup params ring->read = 0; ring->write = 0; ring->is_full = 0; ring->mbrsize = mbrsize; ring->size = size; /// Allocate size + 1 so that we have an empty block to keep track of things ring->data = (uint8_t *)calloc (1, (mbrsize * (size + 1))); if (NULL == ring->data) { cutlass_sysmsg(NULL, CUT_ERROR, "cutlass_ring_new: " "can't allocate memory for ring buffer: ", strerror(errno)); goto ring_new_death; } //// Make a pthread mutex lock for the ring. err = pthread_mutex_init(&(ring->lock), NULL); if (0 != err) { // this should never happen cutlass_sysmsg(NULL, CUT_ERROR, "cutlass_ring_new: " "pthread_mutex_init returned non-zero value!\n"); goto ring_new_death; } // ready to go return (ring); ring_new_death: if (NULL != ring) { if (NULL != ring->data) free(ring->data); free(ring); } return (NULL); } /** * @brief Lock a ring buffer for the current pthread. * * @detailed This means you're entering a critical section and so * the caller must be seriously wary of failing to unlock the buffer. * This function will block. * * @return 0 if locked, anything else is an error. */ int cutlass_ring_lock(cutlass_ring_t *ring) { if (NULL == ring) return (-1); pthread_mutex_lock(&(ring->lock)); return (0); } /** * @brief Try to lock a ring buffer, but don't block. * * @detailed This means you're considering entering a critical section * if you can acquire the ring's lock. This call will not block if the * lock cannot be acquired, but will return immediately with the error * code EBUSY. See pthread_mutex_trylock. * * @return 0 if locked, anything else indicates an error, EBUSY indicates * the lock is owned by someone else at the moment. */ int cutlass_ring_trylock(cutlass_ring_t *ring) { int x = 0; if (NULL == ring) return (-1); x = pthread_mutex_trylock(&(ring->lock)); return (x); } /** * @brief Unlock a ring buffer for the current thread. * * @detailed This means you're exiting a critical section; congrats! * You've possibly avoided deadlock yet again! CAUTION: WARNING: NOTE: * Don't call this function if you don't hold the ring's lock. I.e., if * you haven't previously called cutlass_ring_lock and gotten a zero * return code. * * @return 0 if unlocked, anything else is an error. */ int cutlass_ring_unlock(cutlass_ring_t *ring) { if (NULL == ring) return (-1); pthread_mutex_unlock(&(ring->lock)); return (0); } /** * @brief Free a ring buffer. */ void cutlass_ring_free(cutlass_ring_t *ring) { if (NULL == ring) return; //// We have to verify that the lock is available before calling //// pthread_mutex_destroy on this thing... there's an icky race //// condition here... pthread_mutex_lock(&(ring->lock)); if (NULL != ring->data) free(ring->data); ring->mbrsize = 0; ring->size = 0; ring->read = 0; ring->write = 0; pthread_mutex_unlock(&(ring->lock)); pthread_mutex_destroy(&(ring->lock)); free (ring); return; } /* * cutlass_ring_write writes the members in write_buf into the * ring buffer, advancing the write pointer as possible. If * it hits the read buffer, it will stop, set the is_full * flag, and return 1. Returns 0 on success. Returns -1 * on non-full type errors. */ int cutlass_ring_write(cutlass_ring_t *ring, void *write_buf, uint32_t num) { uint32_t to_end; uint32_t left; if((NULL == ring) || (NULL == write_buf)) return(-1); pthread_mutex_lock(&(ring->lock)); if(ring->is_full) goto full; if((ring->write + num) <= ring->size) { if(ring->write < ring->read) { if((ring->write + num) >= ring->read) { memcpy((ring->data + (ring->write * ring->mbrsize)), write_buf, (ring->read - ring->write) * ring->mbrsize); ring->write = ring->read; ring->is_full = 1; goto full; } } memcpy((ring->data + (ring->write * ring->mbrsize)), write_buf, num * ring->mbrsize); ring->write += num; } else { if(ring->write < ring->read) { memcpy((ring->data + (ring->write * ring->mbrsize)), write_buf, (ring->read - ring->write) * ring->mbrsize); ring->write = ring->read; ring->is_full = 1; goto full; } to_end = ring->size - ring->write; left = num - to_end; memcpy((ring->data + (ring->write * ring->mbrsize)), write_buf, to_end * ring->mbrsize); ring->write = 0; if(ring->write + left >= ring->read) { memcpy((ring->data + (ring->write * ring->mbrsize)), write_buf + (to_end * ring->mbrsize), (ring->read - ring->write) * ring->mbrsize); ring->write = ring->read; ring->is_full = 1; goto full; } memcpy(ring->data, write_buf + (to_end *ring->mbrsize), left * ring->mbrsize); ring->write += left; } pthread_mutex_unlock(&(ring->lock)); return(0); full: pthread_mutex_unlock(&(ring->lock)); return(1); } /* * cutlass_ring_read fills in read_buf with contents from the * ring buffer, up to a maximum of max entries. The number of entries read * in will be an even multiple of blocksize. It returns the * number of entries actually read, or -1 on error. */ int cutlass_ring_read(cutlass_ring_t *ring, void *read_buf, uint32_t blocksize, uint32_t max) { uint32_t available; uint32_t to_read; uint32_t to_end; uint32_t left; if((NULL == ring) || (NULL == read_buf) || (0 == blocksize)) return(-1); pthread_mutex_lock(&(ring->lock)); if(ring->is_full) { available = ring->size; } else { if(ring->write >= ring->read) { available = ring->write - ring->read; } else { available = ring->write + (ring->size - ring->read); } } if(available > max) available = max; to_read = available - (available % blocksize); if(ring->read + to_read > ring->size) { to_end = ring->size - ring->read; left = to_read - to_end; memcpy(read_buf, ring->data + (ring->read * ring->mbrsize), to_end * ring->mbrsize); ring->read = 0; memcpy(read_buf + (to_end * ring->mbrsize), ring->data, left * ring->mbrsize); ring->read = left; } else { memcpy(read_buf, ring->data + (ring->read * ring->mbrsize), to_read * ring->mbrsize); ring->read += to_read; } if(to_read > 0) ring->is_full = 0; pthread_mutex_unlock(&(ring->lock)); return(to_read); }