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:
Lock(pthread_mutex_t * mutex)22 inline bool Lock(pthread_mutex_t* mutex)
23 {
24 return pthread_mutex_lock(mutex) == 0;
25 }
26
Unlock(pthread_mutex_t * mutex)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 {
operator ()ReadCondition38 inline bool operator()(BMemoryRingIO &ring) {
39 return ring.BytesAvailable() != 0;
40 }
41 };
42
43
44 struct WriteCondition {
operator ()WriteCondition45 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
next_power_of_two(size_t value)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
BMemoryRingIO(size_t size)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
~BMemoryRingIO()92 BMemoryRingIO::~BMemoryRingIO()
93 {
94 SetSize(0);
95
96 pthread_mutex_destroy(&fLock);
97 pthread_cond_destroy(&fEvent);
98 }
99
100
101 status_t
InitCheck() const102 BMemoryRingIO::InitCheck() const
103 {
104 if (fBufferSize == 0)
105 return B_NO_INIT;
106
107 return B_OK;
108 }
109
110
111 ssize_t
Read(void * _buffer,size_t size)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
Write(const void * _buffer,size_t size)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
SetSize(size_t _size)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
Clear()219 BMemoryRingIO::Clear()
220 {
221 PThreadAutoLocker locker(fLock);
222
223 fReadAtNext = 0;
224 fWriteAtNext = 0;
225 fBufferFull = false;
226 }
227
228
229 size_t
BytesAvailable()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
SpaceAvailable()244 BMemoryRingIO::SpaceAvailable()
245 {
246 PThreadAutoLocker locker(fLock);
247
248 return fBufferSize - BytesAvailable();
249 }
250
251
252 size_t
BufferSize()253 BMemoryRingIO::BufferSize()
254 {
255 PThreadAutoLocker locker(fLock);
256
257 return fBufferSize;
258 }
259
260
261 template<typename Condition>
262 status_t
_WaitForCondition(bigtime_t timeout)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
WaitForRead(bigtime_t timeout)303 BMemoryRingIO::WaitForRead(bigtime_t timeout)
304 {
305 return _WaitForCondition<ReadCondition>(timeout);
306 }
307
308
309 status_t
WaitForWrite(bigtime_t timeout)310 BMemoryRingIO::WaitForWrite(bigtime_t timeout)
311 {
312 return _WaitForCondition<WriteCondition>(timeout);
313 }
314
315
316 void
SetWriteDisabled(bool disabled)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
WriteDisabled()328 BMemoryRingIO::WriteDisabled()
329 {
330 PThreadAutoLocker autoLocker(fLock);
331
332 return fWriteDisabled;
333 }
334