Move RWLock class to laf module

This commit is contained in:
David Capello 2018-07-06 20:55:10 -03:00
parent 3ed516bc11
commit 9449bb3a43
7 changed files with 7 additions and 381 deletions

2
laf

@ -1 +1 @@
Subproject commit b135dbfb5adb32f9b14f85eef1e5fa744d1c553e
Subproject commit dd944a8c91fec41d83c60496eda268a586d650cd

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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();
}