blob: 2089ca31e4d87f87d9915b795233f2d088218450 [file] [log] [blame]
// Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Templatized, thread-safe RingBuffer. Acts like a FIFO, but using a
// fixed-size buffer that wraps around and optimized for operations that
// read or write many elements at a time. Commonly used to buffer audio
// samples that need to be passed from one thread to another.
//
// Supports a flag "finished" so the writing thread can notify the reading
// thread that there's no more data.
//
// The implementation is all contained within this .h file because
// it's templatized.
#ifndef SPEECH_CLIENT_SYNTHESIS_SERVICE_RINGBUFFER_H_
#define SPEECH_CLIENT_SYNTHESIS_SERVICE_RINGBUFFER_H_
#include "threading.h"
namespace speech_synthesis {
template<typename T> class RingBuffer {
public:
// Construct a RingBuffer using a given Threading object (used for
// locking - may not be NULL) of a given capacity (cannot be increased
// later).
RingBuffer(Threading* theading, int capacity);
// Destructor.
~RingBuffer();
//
// Methods for either thread
//
int GetCapacity() { return capacity_; }
//
// Methods for the writer thread
//
// Reset to the initial state: the ring buffer is empty and marked as
// unfinished.
void Reset();
// Get the number of items of type T that are available to be written.
// This will be a number between 0 and capacity, inclusive.
int WriteAvail();
// Write *len* elements to the end of the ring buffer. Returns true on
// success. If all *len* elements cannot be written without blocking,
// returns false and writes nothing.
bool Write(const T* data, int len);
// Mark the buffer as finished. Future write operations will fail.
// Read operations will succeed until the buffer is empty, but
// IsFinished() will return true immediately.
void MarkFinished();
//
// Methods for the reader thread
//
// Get the number of items of type T that are available to be read.
// This will be a number between 0 and capacity, inclusive.
int ReadAvail();
// Read *len* elements from the front of the ring buffer. Returns true on
// success. If all *len* elements cannot be read without blocking,
// returns false and reads nothing.
bool Read(T* data, int len);
// Returns true if the buffer has been marked as finished by a call to
// MarkFinished, whether the buffer is empty or not.
bool IsFinished();
private:
Mutex* mutex_;
T* buffer_;
bool finished_;
int capacity_;
volatile int read_pos_;
volatile int write_pos_;
};
//
// Implementation notes:
//
// We use a simple mutex to protect all of the fields.
//
// A special value of -1 is used for read_pos_ to indicate that the buffer
// is empty, otherwise there would be no way to distinguish between an empty
// buffer and a full buffer when read_pos_ == write_pos_.
//
template<typename T> RingBuffer<T>::RingBuffer(
Threading* threading, int capacity) {
capacity_ = capacity;
mutex_ = threading->CreateMutex();
buffer_ = new T[capacity_];
Reset();
}
template<typename T> RingBuffer<T>::~RingBuffer() {
delete mutex_;
delete[] buffer_;
}
template<typename T> void RingBuffer<T>::Reset() {
mutex_->Lock();
finished_ = false;
read_pos_ = -1;
write_pos_ = 0;
mutex_->Unlock();
};
template<typename T> int RingBuffer<T>::WriteAvail() {
int avail = capacity_;
mutex_->Lock();
if (read_pos_ != -1) {
avail = read_pos_ - write_pos_;
}
mutex_->Unlock();
if (avail < 0) {
avail += capacity_;
}
return avail;
}
template<typename T> bool RingBuffer<T>::Write(const T* data, int len) {
mutex_->Lock();
if (finished_) {
mutex_->Unlock();
return false;
}
int avail = capacity_;
if (read_pos_ != -1) {
avail = read_pos_ - write_pos_;
}
if (avail < 0) {
avail += capacity_;
}
if (len > avail) {
mutex_->Unlock();
return false;
}
if (read_pos_ == -1) {
read_pos_ = write_pos_;
}
for (int i = 0; i < len; i++) {
buffer_[write_pos_] = data[i];
write_pos_ = (write_pos_ + 1) % capacity_;
}
mutex_->Unlock();
return true;
}
template<typename T> void RingBuffer<T>::MarkFinished() {
mutex_->Lock();
finished_ = true;
mutex_->Unlock();
}
template<typename T> int RingBuffer<T>::ReadAvail() {
mutex_->Lock();
if (read_pos_ == -1) {
mutex_->Unlock();
return 0;
}
int avail = write_pos_ - read_pos_;
mutex_->Unlock();
if (avail <= 0) {
avail += capacity_;
}
return avail;
}
template<typename T> bool RingBuffer<T>::Read(T* data, int len) {
mutex_->Lock();
int avail;
if (read_pos_ == -1) {
avail = 0;
} else {
avail = write_pos_ - read_pos_;
if (avail <= 0) {
avail += capacity_;
}
}
if (len > avail) {
mutex_->Unlock();
return false;
}
for (int i = 0; i < len; i++) {
data[i] = buffer_[read_pos_];
read_pos_ = (read_pos_ + 1) % capacity_;
}
if (read_pos_ == write_pos_) {
read_pos_ = -1;
}
mutex_->Unlock();
return true;
}
template<typename T> bool RingBuffer<T>::IsFinished() {
mutex_->Lock();
bool is_finished = finished_;
mutex_->Unlock();
return is_finished;
}
} // namespace speech_synthesis
#endif // SPEECH_CLIENT_SYNTHESIS_SERVICE