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