1 /* 2 * Copyright 2009-2012, Axel Dörfler, axeld@pinc-software.de. 3 * Copyright 2002, Marcus Overhagen. All Rights Reserved. 4 * Distributed under the terms of the MIT License. 5 */ 6 7 8 /*! Used for BBufferGroup and BBuffer management across teams. 9 Created in the media server, cloned into each BBufferGroup (visible in 10 all address spaces). 11 */ 12 13 // TODO: don't use a simple list! 14 15 16 #include <SharedBufferList.h> 17 18 #include <string.h> 19 20 #include <Autolock.h> 21 #include <Buffer.h> 22 #include <Locker.h> 23 24 #include <MediaDebug.h> 25 #include <DataExchange.h> 26 27 28 static BPrivate::SharedBufferList* sList = NULL; 29 static area_id sArea = -1; 30 static int32 sRefCount = 0; 31 static BLocker sLocker("shared buffer list"); 32 33 34 namespace BPrivate { 35 36 37 /*static*/ area_id 38 SharedBufferList::Create(SharedBufferList** _list) 39 { 40 CALLED(); 41 42 size_t size = (sizeof(SharedBufferList) + (B_PAGE_SIZE - 1)) 43 & ~(B_PAGE_SIZE - 1); 44 SharedBufferList* list; 45 46 area_id area = create_area("shared buffer list", (void**)&list, 47 B_ANY_ADDRESS, size, B_LAZY_LOCK, B_READ_AREA | B_WRITE_AREA); 48 if (area < 0) 49 return area; 50 51 status_t status = list->_Init(); 52 if (status != B_OK) { 53 delete_area(area); 54 return status; 55 } 56 57 return area; 58 } 59 60 61 /*static*/ SharedBufferList* 62 SharedBufferList::Get() 63 { 64 CALLED(); 65 66 BAutolock _(sLocker); 67 68 if (atomic_add(&sRefCount, 1) > 0 && sList != NULL) 69 return sList; 70 71 // ask media_server to get the area_id of the shared buffer list 72 server_get_shared_buffer_area_request areaRequest; 73 server_get_shared_buffer_area_reply areaReply; 74 if (QueryServer(SERVER_GET_SHARED_BUFFER_AREA, &areaRequest, 75 sizeof(areaRequest), &areaReply, sizeof(areaReply)) != B_OK) { 76 ERROR("SharedBufferList::Get() SERVER_GET_SHARED_BUFFER_AREA failed\n"); 77 return NULL; 78 } 79 80 sArea = clone_area("shared buffer list clone", (void**)&sList, 81 B_ANY_ADDRESS, B_READ_AREA | B_WRITE_AREA, areaReply.area); 82 if (sArea < 0) { 83 ERROR("SharedBufferList::Get() clone area %" B_PRId32 ": %s\n", 84 areaReply.area, strerror(sArea)); 85 return NULL; 86 } 87 88 return sList; 89 } 90 91 92 /*static*/ void 93 SharedBufferList::Invalidate() 94 { 95 delete_area(sArea); 96 sList = NULL; 97 } 98 99 100 void 101 SharedBufferList::Put() 102 { 103 CALLED(); 104 BAutolock _(sLocker); 105 106 if (atomic_add(&sRefCount, -1) == 1) 107 Invalidate(); 108 } 109 110 111 /*! Deletes all BBuffers of the group specified by \a groupReclaimSem, then 112 unmaps the list from memory. 113 */ 114 void 115 SharedBufferList::DeleteGroupAndPut(sem_id groupReclaimSem) 116 { 117 CALLED(); 118 119 if (Lock() == B_OK) { 120 for (int32 i = 0; i < fCount; i++) { 121 if (fInfos[i].reclaim_sem == groupReclaimSem) { 122 // delete the associated buffer 123 delete fInfos[i].buffer; 124 125 // Decrement buffer count by one, and fill the gap 126 // in the list with its last entry 127 fCount--; 128 if (fCount > 0) 129 fInfos[i--] = fInfos[fCount]; 130 } 131 } 132 133 Unlock(); 134 } 135 136 Put(); 137 } 138 139 140 status_t 141 SharedBufferList::Lock() 142 { 143 if (atomic_add(&fAtom, 1) > 0) { 144 status_t status; 145 do { 146 status = acquire_sem(fSemaphore); 147 } while (status == B_INTERRUPTED); 148 149 return status; 150 } 151 return B_OK; 152 } 153 154 155 status_t 156 SharedBufferList::Unlock() 157 { 158 if (atomic_add(&fAtom, -1) > 1) 159 return release_sem(fSemaphore); 160 161 return B_OK; 162 } 163 164 165 status_t 166 SharedBufferList::AddBuffer(sem_id groupReclaimSem, 167 const buffer_clone_info& info, BBuffer** _buffer) 168 { 169 status_t status = Lock(); 170 if (status != B_OK) 171 return status; 172 173 // Check if the id exists 174 status = CheckID(groupReclaimSem, info.buffer); 175 if (status != B_OK) { 176 Unlock(); 177 return status; 178 } 179 BBuffer* buffer = new(std::nothrow) BBuffer(info); 180 if (buffer == NULL) { 181 Unlock(); 182 return B_NO_MEMORY; 183 } 184 185 if (buffer->Data() == NULL) { 186 // BBuffer::Data() will return NULL if an error occured 187 ERROR("BBufferGroup: error while creating buffer\n"); 188 delete buffer; 189 Unlock(); 190 return B_ERROR; 191 } 192 193 status = AddBuffer(groupReclaimSem, buffer); 194 if (status != B_OK) { 195 delete buffer; 196 Unlock(); 197 return status; 198 } else if (_buffer != NULL) 199 *_buffer = buffer; 200 201 return Unlock(); 202 } 203 204 205 status_t 206 SharedBufferList::AddBuffer(sem_id groupReclaimSem, BBuffer* buffer) 207 { 208 CALLED(); 209 210 if (buffer == NULL) 211 return B_BAD_VALUE; 212 213 if (fCount == kMaxBuffers) { 214 return B_MEDIA_TOO_MANY_BUFFERS; 215 } 216 217 fInfos[fCount].id = buffer->ID(); 218 fInfos[fCount].buffer = buffer; 219 fInfos[fCount].reclaim_sem = groupReclaimSem; 220 fInfos[fCount].reclaimed = true; 221 fCount++; 222 223 return release_sem_etc(groupReclaimSem, 1, B_DO_NOT_RESCHEDULE); 224 } 225 226 227 status_t 228 SharedBufferList::CheckID(sem_id groupSem, media_buffer_id id) const 229 { 230 CALLED(); 231 232 if (id == 0) 233 return B_OK; 234 if (id < 0) 235 return B_BAD_VALUE; 236 237 for (int32 i = 0; i < fCount; i++) { 238 if (fInfos[i].id == id 239 && fInfos[i].reclaim_sem == groupSem) { 240 return B_ERROR; 241 } 242 } 243 return B_OK; 244 } 245 246 247 status_t 248 SharedBufferList::RequestBuffer(sem_id groupReclaimSem, int32 buffersInGroup, 249 size_t size, media_buffer_id wantID, BBuffer** _buffer, bigtime_t timeout) 250 { 251 CALLED(); 252 // We always search for a buffer from the group indicated by groupReclaimSem 253 // first. 254 // If "size" != 0, we search for a buffer that is "size" bytes or larger. 255 // If "wantID" != 0, we search for a buffer with this ID. 256 // If "*_buffer" != NULL, we search for a buffer at this address. 257 // 258 // If we found a buffer, we also need to mark it in all other groups as 259 // requested and also once need to acquire the reclaim_sem of the other 260 // groups 261 262 uint32 acquireFlags; 263 264 if (timeout <= 0) { 265 timeout = 0; 266 acquireFlags = B_RELATIVE_TIMEOUT; 267 } else if (timeout == B_INFINITE_TIMEOUT) { 268 acquireFlags = B_RELATIVE_TIMEOUT; 269 } else { 270 timeout += system_time(); 271 acquireFlags = B_ABSOLUTE_TIMEOUT; 272 } 273 274 // With each itaration we request one more buffer, since we need to skip 275 // the buffers that don't fit the request 276 int32 count = 1; 277 278 do { 279 status_t status; 280 do { 281 status = acquire_sem_etc(groupReclaimSem, count, acquireFlags, 282 timeout); 283 } while (status == B_INTERRUPTED); 284 285 if (status != B_OK) 286 return status; 287 288 // try to exit savely if the lock fails 289 status = Lock(); 290 if (status != B_OK) { 291 ERROR("SharedBufferList:: RequestBuffer: Lock failed: %s\n", 292 strerror(status)); 293 release_sem_etc(groupReclaimSem, count, 0); 294 return B_ERROR; 295 } 296 297 for (int32 i = 0; i < fCount; i++) { 298 // We need a BBuffer from the group, and it must be marked as 299 // reclaimed 300 if (fInfos[i].reclaim_sem == groupReclaimSem 301 && fInfos[i].reclaimed) { 302 if ((size != 0 && size <= fInfos[i].buffer->SizeAvailable()) 303 || (*_buffer != 0 && fInfos[i].buffer == *_buffer) 304 || (wantID != 0 && fInfos[i].id == wantID)) { 305 // we found a buffer 306 fInfos[i].reclaimed = false; 307 *_buffer = fInfos[i].buffer; 308 309 // if we requested more than one buffer, release the rest 310 if (count > 1) { 311 release_sem_etc(groupReclaimSem, count - 1, 312 B_DO_NOT_RESCHEDULE); 313 } 314 315 // And mark all buffers with the same ID as requested in 316 // all other buffer groups 317 _RequestBufferInOtherGroups(groupReclaimSem, 318 fInfos[i].buffer->ID()); 319 320 Unlock(); 321 return B_OK; 322 } 323 } 324 } 325 326 release_sem_etc(groupReclaimSem, count, B_DO_NOT_RESCHEDULE); 327 if (Unlock() != B_OK) { 328 ERROR("SharedBufferList:: RequestBuffer: unlock failed\n"); 329 return B_ERROR; 330 } 331 // prepare to request one more buffer next time 332 count++; 333 } while (count <= buffersInGroup); 334 335 ERROR("SharedBufferList:: RequestBuffer: no buffer found\n"); 336 return B_ERROR; 337 } 338 339 340 status_t 341 SharedBufferList::RecycleBuffer(BBuffer* buffer) 342 { 343 CALLED(); 344 345 media_buffer_id id = buffer->ID(); 346 347 if (Lock() != B_OK) 348 return B_ERROR; 349 350 int32 reclaimedCount = 0; 351 352 for (int32 i = 0; i < fCount; i++) { 353 // find the buffer id, and reclaim it in all groups it belongs to 354 if (fInfos[i].id == id) { 355 reclaimedCount++; 356 if (fInfos[i].reclaimed) { 357 ERROR("SharedBufferList::RecycleBuffer, BBuffer %p, id = %" 358 B_PRId32 " already reclaimed\n", buffer, id); 359 DEBUG_ONLY(debugger("buffer already reclaimed")); 360 continue; 361 } 362 fInfos[i].reclaimed = true; 363 release_sem_etc(fInfos[i].reclaim_sem, 1, B_DO_NOT_RESCHEDULE); 364 } 365 } 366 367 if (Unlock() != B_OK) 368 return B_ERROR; 369 370 if (reclaimedCount == 0) { 371 ERROR("shared_buffer_list::RecycleBuffer, BBuffer %p, id = %" B_PRId32 372 " NOT reclaimed\n", buffer, id); 373 return B_ERROR; 374 } 375 376 return B_OK; 377 } 378 379 380 /*! Returns exactly \a bufferCount buffers from the group specified via its 381 \a groupReclaimSem if successful. 382 */ 383 status_t 384 SharedBufferList::GetBufferList(sem_id groupReclaimSem, int32 bufferCount, 385 BBuffer** buffers) 386 { 387 CALLED(); 388 389 if (Lock() != B_OK) 390 return B_ERROR; 391 392 int32 found = 0; 393 394 for (int32 i = 0; i < fCount; i++) 395 if (fInfos[i].reclaim_sem == groupReclaimSem) { 396 buffers[found++] = fInfos[i].buffer; 397 if (found == bufferCount) 398 break; 399 } 400 401 if (Unlock() != B_OK) 402 return B_ERROR; 403 404 return found == bufferCount ? B_OK : B_ERROR; 405 } 406 407 408 status_t 409 SharedBufferList::_Init() 410 { 411 CALLED(); 412 413 fSemaphore = create_sem(0, "shared buffer list lock"); 414 if (fSemaphore < 0) 415 return fSemaphore; 416 417 fAtom = 0; 418 419 for (int32 i = 0; i < kMaxBuffers; i++) { 420 fInfos[i].id = -1; 421 } 422 fCount = 0; 423 424 return B_OK; 425 } 426 427 428 /*! Used by RequestBuffer, call this one with the list locked! 429 */ 430 void 431 SharedBufferList::_RequestBufferInOtherGroups(sem_id groupReclaimSem, 432 media_buffer_id id) 433 { 434 for (int32 i = 0; i < fCount; i++) { 435 // find buffers with same id, but belonging to other groups 436 if (fInfos[i].id == id && fInfos[i].reclaim_sem != groupReclaimSem) { 437 // and mark them as requested 438 // TODO: this can deadlock if BBuffers with same media_buffer_id 439 // exist in more than one BBufferGroup, and RequestBuffer() 440 // is called on both groups (which should not be done). 441 status_t status; 442 do { 443 status = acquire_sem(fInfos[i].reclaim_sem); 444 } while (status == B_INTERRUPTED); 445 446 // try to skip entries that belong to crashed teams 447 if (status != B_OK) 448 continue; 449 450 if (fInfos[i].reclaimed == false) { 451 ERROR("SharedBufferList:: RequestBufferInOtherGroups BBuffer " 452 "%p, id = %" B_PRId32 " not reclaimed while requesting\n", 453 fInfos[i].buffer, id); 454 continue; 455 } 456 457 fInfos[i].reclaimed = false; 458 } 459 } 460 } 461 462 463 } // namespace BPrivate 464