blob: 34c530071036ff5d1d1f6aab002da9d3d5586576 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
|
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef PPAPI_UTILITY_THREAD_SAFE_THREAD_TRAITS_H_
#define PPAPI_UTILITY_THREAD_SAFE_THREAD_TRAITS_H_
#include "ppapi/cpp/logging.h"
#include "ppapi/cpp/module.h"
#include "ppapi/utility/threading/lock.h"
/// @file
/// Defines the traits structures for thread-safety of a completion callback
/// factory. We provide thread-safe and non-thread-safe version. The thread-safe
/// version is always correct (if you follow the thread usage rules of the
/// callback factory), but if you know your object will only be used on one
/// thread, you can uses the non-thread-safe version.
///
/// The traits defines three nested classes to perform reference counting,
/// locks, and scoped locking.
namespace pp {
/// The thread-safe version of thread traits. Using this class as the "traits"
/// template argument to a completion callback factory will make it "somewhat
/// thread-friendly." It will allow you to create completion callbacks from
/// background threads and post them to another thread to run.
///
/// Care still must be taken to ensure that the completion callbacks are
/// executed on the same thread that the factory is destroyed on to avoid a
/// race on destruction.
///
/// Implementation note: this uses a lock instead of atomic add instructions.
/// The number of platforms we need to support right now makes atomic
/// operations unwieldy for this case that we don't actually use that often.
/// As a further optimization, we can add support for this later.
class ThreadSafeThreadTraits {
public:
class RefCount {
public:
/// Default constructor. In debug mode, this checks that the object is being
/// created on the main thread.
RefCount() : ref_(0) {
}
/// AddRef() increments the reference counter.
///
/// @return An int32_t with the incremented reference counter.
int32_t AddRef() {
AutoLock lock(lock_);
return ++ref_;
}
/// Release() decrements the reference counter.
///
/// @return An int32_t with the decremeneted reference counter.
int32_t Release() {
AutoLock lock(lock_);
PP_DCHECK(ref_ > 0);
return --ref_;
}
private:
Lock lock_;
int32_t ref_;
};
typedef pp::Lock Lock;
typedef pp::AutoLock AutoLock;
};
/// The non-thread-safe version of thread traits. Using this class as the
/// "traits" template argument to a completion callback factory will make it
/// not thread-safe but with potential extra performance.
class NonThreadSafeThreadTraits {
public:
/// A simple reference counter that is not thread-safe.
///
/// <strong>Note:</strong> in Debug mode, it checks that it is either called
/// on the main thread, or always called on another thread.
class RefCount {
public:
/// Default constructor. In debug mode, this checks that the object is being
/// created on the main thread.
RefCount() : ref_(0) {
#ifndef NDEBUG
is_main_thread_ = Module::Get()->core()->IsMainThread();
#endif
}
/// Destructor.
~RefCount() {
PP_DCHECK(is_main_thread_ == Module::Get()->core()->IsMainThread());
}
/// AddRef() increments the reference counter.
///
/// @return An int32_t with the incremented reference counter.
int32_t AddRef() {
PP_DCHECK(is_main_thread_ == Module::Get()->core()->IsMainThread());
return ++ref_;
}
/// Release() decrements the reference counter.
///
/// @return An int32_t with the decremeneted reference counter.
int32_t Release() {
PP_DCHECK(is_main_thread_ == Module::Get()->core()->IsMainThread());
return --ref_;
}
private:
int32_t ref_;
#ifndef NDEBUG
bool is_main_thread_;
#endif
};
/// A simple object that acts like a lock but does nothing.
///
/// <strong>Note:</strong> in Debug mode, it checks that it is either
/// called on the main thread, or always called on another thread. It also
/// asserts that the caller does not recursively lock.
class Lock {
public:
Lock() {
#ifndef NDEBUG
is_main_thread_ = Module::Get()->core()->IsMainThread();
lock_held_ = false;
#endif
}
~Lock() {
PP_DCHECK(is_main_thread_ == Module::Get()->core()->IsMainThread());
}
/// Acquires the fake "lock". This does nothing except perform checks in
/// debug mode.
void Acquire() {
#ifndef NDEBUG
PP_DCHECK(!lock_held_);
lock_held_ = true;
#endif
}
/// Releases the fake "lock". This does nothing except perform checks in
/// debug mode.
void Release() {
#ifndef NDEBUG
PP_DCHECK(lock_held_);
lock_held_ = false;
#endif
}
private:
#ifndef NDEBUG
bool is_main_thread_;
bool lock_held_;
#endif
};
class AutoLock {
public:
explicit AutoLock(Lock& lock) : lock_(lock) {
lock_.Acquire();
}
~AutoLock() {
lock_.Release();
}
private:
Lock& lock_;
};
};
} // namespace pp
#endif // PPAPI_UTILITY_THREAD_SAFE_THREAD_TRAITS_H_
|