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 "MediaDebug.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 fInitError = _Init(); 45 if (fInitError != B_OK) 46 return; 47 48 // This one is easy. We need to create "count" BBuffers, 49 // each one "size" bytes large. They all go into one 50 // area, with "placement" and "lock" attributes. 51 // The BBuffers created will clone the area, and 52 // then we delete our area. This way BBuffers are 53 // independent from the BBufferGroup 54 55 // don't allow all placement parameter values 56 if (placement != B_ANY_ADDRESS && placement != B_ANY_KERNEL_ADDRESS) { 57 ERROR("BBufferGroup: placement != B_ANY_ADDRESS " 58 "&& placement != B_ANY_KERNEL_ADDRESS (0x%#" B_PRIx32 ")\n", 59 placement); 60 placement = B_ANY_ADDRESS; 61 } 62 63 // first we roundup for a better placement in memory 64 size_t allocSize = (size + 63) & ~63; 65 66 // now we create the area 67 size_t areaSize 68 = ((allocSize * count) + B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1); 69 70 void* startAddress; 71 area_id bufferArea = create_area("some buffers area", &startAddress, 72 placement, areaSize, lock, B_READ_AREA | B_WRITE_AREA); 73 if (bufferArea < 0) { 74 ERROR("BBufferGroup: failed to allocate %ld bytes area\n", areaSize); 75 fInitError = (status_t)bufferArea; 76 return; 77 } 78 79 buffer_clone_info info; 80 81 for (int32 i = 0; i < count; i++) { 82 info.area = bufferArea; 83 info.offset = i * allocSize; 84 info.size = size; 85 86 fInitError = AddBuffer(info); 87 if (fInitError != B_OK) 88 break; 89 } 90 91 delete_area(bufferArea); 92 } 93 94 95 BBufferGroup::BBufferGroup() 96 { 97 CALLED(); 98 fInitError = _Init(); 99 if (fInitError != B_OK) 100 return; 101 102 // this one simply creates an empty BBufferGroup 103 } 104 105 106 BBufferGroup::BBufferGroup(int32 count, const media_buffer_id* buffers) 107 { 108 CALLED(); 109 fInitError = _Init(); 110 if (fInitError != B_OK) 111 return; 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 status_t status = fBufferList->AddBuffer(fReclaimSem, info, _buffer); 154 if (status != B_OK) { 155 ERROR("BBufferGroup: error when adding buffer\n"); 156 return status; 157 } 158 atomic_add(&fBufferCount, 1); 159 return B_OK; 160 } 161 162 163 BBuffer* 164 BBufferGroup::RequestBuffer(size_t size, bigtime_t timeout) 165 { 166 CALLED(); 167 if (fInitError != B_OK) 168 return NULL; 169 170 if (size <= 0) 171 return NULL; 172 173 BBuffer *buffer = NULL; 174 fRequestError = fBufferList->RequestBuffer(fReclaimSem, fBufferCount, 175 size, 0, &buffer, timeout); 176 177 return fRequestError == B_OK ? buffer : NULL; 178 } 179 180 181 status_t 182 BBufferGroup::RequestBuffer(BBuffer* buffer, bigtime_t timeout) 183 { 184 CALLED(); 185 if (fInitError != B_OK) 186 return B_NO_INIT; 187 188 if (buffer == NULL) 189 return B_BAD_VALUE; 190 191 fRequestError = fBufferList->RequestBuffer(fReclaimSem, fBufferCount, 0, 0, 192 &buffer, timeout); 193 194 return fRequestError; 195 } 196 197 198 status_t 199 BBufferGroup::RequestError() 200 { 201 CALLED(); 202 if (fInitError != B_OK) 203 return B_NO_INIT; 204 205 return fRequestError; 206 } 207 208 209 status_t 210 BBufferGroup::CountBuffers(int32* _count) 211 { 212 CALLED(); 213 if (fInitError != B_OK) 214 return B_NO_INIT; 215 216 *_count = fBufferCount; 217 return B_OK; 218 } 219 220 221 status_t 222 BBufferGroup::GetBufferList(int32 bufferCount, BBuffer** _buffers) 223 { 224 CALLED(); 225 if (fInitError != B_OK) 226 return B_NO_INIT; 227 228 if (bufferCount <= 0 || bufferCount > fBufferCount) 229 return B_BAD_VALUE; 230 231 return fBufferList->GetBufferList(fReclaimSem, bufferCount, _buffers); 232 } 233 234 235 status_t 236 BBufferGroup::WaitForBuffers() 237 { 238 CALLED(); 239 if (fInitError != B_OK) 240 return B_NO_INIT; 241 242 // TODO: this function is not really useful anyway, and will 243 // not work exactly as documented, but it is close enough 244 245 if (fBufferCount < 0) 246 return B_BAD_VALUE; 247 if (fBufferCount == 0) 248 return B_OK; 249 250 // We need to wait until at least one buffer belonging to this group is 251 // reclaimed. 252 // This has happened when can aquire "fReclaimSem" 253 254 status_t status; 255 while ((status = acquire_sem(fReclaimSem)) == B_INTERRUPTED) 256 ; 257 if (status != B_OK) 258 return status; 259 260 // we need to release the "fReclaimSem" now, else we would block 261 // requesting of new buffers 262 263 return release_sem(fReclaimSem); 264 } 265 266 267 status_t 268 BBufferGroup::ReclaimAllBuffers() 269 { 270 CALLED(); 271 if (fInitError != B_OK) 272 return B_NO_INIT; 273 274 // because additional BBuffers might get added to this group betweeen 275 // acquire and release 276 int32 count = fBufferCount; 277 278 if (count < 0) 279 return B_BAD_VALUE; 280 if (count == 0) 281 return B_OK; 282 283 // we need to wait until all BBuffers belonging to this group are reclaimed. 284 // this has happened when the "fReclaimSem" can be aquired "fBufferCount" 285 // times 286 287 status_t status = B_ERROR; 288 do { 289 status = acquire_sem_etc(fReclaimSem, count, B_RELATIVE_TIMEOUT, 0); 290 } while (status == B_INTERRUPTED); 291 292 if (status != B_OK) 293 return status; 294 295 // we need to release the "fReclaimSem" now, else we would block 296 // requesting of new buffers 297 298 return release_sem_etc(fReclaimSem, count, 0); 299 } 300 301 302 // #pragma mark - deprecated BeOS R4 API 303 304 305 status_t 306 BBufferGroup::AddBuffersTo(BMessage* message, const char* name, bool needLock) 307 { 308 CALLED(); 309 if (fInitError != B_OK) 310 return B_NO_INIT; 311 312 // BeOS R4 legacy API. Implemented as a wrapper around GetBufferList 313 // "needLock" is ignored, GetBufferList will do locking 314 315 if (message == NULL) 316 return B_BAD_VALUE; 317 318 if (name == NULL || strlen(name) == 0) 319 return B_BAD_VALUE; 320 321 BBuffer* buffers[fBufferCount]; 322 status_t status = GetBufferList(fBufferCount, buffers); 323 if (status != B_OK) 324 return status; 325 326 for (int32 i = 0; i < fBufferCount; i++) { 327 status = message->AddInt32(name, int32(buffers[i]->ID())); 328 if (status != B_OK) 329 return status; 330 } 331 332 return B_OK; 333 } 334 335 336 // #pragma mark - private methods 337 338 339 /* not implemented */ 340 //BBufferGroup::BBufferGroup(const BBufferGroup &) 341 //BBufferGroup & BBufferGroup::operator=(const BBufferGroup &) 342 343 344 status_t 345 BBufferGroup::_Init() 346 { 347 CALLED(); 348 349 // some defaults in case we drop out early 350 fBufferList = NULL; 351 fRequestError = B_ERROR; 352 fBufferCount = 0; 353 354 // Create the reclaim semaphore 355 // This is also used as a system wide unique identifier for this group 356 fReclaimSem = create_sem(0, "buffer reclaim sem"); 357 if (fReclaimSem < 0) { 358 ERROR("BBufferGroup::InitBufferGroup: couldn't create fReclaimSem\n"); 359 return (status_t)fReclaimSem; 360 } 361 362 fBufferList = BPrivate::SharedBufferList::Get(); 363 if (fBufferList == NULL) { 364 ERROR("BBufferGroup::InitBufferGroup: SharedBufferList::Get() " 365 "failed\n"); 366 return B_ERROR; 367 } 368 369 return B_OK; 370 } 371 372