Bug 1786136 - Make UserData thread-safe. r=jrmuizel

Differential Revision: https://phabricator.services.mozilla.com/D155997
This commit is contained in:
Lee Salzman 2022-09-01 05:29:14 +00:00
Родитель 8f7ed31c01
Коммит 3964d1b8ec
2 изменённых файлов: 82 добавлений и 6 удалений

Просмотреть файл

@ -711,7 +711,7 @@ class SourceSurface : public SupportsThreadSafeWeakPtr<SourceSurface> {
protected:
friend class StoredPattern;
UserData mUserData;
ThreadSafeUserData mUserData;
};
class DataSourceSurface : public SourceSurface {
@ -1241,7 +1241,7 @@ class ScaledFont : public SupportsThreadSafeWeakPtr<ScaledFont> {
explicit ScaledFont(const RefPtr<UnscaledFont>& aUnscaledFont)
: mUnscaledFont(aUnscaledFont), mSyntheticObliqueAngle(0.0f) {}
UserData mUserData;
ThreadSafeUserData mUserData;
RefPtr<UnscaledFont> mUnscaledFont;
Float mSyntheticObliqueAngle;

Просмотреть файл

@ -10,6 +10,8 @@
#include <stdlib.h>
#include "Types.h"
#include "mozilla/Assertions.h"
#include "mozilla/Atomics.h"
#include "mozilla/Mutex.h"
namespace mozilla {
namespace gfx {
@ -20,14 +22,14 @@ struct UserDataKey {
/* this class is basically a clone of the user data concept from cairo */
class UserData {
typedef void (*destroyFunc)(void* data);
public:
typedef void (*DestroyFunc)(void* data);
UserData() : count(0), entries(nullptr) {}
/* Attaches untyped userData associated with key. destroy is called on
* destruction */
void Add(UserDataKey* key, void* userData, destroyFunc destroy) {
void Add(UserDataKey* key, void* userData, DestroyFunc destroy) {
for (int i = 0; i < count; i++) {
if (key == entries[i].key) {
if (entries[i].destroy) {
@ -109,6 +111,9 @@ class UserData {
}
void Destroy() {
if (!entries) {
return;
}
for (int i = 0; i < count; i++) {
if (entries[i].destroy) {
entries[i].destroy(entries[i].userData);
@ -125,13 +130,84 @@ class UserData {
struct Entry {
const UserDataKey* key;
void* userData;
destroyFunc destroy;
DestroyFunc destroy;
};
int count;
Entry* entries;
};
class ThreadSafeUserData {
protected:
struct LockedUserData : public UserData {
Mutex mLock;
LockedUserData() : mLock("LockedUserData::mLock") {}
};
public:
~ThreadSafeUserData() {
if (LockedUserData* userData = mUserData.exchange(nullptr)) {
{
MutexAutoLock lock(userData->mLock);
userData->Destroy();
}
delete userData;
}
}
void Add(UserDataKey* key, void* value, UserData::DestroyFunc destroy) {
LockedUserData* userData = GetUserData();
MutexAutoLock lock(userData->mLock);
userData->Add(key, value, destroy);
}
void* Remove(UserDataKey* key) {
LockedUserData* userData = GetUserData();
MutexAutoLock lock(userData->mLock);
return userData->Remove(key);
}
void RemoveAndDestroy(UserDataKey* key) {
LockedUserData* userData = GetUserData();
MutexAutoLock lock(userData->mLock);
userData->RemoveAndDestroy(key);
}
void* Get(UserDataKey* key) const {
LockedUserData* userData = GetUserData();
MutexAutoLock lock(userData->mLock);
return userData->Get(key);
}
bool Has(UserDataKey* key) {
LockedUserData* userData = GetUserData();
MutexAutoLock lock(userData->mLock);
return userData->Has(key);
}
private:
LockedUserData* GetUserData() const {
LockedUserData* userData = mUserData;
if (!userData) {
userData = new LockedUserData;
if (!mUserData.compareExchange(nullptr, userData)) {
delete userData;
userData = mUserData;
MOZ_ASSERT(userData);
}
}
return userData;
}
// The Mutex class is quite large. For small, frequent classes (ScaledFont,
// SourceSurface, etc.) this can add a lot of memory overhead, especially if
// UserData is only infrequently used. To avoid this, we only allocate the
// LockedUserData if it is actually used. If unused, it only adds a single
// pointer as overhead.
mutable Atomic<LockedUserData*> mUserData;
};
} // namespace gfx
} // namespace mozilla