UserData.h (5612B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef MOZILLA_GFX_USERDATA_H_ 8 #define MOZILLA_GFX_USERDATA_H_ 9 10 #include <stdlib.h> 11 #include "Types.h" 12 #include "mozilla/Assertions.h" 13 #include "mozilla/Atomics.h" 14 #include "mozilla/Mutex.h" 15 16 namespace mozilla { 17 namespace gfx { 18 19 struct UserDataKey { 20 int unused; 21 }; 22 23 /* this class is basically a clone of the user data concept from cairo */ 24 class UserData { 25 public: 26 typedef void (*DestroyFunc)(void* data); 27 28 UserData() : count(0), entries(nullptr) {} 29 30 /* Attaches untyped userData associated with key. destroy is called on 31 * destruction */ 32 void Add(UserDataKey* key, void* userData, DestroyFunc destroy) { 33 for (int i = 0; i < count; i++) { 34 if (key == entries[i].key) { 35 if (entries[i].destroy) { 36 entries[i].destroy(entries[i].userData); 37 } 38 entries[i].userData = userData; 39 entries[i].destroy = destroy; 40 return; 41 } 42 } 43 44 // We could keep entries in a std::vector instead of managing it by hand 45 // but that would propagate an stl dependency out which we'd rather not 46 // do (see bug 666609). Plus, the entries array is expect to stay small 47 // so doing a realloc everytime we add a new entry shouldn't be too costly 48 entries = 49 static_cast<Entry*>(realloc(entries, sizeof(Entry) * (count + 1))); 50 51 if (!entries) { 52 MOZ_CRASH("GFX: UserData::Add"); 53 } 54 55 entries[count].key = key; 56 entries[count].userData = userData; 57 entries[count].destroy = destroy; 58 59 count++; 60 } 61 62 /* Remove and return user data associated with key, without destroying it */ 63 void* Remove(UserDataKey* key) { 64 for (int i = 0; i < count; i++) { 65 if (key == entries[i].key) { 66 void* userData = entries[i].userData; 67 // decrement before looping so entries[i+1] doesn't read past the end: 68 --count; 69 for (; i < count; i++) { 70 entries[i] = entries[i + 1]; 71 } 72 return userData; 73 } 74 } 75 return nullptr; 76 } 77 78 /* Remove and destroy a given key */ 79 void RemoveAndDestroy(UserDataKey* key) { 80 for (int i = 0; i < count; i++) { 81 if (key == entries[i].key) { 82 if (entries[i].destroy) { 83 entries[i].destroy(entries[i].userData); 84 } 85 // decrement before looping so entries[i+1] doesn't read past the end: 86 --count; 87 for (; i < count; i++) { 88 entries[i] = entries[i + 1]; 89 } 90 } 91 } 92 } 93 94 /* Retrives the userData for the associated key */ 95 void* Get(UserDataKey* key) const { 96 for (int i = 0; i < count; i++) { 97 if (key == entries[i].key) { 98 return entries[i].userData; 99 } 100 } 101 return nullptr; 102 } 103 104 bool Has(UserDataKey* key) { 105 for (int i = 0; i < count; i++) { 106 if (key == entries[i].key) { 107 return true; 108 } 109 } 110 return false; 111 } 112 113 void Destroy() { 114 if (!entries) { 115 return; 116 } 117 for (int i = 0; i < count; i++) { 118 if (entries[i].destroy) { 119 entries[i].destroy(entries[i].userData); 120 } 121 } 122 free(entries); 123 entries = nullptr; 124 count = 0; 125 } 126 127 ~UserData() { Destroy(); } 128 129 private: 130 struct Entry { 131 const UserDataKey* key; 132 void* userData; 133 DestroyFunc destroy; 134 }; 135 136 int count; 137 Entry* entries; 138 }; 139 140 class ThreadSafeUserData { 141 protected: 142 struct LockedUserData : public UserData { 143 Mutex mLock; 144 145 LockedUserData() : mLock("LockedUserData::mLock") {} 146 }; 147 148 public: 149 ~ThreadSafeUserData() { 150 if (LockedUserData* userData = mUserData.exchange(nullptr)) { 151 { 152 MutexAutoLock lock(userData->mLock); 153 userData->Destroy(); 154 } 155 delete userData; 156 } 157 } 158 159 void Add(UserDataKey* key, void* value, UserData::DestroyFunc destroy) { 160 LockedUserData* userData = GetUserData(); 161 MutexAutoLock lock(userData->mLock); 162 userData->Add(key, value, destroy); 163 } 164 165 void* Remove(UserDataKey* key) { 166 LockedUserData* userData = GetUserData(); 167 MutexAutoLock lock(userData->mLock); 168 return userData->Remove(key); 169 } 170 171 void RemoveAndDestroy(UserDataKey* key) { 172 LockedUserData* userData = GetUserData(); 173 MutexAutoLock lock(userData->mLock); 174 userData->RemoveAndDestroy(key); 175 } 176 177 void* Get(UserDataKey* key) const { 178 LockedUserData* userData = GetUserData(); 179 MutexAutoLock lock(userData->mLock); 180 return userData->Get(key); 181 } 182 183 bool Has(UserDataKey* key) { 184 LockedUserData* userData = GetUserData(); 185 MutexAutoLock lock(userData->mLock); 186 return userData->Has(key); 187 } 188 189 private: 190 LockedUserData* GetUserData() const { 191 LockedUserData* userData = mUserData; 192 if (!userData) { 193 userData = new LockedUserData; 194 if (!mUserData.compareExchange(nullptr, userData)) { 195 delete userData; 196 userData = mUserData; 197 MOZ_ASSERT(userData); 198 } 199 } 200 return userData; 201 } 202 203 // The Mutex class is quite large. For small, frequent classes (ScaledFont, 204 // SourceSurface, etc.) this can add a lot of memory overhead, especially if 205 // UserData is only infrequently used. To avoid this, we only allocate the 206 // LockedUserData if it is actually used. If unused, it only adds a single 207 // pointer as overhead. 208 mutable Atomic<LockedUserData*> mUserData; 209 }; 210 211 } // namespace gfx 212 } // namespace mozilla 213 214 #endif /* MOZILLA_GFX_USERDATA_H_ */