Home | History | Annotate | Download | only in nacl_ppapi_util
      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 "ppapi/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 
     83 namespace nacl_ppapi {
     84 
     85 template <typename R> class EventThreadWorkStateWrapper;  // fwd
     86 
     87 // the worker thread should own the EventThreadWorkState<R> object
     88 template <typename R>
     89 class 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>.
    151 template <typename R>
    152 class 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 
    169 class VoidResult;
    170 
    171 extern VoidResult *const g_void_result;
    172 
    173 class 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.
    190 class 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