4 * Copyright (c) 2004 Network for Educational Technology ETH
6 * Written by Hannes Friederich, Andreas Fenkart.
7 * Based on work of Shawn Pai-Hsiang Hsiao
9 * The contents of this file are subject to the Mozilla Public License
10 * Version 1.0 (the "License"); you may not use this file except in
11 * compliance with the License. You may obtain a copy of the License at
12 * http://www.mozilla.org/MPL/
14 * Software distributed under the License is distributed on an "AS IS"
15 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
16 * the License for the specific language governing rights and limitations
23 #ifndef CIRCULAR_BUFFER_H
24 #define CIRCULAR_BUFFER_H
27 //#define PTRACE_CIRC(level, str) PTRACE(level, str)
28 #define PTRACE_CIRC(level, str)
31 * Simple circular buffer for one producer and consumer with a head and a
32 * tail that chase each other. The capacity is 1 byte bigger than necessary
33 * due to a sentinel, to tell apart full from empty by the following equations:
35 * full := head_next == tail
36 * empty := head == tail
38 * Inspired by CircularBuffer from beaudio.
39 * We need a lock when updating the tail index, due to the overwrite mechanism
40 * in case of buffer overflow. The head index needs no locking because
41 * overwrite does not make sense in case of buffer underrun.
43 * Keep in mind that this buffer does no know about frames or packets, it's up
44 * to you to make sure you get the right number of bytes. In doubt use size()
45 * to compute how many frames/packets it is save to drain/fill.
53 explicit CircularBuffer(PINDEX len)
54 : capacity(len + 1), /* plus sentinel */ head(0), tail(0)
56 buffer = (char *)malloc(capacity*sizeof(char));
58 PTRACE(1, "Could not allocate circular buffer");
65 * Mutex to block write thread when buffer is full
68 rval = pthread_mutex_init(&mutex, NULL);
70 PTRACE(1, __func__ << " can not init mutex");
74 rval = pthread_cond_init(&cond, NULL);
76 PTRACE(1, __func__ << " can not init cond");
89 pthread_mutex_destroy(&mutex);
90 pthread_cond_destroy(&cond);
96 /* head + 1 == tail */
97 return head_next() == tail;
106 /* sentinel is outside of occupied area */
107 return (head < tail ) ? head + capacity - tail : head - tail;
111 return (capacity - size() -1 /*sentinel */);
115 * Fill inserts data into the circular buffer.
116 * Remind that lock and overwrite are mutually exclusive. If you set lock,
117 * overwrite will be ignored
119 PINDEX Fill(const char* inbuf, PINDEX len, Boolean lock = true,
120 Boolean overwrite = false);
124 PINDEX Drain(char* outbuf, PINDEX len, Boolean lock = true);
129 //return (head + 1 == capacity) ? 0 : (head + 1);
130 return (head + 1 % capacity);
133 void increment_index(PINDEX &index, PINDEX inc)
139 void increment_head(PINDEX inc)
141 increment_index(head, inc);
144 void increment_tail(PINDEX inc)
146 increment_index(tail, inc);
153 const PINDEX capacity;
157 pthread_mutex_t mutex;
162 int CircularBuffer::Fill(const char *inbuf, PINDEX len,
163 Boolean lock, Boolean overwrite)
165 int done = 0, todo = 0;
167 PTRACE_CIRC(1, "Fill " << len << " bytes, contains " << size());
170 PTRACE(1, __func__ << "Input buffer is empty");
174 while(done != len && !Full()) {
176 // head unwrapped, fill from head till end of buffer
177 if(tail == 0) /* buffer[capacity] == sentinel */
178 todo = MIN(capacity -1 /*sentinel*/ - head, len - done);
180 todo = MIN(capacity - head, len - done);
182 // fill from head till tail
183 todo = MIN(tail -1 /*sentinel*/ - head, len - done);
185 memcpy(buffer + head, inbuf + done, todo);
187 increment_head(todo);
188 PTRACE_CIRC(1, "copied " << done << " from input buffer"
189 << " available " << size());
192 // What to do if buffer is full and more bytes
193 // need to be copied ?
194 if(Full() && done != len && (overwrite || lock)) {
195 PTRACE_CIRC(1, __func__ << "Circular buffer is full, while Fill "
198 pthread_mutex_lock(&mutex);
199 PTRACE_CIRC(1, "Fill going to sleep");
200 pthread_cond_wait(&cond, &mutex);
201 // pthread_cond_timedwait
202 PTRACE_CIRC(1, "Fill woke up");
203 pthread_mutex_unlock(&mutex);
204 } else if(overwrite){
205 pthread_mutex_lock(&mutex);
207 tail += len - done; // also shifts sentinel
208 tail %= capacity; // wrap around
210 pthread_mutex_unlock(&mutex);
213 done += Fill(inbuf + done, len - done, lock, overwrite);
216 // wake up read thread if necessary
218 pthread_cond_signal(&cond);
221 PTRACE_CIRC(1, __func__ << " " << len << " bytes, stored " << done
222 << " available " << size());
227 PINDEX CircularBuffer::Drain(char *outbuf, PINDEX len, Boolean lock) {
228 PINDEX done = 0, todo = 0;
230 PTRACE_CIRC(6, __func__ << " " << len << " bytes, available " << size() );
233 PTRACE(1, __func__ << " Out buffer is NULL");
237 /* protect agains buffer corruption when write thread
239 pthread_mutex_lock(&mutex);
240 PTRACE(6, "aquired lock");
242 while(done != len && !Empty()) {
245 todo = MIN(head - tail, len - done);
248 todo = MIN(capacity - tail, len - done);
250 memcpy(outbuf + done, buffer + tail, todo);
252 increment_tail(todo);
253 PTRACE_CIRC(1, __func__ << " for " << len " bytes, copied " << done
254 << " to output buffer, available in buffer " << size());
258 if(done != len && (lock)) /* && Empty() */ {
259 PTRACE(3, "Buffer underrun for Drain " << len << " bytes");
262 // what to do if not as many bytes are available then
264 if(done != len && (lock)) /* && Empty() */ {
266 pthread_cond_wait(&cond, &mutex);
267 PTRACE_CIRC(2, "Read thread woke up, calling Drain");
268 pthread_mutex_unlock(&mutex); // race with write thread...
269 done += Drain(outbuf + done, len - done, lock);
270 PTRACE_CIRC(2, "Returned from recursive");
274 pthread_mutex_unlock(&mutex);
277 pthread_cond_signal(&cond);
279 PTRACE_CIRC(2, "End Drain " << len << " bytes, fetched " << done
280 << " buffer fill " << size() );