mirror of
https://github.com/aseprite/aseprite.git
synced 2025-01-02 11:59:58 +00:00
Move RWLock class to laf module
This commit is contained in:
parent
3ed516bc11
commit
9449bb3a43
2
laf
2
laf
@ -1 +1 @@
|
||||
Subproject commit b135dbfb5adb32f9b14f85eef1e5fa744d1c553e
|
||||
Subproject commit dd944a8c91fec41d83c60496eda268a586d650cd
|
@ -533,7 +533,6 @@ add_library(app-lib
|
||||
res/resources_loader.cpp
|
||||
resource_finder.cpp
|
||||
restore_visible_layers.cpp
|
||||
rw_lock.cpp
|
||||
shade.cpp
|
||||
snap_to_grid.cpp
|
||||
sprite_job.cpp
|
||||
|
@ -10,10 +10,10 @@
|
||||
|
||||
#include "app/extra_cel.h"
|
||||
#include "app/file/format_options.h"
|
||||
#include "app/rw_lock.h"
|
||||
#include "app/transformation.h"
|
||||
#include "base/disable_copying.h"
|
||||
#include "base/mutex.h"
|
||||
#include "base/rw_lock.h"
|
||||
#include "base/shared_ptr.h"
|
||||
#include "base/unique_ptr.h"
|
||||
#include "doc/blend_mode.h"
|
||||
@ -52,7 +52,7 @@ namespace app {
|
||||
// An application document. It is the class used to contain one file
|
||||
// opened and being edited by the user (a sprite).
|
||||
class Document : public doc::Document,
|
||||
public RWLock {
|
||||
public base::RWLock {
|
||||
enum Flags {
|
||||
kAssociatedToFile = 1, // This sprite is associated to a file in the file-system
|
||||
kMaskVisible = 2, // The mask wasn't hidden by the user
|
||||
|
@ -203,7 +203,7 @@ namespace app {
|
||||
|
||||
explicit WeakDocumentReader(Document* doc)
|
||||
: DocumentAccess(doc)
|
||||
, m_weak_lock(RWLock::WeakUnlocked) {
|
||||
, m_weak_lock(base::RWLock::WeakUnlocked) {
|
||||
if (m_document)
|
||||
m_document->weakLock(&m_weak_lock);
|
||||
}
|
||||
@ -213,12 +213,12 @@ namespace app {
|
||||
}
|
||||
|
||||
bool isLocked() const {
|
||||
return (m_weak_lock == RWLock::WeakLocked);
|
||||
return (m_weak_lock == base::RWLock::WeakLocked);
|
||||
}
|
||||
|
||||
protected:
|
||||
void weakUnlock() {
|
||||
if (m_document && m_weak_lock != RWLock::WeakUnlocked) {
|
||||
if (m_document && m_weak_lock != base::RWLock::WeakUnlocked) {
|
||||
m_document->weakUnlock();
|
||||
m_document = nullptr;
|
||||
}
|
||||
@ -229,7 +229,7 @@ namespace app {
|
||||
WeakDocumentReader(const WeakDocumentReader&);
|
||||
WeakDocumentReader& operator=(const WeakDocumentReader&);
|
||||
|
||||
RWLock::WeakLock m_weak_lock;
|
||||
base::RWLock::WeakLock m_weak_lock;
|
||||
};
|
||||
|
||||
} // namespace app
|
||||
|
@ -1,216 +0,0 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2001-2016 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
// Uncomment this line in case that you want TRACE() lock/unlock
|
||||
// operations.
|
||||
//#define DEBUG_OBJECT_LOCKS
|
||||
|
||||
#include "app/document.h"
|
||||
|
||||
#include "base/scoped_lock.h"
|
||||
#include "base/thread.h"
|
||||
|
||||
namespace app {
|
||||
|
||||
using namespace base;
|
||||
|
||||
RWLock::RWLock()
|
||||
: m_write_lock(false)
|
||||
, m_read_locks(0)
|
||||
, m_weak_lock(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
RWLock::~RWLock()
|
||||
{
|
||||
ASSERT(!m_write_lock);
|
||||
ASSERT(m_read_locks == 0);
|
||||
ASSERT(m_weak_lock == nullptr);
|
||||
}
|
||||
|
||||
bool RWLock::lock(LockType lockType, int timeout)
|
||||
{
|
||||
while (timeout >= 0) {
|
||||
{
|
||||
scoped_lock lock(m_mutex);
|
||||
|
||||
// Check that there is no weak lock
|
||||
if (m_weak_lock) {
|
||||
if (*m_weak_lock == WeakLocked)
|
||||
*m_weak_lock = WeakUnlocking;
|
||||
|
||||
// Wait some time
|
||||
if (*m_weak_lock == WeakUnlocking)
|
||||
goto go_wait;
|
||||
|
||||
ASSERT(*m_weak_lock == WeakUnlocked);
|
||||
}
|
||||
|
||||
switch (lockType) {
|
||||
|
||||
case ReadLock:
|
||||
// If no body is writting the object...
|
||||
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 object...
|
||||
m_write_lock = true;
|
||||
|
||||
#ifdef DEBUG_OBJECT_LOCKS
|
||||
TRACE("LCK: lock: Locked <%p> to write\n", this);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
go_wait:;
|
||||
}
|
||||
|
||||
if (timeout > 0) {
|
||||
int delay = MIN(100, timeout);
|
||||
timeout -= delay;
|
||||
|
||||
#ifdef DEBUG_OBJECT_LOCKS
|
||||
TRACE("LCK: lock: wait 100 msecs for <%p>\n", this);
|
||||
#endif
|
||||
|
||||
base::this_thread::sleep_for(double(delay) / 1000.0);
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_OBJECT_LOCKS
|
||||
TRACE("LCK: lock: Cannot lock <%p> to %s (has %d read locks and %d write locks)\n",
|
||||
this, (lockType == ReadLock ? "read": "write"), m_read_locks, m_write_lock);
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void RWLock::downgradeToRead()
|
||||
{
|
||||
scoped_lock lock(m_mutex);
|
||||
|
||||
ASSERT(m_read_locks == 0);
|
||||
ASSERT(m_write_lock);
|
||||
|
||||
m_write_lock = false;
|
||||
m_read_locks = 1;
|
||||
}
|
||||
|
||||
void RWLock::unlock()
|
||||
{
|
||||
scoped_lock lock(m_mutex);
|
||||
|
||||
if (m_write_lock) {
|
||||
m_write_lock = false;
|
||||
}
|
||||
else if (m_read_locks > 0) {
|
||||
--m_read_locks;
|
||||
}
|
||||
else {
|
||||
ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
bool RWLock::weakLock(WeakLock* weak_lock_flag)
|
||||
{
|
||||
scoped_lock lock(m_mutex);
|
||||
|
||||
if (m_weak_lock ||
|
||||
m_write_lock ||
|
||||
m_read_locks > 0)
|
||||
return false;
|
||||
|
||||
m_weak_lock = weak_lock_flag;
|
||||
*m_weak_lock = WeakLocked;
|
||||
return true;
|
||||
}
|
||||
|
||||
void RWLock::weakUnlock()
|
||||
{
|
||||
ASSERT(m_weak_lock);
|
||||
ASSERT(*m_weak_lock != WeakLock::WeakUnlocked);
|
||||
ASSERT(!m_write_lock);
|
||||
ASSERT(m_read_locks == 0);
|
||||
|
||||
if (m_weak_lock) {
|
||||
*m_weak_lock = WeakLock::WeakUnlocked;
|
||||
m_weak_lock = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool RWLock::upgradeToWrite(int timeout)
|
||||
{
|
||||
while (timeout >= 0) {
|
||||
{
|
||||
scoped_lock lock(m_mutex);
|
||||
|
||||
// Check that there is no weak lock
|
||||
if (m_weak_lock) {
|
||||
if (*m_weak_lock == WeakLocked)
|
||||
*m_weak_lock = WeakUnlocking;
|
||||
|
||||
// Wait some time
|
||||
if (*m_weak_lock == WeakUnlocking)
|
||||
goto go_wait;
|
||||
|
||||
ASSERT(*m_weak_lock == WeakUnlocked);
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
#ifdef DEBUG_OBJECT_LOCKS
|
||||
TRACE("LCK: upgradeToWrite: Locked <%p> to write\n", this);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
go_wait:;
|
||||
}
|
||||
|
||||
if (timeout > 0) {
|
||||
int delay = MIN(100, timeout);
|
||||
timeout -= delay;
|
||||
|
||||
#ifdef DEBUG_OBJECT_LOCKS
|
||||
TRACE("LCK: upgradeToWrite: wait 100 msecs for <%p>\n", this);
|
||||
#endif
|
||||
|
||||
base::this_thread::sleep_for(double(delay) / 1000.0);
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_OBJECT_LOCKS
|
||||
TRACE("LCK: upgradeToWrite: Cannot lock <%p> to write (has %d read locks and %d write locks)\n",
|
||||
this, m_read_locks, m_write_lock);
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace app
|
@ -1,79 +0,0 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2001-2016 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
|
||||
#ifndef APP_RW_LOCK_H_INCLUDED
|
||||
#define APP_RW_LOCK_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "base/disable_copying.h"
|
||||
#include "base/mutex.h"
|
||||
|
||||
namespace app {
|
||||
|
||||
// A readers-writer lock implementation
|
||||
class RWLock {
|
||||
public:
|
||||
enum LockType {
|
||||
ReadLock,
|
||||
WriteLock
|
||||
};
|
||||
|
||||
enum WeakLock {
|
||||
WeakUnlocked,
|
||||
WeakUnlocking,
|
||||
WeakLocked,
|
||||
};
|
||||
|
||||
RWLock();
|
||||
~RWLock();
|
||||
|
||||
// Locks the object to read or write on it, returning true if the
|
||||
// object can be accessed in the desired mode.
|
||||
bool lock(LockType lockType, int timeout);
|
||||
|
||||
// If you've locked the object to read, using this method you can
|
||||
// raise your access level to write it.
|
||||
bool upgradeToWrite(int timeout);
|
||||
|
||||
// If we've locked the object to write, using this method we can
|
||||
// lower our access to read-only.
|
||||
void downgradeToRead();
|
||||
|
||||
// Unlocks a previously successfully lock() operation.
|
||||
void unlock();
|
||||
|
||||
// Tries to lock the object for read access in a "weak way" so
|
||||
// other thread (e.g. UI thread) can lock the object removing this
|
||||
// weak lock.
|
||||
//
|
||||
// The "weak_lock_flag" is used to notify when the "weak lock" is
|
||||
// lost.
|
||||
bool weakLock(WeakLock* weak_lock_flag);
|
||||
void weakUnlock();
|
||||
|
||||
private:
|
||||
// Mutex to modify the 'locked' flag.
|
||||
base::mutex m_mutex;
|
||||
|
||||
// True if some thread is writing the object.
|
||||
bool m_write_lock;
|
||||
|
||||
// Greater than zero when one or more threads are reading the object.
|
||||
int m_read_locks;
|
||||
|
||||
// If this isn' nullptr, it means that it points to an unique
|
||||
// "weak" lock that can be unlocked from other thread. E.g. the
|
||||
// backup/data recovery thread might weakly lock the object so if
|
||||
// the user UI thread needs the object again, the backup process
|
||||
// can stop.
|
||||
WeakLock* m_weak_lock;
|
||||
|
||||
DISABLE_COPYING(RWLock);
|
||||
};
|
||||
|
||||
} // namespace app
|
||||
|
||||
#endif
|
@ -1,78 +0,0 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2001-2016 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
|
||||
#include "tests/test.h"
|
||||
|
||||
#include "app/rw_lock.h"
|
||||
|
||||
using namespace app;
|
||||
|
||||
TEST(RWLock, MultipleReaders)
|
||||
{
|
||||
RWLock a;
|
||||
EXPECT_TRUE(a.lock(RWLock::ReadLock, 0));
|
||||
EXPECT_TRUE(a.lock(RWLock::ReadLock, 0));
|
||||
EXPECT_TRUE(a.lock(RWLock::ReadLock, 0));
|
||||
EXPECT_TRUE(a.lock(RWLock::ReadLock, 0));
|
||||
EXPECT_FALSE(a.lock(RWLock::WriteLock, 0));
|
||||
a.unlock();
|
||||
a.unlock();
|
||||
a.unlock();
|
||||
a.unlock();
|
||||
}
|
||||
|
||||
TEST(RWLock, OneWriter)
|
||||
{
|
||||
RWLock a;
|
||||
|
||||
EXPECT_TRUE(a.lock(RWLock::WriteLock, 0));
|
||||
EXPECT_FALSE(a.lock(RWLock::ReadLock, 0));
|
||||
a.unlock();
|
||||
|
||||
EXPECT_TRUE(a.lock(RWLock::ReadLock, 0));
|
||||
EXPECT_FALSE(a.lock(RWLock::WriteLock, 0));
|
||||
a.unlock();
|
||||
|
||||
EXPECT_TRUE(a.lock(RWLock::ReadLock, 0));
|
||||
EXPECT_TRUE(a.lock(RWLock::ReadLock, 0));
|
||||
EXPECT_FALSE(a.lock(RWLock::WriteLock, 0));
|
||||
a.unlock();
|
||||
EXPECT_FALSE(a.lock(RWLock::WriteLock, 0));
|
||||
a.unlock();
|
||||
EXPECT_TRUE(a.lock(RWLock::WriteLock, 0));
|
||||
EXPECT_FALSE(a.lock(RWLock::WriteLock, 0));
|
||||
a.unlock();
|
||||
}
|
||||
|
||||
TEST(RWLock, UpgradeToWrite)
|
||||
{
|
||||
RWLock a;
|
||||
|
||||
EXPECT_TRUE(a.lock(RWLock::ReadLock, 0));
|
||||
EXPECT_FALSE(a.lock(RWLock::WriteLock, 0));
|
||||
EXPECT_TRUE(a.upgradeToWrite(0));
|
||||
EXPECT_FALSE(a.lock(RWLock::ReadLock, 0));
|
||||
EXPECT_FALSE(a.lock(RWLock::WriteLock, 0));
|
||||
a.downgradeToRead();
|
||||
a.unlock();
|
||||
}
|
||||
|
||||
TEST(RWLock, WeakLock)
|
||||
{
|
||||
RWLock a;
|
||||
RWLock::WeakLock flag = RWLock::WeakUnlocked;
|
||||
|
||||
EXPECT_TRUE(a.weakLock(&flag));
|
||||
EXPECT_EQ(RWLock::WeakLocked, flag);
|
||||
EXPECT_FALSE(a.lock(RWLock::ReadLock, 0));
|
||||
EXPECT_EQ(RWLock::WeakUnlocking, flag);
|
||||
a.weakUnlock();
|
||||
EXPECT_EQ(RWLock::WeakUnlocked, flag);
|
||||
|
||||
EXPECT_TRUE(a.lock(RWLock::ReadLock, 0));
|
||||
EXPECT_FALSE(a.weakLock(&flag));
|
||||
a.unlock();
|
||||
}
|
Loading…
Reference in New Issue
Block a user