mirror of
https://github.com/aseprite/aseprite.git
synced 2025-04-01 01:20:25 +00:00
Add timeout parameter to Context/Document lock/unlock operations
This commit is contained in:
parent
e725bf93a0
commit
1bed018ab0
@ -169,7 +169,7 @@ private:
|
|||||||
releaseEditor();
|
releaseEditor();
|
||||||
|
|
||||||
if (m_fileOpened) {
|
if (m_fileOpened) {
|
||||||
DocumentDestroyer destroyer(m_context, oldDocument);
|
DocumentDestroyer destroyer(m_context, oldDocument, 100);
|
||||||
destroyer.destroyDocument();
|
destroyer.destroyDocument();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -45,17 +45,17 @@ namespace app {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
ContextAccess(const Context* context)
|
ContextAccess(const Context* context, int timeout)
|
||||||
: m_context(context)
|
: m_context(context)
|
||||||
, m_document(context->activeDocument())
|
, m_document(context->activeDocument(), timeout)
|
||||||
, m_location(context->activeLocation())
|
, m_location(context->activeLocation())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename DocumentReaderT>
|
template<typename DocumentReaderT>
|
||||||
ContextAccess(const Context* context, const DocumentReaderT& documentReader)
|
ContextAccess(const Context* context, const DocumentReaderT& documentReader, int timeout)
|
||||||
: m_context(context)
|
: m_context(context)
|
||||||
, m_document(documentReader)
|
, m_document(documentReader, timeout)
|
||||||
, m_location(context->activeLocation())
|
, m_location(context->activeLocation())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@ -70,8 +70,8 @@ namespace app {
|
|||||||
// active document.
|
// active document.
|
||||||
class ContextReader : public ContextAccess<DocumentReader> {
|
class ContextReader : public ContextAccess<DocumentReader> {
|
||||||
public:
|
public:
|
||||||
ContextReader(const Context* context)
|
ContextReader(const Context* context, int timeout = 0)
|
||||||
: ContextAccess<DocumentReader>(context) {
|
: ContextAccess<DocumentReader>(context, timeout) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -79,12 +79,12 @@ namespace app {
|
|||||||
// active document.
|
// active document.
|
||||||
class ContextWriter : public ContextAccess<DocumentWriter> {
|
class ContextWriter : public ContextAccess<DocumentWriter> {
|
||||||
public:
|
public:
|
||||||
ContextWriter(const Context* context)
|
ContextWriter(const Context* context, int timeout = 0)
|
||||||
: ContextAccess<DocumentWriter>(context) {
|
: ContextAccess<DocumentWriter>(context, timeout) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ContextWriter(const ContextReader& reader)
|
ContextWriter(const ContextReader& reader, int timeout = 0)
|
||||||
: ContextAccess<DocumentWriter>(reader.context(), reader.document()) {
|
: ContextAccess<DocumentWriter>(reader.context(), reader.document(), timeout) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ void ContextFlags::update(Context* context)
|
|||||||
if (document) {
|
if (document) {
|
||||||
m_flags |= HasActiveDocument;
|
m_flags |= HasActiveDocument;
|
||||||
|
|
||||||
if (document->lock(Document::ReadLock)) {
|
if (document->lock(Document::ReadLock, 0)) {
|
||||||
m_flags |= ActiveDocumentIsReadable;
|
m_flags |= ActiveDocumentIsReadable;
|
||||||
|
|
||||||
if (document->isMaskVisible())
|
if (document->isMaskVisible())
|
||||||
@ -75,7 +75,7 @@ void ContextFlags::update(Context* context)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (document->lockToWrite())
|
if (document->lockToWrite(0))
|
||||||
m_flags |= ActiveDocumentIsWritable;
|
m_flags |= ActiveDocumentIsWritable;
|
||||||
|
|
||||||
document->unlock();
|
document->unlock();
|
||||||
|
@ -134,8 +134,8 @@ void Session::removeFromDisk()
|
|||||||
|
|
||||||
void Session::saveDocumentChanges(app::Document* doc)
|
void Session::saveDocumentChanges(app::Document* doc)
|
||||||
{
|
{
|
||||||
DocumentReader reader(doc);
|
DocumentReader reader(doc, 250);
|
||||||
DocumentWriter writer(reader);
|
DocumentWriter writer(reader, 250);
|
||||||
app::Context ctx;
|
app::Context ctx;
|
||||||
std::string dir = base::join_path(m_path,
|
std::string dir = base::join_path(m_path,
|
||||||
base::convert_to<std::string>(doc->id()));
|
base::convert_to<std::string>(doc->id()));
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
#include "base/memory.h"
|
#include "base/memory.h"
|
||||||
#include "base/mutex.h"
|
#include "base/mutex.h"
|
||||||
#include "base/scoped_lock.h"
|
#include "base/scoped_lock.h"
|
||||||
|
#include "base/thread.h"
|
||||||
#include "base/unique_ptr.h"
|
#include "base/unique_ptr.h"
|
||||||
#include "doc/cel.h"
|
#include "doc/cel.h"
|
||||||
#include "doc/context.h"
|
#include "doc/context.h"
|
||||||
@ -510,31 +511,42 @@ Document* Document::duplicate(DuplicateType type) const
|
|||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
// Multi-threading ("sprite wrappers" use this)
|
// Multi-threading ("sprite wrappers" use this)
|
||||||
|
|
||||||
bool Document::lock(LockType lockType)
|
bool Document::lock(LockType lockType, int timeout)
|
||||||
{
|
{
|
||||||
scoped_lock lock(*m_mutex);
|
while (timeout >= 0) {
|
||||||
|
{
|
||||||
|
scoped_lock lock(*m_mutex);
|
||||||
|
switch (lockType) {
|
||||||
|
|
||||||
switch (lockType) {
|
case ReadLock:
|
||||||
|
// If no body is writting the sprite...
|
||||||
|
if (!m_write_lock) {
|
||||||
|
// We can read it
|
||||||
|
++m_read_locks;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case WriteLock:
|
||||||
|
// If no body is reading and writting...
|
||||||
|
if (m_read_locks == 0 && !m_write_lock) {
|
||||||
|
// We can start writting the sprite...
|
||||||
|
m_write_lock = true;
|
||||||
|
TRACE("Document::lock: Locked <%d> to write\n", id());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case ReadLock:
|
|
||||||
// If no body is writting the sprite...
|
|
||||||
if (!m_write_lock) {
|
|
||||||
// We can read it
|
|
||||||
++m_read_locks;
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
|
|
||||||
case WriteLock:
|
if (timeout > 0) {
|
||||||
// If no body is reading and writting...
|
int delay = MIN(100, timeout);
|
||||||
if (m_read_locks == 0 && !m_write_lock) {
|
timeout -= delay;
|
||||||
// We can start writting the sprite...
|
base::this_thread::sleep_for(double(delay) / 1000.0);
|
||||||
m_write_lock = true;
|
}
|
||||||
TRACE("Document::lock: Locked <%d> to write\n", id());
|
else
|
||||||
return true;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TRACE("Document::lock: Cannot lock <%d> to %s (has %d read locks and %d write locks)\n",
|
TRACE("Document::lock: Cannot lock <%d> to %s (has %d read locks and %d write locks)\n",
|
||||||
@ -542,23 +554,27 @@ bool Document::lock(LockType lockType)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Document::lockToWrite()
|
bool Document::lockToWrite(int timeout)
|
||||||
{
|
{
|
||||||
scoped_lock lock(*m_mutex);
|
while (timeout >= 0) {
|
||||||
|
{
|
||||||
|
scoped_lock lock(*m_mutex);
|
||||||
|
// this only is possible if there are just one reader
|
||||||
|
if (m_read_locks == 1) {
|
||||||
|
ASSERT(!m_write_lock);
|
||||||
|
m_read_locks = 0;
|
||||||
|
m_write_lock = true;
|
||||||
|
TRACE("Document::lockToWrite: Locked <%d> to write\n", id());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
timeout -= 100;
|
||||||
|
base::this_thread::sleep_for(0.100);
|
||||||
|
}
|
||||||
|
|
||||||
// this only is possible if there are just one reader
|
TRACE("Document::lockToWrite: Cannot lock <%d> to write (has %d read locks and %d write locks)\n",
|
||||||
if (m_read_locks == 1) {
|
id(), m_read_locks, m_write_lock);
|
||||||
ASSERT(!m_write_lock);
|
return false;
|
||||||
m_read_locks = 0;
|
|
||||||
m_write_lock = true;
|
|
||||||
TRACE("Document::lockToWrite: Locked <%d> to write\n", id());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
TRACE("Document::lockToWrite: Cannot lock <%d> to write (has %d read locks and %d write locks)\n",
|
|
||||||
id(), m_read_locks, m_write_lock);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Document::unlockToRead()
|
void Document::unlockToRead()
|
||||||
|
@ -170,11 +170,11 @@ namespace app {
|
|||||||
|
|
||||||
// Locks the sprite to read or write on it, returning true if the
|
// Locks the sprite to read or write on it, returning true if the
|
||||||
// sprite can be accessed in the desired mode.
|
// sprite can be accessed in the desired mode.
|
||||||
bool lock(LockType lockType);
|
bool lock(LockType lockType, int timeout);
|
||||||
|
|
||||||
// If you've locked the sprite to read, using this method you can
|
// If you've locked the sprite to read, using this method you can
|
||||||
// raise your access level to write it.
|
// raise your access level to write it.
|
||||||
bool lockToWrite();
|
bool lockToWrite(int timeout);
|
||||||
|
|
||||||
// If you've locked the sprite to write, using this method you can
|
// If you've locked the sprite to write, using this method you can
|
||||||
// your access level to only read it.
|
// your access level to only read it.
|
||||||
|
@ -69,35 +69,20 @@ namespace app {
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
explicit DocumentReader(Document* document)
|
explicit DocumentReader(Document* document, int timeout)
|
||||||
: DocumentAccess(document)
|
: DocumentAccess(document)
|
||||||
{
|
{
|
||||||
if (m_document && !m_document->lock(Document::ReadLock))
|
if (m_document && !m_document->lock(Document::ReadLock, timeout))
|
||||||
throw LockedDocumentException();
|
throw LockedDocumentException();
|
||||||
}
|
}
|
||||||
|
|
||||||
explicit DocumentReader(const DocumentReader& copy)
|
explicit DocumentReader(const DocumentReader& copy, int timeout)
|
||||||
: DocumentAccess(copy)
|
: DocumentAccess(copy)
|
||||||
{
|
{
|
||||||
if (m_document && !m_document->lock(Document::ReadLock))
|
if (m_document && !m_document->lock(Document::ReadLock, timeout))
|
||||||
throw LockedDocumentException();
|
throw LockedDocumentException();
|
||||||
}
|
}
|
||||||
|
|
||||||
DocumentReader& operator=(const DocumentReader& copy)
|
|
||||||
{
|
|
||||||
// unlock old document
|
|
||||||
if (m_document)
|
|
||||||
m_document->unlock();
|
|
||||||
|
|
||||||
DocumentAccess::operator=(copy);
|
|
||||||
|
|
||||||
// relock the document
|
|
||||||
if (m_document && !m_document->lock(Document::ReadLock))
|
|
||||||
throw LockedDocumentException();
|
|
||||||
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
~DocumentReader()
|
~DocumentReader()
|
||||||
{
|
{
|
||||||
// unlock the document
|
// unlock the document
|
||||||
@ -105,6 +90,9 @@ namespace app {
|
|||||||
m_document->unlock();
|
m_document->unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Disable operator=
|
||||||
|
DocumentReader& operator=(const DocumentReader&);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Class to modify the document's state. Its constructor request a
|
// Class to modify the document's state. Its constructor request a
|
||||||
@ -120,13 +108,13 @@ namespace app {
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
explicit DocumentWriter(Document* document)
|
explicit DocumentWriter(Document* document, int timeout)
|
||||||
: DocumentAccess(document)
|
: DocumentAccess(document)
|
||||||
, m_from_reader(false)
|
, m_from_reader(false)
|
||||||
, m_locked(false)
|
, m_locked(false)
|
||||||
{
|
{
|
||||||
if (m_document) {
|
if (m_document) {
|
||||||
if (!m_document->lock(Document::WriteLock))
|
if (!m_document->lock(Document::WriteLock, timeout))
|
||||||
throw LockedDocumentException();
|
throw LockedDocumentException();
|
||||||
|
|
||||||
m_locked = true;
|
m_locked = true;
|
||||||
@ -135,13 +123,13 @@ namespace app {
|
|||||||
|
|
||||||
// Constructor that can be used to elevate the given reader-lock to
|
// Constructor that can be used to elevate the given reader-lock to
|
||||||
// writer permission.
|
// writer permission.
|
||||||
explicit DocumentWriter(const DocumentReader& document)
|
explicit DocumentWriter(const DocumentReader& document, int timeout)
|
||||||
: DocumentAccess(document)
|
: DocumentAccess(document)
|
||||||
, m_from_reader(true)
|
, m_from_reader(true)
|
||||||
, m_locked(false)
|
, m_locked(false)
|
||||||
{
|
{
|
||||||
if (m_document) {
|
if (m_document) {
|
||||||
if (!m_document->lockToWrite())
|
if (!m_document->lockToWrite(timeout))
|
||||||
throw LockedDocumentException();
|
throw LockedDocumentException();
|
||||||
|
|
||||||
m_locked = true;
|
m_locked = true;
|
||||||
@ -153,24 +141,6 @@ namespace app {
|
|||||||
unlockWriter();
|
unlockWriter();
|
||||||
}
|
}
|
||||||
|
|
||||||
DocumentWriter& operator=(const DocumentReader& copy)
|
|
||||||
{
|
|
||||||
unlockWriter();
|
|
||||||
|
|
||||||
DocumentAccess::operator=(copy);
|
|
||||||
|
|
||||||
if (m_document) {
|
|
||||||
m_from_reader = true;
|
|
||||||
|
|
||||||
if (!m_document->lockToWrite())
|
|
||||||
throw LockedDocumentException();
|
|
||||||
|
|
||||||
m_locked = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
void unlockWriter()
|
void unlockWriter()
|
||||||
@ -191,13 +161,14 @@ namespace app {
|
|||||||
// Non-copyable
|
// Non-copyable
|
||||||
DocumentWriter(const DocumentWriter&);
|
DocumentWriter(const DocumentWriter&);
|
||||||
DocumentWriter& operator=(const DocumentWriter&);
|
DocumentWriter& operator=(const DocumentWriter&);
|
||||||
|
DocumentWriter& operator=(const DocumentReader&);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Used to destroy the active document in the context.
|
// Used to destroy the active document in the context.
|
||||||
class DocumentDestroyer : public DocumentWriter {
|
class DocumentDestroyer : public DocumentWriter {
|
||||||
public:
|
public:
|
||||||
explicit DocumentDestroyer(Context* context, Document* document)
|
explicit DocumentDestroyer(Context* context, Document* document, int timeout)
|
||||||
: DocumentWriter(document)
|
: DocumentWriter(document, timeout)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -290,7 +290,7 @@ bool DocumentView::onCloseView(Workspace* workspace)
|
|||||||
|
|
||||||
// Destroy the sprite (locking it as writer)
|
// Destroy the sprite (locking it as writer)
|
||||||
DocumentDestroyer destroyer(
|
DocumentDestroyer destroyer(
|
||||||
static_cast<app::Context*>(m_document->context()), m_document);
|
static_cast<app::Context*>(m_document->context()), m_document, 250);
|
||||||
|
|
||||||
StatusBar::instance()
|
StatusBar::instance()
|
||||||
->setStatusText(0, "Sprite '%s' closed.",
|
->setStatusText(0, "Sprite '%s' closed.",
|
||||||
|
@ -1318,7 +1318,7 @@ void Editor::onPaint(ui::PaintEvent& ev)
|
|||||||
else {
|
else {
|
||||||
try {
|
try {
|
||||||
// Lock the sprite to read/render it.
|
// Lock the sprite to read/render it.
|
||||||
DocumentReader documentReader(m_document);
|
DocumentReader documentReader(m_document, 250);
|
||||||
|
|
||||||
// Draw the sprite in the editor
|
// Draw the sprite in the editor
|
||||||
drawSpriteUnclippedRect(g, gfx::Rect(0, 0, m_sprite->width(), m_sprite->height()));
|
drawSpriteUnclippedRect(g, gfx::Rect(0, 0, m_sprite->width(), m_sprite->height()));
|
||||||
|
@ -191,20 +191,13 @@ public:
|
|||||||
if (!m_canceled) {
|
if (!m_canceled) {
|
||||||
// Paint ink
|
// Paint ink
|
||||||
if (getInk()->isPaint()) {
|
if (getInk()->isPaint()) {
|
||||||
for (int i=0; ; ++i) {
|
try {
|
||||||
// TODO add a "wait_n_seconds" parameter to ContextReader/Writer
|
ContextReader reader(m_context, 500);
|
||||||
try {
|
ContextWriter writer(reader, 500);
|
||||||
ContextReader reader(m_context);
|
m_expandCelCanvas.commit();
|
||||||
ContextWriter writer(reader);
|
}
|
||||||
m_expandCelCanvas.commit();
|
catch (const LockedDocumentException& ex) {
|
||||||
break;
|
Console::showException(ex);
|
||||||
}
|
|
||||||
catch (const LockedDocumentException& ex) {
|
|
||||||
if (i == 10)
|
|
||||||
Console::showException(ex);
|
|
||||||
else
|
|
||||||
base::this_thread::sleep_for(0.10);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Selection ink
|
// Selection ink
|
||||||
@ -220,19 +213,13 @@ public:
|
|||||||
|
|
||||||
// If the trace was canceled or it is not a 'paint' ink...
|
// If the trace was canceled or it is not a 'paint' ink...
|
||||||
if (m_canceled || !getInk()->isPaint()) {
|
if (m_canceled || !getInk()->isPaint()) {
|
||||||
for (int i=0; ; ++i) {
|
try {
|
||||||
try {
|
ContextReader reader(m_context, 500);
|
||||||
ContextReader reader(m_context);
|
ContextWriter writer(reader, 500);
|
||||||
ContextWriter writer(reader);
|
m_expandCelCanvas.rollback();
|
||||||
m_expandCelCanvas.rollback();
|
}
|
||||||
break;
|
catch (const LockedDocumentException& ex) {
|
||||||
}
|
Console::showException(ex);
|
||||||
catch (const LockedDocumentException& ex) {
|
|
||||||
if (i == 10)
|
|
||||||
Console::showException(ex);
|
|
||||||
else
|
|
||||||
base::this_thread::sleep_for(0.10);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -450,7 +450,7 @@ void StatusBar::updateFromDocument(Editor* editor)
|
|||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
if (editor && editor->document()) {
|
if (editor && editor->document()) {
|
||||||
const DocumentReader reader(editor->document());
|
const DocumentReader reader(editor->document(), 100);
|
||||||
m_commandsBox->setVisible(true);
|
m_commandsBox->setVisible(true);
|
||||||
|
|
||||||
// Cel opacity
|
// Cel opacity
|
||||||
|
@ -809,7 +809,7 @@ void Timeline::onPaint(ui::PaintEvent& ev)
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// Lock the sprite to read/render it.
|
// Lock the sprite to read/render it.
|
||||||
const DocumentReader documentReader(m_document);
|
const DocumentReader documentReader(m_document, 250);
|
||||||
|
|
||||||
LayerIndex layer, first_layer, last_layer;
|
LayerIndex layer, first_layer, last_layer;
|
||||||
frame_t frame, first_frame, last_frame;
|
frame_t frame, first_frame, last_frame;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user