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