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 <debug.h> 25 #include <DataExchange.h> 26 27 28 static BPrivate::SharedBufferList* sList; 29 static area_id sArea; 30 static int32 sRefCount; 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, BBuffer* buffer) 167 { 168 CALLED(); 169 170 if (buffer == NULL) 171 return B_BAD_VALUE; 172 173 status_t status = Lock(); 174 if (status != B_OK) 175 return status; 176 177 if (fCount == kMaxBuffers) { 178 Unlock(); 179 return B_MEDIA_TOO_MANY_BUFFERS; 180 } 181 182 fInfos[fCount].id = buffer->ID(); 183 fInfos[fCount].buffer = buffer; 184 fInfos[fCount].reclaim_sem = groupReclaimSem; 185 fInfos[fCount].reclaimed = true; 186 fCount++; 187 188 status = release_sem_etc(groupReclaimSem, 1, B_DO_NOT_RESCHEDULE); 189 if (status != B_OK) 190 return status; 191 192 return Unlock(); 193 } 194 195 196 status_t 197 SharedBufferList::RequestBuffer(sem_id groupReclaimSem, int32 buffersInGroup, 198 size_t size, media_buffer_id wantID, BBuffer** _buffer, bigtime_t timeout) 199 { 200 CALLED(); 201 // We always search for a buffer from the group indicated by groupReclaimSem 202 // first. 203 // If "size" != 0, we search for a buffer that is "size" bytes or larger. 204 // If "wantID" != 0, we search for a buffer with this ID. 205 // If "*_buffer" != NULL, we search for a buffer at this address. 206 // 207 // If we found a buffer, we also need to mark it in all other groups as 208 // requested and also once need to acquire the reclaim_sem of the other 209 // groups 210 211 uint32 acquireFlags; 212 213 if (timeout <= 0) { 214 timeout = 0; 215 acquireFlags = B_RELATIVE_TIMEOUT; 216 } else if (timeout == B_INFINITE_TIMEOUT) { 217 acquireFlags = B_RELATIVE_TIMEOUT; 218 } else { 219 timeout += system_time(); 220 acquireFlags = B_ABSOLUTE_TIMEOUT; 221 } 222 223 // With each itaration we request one more buffer, since we need to skip 224 // the buffers that don't fit the request 225 int32 count = 1; 226 227 do { 228 status_t status; 229 do { 230 status = acquire_sem_etc(groupReclaimSem, count, acquireFlags, 231 timeout); 232 } while (status == B_INTERRUPTED); 233 234 if (status != B_OK) 235 return status; 236 237 // try to exit savely if the lock fails 238 status = Lock(); 239 if (status != B_OK) { 240 ERROR("SharedBufferList:: RequestBuffer: Lock failed: %s\n", 241 strerror(status)); 242 release_sem_etc(groupReclaimSem, count, 0); 243 return B_ERROR; 244 } 245 246 for (int32 i = 0; i < fCount; i++) { 247 // We need a BBuffer from the group, and it must be marked as 248 // reclaimed 249 if (fInfos[i].reclaim_sem == groupReclaimSem 250 && fInfos[i].reclaimed) { 251 if ((size != 0 && size <= fInfos[i].buffer->SizeAvailable()) 252 || (*_buffer != 0 && fInfos[i].buffer == *_buffer) 253 || (wantID != 0 && fInfos[i].id == wantID)) { 254 // we found a buffer 255 fInfos[i].reclaimed = false; 256 *_buffer = fInfos[i].buffer; 257 258 // if we requested more than one buffer, release the rest 259 if (count > 1) { 260 release_sem_etc(groupReclaimSem, count - 1, 261 B_DO_NOT_RESCHEDULE); 262 } 263 264 // And mark all buffers with the same ID as requested in 265 // all other buffer groups 266 _RequestBufferInOtherGroups(groupReclaimSem, 267 fInfos[i].buffer->ID()); 268 269 Unlock(); 270 return B_OK; 271 } 272 } 273 } 274 275 release_sem_etc(groupReclaimSem, count, B_DO_NOT_RESCHEDULE); 276 if (Unlock() != B_OK) { 277 ERROR("SharedBufferList:: RequestBuffer: unlock failed\n"); 278 return B_ERROR; 279 } 280 // prepare to request one more buffer next time 281 count++; 282 } while (count <= buffersInGroup); 283 284 ERROR("SharedBufferList:: RequestBuffer: no buffer found\n"); 285 return B_ERROR; 286 } 287 288 289 status_t 290 SharedBufferList::RecycleBuffer(BBuffer* buffer) 291 { 292 CALLED(); 293 294 media_buffer_id id = buffer->ID(); 295 296 if (Lock() != B_OK) 297 return B_ERROR; 298 299 int32 reclaimedCount = 0; 300 301 for (int32 i = 0; i < fCount; i++) { 302 // find the buffer id, and reclaim it in all groups it belongs to 303 if (fInfos[i].id == id) { 304 reclaimedCount++; 305 if (fInfos[i].reclaimed) { 306 ERROR("SharedBufferList::RecycleBuffer, BBuffer %p, id = %" 307 B_PRId32 " already reclaimed\n", buffer, id); 308 DEBUG_ONLY(debugger("buffer already reclaimed")); 309 continue; 310 } 311 fInfos[i].reclaimed = true; 312 release_sem_etc(fInfos[i].reclaim_sem, 1, B_DO_NOT_RESCHEDULE); 313 } 314 } 315 316 if (Unlock() != B_OK) 317 return B_ERROR; 318 319 if (reclaimedCount == 0) { 320 ERROR("shared_buffer_list::RecycleBuffer, BBuffer %p, id = %" B_PRId32 321 " NOT reclaimed\n", buffer, id); 322 return B_ERROR; 323 } 324 325 return B_OK; 326 } 327 328 329 /*! Returns exactly \a bufferCount buffers from the group specified via its 330 \a groupReclaimSem if successful. 331 */ 332 status_t 333 SharedBufferList::GetBufferList(sem_id groupReclaimSem, int32 bufferCount, 334 BBuffer** buffers) 335 { 336 CALLED(); 337 338 if (Lock() != B_OK) 339 return B_ERROR; 340 341 int32 found = 0; 342 343 for (int32 i = 0; i < fCount; i++) 344 if (fInfos[i].reclaim_sem == groupReclaimSem) { 345 buffers[found++] = fInfos[i].buffer; 346 if (found == bufferCount) 347 break; 348 } 349 350 if (Unlock() != B_OK) 351 return B_ERROR; 352 353 return found == bufferCount ? B_OK : B_ERROR; 354 } 355 356 357 status_t 358 SharedBufferList::_Init() 359 { 360 CALLED(); 361 362 fSemaphore = create_sem(0, "shared buffer list lock"); 363 if (fSemaphore < 0) 364 return fSemaphore; 365 366 fAtom = 0; 367 368 for (int32 i = 0; i < kMaxBuffers; i++) { 369 fInfos[i].id = -1; 370 } 371 372 return B_OK; 373 } 374 375 376 /*! Used by RequestBuffer, call this one with the list locked! 377 */ 378 void 379 SharedBufferList::_RequestBufferInOtherGroups(sem_id groupReclaimSem, 380 media_buffer_id id) 381 { 382 for (int32 i = 0; i < fCount; i++) { 383 // find buffers with same id, but belonging to other groups 384 if (fInfos[i].id == id && fInfos[i].reclaim_sem != groupReclaimSem) { 385 // and mark them as requested 386 // TODO: this can deadlock if BBuffers with same media_buffer_id 387 // exist in more than one BBufferGroup, and RequestBuffer() 388 // is called on both groups (which should not be done). 389 status_t status; 390 do { 391 status = acquire_sem(fInfos[i].reclaim_sem); 392 } while (status == B_INTERRUPTED); 393 394 // try to skip entries that belong to crashed teams 395 if (status != B_OK) 396 continue; 397 398 if (fInfos[i].reclaimed == false) { 399 ERROR("SharedBufferList:: RequestBufferInOtherGroups BBuffer " 400 "%p, id = %" B_PRId32 " not reclaimed while requesting\n", 401 fInfos[i].buffer, id); 402 continue; 403 } 404 405 fInfos[i].reclaimed = false; 406 } 407 } 408 } 409 410 411 } // namespace BPrivate 412