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