* probably fixed the build, did I forget some files before?
* implemented full undo/redo for any playlist operations git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@21317 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -6,6 +6,7 @@ AddSubDirSupportedPlatforms libbe_test ;
|
|||||||
|
|
||||||
# source directories
|
# source directories
|
||||||
local sourceDirs =
|
local sourceDirs =
|
||||||
|
playlist
|
||||||
supplier
|
supplier
|
||||||
support
|
support
|
||||||
;
|
;
|
||||||
@@ -16,6 +17,17 @@ for sourceDir in $(sourceDirs) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Application MediaPlayer :
|
Application MediaPlayer :
|
||||||
|
# playlist
|
||||||
|
CopyPLItemsCommand.cpp
|
||||||
|
ImportPLItemsCommand.cpp
|
||||||
|
ListViews.cpp
|
||||||
|
MovePLItemsCommand.cpp
|
||||||
|
Playlist.cpp
|
||||||
|
PlaylistListView.cpp
|
||||||
|
PlaylistObserver.cpp
|
||||||
|
PlaylistWindow.cpp
|
||||||
|
RemovePLItemsCommand.cpp
|
||||||
|
|
||||||
# supplier
|
# supplier
|
||||||
AudioSupplier.cpp
|
AudioSupplier.cpp
|
||||||
MediaTrackAudioSupplier.cpp
|
MediaTrackAudioSupplier.cpp
|
||||||
@@ -37,13 +49,8 @@ Application MediaPlayer :
|
|||||||
ControllerView.cpp
|
ControllerView.cpp
|
||||||
DrawingTidbits.cpp
|
DrawingTidbits.cpp
|
||||||
InfoWin.cpp
|
InfoWin.cpp
|
||||||
ListViews.cpp
|
|
||||||
MainApp.cpp
|
MainApp.cpp
|
||||||
MainWin.cpp
|
MainWin.cpp
|
||||||
Playlist.cpp
|
|
||||||
PlaylistListView.cpp
|
|
||||||
PlaylistObserver.cpp
|
|
||||||
PlaylistWindow.cpp
|
|
||||||
SoundOutput.cpp
|
SoundOutput.cpp
|
||||||
TransportButton.cpp
|
TransportButton.cpp
|
||||||
TransportControlGroup.cpp
|
TransportControlGroup.cpp
|
||||||
|
|||||||
@@ -0,0 +1,116 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2007, Haiku. All rights reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Stephan Aßmus <[email protected]>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "CopyPLItemsCommand.h"
|
||||||
|
|
||||||
|
#include <new>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include <Autolock.h>
|
||||||
|
|
||||||
|
#include "Playlist.h"
|
||||||
|
|
||||||
|
|
||||||
|
using std::nothrow;
|
||||||
|
|
||||||
|
|
||||||
|
CopyPLItemsCommand::CopyPLItemsCommand(Playlist* playlist,
|
||||||
|
const int32* indices, int32 count, int32 toIndex)
|
||||||
|
: Command()
|
||||||
|
, fPlaylist(playlist)
|
||||||
|
, fRefs(count > 0 ? new (nothrow) entry_ref[count] : NULL)
|
||||||
|
, fToIndex(toIndex)
|
||||||
|
, fCount(count)
|
||||||
|
{
|
||||||
|
if (!indices || !fPlaylist || !fRefs) {
|
||||||
|
// indicate a bad object state
|
||||||
|
delete[] fRefs;
|
||||||
|
fRefs = NULL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// init original entries and
|
||||||
|
for (int32 i = 0; i < fCount; i++) {
|
||||||
|
if (fPlaylist->GetRefAt(indices[i], &fRefs[i]) < B_OK) {
|
||||||
|
delete[] fRefs;
|
||||||
|
fRefs = NULL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
CopyPLItemsCommand::~CopyPLItemsCommand()
|
||||||
|
{
|
||||||
|
delete[] fRefs;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
CopyPLItemsCommand::InitCheck()
|
||||||
|
{
|
||||||
|
if (!fPlaylist || !fRefs)
|
||||||
|
return B_NO_INIT;
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
CopyPLItemsCommand::Perform()
|
||||||
|
{
|
||||||
|
BAutolock _(fPlaylist);
|
||||||
|
|
||||||
|
status_t ret = B_OK;
|
||||||
|
|
||||||
|
// add refs to playlist at the insertion index
|
||||||
|
int32 index = fToIndex;
|
||||||
|
for (int32 i = 0; i < fCount; i++) {
|
||||||
|
if (!fPlaylist->AddRef(fRefs[i], index++)) {
|
||||||
|
ret = B_NO_MEMORY;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ret < B_OK)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
CopyPLItemsCommand::Undo()
|
||||||
|
{
|
||||||
|
BAutolock _(fPlaylist);
|
||||||
|
|
||||||
|
// remember currently playling ref in case we copy items over it
|
||||||
|
entry_ref currentRef;
|
||||||
|
bool adjustCurrentRef = fPlaylist->GetRefAt(fPlaylist->CurrentRefIndex(),
|
||||||
|
¤tRef) == B_OK;
|
||||||
|
|
||||||
|
// remove refs from playlist
|
||||||
|
int32 index = fToIndex;
|
||||||
|
for (int32 i = 0; i < fCount; i++) {
|
||||||
|
fPlaylist->RemoveRef(index++, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// take care about currently played ref
|
||||||
|
if (adjustCurrentRef)
|
||||||
|
fPlaylist->SetCurrentRefIndex(fPlaylist->IndexOf(currentRef));
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
CopyPLItemsCommand::GetName(BString& name)
|
||||||
|
{
|
||||||
|
if (fCount > 1)
|
||||||
|
name << "Copy Entries";
|
||||||
|
else
|
||||||
|
name << "Copy Entry";
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2007, Haiku. All rights reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Stephan Aßmus <[email protected]>
|
||||||
|
*/
|
||||||
|
#ifndef COPY_PL_ITEMS_COMMAND_H
|
||||||
|
#define COPY_PL_ITEMS_COMMAND_H
|
||||||
|
|
||||||
|
|
||||||
|
#include "Command.h"
|
||||||
|
|
||||||
|
class Playlist;
|
||||||
|
|
||||||
|
class CopyPLItemsCommand : public Command {
|
||||||
|
public:
|
||||||
|
CopyPLItemsCommand(
|
||||||
|
Playlist* playlist,
|
||||||
|
const int32* indices,
|
||||||
|
int32 count,
|
||||||
|
int32 toIndex);
|
||||||
|
virtual ~CopyPLItemsCommand();
|
||||||
|
|
||||||
|
virtual status_t InitCheck();
|
||||||
|
|
||||||
|
virtual status_t Perform();
|
||||||
|
virtual status_t Undo();
|
||||||
|
|
||||||
|
virtual void GetName(BString& name);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Playlist* fPlaylist;
|
||||||
|
entry_ref* fRefs;
|
||||||
|
int32 fToIndex;
|
||||||
|
int32 fCount;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // COPY_PL_ITEMS_COMMAND_H
|
||||||
@@ -0,0 +1,156 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2007, Haiku. All rights reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Stephan Aßmus <[email protected]>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "ImportPLItemsCommand.h"
|
||||||
|
|
||||||
|
#include <new>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include <Autolock.h>
|
||||||
|
|
||||||
|
#include "Playlist.h"
|
||||||
|
|
||||||
|
|
||||||
|
using std::nothrow;
|
||||||
|
|
||||||
|
|
||||||
|
ImportPLItemsCommand::ImportPLItemsCommand(Playlist* playlist,
|
||||||
|
const BMessage* refsMessage, int32 toIndex)
|
||||||
|
: Command()
|
||||||
|
, fPlaylist(playlist)
|
||||||
|
|
||||||
|
, fOldRefs(NULL)
|
||||||
|
, fOldCount(0)
|
||||||
|
|
||||||
|
, fNewRefs(NULL)
|
||||||
|
, fNewCount(0)
|
||||||
|
|
||||||
|
, fToIndex(toIndex)
|
||||||
|
{
|
||||||
|
if (!fPlaylist)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Playlist temp;
|
||||||
|
temp.AppendRefs(refsMessage);
|
||||||
|
|
||||||
|
fNewCount = temp.CountItems();
|
||||||
|
if (fNewCount <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
fNewRefs = new (nothrow) entry_ref[fNewCount];
|
||||||
|
if (!fNewRefs)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// init new entries
|
||||||
|
for (int32 i = 0; i < fNewCount; i++) {
|
||||||
|
if (temp.GetRefAt(i, &fNewRefs[i]) < B_OK) {
|
||||||
|
delete[] fNewRefs;
|
||||||
|
fNewRefs = NULL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fToIndex < 0) {
|
||||||
|
fOldCount = fPlaylist->CountItems();
|
||||||
|
if (fOldCount > 0) {
|
||||||
|
fOldRefs = new (nothrow) entry_ref[fOldCount];
|
||||||
|
if (!fOldRefs) {
|
||||||
|
// indicate bad object init
|
||||||
|
delete[] fNewRefs;
|
||||||
|
fNewRefs = NULL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int32 i = 0; i < fOldCount; i++) {
|
||||||
|
if (fPlaylist->GetRefAt(i, &fOldRefs[i]) < B_OK) {
|
||||||
|
// indicate bad object init
|
||||||
|
delete[] fNewRefs;
|
||||||
|
fNewRefs = NULL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ImportPLItemsCommand::~ImportPLItemsCommand()
|
||||||
|
{
|
||||||
|
delete[] fOldRefs;
|
||||||
|
delete[] fNewRefs;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
ImportPLItemsCommand::InitCheck()
|
||||||
|
{
|
||||||
|
if (!fPlaylist || !fNewRefs)
|
||||||
|
return B_NO_INIT;
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
ImportPLItemsCommand::Perform()
|
||||||
|
{
|
||||||
|
BAutolock _(fPlaylist);
|
||||||
|
|
||||||
|
int32 index = fToIndex;
|
||||||
|
if (fToIndex < 0) {
|
||||||
|
fPlaylist->MakeEmpty();
|
||||||
|
index = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool startPlaying = fPlaylist->CountItems() == 0;
|
||||||
|
|
||||||
|
// add refs to playlist at the insertion index
|
||||||
|
for (int32 i = 0; i < fNewCount; i++) {
|
||||||
|
if (!fPlaylist->AddRef(fNewRefs[i], index++))
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (startPlaying) {
|
||||||
|
// open first file
|
||||||
|
fPlaylist->SetCurrentRefIndex(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
ImportPLItemsCommand::Undo()
|
||||||
|
{
|
||||||
|
BAutolock _(fPlaylist);
|
||||||
|
|
||||||
|
if (fToIndex < 0) {
|
||||||
|
// remove new refs from playlist and restore old refs
|
||||||
|
fPlaylist->MakeEmpty();
|
||||||
|
for (int32 i = 0; i < fOldCount; i++) {
|
||||||
|
if (!fPlaylist->AddRef(fOldRefs[i], i))
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// remove refs from playlist
|
||||||
|
for (int32 i = 0; i < fNewCount; i++) {
|
||||||
|
fPlaylist->RemoveRef(fToIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
ImportPLItemsCommand::GetName(BString& name)
|
||||||
|
{
|
||||||
|
if (fNewCount > 1)
|
||||||
|
name << "Import Entries";
|
||||||
|
else
|
||||||
|
name << "Import Entry";
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2007, Haiku. All rights reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Stephan Aßmus <[email protected]>
|
||||||
|
*/
|
||||||
|
#ifndef IMPORT_PL_ITEMS_COMMAND_H
|
||||||
|
#define IMPORT_PL_ITEMS_COMMAND_H
|
||||||
|
|
||||||
|
|
||||||
|
#include "Command.h"
|
||||||
|
|
||||||
|
class BMessage;
|
||||||
|
class Playlist;
|
||||||
|
|
||||||
|
class ImportPLItemsCommand : public Command {
|
||||||
|
public:
|
||||||
|
ImportPLItemsCommand(
|
||||||
|
Playlist* playlist,
|
||||||
|
const BMessage* refsMessage,
|
||||||
|
int32 toIndex);
|
||||||
|
virtual ~ImportPLItemsCommand();
|
||||||
|
|
||||||
|
virtual status_t InitCheck();
|
||||||
|
|
||||||
|
virtual status_t Perform();
|
||||||
|
virtual status_t Undo();
|
||||||
|
|
||||||
|
virtual void GetName(BString& name);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Playlist* fPlaylist;
|
||||||
|
entry_ref* fOldRefs;
|
||||||
|
int32 fOldCount;
|
||||||
|
entry_ref* fNewRefs;
|
||||||
|
int32 fNewCount;
|
||||||
|
int32 fToIndex;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // IMPORT_PL_ITEMS_COMMAND_H
|
||||||
@@ -0,0 +1,181 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2007, Haiku. All rights reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Stephan Aßmus <[email protected]>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "MovePLItemsCommand.h"
|
||||||
|
|
||||||
|
#include <new>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include <Autolock.h>
|
||||||
|
|
||||||
|
#include "Playlist.h"
|
||||||
|
|
||||||
|
|
||||||
|
using std::nothrow;
|
||||||
|
|
||||||
|
|
||||||
|
MovePLItemsCommand::MovePLItemsCommand(Playlist* playlist,
|
||||||
|
const int32* indices, int32 count, int32 toIndex)
|
||||||
|
: Command()
|
||||||
|
, fPlaylist(playlist)
|
||||||
|
, fRefs(count > 0 ? new (nothrow) entry_ref[count] : NULL)
|
||||||
|
, fIndices(count > 0 ? new (nothrow) int32[count] : NULL)
|
||||||
|
, fToIndex(toIndex)
|
||||||
|
, fCount(count)
|
||||||
|
{
|
||||||
|
if (!indices || !fPlaylist || !fRefs || !fIndices) {
|
||||||
|
// indicate a bad object state
|
||||||
|
delete[] fRefs;
|
||||||
|
fRefs = NULL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(fIndices, indices, fCount * sizeof(int32));
|
||||||
|
|
||||||
|
// init original entry indices and
|
||||||
|
// adjust toIndex compensating for items that
|
||||||
|
// are removed before that index
|
||||||
|
int32 itemsBeforeIndex = 0;
|
||||||
|
for (int32 i = 0; i < fCount; i++) {
|
||||||
|
if (fPlaylist->GetRefAt(fIndices[i], &fRefs[i]) < B_OK) {
|
||||||
|
delete[] fRefs;
|
||||||
|
fRefs = NULL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (fIndices[i] < fToIndex)
|
||||||
|
itemsBeforeIndex++;
|
||||||
|
}
|
||||||
|
fToIndex -= itemsBeforeIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
MovePLItemsCommand::~MovePLItemsCommand()
|
||||||
|
{
|
||||||
|
delete[] fRefs;
|
||||||
|
delete[] fIndices;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
MovePLItemsCommand::InitCheck()
|
||||||
|
{
|
||||||
|
if (!fPlaylist || !fRefs || !fIndices)
|
||||||
|
return B_NO_INIT;
|
||||||
|
|
||||||
|
// analyse the move, don't return B_OK in case
|
||||||
|
// the container state does not change...
|
||||||
|
|
||||||
|
int32 index = fIndices[0];
|
||||||
|
// NOTE: fIndices == NULL if fCount < 1
|
||||||
|
|
||||||
|
if (index != fToIndex) {
|
||||||
|
// a change is guaranteed
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
// the insertion index is the same as the index of the first
|
||||||
|
// moved item, a change only occures if the indices of the
|
||||||
|
// moved items is not contiguous
|
||||||
|
bool isContiguous = true;
|
||||||
|
for (int32 i = 1; i < fCount; i++) {
|
||||||
|
if (fIndices[i] != index + 1) {
|
||||||
|
isContiguous = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
index = fIndices[i];
|
||||||
|
}
|
||||||
|
if (isContiguous) {
|
||||||
|
// the container state will not change because of the move
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
MovePLItemsCommand::Perform()
|
||||||
|
{
|
||||||
|
BAutolock _(fPlaylist);
|
||||||
|
|
||||||
|
status_t ret = B_OK;
|
||||||
|
|
||||||
|
// remember currently playling ref in case we move it
|
||||||
|
entry_ref currentRef;
|
||||||
|
bool adjustCurrentRef = fPlaylist->GetRefAt(fPlaylist->CurrentRefIndex(),
|
||||||
|
¤tRef) == B_OK;
|
||||||
|
|
||||||
|
// remove refs from playlist
|
||||||
|
for (int32 i = 0; i < fCount; i++) {
|
||||||
|
// "- i" to account for the items already removed
|
||||||
|
fPlaylist->RemoveRef(fIndices[i] - i, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// add refs to playlist at the insertion index
|
||||||
|
int32 index = fToIndex;
|
||||||
|
for (int32 i = 0; i < fCount; i++) {
|
||||||
|
if (!fPlaylist->AddRef(fRefs[i], index++)) {
|
||||||
|
ret = B_NO_MEMORY;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ret < B_OK)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
// take care about currently played ref
|
||||||
|
if (adjustCurrentRef)
|
||||||
|
fPlaylist->SetCurrentRefIndex(fPlaylist->IndexOf(currentRef));
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
MovePLItemsCommand::Undo()
|
||||||
|
{
|
||||||
|
BAutolock _(fPlaylist);
|
||||||
|
|
||||||
|
status_t ret = B_OK;
|
||||||
|
|
||||||
|
// remember currently playling ref in case we move it
|
||||||
|
entry_ref currentRef;
|
||||||
|
bool adjustCurrentRef = fPlaylist->GetRefAt(fPlaylist->CurrentRefIndex(),
|
||||||
|
¤tRef) == B_OK;
|
||||||
|
|
||||||
|
// remove refs from playlist
|
||||||
|
int32 index = fToIndex;
|
||||||
|
for (int32 i = 0; i < fCount; i++) {
|
||||||
|
fPlaylist->RemoveRef(index++, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// add ref to playlist at remembered indices
|
||||||
|
for (int32 i = 0; i < fCount; i++) {
|
||||||
|
if (!fPlaylist->AddRef(fRefs[i], fIndices[i])) {
|
||||||
|
ret = B_NO_MEMORY;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ret < B_OK)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
// take care about currently played ref
|
||||||
|
if (adjustCurrentRef)
|
||||||
|
fPlaylist->SetCurrentRefIndex(fPlaylist->IndexOf(currentRef));
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
MovePLItemsCommand::GetName(BString& name)
|
||||||
|
{
|
||||||
|
if (fCount > 1)
|
||||||
|
name << "Move Entries";
|
||||||
|
else
|
||||||
|
name << "Move Entry";
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2007, Haiku. All rights reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Stephan Aßmus <[email protected]>
|
||||||
|
*/
|
||||||
|
#ifndef MOVE_PL_ITEMS_COMMAND_H
|
||||||
|
#define MOVE_PL_ITEMS_COMMAND_H
|
||||||
|
|
||||||
|
|
||||||
|
#include "Command.h"
|
||||||
|
|
||||||
|
class Playlist;
|
||||||
|
|
||||||
|
class MovePLItemsCommand : public Command {
|
||||||
|
public:
|
||||||
|
MovePLItemsCommand(
|
||||||
|
Playlist* playlist,
|
||||||
|
const int32* indices,
|
||||||
|
int32 count,
|
||||||
|
int32 toIndex);
|
||||||
|
virtual ~MovePLItemsCommand();
|
||||||
|
|
||||||
|
virtual status_t InitCheck();
|
||||||
|
|
||||||
|
virtual status_t Perform();
|
||||||
|
virtual status_t Undo();
|
||||||
|
|
||||||
|
virtual void GetName(BString& name);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Playlist* fPlaylist;
|
||||||
|
entry_ref* fRefs;
|
||||||
|
int32* fIndices;
|
||||||
|
int32 fToIndex;
|
||||||
|
int32 fCount;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // MOVE_PL_ITEMS_COMMAND_H
|
||||||
@@ -260,7 +260,7 @@ Playlist::RemoveListener(Listener* listener)
|
|||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
Playlist::AppendRefs(BMessage* refsReceivedMessage, int32 appendIndex)
|
Playlist::AppendRefs(const BMessage* refsReceivedMessage, int32 appendIndex)
|
||||||
{
|
{
|
||||||
// the playlist ist replaced by the refs in the message
|
// the playlist ist replaced by the refs in the message
|
||||||
// or the refs are appended at the appendIndex
|
// or the refs are appended at the appendIndex
|
||||||
@@ -74,7 +74,7 @@ public:
|
|||||||
void RemoveListener(Listener* listener);
|
void RemoveListener(Listener* listener);
|
||||||
|
|
||||||
// support functions
|
// support functions
|
||||||
void AppendRefs(BMessage* refsReceivedMessage,
|
void AppendRefs(const BMessage* refsReceivedMessage,
|
||||||
int32 appendIndex = -1);
|
int32 appendIndex = -1);
|
||||||
static void AppendToPlaylistRecursive(const entry_ref& ref,
|
static void AppendToPlaylistRecursive(const entry_ref& ref,
|
||||||
Playlist* playlist);
|
Playlist* playlist);
|
||||||
+16
-74
@@ -16,12 +16,17 @@
|
|||||||
#include <ScrollView.h>
|
#include <ScrollView.h>
|
||||||
#include <Window.h>
|
#include <Window.h>
|
||||||
|
|
||||||
|
#include "CommandStack.h"
|
||||||
#include "Controller.h"
|
#include "Controller.h"
|
||||||
#include "ControllerObserver.h"
|
#include "ControllerObserver.h"
|
||||||
|
#include "CopyPLItemsCommand.h"
|
||||||
|
#include "ImportPLItemsCommand.h"
|
||||||
#include "ListViews.h"
|
#include "ListViews.h"
|
||||||
|
#include "MovePLItemsCommand.h"
|
||||||
#include "PlaybackState.h"
|
#include "PlaybackState.h"
|
||||||
#include "Playlist.h"
|
#include "Playlist.h"
|
||||||
#include "PlaylistObserver.h"
|
#include "PlaylistObserver.h"
|
||||||
|
#include "RemovePLItemsCommand.h"
|
||||||
|
|
||||||
using std::nothrow;
|
using std::nothrow;
|
||||||
|
|
||||||
@@ -169,7 +174,7 @@ PlaylistItem::Draw(BView* owner, BRect frame, const font_height& fh,
|
|||||||
|
|
||||||
|
|
||||||
PlaylistListView::PlaylistListView(BRect frame, Playlist* playlist,
|
PlaylistListView::PlaylistListView(BRect frame, Playlist* playlist,
|
||||||
Controller* controller)
|
Controller* controller, CommandStack* stack)
|
||||||
: SimpleListView(frame, "playlist listview", NULL)
|
: SimpleListView(frame, "playlist listview", NULL)
|
||||||
|
|
||||||
, fPlaylist(playlist)
|
, fPlaylist(playlist)
|
||||||
@@ -179,6 +184,8 @@ PlaylistListView::PlaylistListView(BRect frame, Playlist* playlist,
|
|||||||
, fControllerObserver(new ControllerObserver(this,
|
, fControllerObserver(new ControllerObserver(this,
|
||||||
OBSERVE_PLAYBACK_STATE_CHANGES))
|
OBSERVE_PLAYBACK_STATE_CHANGES))
|
||||||
|
|
||||||
|
, fCommandStack(stack)
|
||||||
|
|
||||||
, fCurrentPlaylistIndex(-1)
|
, fCurrentPlaylistIndex(-1)
|
||||||
, fPlaybackState(PLAYBACK_STATE_STOPPED)
|
, fPlaybackState(PLAYBACK_STATE_STOPPED)
|
||||||
|
|
||||||
@@ -329,85 +336,24 @@ PlaylistListView::KeyDown(const char* bytes, int32 numBytes)
|
|||||||
void
|
void
|
||||||
PlaylistListView::MoveItems(BList& indices, int32 toIndex)
|
PlaylistListView::MoveItems(BList& indices, int32 toIndex)
|
||||||
{
|
{
|
||||||
if (!fPlaylist->Lock())
|
fCommandStack->Perform(new (nothrow) MovePLItemsCommand(fPlaylist,
|
||||||
return;
|
(int32*)indices.Items(), indices.CountItems(), toIndex));
|
||||||
|
|
||||||
entry_ref currentRef;
|
|
||||||
bool adjustCurrentRef = fPlaylist->GetRefAt(fPlaylist->CurrentRefIndex(),
|
|
||||||
¤tRef) == B_OK;
|
|
||||||
|
|
||||||
int32 count = indices.CountItems();
|
|
||||||
entry_ref refs[count];
|
|
||||||
for (int32 i = 0; i < count; i++) {
|
|
||||||
int32 index = (int32)indices.ItemAtFast(i) - i;
|
|
||||||
// "-i" to account for items already removed in the
|
|
||||||
// target list
|
|
||||||
if (index < 0) {
|
|
||||||
// asynchronous message is out of date
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
refs[i] = fPlaylist->RemoveRef(index, false);
|
|
||||||
if (index < toIndex)
|
|
||||||
toIndex --;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int32 i = 0; i < count; i++) {
|
|
||||||
fPlaylist->AddRef(refs[i], toIndex++);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (adjustCurrentRef)
|
|
||||||
fPlaylist->SetCurrentRefIndex(fPlaylist->IndexOf(currentRef));
|
|
||||||
|
|
||||||
fPlaylist->Unlock();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
PlaylistListView::CopyItems(BList& indices, int32 toIndex)
|
PlaylistListView::CopyItems(BList& indices, int32 toIndex)
|
||||||
{
|
{
|
||||||
if (!fPlaylist->Lock())
|
fCommandStack->Perform(new (nothrow) CopyPLItemsCommand(fPlaylist,
|
||||||
return;
|
(int32*)indices.Items(), indices.CountItems(), toIndex));
|
||||||
|
|
||||||
int32 count = indices.CountItems();
|
|
||||||
entry_ref refs[count];
|
|
||||||
for (int32 i = 0; i < count; i++) {
|
|
||||||
int32 index = (int32)indices.ItemAtFast(i);
|
|
||||||
if (index < 0) {
|
|
||||||
// asynchronous message is out of date
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (fPlaylist->GetRefAt(index, &refs[i]) < B_OK)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int32 i = 0; i < count; i++) {
|
|
||||||
fPlaylist->AddRef(refs[i], toIndex++);
|
|
||||||
}
|
|
||||||
|
|
||||||
fPlaylist->Unlock();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
PlaylistListView::RemoveItemList(BList& indices)
|
PlaylistListView::RemoveItemList(BList& indices)
|
||||||
{
|
{
|
||||||
if (!fPlaylist->Lock())
|
fCommandStack->Perform(new (nothrow) RemovePLItemsCommand(fPlaylist,
|
||||||
return;
|
(int32*)indices.Items(), indices.CountItems()));
|
||||||
|
|
||||||
int32 count = indices.CountItems();
|
|
||||||
int32 lastRemovedIndex = -1;
|
|
||||||
for (int32 i = 0; i < count; i++) {
|
|
||||||
lastRemovedIndex = (int32)indices.ItemAtFast(i) - i;
|
|
||||||
// "-i" to account for items already removed in the
|
|
||||||
// target list
|
|
||||||
fPlaylist->RemoveRef(lastRemovedIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
// in case we removed the currently playing file
|
|
||||||
if (fPlaylist->CurrentRefIndex() == -1)
|
|
||||||
fPlaylist->SetCurrentRefIndex(lastRemovedIndex);
|
|
||||||
|
|
||||||
fPlaylist->Unlock();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -424,12 +370,8 @@ PlaylistListView::DrawListItem(BView* owner, int32 index, BRect frame) const
|
|||||||
void
|
void
|
||||||
PlaylistListView::RefsReceived(BMessage* message, int32 appendIndex)
|
PlaylistListView::RefsReceived(BMessage* message, int32 appendIndex)
|
||||||
{
|
{
|
||||||
if (!fPlaylist->Lock())
|
fCommandStack->Perform(new (nothrow) ImportPLItemsCommand(fPlaylist,
|
||||||
return;
|
message, appendIndex));
|
||||||
|
|
||||||
fPlaylist->AppendRefs(message, appendIndex);
|
|
||||||
|
|
||||||
fPlaylist->Unlock();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
+5
-1
@@ -10,6 +10,7 @@
|
|||||||
|
|
||||||
#include "ListViews.h"
|
#include "ListViews.h"
|
||||||
|
|
||||||
|
class CommandStack;
|
||||||
class Controller;
|
class Controller;
|
||||||
class ControllerObserver;
|
class ControllerObserver;
|
||||||
class Playlist;
|
class Playlist;
|
||||||
@@ -20,7 +21,8 @@ class PlaylistListView : public SimpleListView {
|
|||||||
public:
|
public:
|
||||||
PlaylistListView(BRect frame,
|
PlaylistListView(BRect frame,
|
||||||
Playlist* playlist,
|
Playlist* playlist,
|
||||||
Controller* controller);
|
Controller* controller,
|
||||||
|
CommandStack* stack);
|
||||||
virtual ~PlaylistListView();
|
virtual ~PlaylistListView();
|
||||||
|
|
||||||
// BView interface
|
// BView interface
|
||||||
@@ -56,6 +58,8 @@ class PlaylistListView : public SimpleListView {
|
|||||||
Controller* fController;
|
Controller* fController;
|
||||||
ControllerObserver* fControllerObserver;
|
ControllerObserver* fControllerObserver;
|
||||||
|
|
||||||
|
CommandStack* fCommandStack;
|
||||||
|
|
||||||
int32 fCurrentPlaylistIndex;
|
int32 fCurrentPlaylistIndex;
|
||||||
uint32 fPlaybackState;
|
uint32 fPlaybackState;
|
||||||
|
|
||||||
+2
-1
@@ -33,7 +33,8 @@ PlaylistWindow::PlaylistWindow(BRect frame, Playlist* playlist,
|
|||||||
_CreateMenu(frame);
|
_CreateMenu(frame);
|
||||||
|
|
||||||
frame.right -= B_V_SCROLL_BAR_WIDTH;
|
frame.right -= B_V_SCROLL_BAR_WIDTH;
|
||||||
fListView = new PlaylistListView(frame, playlist, controller);
|
fListView = new PlaylistListView(frame, playlist, controller,
|
||||||
|
fCommandStack);
|
||||||
|
|
||||||
BScrollView* scrollView = new BScrollView("playlist scrollview",
|
BScrollView* scrollView = new BScrollView("playlist scrollview",
|
||||||
fListView, B_FOLLOW_ALL, 0, false, true, B_NO_BORDER);
|
fListView, B_FOLLOW_ALL, 0, false, true, B_NO_BORDER);
|
||||||
@@ -0,0 +1,125 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2007, Haiku. All rights reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Stephan Aßmus <[email protected]>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "RemovePLItemsCommand.h"
|
||||||
|
|
||||||
|
#include <new>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include <Autolock.h>
|
||||||
|
|
||||||
|
#include "Playlist.h"
|
||||||
|
|
||||||
|
|
||||||
|
using std::nothrow;
|
||||||
|
|
||||||
|
|
||||||
|
RemovePLItemsCommand::RemovePLItemsCommand(Playlist* playlist,
|
||||||
|
const int32* indices, int32 count)
|
||||||
|
: Command()
|
||||||
|
, fPlaylist(playlist)
|
||||||
|
, fRefs(count > 0 ? new (nothrow) entry_ref[count] : NULL)
|
||||||
|
, fIndices(count > 0 ? new (nothrow) int32[count] : NULL)
|
||||||
|
, fCount(count)
|
||||||
|
{
|
||||||
|
if (!indices || !fPlaylist || !fRefs || !fIndices) {
|
||||||
|
// indicate a bad object state
|
||||||
|
delete[] fRefs;
|
||||||
|
fRefs = NULL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(fIndices, indices, fCount * sizeof(int32));
|
||||||
|
|
||||||
|
// init original entry indices
|
||||||
|
for (int32 i = 0; i < fCount; i++) {
|
||||||
|
if (fPlaylist->GetRefAt(fIndices[i], &fRefs[i]) < B_OK) {
|
||||||
|
delete[] fRefs;
|
||||||
|
fRefs = NULL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
RemovePLItemsCommand::~RemovePLItemsCommand()
|
||||||
|
{
|
||||||
|
delete[] fRefs;
|
||||||
|
delete[] fIndices;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
RemovePLItemsCommand::InitCheck()
|
||||||
|
{
|
||||||
|
if (!fPlaylist || !fRefs || !fIndices)
|
||||||
|
return B_NO_INIT;
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
RemovePLItemsCommand::Perform()
|
||||||
|
{
|
||||||
|
BAutolock _(fPlaylist);
|
||||||
|
|
||||||
|
int32 lastRemovedIndex = -1;
|
||||||
|
|
||||||
|
// remove refs from playlist
|
||||||
|
for (int32 i = 0; i < fCount; i++) {
|
||||||
|
// "- i" to account for the items already removed
|
||||||
|
lastRemovedIndex = fIndices[i] - i;
|
||||||
|
fPlaylist->RemoveRef(lastRemovedIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
// in case we removed the currently playing file
|
||||||
|
if (fPlaylist->CurrentRefIndex() == -1)
|
||||||
|
fPlaylist->SetCurrentRefIndex(lastRemovedIndex);
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
RemovePLItemsCommand::Undo()
|
||||||
|
{
|
||||||
|
BAutolock _(fPlaylist);
|
||||||
|
|
||||||
|
status_t ret = B_OK;
|
||||||
|
|
||||||
|
// remember currently playling ref in case we move it
|
||||||
|
entry_ref currentRef;
|
||||||
|
bool adjustCurrentRef = fPlaylist->GetRefAt(fPlaylist->CurrentRefIndex(),
|
||||||
|
¤tRef) == B_OK;
|
||||||
|
|
||||||
|
// add refs to playlist at remembered indices
|
||||||
|
for (int32 i = 0; i < fCount; i++) {
|
||||||
|
if (!fPlaylist->AddRef(fRefs[i], fIndices[i])) {
|
||||||
|
ret = B_NO_MEMORY;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ret < B_OK)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
// take care about currently played ref
|
||||||
|
if (adjustCurrentRef)
|
||||||
|
fPlaylist->SetCurrentRefIndex(fPlaylist->IndexOf(currentRef));
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
RemovePLItemsCommand::GetName(BString& name)
|
||||||
|
{
|
||||||
|
if (fCount > 1)
|
||||||
|
name << "Remove Entries";
|
||||||
|
else
|
||||||
|
name << "Remove Entry";
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2007, Haiku. All rights reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Stephan Aßmus <superstippi@gmx.de>
|
||||||
|
*/
|
||||||
|
#ifndef REMOVE_PL_ITEMS_COMMAND_H
|
||||||
|
#define REMOVE_PL_ITEMS_COMMAND_H
|
||||||
|
|
||||||
|
|
||||||
|
#include "Command.h"
|
||||||
|
|
||||||
|
class Playlist;
|
||||||
|
|
||||||
|
class RemovePLItemsCommand : public Command {
|
||||||
|
public:
|
||||||
|
RemovePLItemsCommand(
|
||||||
|
Playlist* playlist,
|
||||||
|
const int32* indices,
|
||||||
|
int32 count);
|
||||||
|
virtual ~RemovePLItemsCommand();
|
||||||
|
|
||||||
|
virtual status_t InitCheck();
|
||||||
|
|
||||||
|
virtual status_t Perform();
|
||||||
|
virtual status_t Undo();
|
||||||
|
|
||||||
|
virtual void GetName(BString& name);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Playlist* fPlaylist;
|
||||||
|
entry_ref* fRefs;
|
||||||
|
int32* fIndices;
|
||||||
|
int32 fCount;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // REMOVE_PL_ITEMS_COMMAND_H
|
||||||
@@ -0,0 +1,151 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2004-2006, Haiku.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* IngoWeinhold <bonefish@cs.tu-berlin.de>
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** Scope-based automatic deletion of objects/arrays.
|
||||||
|
* ObjectDeleter - deletes an object
|
||||||
|
* ArrayDeleter - deletes an array
|
||||||
|
* MemoryDeleter - free()s malloc()ed memory
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef AUTO_LOCKER_H
|
||||||
|
#define AUTO_LOCKER_H
|
||||||
|
|
||||||
|
#include <SupportDefs.h>
|
||||||
|
|
||||||
|
// locking
|
||||||
|
|
||||||
|
// AutoLockerStandardLocking
|
||||||
|
template<typename Lockable>
|
||||||
|
class AutoLockerStandardLocking {
|
||||||
|
public:
|
||||||
|
inline bool Lock(Lockable *lockable)
|
||||||
|
{
|
||||||
|
return lockable->Lock();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void Unlock(Lockable *lockable)
|
||||||
|
{
|
||||||
|
lockable->Unlock();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// AutoLockerReadLocking
|
||||||
|
template<typename Lockable>
|
||||||
|
class AutoLockerReadLocking {
|
||||||
|
public:
|
||||||
|
inline bool Lock(Lockable *lockable)
|
||||||
|
{
|
||||||
|
return lockable->ReadLock();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void Unlock(Lockable *lockable)
|
||||||
|
{
|
||||||
|
lockable->ReadUnlock();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// AutoLockerWriteLocking
|
||||||
|
template<typename Lockable>
|
||||||
|
class AutoLockerWriteLocking {
|
||||||
|
public:
|
||||||
|
inline bool Lock(Lockable *lockable)
|
||||||
|
{
|
||||||
|
return lockable->WriteLock();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void Unlock(Lockable *lockable)
|
||||||
|
{
|
||||||
|
lockable->WriteUnlock();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// AutoLocker
|
||||||
|
template<typename Lockable,
|
||||||
|
typename Locking = AutoLockerStandardLocking<Lockable> >
|
||||||
|
class AutoLocker {
|
||||||
|
private:
|
||||||
|
typedef AutoLocker<Lockable, Locking> ThisClass;
|
||||||
|
public:
|
||||||
|
inline AutoLocker(Lockable *lockable, bool alreadyLocked = false)
|
||||||
|
: fLockable(lockable),
|
||||||
|
fLocked(fLockable && alreadyLocked)
|
||||||
|
{
|
||||||
|
if (!fLocked)
|
||||||
|
_Lock();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline AutoLocker(Lockable &lockable, bool alreadyLocked = false)
|
||||||
|
: fLockable(&lockable),
|
||||||
|
fLocked(fLockable && alreadyLocked)
|
||||||
|
{
|
||||||
|
if (!fLocked)
|
||||||
|
_Lock();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline ~AutoLocker()
|
||||||
|
{
|
||||||
|
Unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void SetTo(Lockable *lockable, bool alreadyLocked)
|
||||||
|
{
|
||||||
|
Unlock();
|
||||||
|
fLockable = lockable;
|
||||||
|
fLocked = alreadyLocked;
|
||||||
|
if (!fLocked)
|
||||||
|
_Lock();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void SetTo(Lockable &lockable, bool alreadyLocked)
|
||||||
|
{
|
||||||
|
SetTo(&lockable, alreadyLocked);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void Unset()
|
||||||
|
{
|
||||||
|
Unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline AutoLocker<Lockable, Locking> &operator=(Lockable *lockable)
|
||||||
|
{
|
||||||
|
SetTo(lockable);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline AutoLocker<Lockable, Locking> &operator=(Lockable &lockable)
|
||||||
|
{
|
||||||
|
SetTo(&lockable);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool IsLocked() const { return fLocked; }
|
||||||
|
|
||||||
|
inline void Unlock()
|
||||||
|
{
|
||||||
|
if (fLockable && fLocked) {
|
||||||
|
fLocking.Unlock(fLockable);
|
||||||
|
fLocked = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline operator bool() const { return fLocked; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
inline void _Lock()
|
||||||
|
{
|
||||||
|
if (fLockable)
|
||||||
|
fLocked = fLocking.Lock(fLockable);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Lockable *fLockable;
|
||||||
|
bool fLocked;
|
||||||
|
Locking fLocking;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // AUTO_LOCKER_H
|
||||||
@@ -0,0 +1,471 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2006, Haiku.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* IngoWeinhold <bonefish@cs.tu-berlin.de>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "RWLocker.h"
|
||||||
|
|
||||||
|
#include <String.h>
|
||||||
|
|
||||||
|
// info about a read lock owner
|
||||||
|
struct RWLocker::ReadLockInfo {
|
||||||
|
thread_id reader;
|
||||||
|
int32 count;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// constructor
|
||||||
|
RWLocker::RWLocker()
|
||||||
|
: fLock(),
|
||||||
|
fMutex(),
|
||||||
|
fQueue(),
|
||||||
|
fReaderCount(0),
|
||||||
|
fWriterCount(0),
|
||||||
|
fReadLockInfos(8),
|
||||||
|
fWriter(B_ERROR),
|
||||||
|
fWriterWriterCount(0),
|
||||||
|
fWriterReaderCount(0)
|
||||||
|
{
|
||||||
|
_Init(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
// constructor
|
||||||
|
RWLocker::RWLocker(const char* name)
|
||||||
|
: fLock(name),
|
||||||
|
fMutex(),
|
||||||
|
fQueue(),
|
||||||
|
fReaderCount(0),
|
||||||
|
fWriterCount(0),
|
||||||
|
fReadLockInfos(8),
|
||||||
|
fWriter(B_ERROR),
|
||||||
|
fWriterWriterCount(0),
|
||||||
|
fWriterReaderCount(0)
|
||||||
|
{
|
||||||
|
_Init(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// destructor
|
||||||
|
RWLocker::~RWLocker()
|
||||||
|
{
|
||||||
|
fLock.Lock();
|
||||||
|
delete_sem(fMutex.semaphore);
|
||||||
|
delete_sem(fQueue.semaphore);
|
||||||
|
for (int32 i = 0; ReadLockInfo* info = _ReadLockInfoAt(i); i++)
|
||||||
|
delete info;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadLock
|
||||||
|
bool
|
||||||
|
RWLocker::ReadLock()
|
||||||
|
{
|
||||||
|
status_t error = _ReadLock(B_INFINITE_TIMEOUT);
|
||||||
|
return (error == B_OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadLockWithTimeout
|
||||||
|
status_t
|
||||||
|
RWLocker::ReadLockWithTimeout(bigtime_t timeout)
|
||||||
|
{
|
||||||
|
bigtime_t absoluteTimeout = system_time() + timeout;
|
||||||
|
// take care of overflow
|
||||||
|
if (timeout > 0 && absoluteTimeout < 0)
|
||||||
|
absoluteTimeout = B_INFINITE_TIMEOUT;
|
||||||
|
return _ReadLock(absoluteTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadUnlock
|
||||||
|
void
|
||||||
|
RWLocker::ReadUnlock()
|
||||||
|
{
|
||||||
|
if (fLock.Lock()) {
|
||||||
|
thread_id thread = find_thread(NULL);
|
||||||
|
if (thread == fWriter) {
|
||||||
|
// We (also) have a write lock.
|
||||||
|
if (fWriterReaderCount > 0)
|
||||||
|
fWriterReaderCount--;
|
||||||
|
// else: error: unmatched ReadUnlock()
|
||||||
|
} else {
|
||||||
|
int32 index = _IndexOf(thread);
|
||||||
|
if (ReadLockInfo* info = _ReadLockInfoAt(index)) {
|
||||||
|
fReaderCount--;
|
||||||
|
if (--info->count == 0) {
|
||||||
|
// The outer read lock bracket for the thread has been
|
||||||
|
// reached. Dispose the info.
|
||||||
|
_DeleteReadLockInfo(index);
|
||||||
|
}
|
||||||
|
if (fReaderCount == 0) {
|
||||||
|
// The last reader needs to unlock the mutex.
|
||||||
|
_ReleaseBenaphore(fMutex);
|
||||||
|
}
|
||||||
|
} // else: error: caller has no read lock
|
||||||
|
}
|
||||||
|
fLock.Unlock();
|
||||||
|
} // else: we are probably going to be destroyed
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsReadLocked
|
||||||
|
//
|
||||||
|
// Returns whether or not the calling thread owns a read lock or even a
|
||||||
|
// write lock.
|
||||||
|
bool
|
||||||
|
RWLocker::IsReadLocked() const
|
||||||
|
{
|
||||||
|
bool result = false;
|
||||||
|
if (fLock.Lock()) {
|
||||||
|
thread_id thread = find_thread(NULL);
|
||||||
|
result = (thread == fWriter || _IndexOf(thread) >= 0);
|
||||||
|
fLock.Unlock();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteLock
|
||||||
|
bool
|
||||||
|
RWLocker::WriteLock()
|
||||||
|
{
|
||||||
|
status_t error = _WriteLock(B_INFINITE_TIMEOUT);
|
||||||
|
return (error == B_OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteLockWithTimeout
|
||||||
|
status_t
|
||||||
|
RWLocker::WriteLockWithTimeout(bigtime_t timeout)
|
||||||
|
{
|
||||||
|
bigtime_t absoluteTimeout = system_time() + timeout;
|
||||||
|
// take care of overflow
|
||||||
|
if (timeout > 0 && absoluteTimeout < 0)
|
||||||
|
absoluteTimeout = B_INFINITE_TIMEOUT;
|
||||||
|
return _WriteLock(absoluteTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteUnlock
|
||||||
|
void
|
||||||
|
RWLocker::WriteUnlock()
|
||||||
|
{
|
||||||
|
if (fLock.Lock()) {
|
||||||
|
thread_id thread = find_thread(NULL);
|
||||||
|
if (thread == fWriter) {
|
||||||
|
fWriterCount--;
|
||||||
|
if (--fWriterWriterCount == 0) {
|
||||||
|
// The outer write lock bracket for the thread has been
|
||||||
|
// reached.
|
||||||
|
fWriter = B_ERROR;
|
||||||
|
if (fWriterReaderCount > 0) {
|
||||||
|
// We still own read locks.
|
||||||
|
_NewReadLockInfo(thread, fWriterReaderCount);
|
||||||
|
// A reader that expects to be the first reader may wait
|
||||||
|
// at the mutex semaphore. We need to wake it up.
|
||||||
|
if (fReaderCount > 0)
|
||||||
|
_ReleaseBenaphore(fMutex);
|
||||||
|
fReaderCount += fWriterReaderCount;
|
||||||
|
fWriterReaderCount = 0;
|
||||||
|
} else {
|
||||||
|
// We don't own any read locks. So we have to release the
|
||||||
|
// mutex benaphore.
|
||||||
|
_ReleaseBenaphore(fMutex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // else: error: unmatched WriteUnlock()
|
||||||
|
fLock.Unlock();
|
||||||
|
} // else: We're probably going to die.
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsWriteLocked
|
||||||
|
//
|
||||||
|
// Returns whether or not the calling thread owns a write lock.
|
||||||
|
bool
|
||||||
|
RWLocker::IsWriteLocked() const
|
||||||
|
{
|
||||||
|
return (fWriter == find_thread(NULL));
|
||||||
|
}
|
||||||
|
|
||||||
|
// _Init
|
||||||
|
void
|
||||||
|
RWLocker::_Init(const char* name)
|
||||||
|
{
|
||||||
|
// init the mutex benaphore
|
||||||
|
BString mutexName(name);
|
||||||
|
mutexName += "_RWLocker_mutex";
|
||||||
|
fMutex.semaphore = create_sem(0, mutexName.String());
|
||||||
|
fMutex.counter = 0;
|
||||||
|
// init the queueing benaphore
|
||||||
|
BString queueName(name);
|
||||||
|
queueName += "_RWLocker_queue";
|
||||||
|
fQueue.semaphore = create_sem(0, queueName.String());
|
||||||
|
fQueue.counter = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// _ReadLock
|
||||||
|
//
|
||||||
|
// /timeout/ -- absolute timeout
|
||||||
|
status_t
|
||||||
|
RWLocker::_ReadLock(bigtime_t timeout)
|
||||||
|
{
|
||||||
|
status_t error = B_OK;
|
||||||
|
thread_id thread = find_thread(NULL);
|
||||||
|
bool locked = false;
|
||||||
|
if (fLock.Lock()) {
|
||||||
|
// Check, if we already own a read (or write) lock. In this case we
|
||||||
|
// can skip the usual locking procedure.
|
||||||
|
if (thread == fWriter) {
|
||||||
|
// We already own a write lock.
|
||||||
|
fWriterReaderCount++;
|
||||||
|
locked = true;
|
||||||
|
} else if (ReadLockInfo* info = _ReadLockInfoAt(_IndexOf(thread))) {
|
||||||
|
// We already own a read lock.
|
||||||
|
info->count++;
|
||||||
|
fReaderCount++;
|
||||||
|
locked = true;
|
||||||
|
}
|
||||||
|
fLock.Unlock();
|
||||||
|
} else // failed to lock the data
|
||||||
|
error = B_ERROR;
|
||||||
|
// Usual locking, i.e. we do not already own a read or write lock.
|
||||||
|
if (error == B_OK && !locked) {
|
||||||
|
error = _AcquireBenaphore(fQueue, timeout);
|
||||||
|
if (error == B_OK) {
|
||||||
|
if (fLock.Lock()) {
|
||||||
|
bool firstReader = false;
|
||||||
|
if (++fReaderCount == 1) {
|
||||||
|
// We are the first reader.
|
||||||
|
_NewReadLockInfo(thread);
|
||||||
|
firstReader = true;
|
||||||
|
} else
|
||||||
|
_NewReadLockInfo(thread);
|
||||||
|
fLock.Unlock();
|
||||||
|
// The first reader needs to lock the mutex.
|
||||||
|
if (firstReader) {
|
||||||
|
error = _AcquireBenaphore(fMutex, timeout);
|
||||||
|
switch (error) {
|
||||||
|
case B_OK:
|
||||||
|
// fine
|
||||||
|
break;
|
||||||
|
case B_TIMED_OUT: {
|
||||||
|
// clean up
|
||||||
|
if (fLock.Lock()) {
|
||||||
|
_DeleteReadLockInfo(_IndexOf(thread));
|
||||||
|
fReaderCount--;
|
||||||
|
fLock.Unlock();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
// Probably we are going to be destroyed.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Let the next candidate enter the game.
|
||||||
|
_ReleaseBenaphore(fQueue);
|
||||||
|
} else {
|
||||||
|
// We couldn't lock the data, which can only happen, if
|
||||||
|
// we're going to be destroyed.
|
||||||
|
error = B_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// _WriteLock
|
||||||
|
//
|
||||||
|
// /timeout/ -- absolute timeout
|
||||||
|
status_t
|
||||||
|
RWLocker::_WriteLock(bigtime_t timeout)
|
||||||
|
{
|
||||||
|
status_t error = B_ERROR;
|
||||||
|
if (fLock.Lock()) {
|
||||||
|
bool infiniteTimeout = (timeout == B_INFINITE_TIMEOUT);
|
||||||
|
bool locked = false;
|
||||||
|
int32 readerCount = 0;
|
||||||
|
thread_id thread = find_thread(NULL);
|
||||||
|
int32 index = _IndexOf(thread);
|
||||||
|
if (ReadLockInfo* info = _ReadLockInfoAt(index)) {
|
||||||
|
// We already own a read lock.
|
||||||
|
if (fWriterCount > 0) {
|
||||||
|
// There are writers before us.
|
||||||
|
if (infiniteTimeout) {
|
||||||
|
// Timeout is infinite and there are writers before us.
|
||||||
|
// Unregister the read locks and lock as usual.
|
||||||
|
readerCount = info->count;
|
||||||
|
fWriterCount++;
|
||||||
|
fReaderCount -= readerCount;
|
||||||
|
_DeleteReadLockInfo(index);
|
||||||
|
error = B_OK;
|
||||||
|
} else {
|
||||||
|
// The timeout is finite and there are readers before us:
|
||||||
|
// let the write lock request fail.
|
||||||
|
error = B_WOULD_BLOCK;
|
||||||
|
}
|
||||||
|
} else if (info->count == fReaderCount) {
|
||||||
|
// No writers before us.
|
||||||
|
// We are the only read lock owners. Just move the read lock
|
||||||
|
// info data to the special writer fields and then we are done.
|
||||||
|
// Note: At this point we may overtake readers that already
|
||||||
|
// have acquired the queueing benaphore, but have not yet
|
||||||
|
// locked the data. But that doesn't harm.
|
||||||
|
fWriter = thread;
|
||||||
|
fWriterCount++;
|
||||||
|
fWriterWriterCount = 1;
|
||||||
|
fWriterReaderCount = info->count;
|
||||||
|
fReaderCount -= fWriterReaderCount;
|
||||||
|
_DeleteReadLockInfo(index);
|
||||||
|
locked = true;
|
||||||
|
error = B_OK;
|
||||||
|
} else {
|
||||||
|
// No writers before us, but other readers.
|
||||||
|
// Note, we're quite restrictive here. If there are only
|
||||||
|
// readers before us, we could reinstall our readers, if
|
||||||
|
// our request times out. Unfortunately it is not easy
|
||||||
|
// to ensure, that no writer overtakes us between unlocking
|
||||||
|
// the data and acquiring the queuing benaphore.
|
||||||
|
if (infiniteTimeout) {
|
||||||
|
// Unregister the readers and lock as usual.
|
||||||
|
readerCount = info->count;
|
||||||
|
fWriterCount++;
|
||||||
|
fReaderCount -= readerCount;
|
||||||
|
_DeleteReadLockInfo(index);
|
||||||
|
error = B_OK;
|
||||||
|
} else
|
||||||
|
error = B_WOULD_BLOCK;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// We don't own a read lock.
|
||||||
|
if (fWriter == thread) {
|
||||||
|
// ... but a write lock.
|
||||||
|
fWriterCount++;
|
||||||
|
fWriterWriterCount++;
|
||||||
|
locked = true;
|
||||||
|
error = B_OK;
|
||||||
|
} else {
|
||||||
|
// We own neither read nor write locks.
|
||||||
|
// Lock as usual.
|
||||||
|
fWriterCount++;
|
||||||
|
error = B_OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fLock.Unlock();
|
||||||
|
// Usual locking...
|
||||||
|
// First step: acquire the queueing benaphore.
|
||||||
|
if (!locked && error == B_OK) {
|
||||||
|
error = _AcquireBenaphore(fQueue, timeout);
|
||||||
|
switch (error) {
|
||||||
|
case B_OK:
|
||||||
|
break;
|
||||||
|
case B_TIMED_OUT: {
|
||||||
|
// clean up
|
||||||
|
if (fLock.Lock()) {
|
||||||
|
fWriterCount--;
|
||||||
|
fLock.Unlock();
|
||||||
|
} // else: failed to lock the data: we're probably going
|
||||||
|
// to die.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
// Probably we're going to die.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Second step: acquire the mutex benaphore.
|
||||||
|
if (!locked && error == B_OK) {
|
||||||
|
error = _AcquireBenaphore(fMutex, timeout);
|
||||||
|
switch (error) {
|
||||||
|
case B_OK: {
|
||||||
|
// Yeah, we made it. Set the special writer fields.
|
||||||
|
fWriter = thread;
|
||||||
|
fWriterWriterCount = 1;
|
||||||
|
fWriterReaderCount = readerCount;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case B_TIMED_OUT: {
|
||||||
|
// clean up
|
||||||
|
if (fLock.Lock()) {
|
||||||
|
fWriterCount--;
|
||||||
|
fLock.Unlock();
|
||||||
|
} // else: failed to lock the data: we're probably going
|
||||||
|
// to die.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
// Probably we're going to die.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Whatever happened, we have to release the queueing benaphore.
|
||||||
|
_ReleaseBenaphore(fQueue);
|
||||||
|
}
|
||||||
|
} else // failed to lock the data
|
||||||
|
error = B_ERROR;
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// _AddReadLockInfo
|
||||||
|
int32
|
||||||
|
RWLocker::_AddReadLockInfo(ReadLockInfo* info)
|
||||||
|
{
|
||||||
|
int32 index = fReadLockInfos.CountItems();
|
||||||
|
fReadLockInfos.AddItem(info, index);
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
// _NewReadLockInfo
|
||||||
|
//
|
||||||
|
// Create a new read lock info for the supplied thread and add it to the
|
||||||
|
// list. Returns the index of the info.
|
||||||
|
int32
|
||||||
|
RWLocker::_NewReadLockInfo(thread_id thread, int32 count)
|
||||||
|
{
|
||||||
|
ReadLockInfo* info = new ReadLockInfo;
|
||||||
|
info->reader = thread;
|
||||||
|
info->count = count;
|
||||||
|
return _AddReadLockInfo(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
// _DeleteReadLockInfo
|
||||||
|
void
|
||||||
|
RWLocker::_DeleteReadLockInfo(int32 index)
|
||||||
|
{
|
||||||
|
if (ReadLockInfo* info = (ReadLockInfo*)fReadLockInfos.RemoveItem(index))
|
||||||
|
delete info;
|
||||||
|
}
|
||||||
|
|
||||||
|
// _ReadLockInfoAt
|
||||||
|
RWLocker::ReadLockInfo*
|
||||||
|
RWLocker::_ReadLockInfoAt(int32 index) const
|
||||||
|
{
|
||||||
|
return (ReadLockInfo*)fReadLockInfos.ItemAt(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
// _IndexOf
|
||||||
|
int32
|
||||||
|
RWLocker::_IndexOf(thread_id thread) const
|
||||||
|
{
|
||||||
|
int32 count = fReadLockInfos.CountItems();
|
||||||
|
for (int32 i = 0; i < count; i++) {
|
||||||
|
if (_ReadLockInfoAt(i)->reader == thread)
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// _AcquireBenaphore
|
||||||
|
status_t
|
||||||
|
RWLocker::_AcquireBenaphore(Benaphore& benaphore, bigtime_t timeout)
|
||||||
|
{
|
||||||
|
status_t error = B_OK;
|
||||||
|
if (atomic_add(&benaphore.counter, 1) > 0) {
|
||||||
|
error = acquire_sem_etc(benaphore.semaphore, 1, B_ABSOLUTE_TIMEOUT,
|
||||||
|
timeout);
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// _ReleaseBenaphore
|
||||||
|
void
|
||||||
|
RWLocker::_ReleaseBenaphore(Benaphore& benaphore)
|
||||||
|
{
|
||||||
|
if (atomic_add(&benaphore.counter, -1) > 1)
|
||||||
|
release_sem(benaphore.semaphore);
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,137 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2006, Haiku.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* IngoWeinhold <bonefish@cs.tu-berlin.de>
|
||||||
|
*/
|
||||||
|
|
||||||
|
// This class provides a reader/writer locking mechanism:
|
||||||
|
// * A writer needs an exclusive lock.
|
||||||
|
// * For a reader a non-exclusive lock to be shared with other readers is
|
||||||
|
// sufficient.
|
||||||
|
// * The ownership of a lock is bound to the thread that requested the lock;
|
||||||
|
// the same thread has to call Unlock() later.
|
||||||
|
// * Nested locking is supported: a number of XXXLock() calls needs to be
|
||||||
|
// bracketed by the same number of XXXUnlock() calls.
|
||||||
|
// * The lock acquiration strategy is fair: a lock applicant needs to wait
|
||||||
|
// only for those threads that already own a lock or requested one before
|
||||||
|
// the current thread. No one can overtake. E.g. if a thread owns a read
|
||||||
|
// lock, another one is waiting for a write lock, then a third one
|
||||||
|
// requesting a read lock has to wait until the write locker is done.
|
||||||
|
// This does not hold for threads that already own a lock (nested locking).
|
||||||
|
// A read lock owner is immediately granted another read lock and a write
|
||||||
|
// lock owner another write or a read lock.
|
||||||
|
// * A write lock owner is allowed to request a read lock and a read lock
|
||||||
|
// owner a write lock. While the first case is not problematic, the
|
||||||
|
// second one needs some further explanation: A read lock owner requesting
|
||||||
|
// a write lock temporarily looses its read lock(s) until the write lock
|
||||||
|
// is granted. Otherwise two read lock owning threads trying to get
|
||||||
|
// write locks at the same time would dead lock each other. The only
|
||||||
|
// problem with this solution is, that the write lock acquiration must
|
||||||
|
// not fail, because in that case the thread could not be given back
|
||||||
|
// its read lock(s), since another thread may have been given a write lock
|
||||||
|
// in the mean time. Fortunately locking can fail only either, if the
|
||||||
|
// locker has been deleted, or, if a timeout occured. Therefore
|
||||||
|
// WriteLockWithTimeout() immediatlely returns with a B_WOULD_BLOCK error
|
||||||
|
// code, if the caller already owns a read lock (but no write lock) and
|
||||||
|
// another thread already owns or has requested a read or write lock.
|
||||||
|
// * Calls to read and write locking methods may interleave arbitrarily,
|
||||||
|
// e.g.: ReadLock(); WriteLock(); ReadUnlock(); WriteUnlock();
|
||||||
|
//
|
||||||
|
// Important note: Read/WriteLock() can fail only, if the locker has been
|
||||||
|
// deleted. However, it is NOT save to invoke any method on a deleted
|
||||||
|
// locker object.
|
||||||
|
//
|
||||||
|
// Implementation details:
|
||||||
|
// A locker needs three semaphores (a BLocker and two semaphores): one
|
||||||
|
// to protect the lockers data, one as a reader/writer mutex (to be
|
||||||
|
// acquired by each writer and the first reader) and one for queueing
|
||||||
|
// waiting readers and writers. The simplified locking/unlocking
|
||||||
|
// algorithm is the following:
|
||||||
|
//
|
||||||
|
// writer reader
|
||||||
|
// queue.acquire() queue.acquire()
|
||||||
|
// mutex.acquire() if (first reader) mutex.acquire()
|
||||||
|
// queue.release() queue.release()
|
||||||
|
// ... ...
|
||||||
|
// mutex.release() if (last reader) mutex.release()
|
||||||
|
//
|
||||||
|
// One thread at maximum waits at the mutex, the others at the queueing
|
||||||
|
// semaphore. Unfortunately features as nested locking and timeouts make
|
||||||
|
// things more difficult. Therefore readers as well as writers need to check
|
||||||
|
// whether they already own a lock before acquiring the queueing semaphore.
|
||||||
|
// The data for the readers are stored in a list of ReadLockInfo structures;
|
||||||
|
// the writer data are stored in some special fields. /fReaderCount/ and
|
||||||
|
// /fWriterCount/ contain the total count of unbalanced Read/WriteLock()
|
||||||
|
// calls, /fWriterReaderCount/ and /fWriterWriterCount/ only from those of
|
||||||
|
// the current write lock owner (/fWriter/). To be a bit more precise:
|
||||||
|
// /fWriterReaderCount/ is not contained in /fReaderCount/, but
|
||||||
|
// /fWriterWriterCount/ is contained in /fWriterCount/. Therefore
|
||||||
|
// /fReaderCount/ can be considered to be the count of true reader's read
|
||||||
|
// locks.
|
||||||
|
|
||||||
|
#ifndef RW_LOCKER_H
|
||||||
|
#define RW_LOCKER_H
|
||||||
|
|
||||||
|
#include <List.h>
|
||||||
|
#include <Locker.h>
|
||||||
|
|
||||||
|
#include "AutoLocker.h"
|
||||||
|
|
||||||
|
class RWLocker {
|
||||||
|
public:
|
||||||
|
RWLocker();
|
||||||
|
RWLocker(const char* name);
|
||||||
|
virtual ~RWLocker();
|
||||||
|
|
||||||
|
bool ReadLock();
|
||||||
|
status_t ReadLockWithTimeout(bigtime_t timeout);
|
||||||
|
void ReadUnlock();
|
||||||
|
bool IsReadLocked() const;
|
||||||
|
|
||||||
|
bool WriteLock();
|
||||||
|
status_t WriteLockWithTimeout(bigtime_t timeout);
|
||||||
|
void WriteUnlock();
|
||||||
|
bool IsWriteLocked() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct ReadLockInfo;
|
||||||
|
struct Benaphore {
|
||||||
|
sem_id semaphore;
|
||||||
|
int32 counter;
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
void _Init(const char* name);
|
||||||
|
status_t _ReadLock(bigtime_t timeout);
|
||||||
|
status_t _WriteLock(bigtime_t timeout);
|
||||||
|
|
||||||
|
int32 _AddReadLockInfo(ReadLockInfo* info);
|
||||||
|
int32 _NewReadLockInfo(thread_id thread,
|
||||||
|
int32 count = 1);
|
||||||
|
void _DeleteReadLockInfo(int32 index);
|
||||||
|
ReadLockInfo* _ReadLockInfoAt(int32 index) const;
|
||||||
|
int32 _IndexOf(thread_id thread) const;
|
||||||
|
|
||||||
|
static status_t _AcquireBenaphore(Benaphore& benaphore,
|
||||||
|
bigtime_t timeout);
|
||||||
|
static void _ReleaseBenaphore(Benaphore& benaphore);
|
||||||
|
|
||||||
|
private:
|
||||||
|
mutable BLocker fLock; // data lock
|
||||||
|
Benaphore fMutex; // critical code mutex
|
||||||
|
Benaphore fQueue; // queueing semaphore
|
||||||
|
int32 fReaderCount; // total count...
|
||||||
|
int32 fWriterCount; // total count...
|
||||||
|
BList fReadLockInfos;
|
||||||
|
thread_id fWriter; // current write lock owner
|
||||||
|
int32 fWriterWriterCount; // write lock owner count
|
||||||
|
int32 fWriterReaderCount; // writer read lock owner
|
||||||
|
// count
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef AutoLocker<RWLocker, AutoLockerReadLocking<RWLocker> > AutoReadLocker;
|
||||||
|
typedef AutoLocker<RWLocker, AutoLockerWriteLocking<RWLocker> > AutoWriteLocker;
|
||||||
|
|
||||||
|
#endif // RW_LOCKER_H
|
||||||
Reference in New Issue
Block a user