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%#" B_PRIx32 ")\n", 58 placement); 59 placement = B_ANY_ADDRESS; 60 } 61 62 // first we roundup for a better placement in memory 63 size_t allocSize = (size + 63) & ~63; 64 65 // now we create the area 66 size_t areaSize 67 = ((allocSize * count) + B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1); 68 69 void* startAddress; 70 area_id bufferArea = create_area("some buffers area", &startAddress, 71 placement, areaSize, lock, B_READ_AREA | B_WRITE_AREA); 72 if (bufferArea < 0) { 73 ERROR("BBufferGroup: failed to allocate %ld bytes area\n", areaSize); 74 fInitError = (status_t)bufferArea; 75 return; 76 } 77 78 buffer_clone_info info; 79 80 for (int32 i = 0; i < count; i++) { 81 info.area = bufferArea; 82 info.offset = i * allocSize; 83 info.size = size; 84 85 fInitError = AddBuffer(info); 86 if (fInitError != B_OK) 87 break; 88 } 89 90 delete_area(bufferArea); 91 } 92 93 94 BBufferGroup::BBufferGroup() 95 { 96 CALLED(); 97 if (_Init() != B_OK) 98 return; 99 100 // this one simply creates an empty BBufferGroup 101 } 102 103 104 BBufferGroup::BBufferGroup(int32 count, const media_buffer_id* buffers) 105 { 106 CALLED(); 107 if (_Init() != B_OK) 108 return; 109 110 // TODO: we need to make sure that a media_buffer_id is only added 111 // once to each group 112 113 // this one creates "BBuffer"s from "media_buffer_id"s passed 114 // by the application. 115 116 buffer_clone_info info; 117 118 for (int32 i = 0; i < count; i++) { 119 info.buffer = buffers[i]; 120 121 fInitError = AddBuffer(info); 122 if (fInitError != B_OK) 123 break; 124 } 125 } 126 127 128 BBufferGroup::~BBufferGroup() 129 { 130 CALLED(); 131 if (fBufferList != NULL) 132 fBufferList->DeleteGroupAndPut(fReclaimSem); 133 134 delete_sem(fReclaimSem); 135 } 136 137 138 status_t 139 BBufferGroup::InitCheck() 140 { 141 CALLED(); 142 return fInitError; 143 } 144 145 146 status_t 147 BBufferGroup::AddBuffer(const buffer_clone_info& info, BBuffer** _buffer) 148 { 149 CALLED(); 150 if (fInitError != B_OK) 151 return B_NO_INIT; 152 153 // TODO: we need to make sure that a media_buffer_id is only added 154 // once to each group 155 156 BBuffer* buffer = new(std::nothrow) BBuffer(info); 157 if (buffer == NULL) 158 return B_NO_MEMORY; 159 160 if (buffer->Data() == NULL) { 161 // BBuffer::Data() will return NULL if an error occured 162 ERROR("BBufferGroup: error while creating buffer\n"); 163 delete buffer; 164 return B_ERROR; 165 } 166 167 status_t status = fBufferList->AddBuffer(fReclaimSem, buffer); 168 if (status != B_OK) { 169 ERROR("BBufferGroup: error when adding buffer\n"); 170 delete buffer; 171 return status; 172 } 173 174 atomic_add(&fBufferCount, 1); 175 176 if (_buffer != NULL) 177 *_buffer = buffer; 178 179 return B_OK; 180 } 181 182 183 BBuffer* 184 BBufferGroup::RequestBuffer(size_t size, bigtime_t timeout) 185 { 186 CALLED(); 187 if (fInitError != B_OK) 188 return NULL; 189 190 if (size <= 0) 191 return NULL; 192 193 BBuffer *buffer; 194 status_t status; 195 196 buffer = NULL; 197 status = fBufferList->RequestBuffer(fReclaimSem, fBufferCount, size, 0, 198 &buffer, timeout); 199 fRequestError = status; 200 201 return status == B_OK ? buffer : NULL; 202 } 203 204 205 status_t 206 BBufferGroup::RequestBuffer(BBuffer* buffer, bigtime_t timeout) 207 { 208 CALLED(); 209 if (fInitError != B_OK) 210 return B_NO_INIT; 211 212 if (buffer == NULL) 213 return B_BAD_VALUE; 214 215 status_t status; 216 status = fBufferList->RequestBuffer(fReclaimSem, fBufferCount, 0, 0, 217 &buffer, timeout); 218 fRequestError = status; 219 220 return status; 221 } 222 223 224 status_t 225 BBufferGroup::RequestError() 226 { 227 CALLED(); 228 if (fInitError != B_OK) 229 return B_NO_INIT; 230 231 return fRequestError; 232 } 233 234 235 status_t 236 BBufferGroup::CountBuffers(int32* _count) 237 { 238 CALLED(); 239 if (fInitError != B_OK) 240 return B_NO_INIT; 241 242 *_count = fBufferCount; 243 return B_OK; 244 } 245 246 247 status_t 248 BBufferGroup::GetBufferList(int32 bufferCount, BBuffer** _buffers) 249 { 250 CALLED(); 251 if (fInitError != B_OK) 252 return B_NO_INIT; 253 254 if (bufferCount <= 0 || bufferCount > fBufferCount) 255 return B_BAD_VALUE; 256 257 return fBufferList->GetBufferList(fReclaimSem, bufferCount, _buffers); 258 } 259 260 261 status_t 262 BBufferGroup::WaitForBuffers() 263 { 264 CALLED(); 265 if (fInitError != B_OK) 266 return B_NO_INIT; 267 268 // TODO: this function is not really useful anyway, and will 269 // not work exactly as documented, but it is close enough 270 271 if (fBufferCount < 0) 272 return B_BAD_VALUE; 273 if (fBufferCount == 0) 274 return B_OK; 275 276 // We need to wait until at least one buffer belonging to this group is 277 // reclaimed. 278 // This has happened when can aquire "fReclaimSem" 279 280 status_t status; 281 while ((status = acquire_sem(fReclaimSem)) == B_INTERRUPTED) 282 ; 283 if (status != B_OK) 284 return status; 285 286 // we need to release the "fReclaimSem" now, else we would block 287 // requesting of new buffers 288 289 return release_sem(fReclaimSem); 290 } 291 292 293 status_t 294 BBufferGroup::ReclaimAllBuffers() 295 { 296 CALLED(); 297 if (fInitError != B_OK) 298 return B_NO_INIT; 299 300 // because additional BBuffers might get added to this group betweeen 301 // acquire and release 302 int32 count = fBufferCount; 303 304 if (count < 0) 305 return B_BAD_VALUE; 306 if (count == 0) 307 return B_OK; 308 309 // we need to wait until all BBuffers belonging to this group are reclaimed. 310 // this has happened when the "fReclaimSem" can be aquired "fBufferCount" 311 // times 312 313 status_t status; 314 do { 315 status = acquire_sem_etc(fReclaimSem, count, 0, 0); 316 } while (status == B_INTERRUPTED); 317 318 if (status != B_OK) 319 return status; 320 321 // we need to release the "fReclaimSem" now, else we would block 322 // requesting of new buffers 323 324 return release_sem_etc(fReclaimSem, count, 0); 325 } 326 327 328 // #pragma mark - deprecated BeOS R4 API 329 330 331 status_t 332 BBufferGroup::AddBuffersTo(BMessage* message, const char* name, bool needLock) 333 { 334 CALLED(); 335 if (fInitError != B_OK) 336 return B_NO_INIT; 337 338 // BeOS R4 legacy API. Implemented as a wrapper around GetBufferList 339 // "needLock" is ignored, GetBufferList will do locking 340 341 if (message == NULL) 342 return B_BAD_VALUE; 343 344 if (name == NULL || strlen(name) == 0) 345 return B_BAD_VALUE; 346 347 BBuffer** buffers; 348 int32 count; 349 350 count = fBufferCount; 351 buffers = new BBuffer * [count]; 352 353 status_t status = GetBufferList(count, buffers); 354 if (status != B_OK) 355 goto end; 356 357 for (int32 i = 0; i < count; i++) { 358 status = message->AddInt32(name, int32(buffers[i]->ID())); 359 if (status != B_OK) 360 goto end; 361 } 362 363 end: 364 delete [] buffers; 365 return status; 366 } 367 368 369 // #pragma mark - private methods 370 371 372 /* not implemented */ 373 //BBufferGroup::BBufferGroup(const BBufferGroup &) 374 //BBufferGroup & BBufferGroup::operator=(const BBufferGroup &) 375 376 377 status_t 378 BBufferGroup::_Init() 379 { 380 CALLED(); 381 382 // some defaults in case we drop out early 383 fBufferList = 0; 384 fInitError = B_ERROR; 385 fRequestError = B_ERROR; 386 fBufferCount = 0; 387 388 // Create the reclaim semaphore 389 // This is also used as a system wide unique identifier for this group 390 fReclaimSem = create_sem(0, "buffer reclaim sem"); 391 if (fReclaimSem < B_OK) { 392 ERROR("BBufferGroup::InitBufferGroup: couldn't create fReclaimSem\n"); 393 fInitError = (status_t)fReclaimSem; 394 return fInitError; 395 } 396 397 fBufferList = BPrivate::SharedBufferList::Get(); 398 if (fBufferList == NULL) { 399 ERROR("BBufferGroup::InitBufferGroup: SharedBufferList::Get() " 400 "failed\n"); 401 fInitError = B_ERROR; 402 return fInitError; 403 } 404 405 fInitError = B_OK; 406 return fInitError; 407 } 408 409