no bug - Bumping Firefox l10n changesets r=release a=l10n-bump DONTBUILD CLOSED TREE
[gecko.git] / gfx / 2d / UserData.h
blob16a7db343ea87b7607689ccbfdf7da860f3af199
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/. */
7 #ifndef MOZILLA_GFX_USERDATA_H_
8 #define MOZILLA_GFX_USERDATA_H_
10 #include <stdlib.h>
11 #include "Types.h"
12 #include "mozilla/Assertions.h"
13 #include "mozilla/Atomics.h"
14 #include "mozilla/Mutex.h"
16 namespace mozilla {
17 namespace gfx {
19 struct UserDataKey {
20 int unused;
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);
28 UserData() : count(0), entries(nullptr) {}
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);
38 entries[i].userData = userData;
39 entries[i].destroy = destroy;
40 return;
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)));
51 if (!entries) {
52 MOZ_CRASH("GFX: UserData::Add");
55 entries[count].key = key;
56 entries[count].userData = userData;
57 entries[count].destroy = destroy;
59 count++;
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];
72 return userData;
75 return nullptr;
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);
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];
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;
101 return nullptr;
104 bool Has(UserDataKey* key) {
105 for (int i = 0; i < count; i++) {
106 if (key == entries[i].key) {
107 return true;
110 return false;
113 void Destroy() {
114 if (!entries) {
115 return;
117 for (int i = 0; i < count; i++) {
118 if (entries[i].destroy) {
119 entries[i].destroy(entries[i].userData);
122 free(entries);
123 entries = nullptr;
124 count = 0;
127 ~UserData() { Destroy(); }
129 private:
130 struct Entry {
131 const UserDataKey* key;
132 void* userData;
133 DestroyFunc destroy;
136 int count;
137 Entry* entries;
140 class ThreadSafeUserData {
141 protected:
142 struct LockedUserData : public UserData {
143 Mutex mLock;
145 LockedUserData() : mLock("LockedUserData::mLock") {}
148 public:
149 ~ThreadSafeUserData() {
150 if (LockedUserData* userData = mUserData.exchange(nullptr)) {
152 MutexAutoLock lock(userData->mLock);
153 userData->Destroy();
155 delete userData;
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);
165 void* Remove(UserDataKey* key) {
166 LockedUserData* userData = GetUserData();
167 MutexAutoLock lock(userData->mLock);
168 return userData->Remove(key);
171 void RemoveAndDestroy(UserDataKey* key) {
172 LockedUserData* userData = GetUserData();
173 MutexAutoLock lock(userData->mLock);
174 userData->RemoveAndDestroy(key);
177 void* Get(UserDataKey* key) const {
178 LockedUserData* userData = GetUserData();
179 MutexAutoLock lock(userData->mLock);
180 return userData->Get(key);
183 bool Has(UserDataKey* key) {
184 LockedUserData* userData = GetUserData();
185 MutexAutoLock lock(userData->mLock);
186 return userData->Has(key);
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);
200 return userData;
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;
211 } // namespace gfx
212 } // namespace mozilla
214 #endif /* MOZILLA_GFX_USERDATA_H_ */