1 /* 2 * Copyright (c) 2002, 2003 Marcus Overhagen <Marcus@Overhagen.de> 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining 5 * a copy of this software and associated documentation files or portions 6 * thereof (the "Software"), to deal in the Software without restriction, 7 * including without limitation the rights to use, copy, modify, merge, 8 * publish, distribute, sublicense, and/or sell copies of the Software, 9 * and to permit persons to whom the Software is furnished to do so, subject 10 * to the following conditions: 11 * 12 * * Redistributions of source code must retain the above copyright notice, 13 * this list of conditions and the following disclaimer. 14 * 15 * * Redistributions in binary form must reproduce the above copyright notice 16 * in the binary, as well as this list of conditions and the following 17 * disclaimer in the documentation and/or other materials provided with 18 * the distribution. 19 * 20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 21 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 23 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 26 * THE SOFTWARE. 27 * 28 */ 29 30 31 #include <BufferGroup.h> 32 33 #include <Buffer.h> 34 35 #include "debug.h" 36 #include "DataExchange.h" 37 #include "SharedBufferList.h" 38 39 40 BBufferGroup::BBufferGroup(size_t size, int32 count, uint32 placement, 41 uint32 lock) 42 { 43 CALLED(); 44 if (_Init() != B_OK) 45 return; 46 47 // This one is easy. We need to create "count" BBuffers, 48 // each one "size" bytes large. They all go into one 49 // area, with "placement" and "lock" attributes. 50 // The BBuffers created will clone the area, and 51 // then we delete our area. This way BBuffers are 52 // independent from the BBufferGroup 53 54 // don't allow all placement parameter values 55 if (placement != B_ANY_ADDRESS && placement != B_ANY_KERNEL_ADDRESS) { 56 ERROR("BBufferGroup: placement != B_ANY_ADDRESS " 57 "&& placement != B_ANY_KERNEL_ADDRESS (0x%08lx)\n", placement); 58 placement = B_ANY_ADDRESS; 59 } 60 61 // first we roundup for a better placement in memory 62 size_t allocSize = (size + 63) & ~63; 63 64 // now we create the area 65 size_t areaSize 66 = ((allocSize * count) + B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1); 67 68 void* startAddress; 69 area_id bufferArea = create_area("some buffers area", &startAddress, 70 placement, areaSize, lock, B_READ_AREA | B_WRITE_AREA); 71 if (bufferArea < 0) { 72 ERROR("BBufferGroup: failed to allocate %ld bytes area\n", areaSize); 73 fInitError = (status_t)bufferArea; 74 return; 75 } 76 77 buffer_clone_info info; 78 79 for (int32 i = 0; i < count; i++) { 80 info.area = bufferArea; 81 info.offset = i * allocSize; 82 info.size = size; 83 84 fInitError = AddBuffer(info); 85 if (fInitError != B_OK) 86 break; 87 } 88 89 delete_area(bufferArea); 90 } 91 92 93 BBufferGroup::BBufferGroup() 94 { 95 CALLED(); 96 if (_Init() != B_OK) 97 return; 98 99 // this one simply creates an empty BBufferGroup 100 } 101 102 103 BBufferGroup::BBufferGroup(int32 count, const media_buffer_id* buffers) 104 { 105 CALLED(); 106 if (_Init() != B_OK) 107 return; 108 109 // TODO: we need to make sure that a media_buffer_id is only added 110 // once to each group 111 112 // this one creates "BBuffer"s from "media_buffer_id"s passed 113 // by the application. 114 115 buffer_clone_info info; 116 117 for (int32 i = 0; i < count; i++) { 118 info.buffer = buffers[i]; 119 120 fInitError = AddBuffer(info); 121 if (fInitError != B_OK) 122 break; 123 } 124 } 125 126 127 BBufferGroup::~BBufferGroup() 128 { 129 CALLED(); 130 if (fBufferList != NULL) 131 fBufferList->DeleteGroupAndPut(fReclaimSem); 132 133 delete_sem(fReclaimSem); 134 } 135 136 137 status_t 138 BBufferGroup::InitCheck() 139 { 140 CALLED(); 141 return fInitError; 142 } 143 144 145 status_t 146 BBufferGroup::AddBuffer(const buffer_clone_info& info, BBuffer** _buffer) 147 { 148 CALLED(); 149 if (fInitError != B_OK) 150 return B_NO_INIT; 151 152 // TODO: we need to make sure that a media_buffer_id is only added 153 // once to each group 154 155 BBuffer* buffer = new(std::nothrow) BBuffer(info); 156 if (buffer == NULL) 157 return B_NO_MEMORY; 158 159 if (buffer->Data() == NULL) { 160 // BBuffer::Data() will return NULL if an error occured 161 ERROR("BBufferGroup: error while creating buffer\n"); 162 delete buffer; 163 return B_ERROR; 164 } 165 166 status_t status = fBufferList->AddBuffer(fReclaimSem, buffer); 167 if (status != B_OK) { 168 ERROR("BBufferGroup: error when adding buffer\n"); 169 delete buffer; 170 return status; 171 } 172 173 atomic_add(&fBufferCount, 1); 174 175 if (_buffer != NULL) 176 *_buffer = buffer; 177 178 return B_OK; 179 } 180 181 182 BBuffer* 183 BBufferGroup::RequestBuffer(size_t size, bigtime_t timeout) 184 { 185 CALLED(); 186 if (fInitError != B_OK) 187 return NULL; 188 189 if (size <= 0) 190 return NULL; 191 192 BBuffer *buffer; 193 status_t status; 194 195 buffer = NULL; 196 status = fBufferList->RequestBuffer(fReclaimSem, fBufferCount, size, 0, 197 &buffer, timeout); 198 fRequestError = status; 199 200 return status == B_OK ? buffer : NULL; 201 } 202 203 204 status_t 205 BBufferGroup::RequestBuffer(BBuffer* buffer, bigtime_t timeout) 206 { 207 CALLED(); 208 if (fInitError != B_OK) 209 return B_NO_INIT; 210 211 if (buffer == NULL) 212 return B_BAD_VALUE; 213 214 status_t status; 215 status = fBufferList->RequestBuffer(fReclaimSem, fBufferCount, 0, 0, 216 &buffer, timeout); 217 fRequestError = status; 218 219 return status; 220 } 221 222 223 status_t 224 BBufferGroup::RequestError() 225 { 226 CALLED(); 227 if (fInitError != B_OK) 228 return B_NO_INIT; 229 230 return fRequestError; 231 } 232 233 234 status_t 235 BBufferGroup::CountBuffers(int32* _count) 236 { 237 CALLED(); 238 if (fInitError != B_OK) 239 return B_NO_INIT; 240 241 *_count = fBufferCount; 242 return B_OK; 243 } 244 245 246 status_t 247 BBufferGroup::GetBufferList(int32 bufferCount, BBuffer** _buffers) 248 { 249 CALLED(); 250 if (fInitError != B_OK) 251 return B_NO_INIT; 252 253 if (bufferCount <= 0 || bufferCount > fBufferCount) 254 return B_BAD_VALUE; 255 256 return fBufferList->GetBufferList(fReclaimSem, bufferCount, _buffers); 257 } 258 259 260 status_t 261 BBufferGroup::WaitForBuffers() 262 { 263 CALLED(); 264 if (fInitError != B_OK) 265 return B_NO_INIT; 266 267 // TODO: this function is not really useful anyway, and will 268 // not work exactly as documented, but it is close enough 269 270 if (fBufferCount < 0) 271 return B_BAD_VALUE; 272 if (fBufferCount == 0) 273 return B_OK; 274 275 // We need to wait until at least one buffer belonging to this group is 276 // reclaimed. 277 // This has happened when can aquire "fReclaimSem" 278 279 status_t status; 280 while ((status = acquire_sem(fReclaimSem)) == B_INTERRUPTED) 281 ; 282 if (status != B_OK) 283 return status; 284 285 // we need to release the "fReclaimSem" now, else we would block 286 // requesting of new buffers 287 288 return release_sem(fReclaimSem); 289 } 290 291 292 status_t 293 BBufferGroup::ReclaimAllBuffers() 294 { 295 CALLED(); 296 if (fInitError != B_OK) 297 return B_NO_INIT; 298 299 // because additional BBuffers might get added to this group betweeen 300 // acquire and release 301 int32 count = fBufferCount; 302 303 if (count < 0) 304 return B_BAD_VALUE; 305 if (count == 0) 306 return B_OK; 307 308 // we need to wait until all BBuffers belonging to this group are reclaimed. 309 // this has happened when the "fReclaimSem" can be aquired "fBufferCount" 310 // times 311 312 status_t status; 313 do { 314 status = acquire_sem_etc(fReclaimSem, count, 0, 0); 315 } while (status == B_INTERRUPTED); 316 317 if (status != B_OK) 318 return status; 319 320 // we need to release the "fReclaimSem" now, else we would block 321 // requesting of new buffers 322 323 return release_sem_etc(fReclaimSem, count, 0); 324 } 325 326 327 // #pragma mark - deprecated BeOS R4 API 328 329 330 status_t 331 BBufferGroup::AddBuffersTo(BMessage* message, const char* name, bool needLock) 332 { 333 CALLED(); 334 if (fInitError != B_OK) 335 return B_NO_INIT; 336 337 // BeOS R4 legacy API. Implemented as a wrapper around GetBufferList 338 // "needLock" is ignored, GetBufferList will do locking 339 340 if (message == NULL) 341 return B_BAD_VALUE; 342 343 if (name == NULL || strlen(name) == 0) 344 return B_BAD_VALUE; 345 346 BBuffer** buffers; 347 int32 count; 348 349 count = fBufferCount; 350 buffers = new BBuffer * [count]; 351 352 status_t status = GetBufferList(count, buffers); 353 if (status != B_OK) 354 goto end; 355 356 for (int32 i = 0; i < count; i++) { 357 status = message->AddInt32(name, int32(buffers[i]->ID())); 358 if (status != B_OK) 359 goto end; 360 } 361 362 end: 363 delete [] buffers; 364 return status; 365 } 366 367 368 // #pragma mark - private methods 369 370 371 /* not implemented */ 372 //BBufferGroup::BBufferGroup(const BBufferGroup &) 373 //BBufferGroup & BBufferGroup::operator=(const BBufferGroup &) 374 375 376 status_t 377 BBufferGroup::_Init() 378 { 379 CALLED(); 380 381 // some defaults in case we drop out early 382 fBufferList = 0; 383 fInitError = B_ERROR; 384 fRequestError = B_ERROR; 385 fBufferCount = 0; 386 387 // Create the reclaim semaphore 388 // This is also used as a system wide unique identifier for this group 389 fReclaimSem = create_sem(0, "buffer reclaim sem"); 390 if (fReclaimSem < B_OK) { 391 ERROR("BBufferGroup::InitBufferGroup: couldn't create fReclaimSem\n"); 392 fInitError = (status_t)fReclaimSem; 393 return fInitError; 394 } 395 396 fBufferList = BPrivate::SharedBufferList::Get(); 397 if (fBufferList == NULL) { 398 ERROR("BBufferGroup::InitBufferGroup: SharedBufferList::Get() " 399 "failed\n"); 400 fInitError = B_ERROR; 401 return fInitError; 402 } 403 404 fInitError = B_OK; 405 return fInitError; 406 } 407 408