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_THUNK_ENTER_H_
6 #define PPAPI_THUNK_ENTER_H_
7 
8 #include <stdint.h>
9 
10 #include <string>
11 
12 #include "base/macros.h"
13 #include "base/memory/ref_counted.h"
14 #include "ppapi/c/pp_errors.h"
15 #include "ppapi/c/pp_resource.h"
16 #include "ppapi/shared_impl/ppapi_globals.h"
17 #include "ppapi/shared_impl/proxy_lock.h"
18 #include "ppapi/shared_impl/resource.h"
19 #include "ppapi/shared_impl/resource_tracker.h"
20 #include "ppapi/shared_impl/singleton_resource_id.h"
21 #include "ppapi/shared_impl/tracked_callback.h"
22 #include "ppapi/thunk/ppapi_thunk_export.h"
23 #include "ppapi/thunk/ppb_instance_api.h"
24 #include "ppapi/thunk/resource_creation_api.h"
25 
26 namespace ppapi {
27 namespace thunk {
28 
29 // Enter* helper objects: These objects wrap a call from the C PPAPI into
30 // the internal implementation. They make sure the lock is acquired and will
31 // automatically set up some stuff for you.
32 //
33 // You should always check whether the enter succeeded before using the object.
34 // If this fails, then the instance or resource ID supplied was invalid.
35 //
36 // The |report_error| arguments to the constructor should indicate if errors
37 // should be logged to the console. If the calling function expects that the
38 // input values are correct (the normal case), this should be set to true. In
39 // some case like |IsFoo(PP_Resource)| the caller is questioning whether their
40 // handle is this type, and we don't want to report an error if it's not.
41 //
42 // Resource member functions: EnterResource
43 //   Automatically interprets the given PP_Resource as a resource ID and sets
44 //   up the resource object for you.
45 
46 namespace subtle {
47 
48 // This helps us define our RAII Enter classes easily. To make an RAII class
49 // which locks the proxy lock on construction and unlocks on destruction,
50 // inherit from |LockOnEntry<true>| before all other base classes. This ensures
51 // that the lock is acquired before any other base class's constructor can run,
52 // and that the lock is only released after all other destructors have run.
53 // (This order of initialization is guaranteed by C++98/C++11 12.6.2.10).
54 //
55 // For cases where you don't want to lock, inherit from |LockOnEntry<false>|.
56 // This allows us to share more code between Enter* and Enter*NoLock classes.
57 template <bool lock_on_entry>
58 struct LockOnEntry;
59 
60 template <>
61 struct LockOnEntry<false> {
62 #if (!NDEBUG)
63   LockOnEntry() {
64     // You must already hold the lock to use Enter*NoLock.
65     ProxyLock::AssertAcquired();
66   }
67   ~LockOnEntry() {
68     // You must not release the lock before leaving the scope of the
69     // Enter*NoLock.
70     ProxyLock::AssertAcquired();
71   }
72 #endif
73 };
74 
75 template <>
76 struct LockOnEntry<true> {
77   LockOnEntry() {
78     ppapi::ProxyLock::Acquire();
79   }
80   ~LockOnEntry() {
81     ppapi::ProxyLock::Release();
82   }
83 };
84 
85 // Keep non-templatized since we need non-inline functions here.
86 class PPAPI_THUNK_EXPORT EnterBase {
87  public:
88   EnterBase();
89   explicit EnterBase(PP_Resource resource);
90   EnterBase(PP_Instance instance, SingletonResourceID resource_id);
91   EnterBase(PP_Resource resource, const PP_CompletionCallback& callback);
92   EnterBase(PP_Instance instance, SingletonResourceID resource_id,
93             const PP_CompletionCallback& callback);
94   virtual ~EnterBase();
95 
96   // Sets the result for calls that use a completion callback. It handles making
97   // sure that "Required" callbacks are scheduled to run asynchronously and
98   // "Blocking" callbacks cause the caller to block. (Interface implementations,
99   // therefore, should not do any special casing based on the type of the
100   // callback.)
101   //
102   // Returns the "retval()". This is to support the typical usage of
103   //   return enter.SetResult(...);
104   // without having to write a separate "return enter.retval();" line.
105   int32_t SetResult(int32_t result);
106 
107   // Use this value as the return value for the function.
108   int32_t retval() const { return retval_; }
109 
110   // All failure conditions cause retval_ to be set to an appropriate error
111   // code.
112   bool succeeded() const { return retval_ == PP_OK; }
113   bool failed() const { return !succeeded(); }
114 
115   const scoped_refptr<TrackedCallback>& callback() { return callback_; }
116 
117  protected:
118   // Helper function to return a Resource from a PP_Resource. Having this
119   // code be in the non-templatized base keeps us from having to instantiate
120   // it in every template.
121   static Resource* GetResource(PP_Resource resource);
122 
123   // Helper function to return a Resource from a PP_Instance and singleton
124   // resource identifier.
125   static Resource* GetSingletonResource(PP_Instance instance,
126                                         SingletonResourceID resource_id);
127 
128   void ClearCallback();
129 
130   // Does error handling associated with entering a resource. The resource_base
131   // is the result of looking up the given pp_resource. The object is the
132   // result of converting the base to the desired object (converted to a void*
133   // so this function doesn't have to be templatized). The reason for passing
134   // both resource_base and object is that we can differentiate "bad resource
135   // ID" from "valid resource ID not of the correct type."
136   //
137   // This will set retval_ = PP_ERROR_BADRESOURCE if the object is invalid, and
138   // if report_error is set, log a message to the programmer.
139   void SetStateForResourceError(PP_Resource pp_resource,
140                                 Resource* resource_base,
141                                 void* object,
142                                 bool report_error);
143 
144   // Same as SetStateForResourceError except for function API.
145   void SetStateForFunctionError(PP_Instance pp_instance,
146                                 void* object,
147                                 bool report_error);
148 
149   // For Enter objects that need a resource, we'll store a pointer to the
150   // Resource object so that we don't need to look it up more than once. For
151   // Enter objects with no resource, this will be null.
152   Resource* resource_ = nullptr;
153 
154  private:
155   bool CallbackIsValid() const;
156 
157   // Checks whether the callback is valid (i.e., if it is either non-blocking,
158   // or blocking and we're on a background thread). If the callback is invalid,
159   // this will set retval_ = PP_ERROR_BLOCKS_MAIN_THREAD, and if report_error is
160   // set, it will log a message to the programmer.
161   void SetStateForCallbackError(bool report_error);
162 
163   // Holds the callback. For Enter objects that aren't given a callback, this
164   // will be null.
165   scoped_refptr<TrackedCallback> callback_;
166 
167   int32_t retval_ = PP_OK;
168 };
169 
170 }  // namespace subtle
171 
172 // EnterResource ---------------------------------------------------------------
173 
174 template<typename ResourceT, bool lock_on_entry = true>
175 class EnterResource
176     : public subtle::LockOnEntry<lock_on_entry>,  // Must be first; see above.
177       public subtle::EnterBase {
178  public:
179   EnterResource(PP_Resource resource, bool report_error)
180       : EnterBase(resource) {
181     Init(resource, report_error);
182   }
183   EnterResource(PP_Resource resource, const PP_CompletionCallback& callback,
184                 bool report_error)
185       : EnterBase(resource, callback) {
186     Init(resource, report_error);
187   }
188   ~EnterResource() {}
189 
190   ResourceT* object() { return object_; }
191   Resource* resource() { return resource_; }
192 
193  private:
194   void Init(PP_Resource resource, bool report_error) {
195     if (resource_)
196       object_ = resource_->GetAs<ResourceT>();
197     else
198       object_ = nullptr;
199     // Validate the resource (note, if both are wrong, we will return
200     // PP_ERROR_BADRESOURCE; last in wins).
201     SetStateForResourceError(resource, resource_, object_, report_error);
202   }
203 
204   ResourceT* object_;
205 
206   DISALLOW_COPY_AND_ASSIGN(EnterResource);
207 };
208 
209 // ----------------------------------------------------------------------------
210 
211 // Like EnterResource but assumes the lock is already held.
212 template<typename ResourceT>
213 class EnterResourceNoLock : public EnterResource<ResourceT, false> {
214  public:
215   EnterResourceNoLock(PP_Resource resource, bool report_error)
216       : EnterResource<ResourceT, false>(resource, report_error) {
217   }
218   EnterResourceNoLock(PP_Resource resource,
219                       const PP_CompletionCallback& callback,
220                       bool report_error)
221       : EnterResource<ResourceT, false>(resource, callback, report_error) {
222   }
223 };
224 
225 // EnterInstance ---------------------------------------------------------------
226 
227 class PPAPI_THUNK_EXPORT EnterInstance
228     : public subtle::LockOnEntry<true>,  // Must be first; see above.
229       public subtle::EnterBase {
230  public:
231   explicit EnterInstance(PP_Instance instance);
232   EnterInstance(PP_Instance instance,
233                 const PP_CompletionCallback& callback);
234   ~EnterInstance();
235 
236   bool succeeded() const { return !!functions_; }
237   bool failed() const { return !functions_; }
238 
239   PPB_Instance_API* functions() const { return functions_; }
240 
241  private:
242   PPB_Instance_API* functions_;
243 };
244 
245 class PPAPI_THUNK_EXPORT EnterInstanceNoLock
246     : public subtle::LockOnEntry<false>,  // Must be first; see above.
247       public subtle::EnterBase {
248  public:
249   explicit EnterInstanceNoLock(PP_Instance instance);
250   EnterInstanceNoLock(PP_Instance instance,
251                       const PP_CompletionCallback& callback);
252   ~EnterInstanceNoLock();
253 
254   PPB_Instance_API* functions() { return functions_; }
255 
256  private:
257   PPB_Instance_API* functions_;
258 };
259 
260 // EnterInstanceAPI ------------------------------------------------------------
261 
262 template<typename ApiT, bool lock_on_entry = true>
263 class EnterInstanceAPI
264     : public subtle::LockOnEntry<lock_on_entry>,  // Must be first; see above
265       public subtle::EnterBase {
266  public:
267   explicit EnterInstanceAPI(PP_Instance instance)
268       : EnterBase(instance, ApiT::kSingletonResourceID) {
269     if (resource_)
270       functions_ = resource_->GetAs<ApiT>();
271     SetStateForFunctionError(instance, functions_, true);
272   }
273   EnterInstanceAPI(PP_Instance instance, const PP_CompletionCallback& callback)
274       : EnterBase(instance, ApiT::kSingletonResourceID, callback) {
275     if (resource_)
276       functions_ = resource_->GetAs<ApiT>();
277     SetStateForFunctionError(instance, functions_, true);
278   }
279   ~EnterInstanceAPI() {}
280 
281   bool succeeded() const { return !!functions_; }
282   bool failed() const { return !functions_; }
283 
284   ApiT* functions() const { return functions_; }
285 
286  private:
287   ApiT* functions_ = nullptr;
288 };
289 
290 template<typename ApiT>
291 class EnterInstanceAPINoLock : public EnterInstanceAPI<ApiT, false> {
292  public:
293   explicit EnterInstanceAPINoLock(PP_Instance instance)
294       : EnterInstanceAPI<ApiT, false>(instance) {
295   }
296 };
297 
298 // EnterResourceCreation -------------------------------------------------------
299 
300 class PPAPI_THUNK_EXPORT EnterResourceCreation
301     : public subtle::LockOnEntry<true>,  // Must be first; see above.
302       public subtle::EnterBase {
303  public:
304   explicit EnterResourceCreation(PP_Instance instance);
305   ~EnterResourceCreation();
306 
307   ResourceCreationAPI* functions() { return functions_; }
308 
309  private:
310   ResourceCreationAPI* functions_;
311 };
312 
313 class PPAPI_THUNK_EXPORT EnterResourceCreationNoLock
314     : public subtle::LockOnEntry<false>,  // Must be first; see above.
315       public subtle::EnterBase {
316  public:
317   explicit EnterResourceCreationNoLock(PP_Instance instance);
318   ~EnterResourceCreationNoLock();
319 
320   ResourceCreationAPI* functions() { return functions_; }
321 
322  private:
323   ResourceCreationAPI* functions_;
324 };
325 
326 }  // namespace thunk
327 }  // namespace ppapi
328 
329 #endif  // PPAPI_THUNK_ENTER_H_
330