1/*
2 * Copyright 2009, Haiku, Inc.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Michael Lotz <mmlr@mlotz.ch>
7 */
8
9#include "StreamingRingBuffer.h"
10
11#include <Autolock.h>
12
13#include <stdio.h>
14#include <stdlib.h>
15#include <string.h>
16
17#define TRACE(x...)			/*debug_printf("StreamingRingBuffer: "x)*/
18#define TRACE_ERROR(x...)	debug_printf("StreamingRingBuffer: "x)
19
20
21StreamingRingBuffer::StreamingRingBuffer(size_t bufferSize)
22	:
23	fReaderWaiting(false),
24	fWriterWaiting(false),
25	fReaderNotifier(-1),
26	fWriterNotifier(-1),
27	fReaderLocker("StreamingRingBuffer reader"),
28	fWriterLocker("StreamingRingBuffer writer"),
29	fDataLocker("StreamingRingBuffer data"),
30	fBuffer(NULL),
31	fBufferSize(bufferSize),
32	fReadable(0),
33	fReadPosition(0),
34	fWritePosition(0)
35{
36	fReaderNotifier = create_sem(0, "StreamingRingBuffer read notify");
37	fWriterNotifier = create_sem(0, "StreamingRingBuffer write notify");
38
39	fBuffer = (uint8 *)malloc(fBufferSize);
40	if (fBuffer == NULL)
41		fBufferSize = 0;
42}
43
44
45StreamingRingBuffer::~StreamingRingBuffer()
46{
47	delete_sem(fReaderNotifier);
48	delete_sem(fWriterNotifier);
49	free(fBuffer);
50}
51
52
53status_t
54StreamingRingBuffer::InitCheck()
55{
56	if (fReaderNotifier < 0)
57		return fReaderNotifier;
58	if (fWriterNotifier < 0)
59		return fWriterNotifier;
60	if (fBuffer == NULL)
61		return B_NO_MEMORY;
62
63	return B_OK;
64}
65
66
67int32
68StreamingRingBuffer::Read(void *buffer, size_t length, bool onlyBlockOnNoData)
69{
70	BAutolock readerLock(fReaderLocker);
71	if (!readerLock.IsLocked())
72		return B_ERROR;
73
74	BAutolock dataLock(fDataLocker);
75	if (!dataLock.IsLocked())
76		return B_ERROR;
77
78	int32 readSize = 0;
79	while (length > 0) {
80		size_t copyLength = min_c(length, fBufferSize - fReadPosition);
81		copyLength = min_c(copyLength, fReadable);
82
83		if (copyLength == 0) {
84			if (onlyBlockOnNoData && readSize > 0)
85				return readSize;
86
87			fReaderWaiting = true;
88			dataLock.Unlock();
89
90			status_t result;
91			do {
92				TRACE("waiting in reader\n");
93				result = acquire_sem(fReaderNotifier);
94				TRACE("done waiting in reader with status: 0x%08lx\n", result);
95			} while (result == B_INTERRUPTED);
96
97			if (result != B_OK)
98				return result;
99
100			if (!dataLock.Lock())
101				return B_ERROR;
102
103			continue;
104		}
105
106		// support discarding input
107		if (buffer != NULL) {
108			memcpy(buffer, fBuffer + fReadPosition, copyLength);
109			buffer = (uint8 *)buffer + copyLength;
110		}
111
112		fReadPosition = (fReadPosition + copyLength) % fBufferSize;
113		fReadable -= copyLength;
114		readSize += copyLength;
115		length -= copyLength;
116
117		if (fWriterWaiting) {
118			release_sem_etc(fWriterNotifier, 1, B_DO_NOT_RESCHEDULE);
119			fWriterWaiting = false;
120		}
121	}
122
123	return readSize;
124}
125
126
127status_t
128StreamingRingBuffer::Write(const void *buffer, size_t length)
129{
130	BAutolock writerLock(fWriterLocker);
131	if (!writerLock.IsLocked())
132		return B_ERROR;
133
134	BAutolock dataLock(fDataLocker);
135	if (!dataLock.IsLocked())
136		return B_ERROR;
137
138	while (length > 0) {
139		size_t copyLength = min_c(length, fBufferSize - fWritePosition);
140		copyLength = min_c(copyLength, fBufferSize - fReadable);
141
142		if (copyLength == 0) {
143			fWriterWaiting = true;
144			dataLock.Unlock();
145
146			status_t result;
147			do {
148				TRACE("waiting in writer\n");
149				result = acquire_sem(fWriterNotifier);
150				TRACE("done waiting in writer with status: 0x%08lx\n", result);
151			} while (result == B_INTERRUPTED);
152
153			if (result != B_OK)
154				return result;
155
156			if (!dataLock.Lock())
157				return B_ERROR;
158
159			continue;
160		}
161
162		memcpy(fBuffer + fWritePosition, buffer, copyLength);
163		fWritePosition = (fWritePosition + copyLength) % fBufferSize;
164		fReadable += copyLength;
165
166		buffer = (uint8 *)buffer + copyLength;
167		length -= copyLength;
168
169		if (fReaderWaiting) {
170			release_sem_etc(fReaderNotifier, 1, B_DO_NOT_RESCHEDULE);
171			fReaderWaiting = false;
172		}
173	}
174
175	return B_OK;
176}
177