Uncommented beaudio code
[pwlib.git] / src / ptlib / unix / beaudio / BlockFIFO.cxx
blob598be93493aa8146425b76b79598470b3c652b47
1 //
2 // (c) Yuri Kiryanov, openh323@kiryanov.com
3 // for www.Openh323.org by Equivalence
4 //
5 // Portions: 1998-1999, Be Incorporated
6 // Be Sample Code License
8 /*----------------------
9 Be Sample Code License
10 ----------------------
12 Copyright 1991-1999, Be Incorporated.
13 All rights reserved.
15 Redistribution and use in source and binary forms, with or without
16 modification, are permitted provided that the following conditions
17 are met:
19 1. Redistributions of source code must retain the above copyright
20 notice, this list of conditions, and the following disclaimer.
22 2. Redistributions in binary form must reproduce the above copyright
23 notice, this list of conditions, and the following disclaimer in the
24 documentation and/or other materials provided with the distribution.
26 3. The name of the author may not be used to endorse or promote products
27 derived from this software without specific prior written permission.
29 THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
30 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
31 OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
32 PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
33 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
34 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
35 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
36 AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
37 TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
38 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
41 #include <stdio.h>
42 #include <string.h>
43 #include <stdlib.h>
45 #include "BlockFIFO.h"
47 #if NDEBUG
48 #define FPRINTF(x)
49 #else
50 #define FPRINTF(x) fprintf x
51 #endif
53 // if we decice to make the FIFO thread safe, use these macros
54 #define ENTER_GET
55 #define LEAVE_GET
56 #define ENTER_PUT
57 #define LEAVE_PUT
59 BBlockFIFO::BBlockFIFO(size_t blockSize, int32 blockCountPerBuffer, int32 bufferCount, uint32 placementFlags, uint32 lockFlags, const char * name)
61 char tname[64];
62 if (!name) {
63 sprintf(tname, "FIFO(0x%6lx,%ld,%ld)", blockSize, blockCountPerBuffer, bufferCount);
64 name = tname;
66 strncpy(_mName, name, 32);
67 _mName[31] = 0;
68 _mBuffer = 0;
69 _mFlags = 0;
70 _mBlockSize = blockSize;
71 _mBufferSize = blockCountPerBuffer * blockSize;
72 _mAreaSize = _mBufferSize * bufferCount;
73 _mGetOff = 0;
74 _mPutOff = 0;
75 _mGetSem = -1;
76 _mPutSem = -1;
77 _mInitErr = B_OK;
79 size_t s = (_mAreaSize + B_PAGE_SIZE-1) & -B_PAGE_SIZE;
80 if ((blockSize < 1) || (blockCountPerBuffer < 1) || (bufferCount < 2) || (s < B_PAGE_SIZE) || (s > 0x1000000UL)) {
81 _mInitErr = B_BAD_VALUE;
83 else {
84 void * addr = 0;
85 _mArea = create_area(name, &addr, placementFlags, s, lockFlags, B_READ_AREA | B_WRITE_AREA);
86 if (_mArea < 0) {
87 _mInitErr = _mArea;
89 else {
90 _mBuffer = (char *)addr;
91 _mInitErr = Reset();
97 BBlockFIFO::~BBlockFIFO()
99 if (_mArea > -1) delete_area(_mArea);
100 if (_mGetSem > -1) delete_sem(_mGetSem);
101 if (_mPutSem > -1) delete_sem(_mPutSem);
104 status_t
105 BBlockFIFO::InitCheck()
107 return _mInitErr;
110 status_t
111 BBlockFIFO::Reset()
113 if (_mInitErr < 0) return _mInitErr;
114 if (_mGetSem > -1) delete_sem(_mGetSem);
115 if (_mPutSem > -1) delete_sem(_mPutSem);
116 ENTER_GET
117 ENTER_PUT
118 char name[32];
119 sprintf(name, "%.27s Get", _mName);
120 _mGetSem = create_sem(0, name);
121 sprintf(name, "%.27s Put", _mName);
122 _mPutSem = create_sem(_mAreaSize, name);
123 if (_mGetSem < 0) _mInitErr = _mGetSem;
124 if (_mPutSem < 0) _mInitErr = _mPutSem;
125 _mGetOff = 0;
126 _mPutOff = 0;
127 _mFlags = 0;
128 LEAVE_PUT
129 LEAVE_GET
130 return _mInitErr;
133 int32
134 BBlockFIFO::SizeAvailableToGet()
136 ENTER_GET // there's always a race with put, so don't pretend to protect it
137 int32 s = _mPutOff - _mGetOff;
138 if (s < 0) s += _mAreaSize;
139 LEAVE_GET
140 return s;
143 int32
144 BBlockFIFO::SizeAvailableToPut()
146 ENTER_PUT // there's always a race with get, so don't pretend to protect it
147 int32 s = _mGetOff - _mPutOff;
148 if (s <= 0) s += _mAreaSize;
149 LEAVE_PUT
150 return s;
153 int32
154 BBlockFIFO::BeginGet(const void **outData, size_t requestSize, bigtime_t timeout)
156 if (!outData) { FPRINTF((stderr, "BAD_VALUE: outData is NULL\n")); return B_BAD_VALUE; }
157 ENTER_GET
158 //FPRINTF((stderr, "requestSize %ld _mBlockSize %ld _mAreaSize %ld _mGetOff %ld\n",
159 // requestSize, _mBlockSize, _mAreaSize, _mGetOff));
160 if (requestSize > _mBlockSize) {
161 requestSize = _mBlockSize;
163 size_t o = _mGetOff + requestSize;
164 if (o > _mAreaSize) {
165 o = _mAreaSize;
167 int32 req = o-_mGetOff;
168 if (_mFlags & flagEndOfData) {
169 int32 tg = _mPutOff-_mGetOff;
170 if (tg < 0) tg += _mAreaSize;
171 if (tg < req) {
172 req = tg;
173 if (req == 0) return 0;
174 o = _mGetOff + req;
177 status_t err = acquire_sem_etc(_mGetSem, req, B_TIMEOUT, timeout);
178 if (err < B_OK) {
179 if (((err == B_TIMED_OUT) || (err == B_BAD_SEM_ID)) && (_mFlags & flagEndOfData)) {
180 int32 tg = _mPutOff-_mGetOff;
181 if (tg < 0) tg += _mAreaSize;
182 if (tg < req) {
183 req = tg;
184 if (req == 0) return 0;
185 o = _mGetOff + req;
187 goto got_it;
189 LEAVE_GET
190 return err;
192 got_it:
193 *outData = _mBuffer + _mGetOff;
194 if (o == _mAreaSize)
195 _mPendingGet = 0;
196 else
197 _mPendingGet = o;
198 atomic_or(&_mFlags, flagPendingGet);
199 return req;
202 int32
203 BBlockFIFO::EndGet()
205 if (!(atomic_and(&_mFlags, ~flagPendingGet) & flagPendingGet))
206 return B_ERROR;
207 int32 o = _mPendingGet - _mGetOff;
208 _mGetOff = _mPendingGet;
209 if (o < 0) o += _mAreaSize;
210 // part of buffer is now free to put into again
211 status_t err = release_sem_etc(_mPutSem, o, B_DO_NOT_RESCHEDULE);
212 LEAVE_GET
213 return err;
216 int32
217 BBlockFIFO::BeginPut(void **outData, size_t requestSize, bigtime_t timeout)
219 if (!outData) { FPRINTF((stderr, "BAD_VALUE: outData == NULL\n")); return B_BAD_VALUE; }
220 if (_mFlags & flagEndOfData) { FPRINTF((stderr, "EPERM: end of data\n")); return EPERM; }
221 ENTER_GET
222 if (requestSize > _mBufferSize) {
223 requestSize = _mBufferSize;
225 ssize_t o = _mPutOff + requestSize;
226 if (o > (ssize_t)_mAreaSize) {
227 o = _mAreaSize;
229 int32 req = o-_mPutOff;
230 status_t err = acquire_sem_etc(_mPutSem, req, B_TIMEOUT, timeout);
231 if (err < B_OK) {
232 LEAVE_PUT
233 FPRINTF((stderr, "BeginPut: acquire_sem_etc() returns %ld (req is %ld)\n", err, req));
234 return err;
236 *outData = _mBuffer + _mPutOff;
237 if (o == (ssize_t)_mAreaSize)
238 _mPendingPut = 0;
239 else
240 _mPendingPut = o;
241 atomic_or(&_mFlags, flagPendingPut);
242 return req;
245 int32
246 BBlockFIFO::EndPut(bool atEndOfData)
248 if (!(atomic_and(&_mFlags, ~flagPendingPut) & flagPendingPut))
249 return B_ERROR;
250 int32 o = _mPendingPut - _mPutOff;
251 _mPutOff = _mPendingPut;
252 if (o < 0) o += _mAreaSize;
253 // part of buffer is now full to get from again
254 status_t err = release_sem_etc(_mGetSem, o, B_DO_NOT_RESCHEDULE);
255 if (atEndOfData) {
256 atomic_or(&_mFlags, flagEndOfData);
257 delete_sem(_mGetSem);
258 _mGetSem = -1;
260 LEAVE_PUT
261 return err;
264 int32
265 BBlockFIFO::CopyNextBlockOut(void *destination, size_t requestSize, bigtime_t timeout)
267 if (destination == 0) return B_BAD_VALUE;
268 if (requestSize == 0) return 0;
269 char * d = (char *)destination;
270 ssize_t total = 0;
271 while (requestSize > 0) {
272 const void * ptr;
273 ssize_t got = BeginGet(&ptr, requestSize, timeout);
274 if (got < 0) { FPRINTF((stderr, "BeginGet returns %ld\n", got)); return (total > 0 ? total : got); }
275 requestSize -= got;
276 memcpy(d, ptr, got);
277 (void)EndGet();
278 d += got;
279 total += got;
281 return total;
284 int32
285 BBlockFIFO::CopyNextBufferIn(const void *source, size_t requestSize, bigtime_t timeout, bool atEndOfData)
287 if (source == 0) { FPRINTF((stderr, "BAD_VALUE: source == NULL\n")); return B_BAD_VALUE; }
288 if (requestSize == 0) {
289 if (atEndOfData) {
290 ENTER_PUT
291 atomic_or(&_mFlags, flagEndOfData);
292 delete_sem(_mGetSem);
293 _mGetSem = -1;
294 LEAVE_PUT
296 return 0;
298 char * s = (char *)source;
299 int32 total = 0;
300 while (requestSize > 0) {
301 void * ptr;
302 int got = BeginPut(&ptr, requestSize, timeout);
303 if (got < 0) return (total > 0 ? total : got);
304 requestSize -= got;
305 memcpy(ptr, s, got);
306 s += got;
307 total += got;
308 (void)EndPut((requestSize == 0) ? atEndOfData : false);
310 return total;