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_
12 #include "mozilla/Assertions.h"
13 #include "mozilla/Atomics.h"
14 #include "mozilla/Mutex.h"
23 /* this class is basically a clone of the user data concept from cairo */
26 typedef void (*DestroyFunc
)(void* data
);
28 UserData() : count(0), entries(nullptr) {}
30 /* Attaches untyped userData associated with key. destroy is called on
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
;
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
49 static_cast<Entry
*>(realloc(entries
, sizeof(Entry
) * (count
+ 1)));
52 MOZ_CRASH("GFX: UserData::Add");
55 entries
[count
].key
= key
;
56 entries
[count
].userData
= userData
;
57 entries
[count
].destroy
= destroy
;
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:
69 for (; i
< count
; i
++) {
70 entries
[i
] = entries
[i
+ 1];
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:
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
;
104 bool Has(UserDataKey
* key
) {
105 for (int i
= 0; i
< count
; i
++) {
106 if (key
== entries
[i
].key
) {
117 for (int i
= 0; i
< count
; i
++) {
118 if (entries
[i
].destroy
) {
119 entries
[i
].destroy(entries
[i
].userData
);
127 ~UserData() { Destroy(); }
131 const UserDataKey
* key
;
140 class ThreadSafeUserData
{
142 struct LockedUserData
: public UserData
{
145 LockedUserData() : mLock("LockedUserData::mLock") {}
149 ~ThreadSafeUserData() {
150 if (LockedUserData
* userData
= mUserData
.exchange(nullptr)) {
152 MutexAutoLock
lock(userData
->mLock
);
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
);
190 LockedUserData
* GetUserData() const {
191 LockedUserData
* userData
= mUserData
;
193 userData
= new LockedUserData
;
194 if (!mUserData
.compareExchange(nullptr, userData
)) {
196 userData
= mUserData
;
197 MOZ_ASSERT(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
;
212 } // namespace mozilla
214 #endif /* MOZILLA_GFX_USERDATA_H_ */