xref: /haiku/src/kits/shared/MemoryRingIO.cpp (revision 4a55cc230cf7566cadcbb23b1928eefff8aea9a2)
1 /*
2  * Copyright 2022 Haiku, Inc. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Leorize, leorize+oss@disroot.org
7  */
8 
9 
10 #include <MemoryRingIO.h>
11 
12 #include <AutoLocker.h>
13 
14 #include <algorithm>
15 
16 #include <stdlib.h>
17 #include <string.h>
18 
19 
20 class PThreadLocking {
21 public:
22 	inline bool Lock(pthread_mutex_t* mutex)
23 	{
24 		return pthread_mutex_lock(mutex) == 0;
25 	}
26 
27 	inline void Unlock(pthread_mutex_t* mutex)
28 	{
29 		pthread_mutex_unlock(mutex);
30 	}
31 };
32 
33 
34 typedef AutoLocker<pthread_mutex_t, PThreadLocking> PThreadAutoLocker;
35 
36 
37 struct ReadCondition {
38 	inline bool operator()(BMemoryRingIO &ring) {
39 		return ring.BytesAvailable() != 0;
40 	}
41 };
42 
43 
44 struct WriteCondition {
45 	inline bool operator()(BMemoryRingIO &ring) {
46 		return ring.SpaceAvailable() != 0;
47 	}
48 };
49 
50 
51 #define RING_MASK(x) ((x) & (fBufferSize - 1))
52 
53 
54 static size_t
55 next_power_of_two(size_t value)
56 {
57 	value--;
58 	value |= value >> 1;
59 	value |= value >> 2;
60 	value |= value >> 4;
61 	value |= value >> 8;
62 	value |= value >> 16;
63 #if SIZE_MAX >= UINT64_MAX
64 	value |= value >> 32;
65 #endif
66 	value++;
67 
68 	return value;
69 }
70 
71 
72 BMemoryRingIO::BMemoryRingIO(size_t size)
73 	:
74 	fBuffer(NULL),
75 	fBufferSize(0),
76 	fWriteAtNext(0),
77 	fReadAtNext(0),
78 	fBufferFull(false),
79 	fWriteDisabled(false)
80 {
81 	// We avoid the use of pthread_mutexattr as it can possibly fail.
82 	//
83 	// The only Haiku-specific behavior that we depend on is that
84 	// PTHREAD_MUTEX_DEFAULT mutexes check for double-locks.
85 	pthread_mutex_init(&fLock, NULL);
86 	pthread_cond_init(&fEvent, NULL);
87 
88 	SetSize(size);
89 }
90 
91 
92 BMemoryRingIO::~BMemoryRingIO()
93 {
94 	SetSize(0);
95 
96 	pthread_mutex_destroy(&fLock);
97 	pthread_cond_destroy(&fEvent);
98 }
99 
100 
101 status_t
102 BMemoryRingIO::InitCheck() const
103 {
104 	if (fBufferSize == 0)
105 		return B_NO_INIT;
106 
107 	return B_OK;
108 }
109 
110 
111 ssize_t
112 BMemoryRingIO::Read(void* _buffer, size_t size)
113 {
114 	if (_buffer == NULL)
115 		return B_BAD_VALUE;
116 	if (size == 0)
117 		return 0;
118 
119 	PThreadAutoLocker _(fLock);
120 
121 	if (!fWriteDisabled)
122 		WaitForRead();
123 
124 	size = std::min(size, BytesAvailable());
125 	uint8* buffer = reinterpret_cast<uint8*>(_buffer);
126 	if (fReadAtNext + size < fBufferSize)
127 		memcpy(buffer, fBuffer + fReadAtNext, size);
128 	else {
129 		const size_t upper = fBufferSize - fReadAtNext;
130 		const size_t lower = size - upper;
131 		memcpy(buffer, fBuffer + fReadAtNext, upper);
132 		memcpy(buffer + upper, fBuffer, lower);
133 	}
134 	fReadAtNext = RING_MASK(fReadAtNext + size);
135 	fBufferFull = false;
136 
137 	pthread_cond_signal(&fEvent);
138 
139 	return size;
140 }
141 
142 
143 ssize_t
144 BMemoryRingIO::Write(const void* _buffer, size_t size)
145 {
146 	if (_buffer == NULL)
147 		return B_BAD_VALUE;
148 	if (size == 0)
149 		return 0;
150 
151 	PThreadAutoLocker locker(fLock);
152 
153 	if (!fWriteDisabled)
154 		WaitForWrite();
155 
156 	// We separate this check from WaitForWrite() as the boolean
157 	// might have been toggled during our wait on the conditional.
158 	if (fWriteDisabled)
159 		return B_READ_ONLY_DEVICE;
160 
161 	const uint8* buffer = reinterpret_cast<const uint8*>(_buffer);
162 	size = std::min(size, SpaceAvailable());
163 	if (fWriteAtNext + size < fBufferSize)
164 		memcpy(fBuffer + fWriteAtNext, buffer, size);
165 	else {
166 		const size_t upper = fBufferSize - fWriteAtNext;
167 		const size_t lower = size - upper;
168 		memcpy(fBuffer + fWriteAtNext, buffer, size);
169 		memcpy(fBuffer, buffer + upper, lower);
170 	}
171 	fWriteAtNext = RING_MASK(fWriteAtNext + size);
172 	fBufferFull = fReadAtNext == fWriteAtNext;
173 
174 	pthread_cond_signal(&fEvent);
175 
176 	return size;
177 }
178 
179 
180 status_t
181 BMemoryRingIO::SetSize(size_t _size)
182 {
183 	PThreadAutoLocker locker(fLock);
184 
185 	const size_t size = next_power_of_two(_size);
186 
187 	const size_t availableBytes = BytesAvailable();
188 	if (size < availableBytes)
189 		return B_BAD_VALUE;
190 
191 	if (size == 0) {
192 		free(fBuffer);
193 		fBuffer = NULL;
194 		fBufferSize = 0;
195 		Clear(); // resets other internal counters
196 		return B_OK;
197 	}
198 
199 	uint8* newBuffer = reinterpret_cast<uint8*>(malloc(size));
200 	if (newBuffer == NULL)
201 		return B_NO_MEMORY;
202 
203 	Read(newBuffer, availableBytes);
204 	free(fBuffer);
205 
206 	fBuffer = newBuffer;
207 	fBufferSize = size;
208 	fReadAtNext = 0;
209 	fWriteAtNext = RING_MASK(availableBytes);
210 	fBufferFull = fBufferSize == availableBytes;
211 
212 	pthread_cond_signal(&fEvent);
213 
214 	return B_OK;
215 }
216 
217 
218 void
219 BMemoryRingIO::Clear()
220 {
221 	PThreadAutoLocker locker(fLock);
222 
223 	fReadAtNext = 0;
224 	fWriteAtNext = 0;
225 	fBufferFull = false;
226 }
227 
228 
229 size_t
230 BMemoryRingIO::BytesAvailable()
231 {
232 	PThreadAutoLocker locker(fLock);
233 
234 	if (fWriteAtNext == fReadAtNext) {
235 		if (fBufferFull)
236 			return fBufferSize;
237 		return 0;
238 	}
239 	return RING_MASK(fWriteAtNext - fReadAtNext);
240 }
241 
242 
243 size_t
244 BMemoryRingIO::SpaceAvailable()
245 {
246 	PThreadAutoLocker locker(fLock);
247 
248 	return fBufferSize - BytesAvailable();
249 }
250 
251 
252 size_t
253 BMemoryRingIO::BufferSize()
254 {
255 	PThreadAutoLocker locker(fLock);
256 
257 	return fBufferSize;
258 }
259 
260 
261 template<typename Condition>
262 status_t
263 BMemoryRingIO::_WaitForCondition(bigtime_t timeout)
264 {
265 	PThreadAutoLocker autoLocker;
266 
267 	struct timespec absTimeout;
268 	if (timeout == B_INFINITE_TIMEOUT) {
269 		autoLocker.SetTo(fLock, false);
270 	} else {
271 		memset(&absTimeout, 0, sizeof(absTimeout));
272 		bigtime_t target = system_time() + timeout;
273 		absTimeout.tv_sec = target / 100000;
274 		absTimeout.tv_nsec = (target % 100000) * 1000L;
275 		int err = pthread_mutex_timedlock(&fLock, &absTimeout);
276 		if (err == ETIMEDOUT)
277 			return B_TIMED_OUT;
278 		if (err != EDEADLK)
279 			autoLocker.SetTo(fLock, true);
280 	}
281 
282 	Condition cond;
283 	while (!cond(*this)) {
284 		if (fWriteDisabled)
285 			return B_READ_ONLY_DEVICE;
286 
287 		int err = 0;
288 
289 		if (timeout == B_INFINITE_TIMEOUT)
290 			err = pthread_cond_wait(&fEvent, &fLock);
291 		else
292 			err = pthread_cond_timedwait(&fEvent, &fLock, &absTimeout);
293 
294 		if (err != 0)
295 			return err;
296 	}
297 
298 	return B_OK;
299 }
300 
301 
302 status_t
303 BMemoryRingIO::WaitForRead(bigtime_t timeout)
304 {
305 	return _WaitForCondition<ReadCondition>(timeout);
306 }
307 
308 
309 status_t
310 BMemoryRingIO::WaitForWrite(bigtime_t timeout)
311 {
312 	return _WaitForCondition<WriteCondition>(timeout);
313 }
314 
315 
316 void
317 BMemoryRingIO::SetWriteDisabled(bool disabled)
318 {
319 	PThreadAutoLocker autoLocker(fLock);
320 
321 	fWriteDisabled = disabled;
322 
323 	pthread_cond_broadcast(&fEvent);
324 }
325 
326 
327 bool
328 BMemoryRingIO::WriteDisabled()
329 {
330 	PThreadAutoLocker autoLocker(fLock);
331 
332 	return fWriteDisabled;
333 }
334