nacl_ppapi_util.h revision 5821806d5e7f356e8fa4b058a389a808ea183019
1/* -*- c++ -*- */ 2/* 3 * Copyright (c) 2011 The Chromium Authors. All rights reserved. 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8#ifndef NATIVE_CLIENT_SRC_UNTRUSTED_NACL_PPAPI_UTIL_NACL_PPAPI_UTIL_H_ 9#define NATIVE_CLIENT_SRC_UNTRUSTED_NACL_PPAPI_UTIL_NACL_PPAPI_UTIL_H_ 10 11#include "ppapi/cpp/instance.h" 12#include "ppapi/cpp/module.h" 13#include "ppapi/cpp/var.h" 14 15#include "native_client/src/include/nacl_base.h" 16#include "native_client/src/include/nacl_scoped_ptr.h" 17#include "native_client/src/shared/platform/nacl_sync.h" 18#include "native_client/src/shared/platform/nacl_sync_checked.h" 19#include "native_client/src/shared/platform/nacl_sync_raii.h" 20 21// TODO(bsy): move weak_ref module to the shared directory 22#include "native_client/src/trusted/weak_ref/weak_ref.h" 23#include "native_client/src/trusted/weak_ref/call_on_main_thread.h" 24 25// The nomenclature used in this file is intended to clarify thinking 26// about the Pepper "main thread". The "main thread" is really an 27// interrupt service thread, or an event handler thread, since it is 28// bad to do blocking operations or execute code that runs for a long 29// time on it. Event handlers should complete quickly -- possibly 30// just enqueuing the event for processing by some worker thread -- 31// and return, so that additional event dispatch can occur. 32 33// Code that does real work (and tests) should run in a separate, 34// worker thread. The main event handler thread is where the 35// post-message handler runs via a pp::Module virtual member function 36// (HandleMessage), which represents the plugin instance. This plugin 37// instance can go away at any time, e.g., due to surf-away. Thus, 38// other threads should not use pointers to the pp::Module, since the 39// object isn't reference counted (and probably shouldn't be, since 40// it really have to shut down / clean up when Pepper tells it to), 41// and main thread-only operations such as PostMessage or 42// GetOSFileDescriptor on the FileIO_Dev PP_Resource must not be done 43// from the worker threads. 44 45// Our solution to this is as follows: 46// 47// The plugin instance object holds a reference to a WeakRefAnchor 48// object, and the plugin instance object's dtor invokes the Abandon 49// method on the anchor. Since the nacl::WeakRefAnchor object is 50// thread-safe and is reference counted, the anchor pointer may be 51// passed to worker threads. Worker threads are responsible for 52// maintaining the anchor refcount: each thread would hold a 53// reference, and must Unref prior to thread exit. The worker threads 54// can use plugin::WeakRefCallOnMainThread to enqueue continuation 55// callbacks to run on the main thread -- these will get cancelled if 56// the WeakRefAnchor was abandoned, which only occurs in the module 57// object's dtor, which can only run in the main thread. Since the 58// continuation won't run if the plugin instance is still valid, the 59// continuation can safely use pointers to the instance to perform 60// main-thread operations or to run member functions in the test 61// object or in the module object. The worker thread may hold a 62// pointer to the plugin instance object in order to schedule 63// callbacks via plugin::WeakRefCallOnMainThread using a method 64// pointer, but should not otherwise use the pointer. 65// 66// So, an operation (test) running on a worker thread must be broken 67// into separate computation phases according to which thread is 68// appropriate for invoking which operations. For compute-only phases 69// or manifest RPCs (which block and also invoke CallOnMainThread), 70// the computation should occur on the worker thread. When the worker 71// thread needs to invoke a main-thread-only operation such as 72// PostMessage, it should use its WeakRefAnchor objecct to schedule a 73// main thread callback and then wait on a condition variable for the 74// operation to complete. The main thread callback can invoke the 75// main-thread-only operation, then signal the condition variable to 76// wake up the worker thread prior to returning. After the worker 77// thread wakes up, it can use other synchronization methods to 78// determine if the worker thread should continue to run or exit 79// (e.g., if the worker thread is associated with the plugin instance, 80// then if the main thread work result is NULL, the worker thread 81// should probably Unref its anchor (and do other cleanup) and exit. 82 83namespace nacl_ppapi { 84 85template <typename R> class EventThreadWorkStateWrapper; // fwd 86 87// the worker thread should own the EventThreadWorkState<R> object 88template <typename R> 89class EventThreadWorkState { 90 public: 91 EventThreadWorkState() 92 : done_(false), 93 result_(NULL) { 94 NaClXMutexCtor(&mu_); 95 NaClXCondVarCtor(&cv_); 96 } 97 98 virtual ~EventThreadWorkState() { 99 NaClMutexDtor(&mu_); 100 NaClCondVarDtor(&cv_); 101 } 102 103 // Pass ownership of result into the EventThreadWorkState. The value 104 // of result should be non-NULL to distinguish between 105 // completion/abandonment. 106 void SetResult(R *result) { 107 nacl::MutexLocker take(&mu_); 108 result_.reset(result); 109 } 110 111 // Returns result if callback completed, NULL if abandoned 112 R *WaitForCompletion() { 113 nacl::MutexLocker take(&mu_); 114 while (!done_) { 115 NaClXCondVarWait(&cv_, &mu_); 116 } 117 return result_.release(); 118 } 119 120 private: 121 friend class EventThreadWorkStateWrapper<R>; 122 void EventThreadWorkDone() { 123 nacl::MutexLocker take(&mu_); 124 done_ = true; 125 NaClXCondVarBroadcast(&cv_); 126 } 127 128 NaClMutex mu_; 129 NaClCondVar cv_; 130 bool done_; 131 nacl::scoped_ptr<R> result_; 132 133 DISALLOW_COPY_AND_ASSIGN(EventThreadWorkState); 134}; 135 136 137// Wrapper around EventThreadWorkState<R> or subclass thereof. The 138// wrapper object should be created by a worker thread and the 139// ownership passed into the COMT machinery, to be used and deleted on 140// the main thread. This object is automatically deleted by the 141// WeakRef machinery when the callback fires, and the dtor will just 142// signal completion. If the anchor corresponding to the callback had 143// not been abandoned, then the callback function should invoke 144// SetResult before returning to pass ownership of a result object (R) 145// from the main thread to the worker thread. 146// 147// Subclasses of EventThreadWorkStateWrapper may be used, so that 148// contained input arguments are automatically deleted when the 149// callback fires, or input arguments may be stashed in subclasses of 150// EventThreadWorkState<R>. 151template <typename R> 152class EventThreadWorkStateWrapper { 153 public: 154 explicit EventThreadWorkStateWrapper(EventThreadWorkState<R> *ws): 155 ws_(ws) {} 156 virtual ~EventThreadWorkStateWrapper() { 157 ws_->EventThreadWorkDone(); 158 }; 159 160 void SetResult(R *result) { 161 ws_->SetResult(result); 162 } 163 private: 164 EventThreadWorkState<R> *ws_; 165 166 DISALLOW_COPY_AND_ASSIGN(EventThreadWorkStateWrapper); 167}; 168 169class VoidResult; 170 171extern VoidResult *const g_void_result; 172 173class VoidResult { 174 public: 175 VoidResult() {} 176 void *operator new(size_t size) { return g_void_result; } 177 void operator delete(void *p) {} 178 private: 179 DISALLOW_COPY_AND_ASSIGN(VoidResult); 180}; 181 182// Canonical pointer return value used with SetResult when the main 183// thread operation does not return a result. The class declaration 184// is private, so the compiler should refuse to allow the use of the 185// delete operator. 186 187// A plugin instance object should be referred to only from the main 188// thread. Pointers to the anchor object can be given to worker 189// thread so they can schedule work on the main thread via COMT. 190class NaClPpapiPluginInstance : public pp::Instance { 191 public: 192 explicit NaClPpapiPluginInstance(PP_Instance instance); 193 virtual ~NaClPpapiPluginInstance(); 194 nacl::WeakRefAnchor* anchor() const { return anchor_; } 195 protected: 196 nacl::WeakRefAnchor* anchor_; 197 DISALLOW_COPY_AND_ASSIGN(NaClPpapiPluginInstance); 198}; 199 200} // namespace nacl_ppapi 201 202#endif 203