Skip to content

Commit d6b98d8

Browse files
smilesa-maurice
authored andcommitted
Added a class to perform thread safe reference counting.
This reduces boilerplate when writing reference counted thread safe modules or classes. PiperOrigin-RevId: 265942588
1 parent 88f19bf commit d6b98d8

File tree

2 files changed

+256
-0
lines changed

2 files changed

+256
-0
lines changed

app/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,7 @@ set(utility_common_HDRS
205205
src/optional.h
206206
src/path.h
207207
src/pthread_condvar.h
208+
src/reference_count.h
208209
src/reference_counted_future_impl.h
209210
src/scheduler.h
210211
src/semaphore.h

app/src/reference_count.h

Lines changed: 255 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,255 @@
1+
/*
2+
* Copyright 2019 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#ifndef FIREBASE_APP_CLIENT_CPP_SRC_REFERENCE_COUNT_H_
18+
#define FIREBASE_APP_CLIENT_CPP_SRC_REFERENCE_COUNT_H_
19+
20+
#include "app/src/mutex.h"
21+
22+
namespace FIREBASE_NAMESPACE {
23+
namespace internal {
24+
25+
// Reference counter.
26+
// To use this in a thread safe fashion, this class should be used in with
27+
// ReferenceCountLock or ReferenceCountedInitializer.
28+
class ReferenceCount {
29+
public:
30+
// Initialize with no references.
31+
ReferenceCount() : references_(0) {}
32+
33+
// Increase the reference count, returning the previous number of references.
34+
int AddReference() {
35+
MutexLock lock(mutex_);
36+
int previous_references = references_;
37+
references_++;
38+
return previous_references;
39+
}
40+
41+
// Decrease the reference count, returning the previous number of references.
42+
// If the object has no references the count is not decreased.
43+
int RemoveReference() {
44+
MutexLock lock(mutex_);
45+
int previous_references = references_;
46+
if (references_) references_--;
47+
return previous_references;
48+
}
49+
50+
// Remove all references to this object.
51+
// This should only be used to clean up during initialization of an object
52+
// while holding the mutex.
53+
int RemoveAllReferences() {
54+
MutexLock lock(mutex_);
55+
int previous_references = references_;
56+
references_ = 0;
57+
return previous_references;
58+
}
59+
60+
// Get the current number of references.
61+
int references() {
62+
MutexLock lock(mutex_);
63+
return references_;
64+
}
65+
66+
// Get the mutex that guards this object.
67+
Mutex& mutex() { return mutex_; }
68+
69+
private:
70+
// Number of references to this object.
71+
int references_;
72+
Mutex mutex_; // Allows users to guard references_.
73+
};
74+
75+
// Increases ReferenceCount (of type T) while the lock is active.
76+
template <typename T>
77+
class ReferenceCountLock {
78+
public:
79+
// Acquire the reference count lock and hold a reference for the lifetime of
80+
// this object.
81+
explicit ReferenceCountLock(T* reference_count)
82+
: reference_count_(reference_count), lock_(reference_count->mutex()) {
83+
reference_count->AddReference();
84+
}
85+
86+
~ReferenceCountLock() { reference_count_->RemoveReference(); }
87+
88+
// Increase the number of references returning the previous count excluding
89+
// the reference added by this lock.
90+
int AddReference() {
91+
return GetBaseReferences(reference_count_->AddReference());
92+
}
93+
94+
// Decrease the number of references returning the previous count excluding
95+
// the reference added by this lock.
96+
// If the object has no references the count is not decreased.
97+
int RemoveReference() {
98+
return GetBaseReferences(reference_count_->RemoveReference());
99+
}
100+
101+
// Remove all references to this object.
102+
// This should only be used to clean up during initialization of an object
103+
// while holding the mutex.
104+
int RemoveAllReferences() {
105+
return GetBaseReferences(reference_count_->RemoveAllReferences());
106+
}
107+
108+
// Get the current number of references excluding the reference added by this
109+
// lock.
110+
int references() const {
111+
return GetBaseReferences(reference_count_->references());
112+
}
113+
114+
private:
115+
// Remove the lock's reference from the specified reference count.
116+
static int GetBaseReferences(int count) { return count ? count - 1 : count; }
117+
118+
private:
119+
T* reference_count_;
120+
MutexLock lock_;
121+
};
122+
123+
// Object which calls the registered Initialize method when the reference
124+
// count is increased to 1 and the registered Terminate method when the
125+
// reference count is decreased to 0.
126+
//
127+
// For example, in a module:
128+
//
129+
// static bool InitializeInternal(void* context) {
130+
// // Allocate resources for the module.
131+
// }
132+
//
133+
// static void TerminateInternal(void* context) {
134+
// // Free resources for the module.
135+
// }
136+
//
137+
// static ReferenceCountedInitializer g_initializer(
138+
// InitializeInternal, TerminateInternal, nullptr); // NOLINT
139+
//
140+
// bool Initialize() {
141+
// return g_initializer.AddReference() > 0;
142+
// }
143+
//
144+
// void DoSomethingWithModuleState() {
145+
// ReferenceCountLock<ReferenceCountedInitializer> lock(&g_initializer);
146+
// assert(g_initializer.references() > 0); // Initialized?
147+
// // Use global state.
148+
// // When lock is destroyed, if another thread decremented the reference
149+
// // count to 1 the module will be cleaned up.
150+
// }
151+
//
152+
// void Terminate() {
153+
// g_initializer.RemoveReference();
154+
// }
155+
template <typename T>
156+
class ReferenceCountedInitializer {
157+
public:
158+
// Called when the reference count is increased from 0 to 1.
159+
typedef bool (*Initialize)(T* context);
160+
// Called when the reference count is decreased from 1 to 0.
161+
typedef void (*Terminate)(T* context);
162+
163+
public:
164+
// Construct the object with no initialize or terminate methods.
165+
ReferenceCountedInitializer()
166+
: initialize_(nullptr), terminate_(nullptr), context_(nullptr) {}
167+
168+
// Construct the object with just a terminate method.
169+
ReferenceCountedInitializer(Terminate terminate, T* context)
170+
: initialize_(nullptr), terminate_(terminate), context_(context) {}
171+
172+
// Construct the object, both initialize and terminate are optional.
173+
ReferenceCountedInitializer(Initialize initialize, Terminate terminate,
174+
T* context)
175+
: initialize_(initialize), terminate_(terminate), context_(context) {}
176+
177+
// Increase the reference count calling the specified initialization method
178+
// with context if increasing the reference count from 0 to 1, returning the
179+
// previous reference count.
180+
// A reference count of -1 is returned if initialization fails.
181+
int AddReference(Initialize initialize, T* context) {
182+
ReferenceCountLock<ReferenceCount> lock(&count_);
183+
int previous_references = lock.AddReference();
184+
if (previous_references == 0 && initialize && !initialize(context)) {
185+
lock.RemoveReference();
186+
return -1;
187+
}
188+
return previous_references;
189+
}
190+
191+
// Increase the reference count calling the initialize method if increasing
192+
// the reference count from 0 to 1, returning the previous reference count.
193+
// A reference count of -1 is returned if initialization fails.
194+
int AddReference() { return AddReference(initialize_, context_); }
195+
196+
// Decrease the reference count, returning the previous number of references.
197+
int RemoveReference() {
198+
ReferenceCountLock<ReferenceCount> lock(&count_);
199+
int previous_references = lock.RemoveReference();
200+
if (previous_references == 1) ExecuteTerminate();
201+
return previous_references;
202+
}
203+
204+
// Clear the reference count and run the registered terminate method.
205+
// This can be used to reset the reference count during initialization.
206+
int RemoveAllReferences() {
207+
MutexLock lock(count_.mutex());
208+
int previous_references = count_.RemoveAllReferences();
209+
if (previous_references) ExecuteTerminate();
210+
return previous_references;
211+
}
212+
213+
// Clear the reference count without running the register terminate method.
214+
// This can be used to reset the reference count during initialization.
215+
int RemoveAllReferencesWithoutTerminate() {
216+
MutexLock lock(count_.mutex());
217+
return count_.RemoveAllReferences();
218+
}
219+
220+
// Get the current number of references.
221+
int references() { return count_.references(); }
222+
223+
// Get the mutex that guards this object.
224+
Mutex& mutex() { return count_.mutex(); }
225+
226+
// Get the initialize method.
227+
Initialize initialize() const { return initialize_; }
228+
229+
// Get the terminate method.
230+
Terminate terminate() const { return terminate_; }
231+
232+
// Set the initialization context.
233+
void set_context(T* new_context) {
234+
MutexLock lock(mutex());
235+
context_ = new_context;
236+
}
237+
238+
// Get the context for the initializer.
239+
T* context() const { return context_; }
240+
241+
private:
242+
// Execute the terminate method.
243+
void ExecuteTerminate() { if (terminate_) terminate_(context_); }
244+
245+
private:
246+
ReferenceCount count_;
247+
Initialize initialize_;
248+
Terminate terminate_;
249+
T* context_;
250+
};
251+
252+
} // namespace internal
253+
} // namespace FIREBASE_NAMESPACE
254+
255+
#endif // FIREBASE_APP_CLIENT_CPP_SRC_REFERENCE_COUNT_H_

0 commit comments

Comments
 (0)