* Copyright 2009-2012, Axel Dörfler, axeld@pinc-software.de.
* Copyright 2002, Marcus Overhagen. All Rights Reserved.
* Distributed under the terms of the MIT License.
*/
Created in the media server, cloned into each BBufferGroup (visible in
all address spaces).
*/
#include <SharedBufferList.h>
#include <string.h>
#include <Autolock.h>
#include <Buffer.h>
#include <Locker.h>
#include <MediaDebug.h>
#include <DataExchange.h>
static BPrivate::SharedBufferList* sList = NULL;
static area_id sArea = -1;
static int32 sRefCount = 0;
static BLocker sLocker("shared buffer list");
namespace BPrivate {
area_id
SharedBufferList::Create(SharedBufferList** _list)
{
CALLED();
size_t size = (sizeof(SharedBufferList) + (B_PAGE_SIZE - 1))
& ~(B_PAGE_SIZE - 1);
SharedBufferList* list;
area_id area = create_area("shared buffer list", (void**)&list,
B_ANY_ADDRESS, size, B_LAZY_LOCK,
B_READ_AREA | B_WRITE_AREA | B_CLONEABLE_AREA);
if (area < 0)
return area;
status_t status = list->_Init();
if (status != B_OK) {
delete_area(area);
return status;
}
return area;
}
SharedBufferList*
SharedBufferList::Get()
{
CALLED();
BAutolock _(sLocker);
if (atomic_add(&sRefCount, 1) > 0 && sList != NULL)
return sList;
server_get_shared_buffer_area_request areaRequest;
server_get_shared_buffer_area_reply areaReply;
if (QueryServer(SERVER_GET_SHARED_BUFFER_AREA, &areaRequest,
sizeof(areaRequest), &areaReply, sizeof(areaReply)) != B_OK) {
ERROR("SharedBufferList::Get() SERVER_GET_SHARED_BUFFER_AREA failed\n");
return NULL;
}
sArea = clone_area("shared buffer list clone", (void**)&sList,
B_ANY_ADDRESS, B_READ_AREA | B_WRITE_AREA, areaReply.area);
if (sArea < 0) {
ERROR("SharedBufferList::Get() clone area %" B_PRId32 ": %s\n",
areaReply.area, strerror(sArea));
return NULL;
}
return sList;
}
void
SharedBufferList::Invalidate()
{
delete_area(sArea);
sList = NULL;
}
void
SharedBufferList::Put()
{
CALLED();
BAutolock _(sLocker);
if (atomic_add(&sRefCount, -1) == 1)
Invalidate();
}
unmaps the list from memory.
*/
void
SharedBufferList::DeleteGroupAndPut(sem_id groupReclaimSem)
{
CALLED();
if (Lock() == B_OK) {
for (int32 i = 0; i < fCount; i++) {
if (fInfos[i].reclaim_sem == groupReclaimSem) {
delete fInfos[i].buffer;
fCount--;
if (fCount > 0)
fInfos[i--] = fInfos[fCount];
}
}
Unlock();
}
Put();
}
status_t
SharedBufferList::Lock()
{
if (atomic_add(&fAtom, 1) > 0) {
status_t status;
do {
status = acquire_sem(fSemaphore);
} while (status == B_INTERRUPTED);
return status;
}
return B_OK;
}
status_t
SharedBufferList::Unlock()
{
if (atomic_add(&fAtom, -1) > 1)
return release_sem(fSemaphore);
return B_OK;
}
status_t
SharedBufferList::AddBuffer(sem_id groupReclaimSem,
const buffer_clone_info& info, BBuffer** _buffer)
{
status_t status = Lock();
if (status != B_OK)
return status;
status = CheckID(groupReclaimSem, info.buffer);
if (status != B_OK) {
Unlock();
return status;
}
BBuffer* buffer = new(std::nothrow) BBuffer(info);
if (buffer == NULL) {
Unlock();
return B_NO_MEMORY;
}
if (buffer->Data() == NULL) {
ERROR("BBufferGroup: error while creating buffer\n");
delete buffer;
Unlock();
return B_ERROR;
}
status = AddBuffer(groupReclaimSem, buffer);
if (status != B_OK) {
delete buffer;
Unlock();
return status;
} else if (_buffer != NULL)
*_buffer = buffer;
return Unlock();
}
status_t
SharedBufferList::AddBuffer(sem_id groupReclaimSem, BBuffer* buffer)
{
CALLED();
if (buffer == NULL)
return B_BAD_VALUE;
if (fCount == kMaxBuffers) {
return B_MEDIA_TOO_MANY_BUFFERS;
}
fInfos[fCount].id = buffer->ID();
fInfos[fCount].buffer = buffer;
fInfos[fCount].reclaim_sem = groupReclaimSem;
fInfos[fCount].reclaimed = true;
fCount++;
return release_sem_etc(groupReclaimSem, 1, B_DO_NOT_RESCHEDULE);
}
status_t
SharedBufferList::CheckID(sem_id groupSem, media_buffer_id id) const
{
CALLED();
if (id == 0)
return B_OK;
if (id < 0)
return B_BAD_VALUE;
for (int32 i = 0; i < fCount; i++) {
if (fInfos[i].id == id
&& fInfos[i].reclaim_sem == groupSem) {
return B_ERROR;
}
}
return B_OK;
}
status_t
SharedBufferList::RequestBuffer(sem_id groupReclaimSem, int32 buffersInGroup,
size_t size, media_buffer_id wantID, BBuffer** _buffer, bigtime_t timeout)
{
CALLED();
uint32 acquireFlags;
if (timeout <= 0) {
timeout = 0;
acquireFlags = B_RELATIVE_TIMEOUT;
} else if (timeout == B_INFINITE_TIMEOUT) {
acquireFlags = B_RELATIVE_TIMEOUT;
} else {
timeout += system_time();
acquireFlags = B_ABSOLUTE_TIMEOUT;
}
int32 count = 1;
do {
status_t status;
do {
status = acquire_sem_etc(groupReclaimSem, count, acquireFlags,
timeout);
} while (status == B_INTERRUPTED);
if (status != B_OK)
return status;
status = Lock();
if (status != B_OK) {
ERROR("SharedBufferList:: RequestBuffer: Lock failed: %s\n",
strerror(status));
release_sem_etc(groupReclaimSem, count, 0);
return B_ERROR;
}
for (int32 i = 0; i < fCount; i++) {
if (fInfos[i].reclaim_sem == groupReclaimSem
&& fInfos[i].reclaimed) {
if ((size != 0 && size <= fInfos[i].buffer->SizeAvailable())
|| (*_buffer != 0 && fInfos[i].buffer == *_buffer)
|| (wantID != 0 && fInfos[i].id == wantID)) {
fInfos[i].reclaimed = false;
*_buffer = fInfos[i].buffer;
if (count > 1) {
release_sem_etc(groupReclaimSem, count - 1,
B_DO_NOT_RESCHEDULE);
}
_RequestBufferInOtherGroups(groupReclaimSem,
fInfos[i].buffer->ID());
Unlock();
return B_OK;
}
}
}
release_sem_etc(groupReclaimSem, count, B_DO_NOT_RESCHEDULE);
if (Unlock() != B_OK) {
ERROR("SharedBufferList:: RequestBuffer: unlock failed\n");
return B_ERROR;
}
count++;
} while (count <= buffersInGroup);
ERROR("SharedBufferList:: RequestBuffer: no buffer found\n");
return B_ERROR;
}
status_t
SharedBufferList::RecycleBuffer(BBuffer* buffer)
{
CALLED();
media_buffer_id id = buffer->ID();
if (Lock() != B_OK)
return B_ERROR;
int32 reclaimedCount = 0;
for (int32 i = 0; i < fCount; i++) {
if (fInfos[i].id == id) {
reclaimedCount++;
if (fInfos[i].reclaimed) {
ERROR("SharedBufferList::RecycleBuffer, BBuffer %p, id = %"
B_PRId32 " already reclaimed\n", buffer, id);
DEBUG_ONLY(debugger("buffer already reclaimed"));
continue;
}
fInfos[i].reclaimed = true;
release_sem_etc(fInfos[i].reclaim_sem, 1, B_DO_NOT_RESCHEDULE);
}
}
if (Unlock() != B_OK)
return B_ERROR;
if (reclaimedCount == 0) {
ERROR("shared_buffer_list::RecycleBuffer, BBuffer %p, id = %" B_PRId32
" NOT reclaimed\n", buffer, id);
return B_ERROR;
}
return B_OK;
}
status_t
SharedBufferList::RemoveBuffer(BBuffer* buffer)
{
CALLED();
media_buffer_id id = buffer->ID();
if (Lock() != B_OK)
return B_ERROR;
int32 notRemovedCount = 0;
for (int32 i = 0; i < fCount; i++) {
if (fInfos[i].id == id) {
if (!fInfos[i].reclaimed) {
notRemovedCount++;
ERROR("SharedBufferList::RequestBuffer, BBuffer %p, id = %"
B_PRId32 " not reclaimed\n", buffer, id);
DEBUG_ONLY(debugger("buffer not reclaimed"));
continue;
}
fInfos[i].buffer = NULL;
fInfos[i].id = -1;
fInfos[i].reclaim_sem = -1;
}
}
if (Unlock() != B_OK)
return B_ERROR;
if (notRemovedCount != 0) {
ERROR("SharedBufferList::RemoveBuffer, BBuffer %p, id = %" B_PRId32
" not reclaimed\n", buffer, id);
return B_ERROR;
}
return B_OK;
}
\a groupReclaimSem if successful.
*/
status_t
SharedBufferList::GetBufferList(sem_id groupReclaimSem, int32 bufferCount,
BBuffer** buffers)
{
CALLED();
if (Lock() != B_OK)
return B_ERROR;
int32 found = 0;
for (int32 i = 0; i < fCount; i++)
if (fInfos[i].reclaim_sem == groupReclaimSem) {
buffers[found++] = fInfos[i].buffer;
if (found == bufferCount)
break;
}
if (Unlock() != B_OK)
return B_ERROR;
return found == bufferCount ? B_OK : B_ERROR;
}
status_t
SharedBufferList::_Init()
{
CALLED();
fSemaphore = create_sem(0, "shared buffer list lock");
if (fSemaphore < 0)
return fSemaphore;
fAtom = 0;
for (int32 i = 0; i < kMaxBuffers; i++) {
fInfos[i].id = -1;
}
fCount = 0;
return B_OK;
}
*/
void
SharedBufferList::_RequestBufferInOtherGroups(sem_id groupReclaimSem,
media_buffer_id id)
{
for (int32 i = 0; i < fCount; i++) {
if (fInfos[i].id == id && fInfos[i].reclaim_sem != groupReclaimSem) {
status_t status;
do {
status = acquire_sem(fInfos[i].reclaim_sem);
} while (status == B_INTERRUPTED);
if (status != B_OK)
continue;
if (fInfos[i].reclaimed == false) {
ERROR("SharedBufferList:: RequestBufferInOtherGroups BBuffer "
"%p, id = %" B_PRId32 " not reclaimed while requesting\n",
fInfos[i].buffer, id);
continue;
}
fInfos[i].reclaimed = false;
}
}
}
}