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.
8 /*! Used for BBufferGroup and BBuffer management across teams.
9 Created in the media server, cloned into each BBufferGroup (visible in
13 // TODO: don't use a simple list!
16 #include <SharedBufferList.h>
25 #include <DataExchange.h>
28 static BPrivate::SharedBufferList
* sList
= NULL
;
29 static area_id sArea
= -1;
30 static int32 sRefCount
= 0;
31 static BLocker
sLocker("shared buffer list");
38 SharedBufferList::Create(SharedBufferList
** _list
)
42 size_t size
= (sizeof(SharedBufferList
) + (B_PAGE_SIZE
- 1))
44 SharedBufferList
* list
;
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
);
51 status_t status
= list
->_Init();
61 /*static*/ SharedBufferList
*
62 SharedBufferList::Get()
68 if (atomic_add(&sRefCount
, 1) > 0 && sList
!= NULL
)
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");
80 sArea
= clone_area("shared buffer list clone", (void**)&sList
,
81 B_ANY_ADDRESS
, B_READ_AREA
| B_WRITE_AREA
, areaReply
.area
);
83 ERROR("SharedBufferList::Get() clone area %" B_PRId32
": %s\n",
84 areaReply
.area
, strerror(sArea
));
93 SharedBufferList::Invalidate()
101 SharedBufferList::Put()
104 BAutolock
_(sLocker
);
106 if (atomic_add(&sRefCount
, -1) == 1)
111 /*! Deletes all BBuffers of the group specified by \a groupReclaimSem, then
112 unmaps the list from memory.
115 SharedBufferList::DeleteGroupAndPut(sem_id groupReclaimSem
)
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
;
125 // Decrement buffer count by one, and fill the gap
126 // in the list with its last entry
129 fInfos
[i
--] = fInfos
[fCount
];
141 SharedBufferList::Lock()
143 if (atomic_add(&fAtom
, 1) > 0) {
146 status
= acquire_sem(fSemaphore
);
147 } while (status
== B_INTERRUPTED
);
156 SharedBufferList::Unlock()
158 if (atomic_add(&fAtom
, -1) > 1)
159 return release_sem(fSemaphore
);
166 SharedBufferList::AddBuffer(sem_id groupReclaimSem
,
167 const buffer_clone_info
& info
, BBuffer
** _buffer
)
169 status_t status
= Lock();
173 // Check if the id exists
174 status
= CheckID(groupReclaimSem
, info
.buffer
);
175 if (status
!= B_OK
) {
179 BBuffer
* buffer
= new(std::nothrow
) BBuffer(info
);
180 if (buffer
== NULL
) {
185 if (buffer
->Data() == NULL
) {
186 // BBuffer::Data() will return NULL if an error occured
187 ERROR("BBufferGroup: error while creating buffer\n");
193 status
= AddBuffer(groupReclaimSem
, buffer
);
194 if (status
!= B_OK
) {
198 } else if (_buffer
!= NULL
)
206 SharedBufferList::AddBuffer(sem_id groupReclaimSem
, BBuffer
* buffer
)
213 if (fCount
== kMaxBuffers
) {
214 return B_MEDIA_TOO_MANY_BUFFERS
;
217 fInfos
[fCount
].id
= buffer
->ID();
218 fInfos
[fCount
].buffer
= buffer
;
219 fInfos
[fCount
].reclaim_sem
= groupReclaimSem
;
220 fInfos
[fCount
].reclaimed
= true;
223 return release_sem_etc(groupReclaimSem
, 1, B_DO_NOT_RESCHEDULE
);
228 SharedBufferList::CheckID(sem_id groupSem
, media_buffer_id id
) const
237 for (int32 i
= 0; i
< fCount
; i
++) {
238 if (fInfos
[i
].id
== id
239 && fInfos
[i
].reclaim_sem
== groupSem
) {
248 SharedBufferList::RequestBuffer(sem_id groupReclaimSem
, int32 buffersInGroup
,
249 size_t size
, media_buffer_id wantID
, BBuffer
** _buffer
, bigtime_t timeout
)
252 // We always search for a buffer from the group indicated by groupReclaimSem
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.
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
266 acquireFlags
= B_RELATIVE_TIMEOUT
;
267 } else if (timeout
== B_INFINITE_TIMEOUT
) {
268 acquireFlags
= B_RELATIVE_TIMEOUT
;
270 timeout
+= system_time();
271 acquireFlags
= B_ABSOLUTE_TIMEOUT
;
274 // With each itaration we request one more buffer, since we need to skip
275 // the buffers that don't fit the request
281 status
= acquire_sem_etc(groupReclaimSem
, count
, acquireFlags
,
283 } while (status
== B_INTERRUPTED
);
288 // try to exit savely if the lock fails
290 if (status
!= B_OK
) {
291 ERROR("SharedBufferList:: RequestBuffer: Lock failed: %s\n",
293 release_sem_etc(groupReclaimSem
, count
, 0);
297 for (int32 i
= 0; i
< fCount
; i
++) {
298 // We need a BBuffer from the group, and it must be marked as
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
)) {
306 fInfos
[i
].reclaimed
= false;
307 *_buffer
= fInfos
[i
].buffer
;
309 // if we requested more than one buffer, release the rest
311 release_sem_etc(groupReclaimSem
, count
- 1,
312 B_DO_NOT_RESCHEDULE
);
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());
326 release_sem_etc(groupReclaimSem
, count
, B_DO_NOT_RESCHEDULE
);
327 if (Unlock() != B_OK
) {
328 ERROR("SharedBufferList:: RequestBuffer: unlock failed\n");
331 // prepare to request one more buffer next time
333 } while (count
<= buffersInGroup
);
335 ERROR("SharedBufferList:: RequestBuffer: no buffer found\n");
341 SharedBufferList::RecycleBuffer(BBuffer
* buffer
)
345 media_buffer_id id
= buffer
->ID();
350 int32 reclaimedCount
= 0;
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
) {
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"));
362 fInfos
[i
].reclaimed
= true;
363 release_sem_etc(fInfos
[i
].reclaim_sem
, 1, B_DO_NOT_RESCHEDULE
);
367 if (Unlock() != B_OK
)
370 if (reclaimedCount
== 0) {
371 ERROR("shared_buffer_list::RecycleBuffer, BBuffer %p, id = %" B_PRId32
372 " NOT reclaimed\n", buffer
, id
);
380 /*! Returns exactly \a bufferCount buffers from the group specified via its
381 \a groupReclaimSem if successful.
384 SharedBufferList::GetBufferList(sem_id groupReclaimSem
, int32 bufferCount
,
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
)
401 if (Unlock() != B_OK
)
404 return found
== bufferCount
? B_OK
: B_ERROR
;
409 SharedBufferList::_Init()
413 fSemaphore
= create_sem(0, "shared buffer list lock");
419 for (int32 i
= 0; i
< kMaxBuffers
; i
++) {
428 /*! Used by RequestBuffer, call this one with the list locked!
431 SharedBufferList::_RequestBufferInOtherGroups(sem_id groupReclaimSem
,
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).
443 status
= acquire_sem(fInfos
[i
].reclaim_sem
);
444 } while (status
== B_INTERRUPTED
);
446 // try to skip entries that belong to crashed teams
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
);
457 fInfos
[i
].reclaimed
= false;
463 } // namespace BPrivate