Reimplement sw::Resource with modern C++ primitives
Bug: b/133127573
Change-Id: I834939c708dcb1a075b397a919fa63c5aec652f5
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/31593
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
Tested-by: Ben Clayton <bclayton@google.com>
Kokoro-Presubmit: kokoro <noreply+kokoro@google.com>
diff --git a/src/Common/Resource.hpp b/src/Common/Resource.hpp
index 2124058..2e8c349 100644
--- a/src/Common/Resource.hpp
+++ b/src/Common/Resource.hpp
@@ -30,7 +30,7 @@
// Resource is a form of shared mutex that guards an internally allocated
// buffer. Resource has an exclusive lock mode (sw::Accessor) and lock
// count, defaulting to sw::Accessor::PUBLIC and 0, respectively.
- // Resource does treat any of the sw::Accessor enumerator lock modes
+ // Resource doesn't treat any of the sw::Accessor enumerator lock modes
// differently, all semantic meaning comes from the usage of Resource.
// You can have multiple locks in mode sw::Accessor::EXCLUSIVE.
class Resource
diff --git a/src/System/Resource.cpp b/src/System/Resource.cpp
index 3a63810..d0bd655 100644
--- a/src/System/Resource.cpp
+++ b/src/System/Resource.cpp
@@ -21,12 +21,6 @@
{
Resource::Resource(size_t bytes) : size(bytes)
{
- blocked = 0;
-
- accessor = PUBLIC;
- count = 0;
- orphaned = false;
-
buffer = allocate(bytes);
}
@@ -37,144 +31,74 @@
void *Resource::lock(Accessor claimer)
{
- criticalSection.lock();
-
- while(count > 0 && accessor != claimer)
- {
- blocked++;
- criticalSection.unlock();
-
- unblock.wait();
-
- criticalSection.lock();
- blocked--;
- }
-
- accessor = claimer;
- count++;
-
- criticalSection.unlock();
-
- return buffer;
+ std::unique_lock<std::mutex> lock(mutex);
+ return acquire(lock, claimer);
}
void *Resource::lock(Accessor relinquisher, Accessor claimer)
{
- criticalSection.lock();
+ std::unique_lock<std::mutex> lock(mutex);
// Release
- while(count > 0 && accessor == relinquisher)
+ if (count > 0 && accessor == relinquisher)
{
- count--;
-
- if(count == 0)
- {
- if(blocked)
- {
- unblock.signal();
- }
- else if(orphaned)
- {
- criticalSection.unlock();
-
- delete this;
-
- return 0;
- }
- }
+ release(lock);
}
// Acquire
- while(count > 0 && accessor != claimer)
- {
- blocked++;
- criticalSection.unlock();
-
- unblock.wait();
-
- criticalSection.lock();
- blocked--;
- }
-
- accessor = claimer;
- count++;
-
- criticalSection.unlock();
+ acquire(lock, claimer);
return buffer;
}
void Resource::unlock()
{
- criticalSection.lock();
+ std::unique_lock<std::mutex> lock(mutex);
+ release(lock);
+ }
+
+ void *Resource::acquire(std::unique_lock<std::mutex> &lock, Accessor claimer)
+ {
+ while (count > 0 && accessor != claimer)
+ {
+ blocked++;
+ released.wait(lock, [&] { return count == 0 || accessor == claimer; });
+ blocked--;
+ }
+
+ accessor = claimer;
+ count++;
+ return buffer;
+ }
+
+ void Resource::release(std::unique_lock<std::mutex> &lock)
+ {
ASSERT(count > 0);
count--;
if(count == 0)
{
- if(blocked)
+ if(orphaned)
{
- unblock.signal();
- }
- else if(orphaned)
- {
- criticalSection.unlock();
-
+ lock.unlock();
delete this;
-
return;
}
+ released.notify_one();
}
-
- criticalSection.unlock();
- }
-
- void Resource::unlock(Accessor relinquisher)
- {
- criticalSection.lock();
- ASSERT(count > 0);
-
- while(count > 0 && accessor == relinquisher)
- {
- count--;
-
- if(count == 0)
- {
- if(blocked)
- {
- unblock.signal();
- }
- else if(orphaned)
- {
- criticalSection.unlock();
-
- delete this;
-
- return;
- }
- }
- }
-
- criticalSection.unlock();
}
void Resource::destruct()
{
- criticalSection.lock();
-
- if(count == 0 && !blocked)
+ std::unique_lock<std::mutex> lock(mutex);
+ if(count == 0 && blocked == 0)
{
- criticalSection.unlock();
-
+ lock.unlock();
delete this;
-
return;
}
-
orphaned = true;
-
- criticalSection.unlock();
}
const void *Resource::data() const
diff --git a/src/System/Resource.hpp b/src/System/Resource.hpp
index 7caec8a..bff79a1 100644
--- a/src/System/Resource.hpp
+++ b/src/System/Resource.hpp
@@ -15,8 +15,7 @@
#ifndef sw_Resource_hpp
#define sw_Resource_hpp
-#include "Synchronization.hpp"
-
+#include <condition_variable>
#include <mutex>
namespace sw
@@ -29,32 +28,78 @@
EXCLUSIVE
};
+ // Resource is a form of shared mutex that guards an internally allocated
+ // buffer. Resource has an exclusive lock mode (sw::Accessor) and lock
+ // count, defaulting to sw::Accessor::PUBLIC and 0, respectively.
+ // Resource doesn't treat any of the sw::Accessor enumerator lock modes
+ // differently, all semantic meaning comes from the usage of Resource.
+ // You can have multiple locks in mode sw::Accessor::EXCLUSIVE.
class Resource
{
public:
Resource(size_t bytes);
- void destruct(); // Asynchronous destructor
+ // destruct() is an asynchronous destructor, that will atomically:
+ // When the resource is unlocked:
+ // * Delete itself.
+ // When the resource is locked:
+ // * Flag itself for deletion when next fully unlocked.
+ void destruct();
+ // lock() will atomically:
+ // When the resource is unlocked OR the lock mode equals claimer:
+ // * Increment the lock count.
+ // * Return a pointer to the buffer.
+ // When the resource is locked AND the lock mode does not equal claimer:
+ // * Block until all existing locks are released (lock count equals 0).
+ // * Switch lock mode to claimer.
+ // * Increment the lock count.
+ // * Return a pointer to the buffer.
void *lock(Accessor claimer);
- void *lock(Accessor relinquisher, Accessor claimer);
- void unlock();
- void unlock(Accessor relinquisher);
+ // lock() will atomically:
+ // When the resource is unlocked OR the lock mode equals claimer:
+ // * Increment the lock count.
+ // * Return a pointer to the buffer.
+ // When the resource is locked AND the lock mode equals relinquisher:
+ // * Release *all* existing locks (regardless of prior count).
+ // * Delete itself and return nullptr if Resource::destruct() had been called while locked.
+ // * Switch lock mode to claimer.
+ // * Increment the lock count to 1.
+ // * Return a pointer to the buffer.
+ // When the resource is locked AND the lock mode does not equal relinquisher:
+ // * Block until all existing locks are released (lock count equals 0)
+ // * Switch lock mode to claimer
+ // * Increment the lock count to 1.
+ // * Return a pointer to the buffer.
+ void *lock(Accessor relinquisher, Accessor claimer);
+
+ // unlock() will atomically:
+ // * Assert if there are no locks.
+ // * Release a single lock.
+ // * Delete itself if Resource::destruct() had been called while locked.
+ void unlock();
+
+ // data() will return the Resource's buffer pointer regardless of lock
+ // state.
const void *data() const;
+
+ // size is the size in bytes of the Resource's buffer.
const size_t size;
private:
+ void *acquire(std::unique_lock<std::mutex> &lock, Accessor claimer);
+ void release(std::unique_lock<std::mutex> &lock);
+
~Resource(); // Always call destruct() instead
- std::mutex criticalSection;
- Event unblock;
- volatile int blocked;
+ std::mutex mutex;
+ std::condition_variable released;
- volatile Accessor accessor;
- volatile int count;
- bool orphaned;
-
+ Accessor accessor = PUBLIC; // guarded by mutex
+ int blocked = 0; // guarded by mutex
+ int count = 0; // guarded by mutex
+ bool orphaned = false; // guarded by mutex
void *buffer;
};
}