/* -*- c++ -*- */
/*
* Copyright (c) 2011 The Chromium Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef NATIVE_CLIENT_SRC_UNTRUSTED_NACL_PPAPI_UTIL_NACL_PPAPI_UTIL_H_
#define NATIVE_CLIENT_SRC_UNTRUSTED_NACL_PPAPI_UTIL_NACL_PPAPI_UTIL_H_
#include "ppapi/cpp/instance.h"
#include "ppapi/cpp/module.h"
#include "ppapi/cpp/var.h"
#include "native_client/src/include/nacl_base.h"
#include "native_client/src/include/nacl_scoped_ptr.h"
#include "native_client/src/shared/platform/nacl_sync.h"
#include "native_client/src/shared/platform/nacl_sync_checked.h"
#include "native_client/src/shared/platform/nacl_sync_raii.h"
// TODO(bsy): move weak_ref module to the shared directory
#include "native_client/src/trusted/weak_ref/weak_ref.h"
#include "ppapi/native_client/src/trusted/weak_ref/call_on_main_thread.h"
// The nomenclature used in this file is intended to clarify thinking
// about the Pepper "main thread". The "main thread" is really an
// interrupt service thread, or an event handler thread, since it is
// bad to do blocking operations or execute code that runs for a long
// time on it. Event handlers should complete quickly -- possibly
// just enqueuing the event for processing by some worker thread --
// and return, so that additional event dispatch can occur.
// Code that does real work (and tests) should run in a separate,
// worker thread. The main event handler thread is where the
// post-message handler runs via a pp::Module virtual member function
// (HandleMessage), which represents the plugin instance. This plugin
// instance can go away at any time, e.g., due to surf-away. Thus,
// other threads should not use pointers to the pp::Module, since the
// object isn't reference counted (and probably shouldn't be, since
// it really have to shut down / clean up when Pepper tells it to),
// and main thread-only operations such as PostMessage or
// GetOSFileDescriptor on the FileIO_Dev PP_Resource must not be done
// from the worker threads.
// Our solution to this is as follows:
//
// The plugin instance object holds a reference to a WeakRefAnchor
// object, and the plugin instance object's dtor invokes the Abandon
// method on the anchor. Since the nacl::WeakRefAnchor object is
// thread-safe and is reference counted, the anchor pointer may be
// passed to worker threads. Worker threads are responsible for
// maintaining the anchor refcount: each thread would hold a
// reference, and must Unref prior to thread exit. The worker threads
// can use plugin::WeakRefCallOnMainThread to enqueue continuation
// callbacks to run on the main thread -- these will get cancelled if
// the WeakRefAnchor was abandoned, which only occurs in the module
// object's dtor, which can only run in the main thread. Since the
// continuation won't run if the plugin instance is still valid, the
// continuation can safely use pointers to the instance to perform
// main-thread operations or to run member functions in the test
// object or in the module object. The worker thread may hold a
// pointer to the plugin instance object in order to schedule
// callbacks via plugin::WeakRefCallOnMainThread using a method
// pointer, but should not otherwise use the pointer.
//
// So, an operation (test) running on a worker thread must be broken
// into separate computation phases according to which thread is
// appropriate for invoking which operations. For compute-only phases
// or manifest RPCs (which block and also invoke CallOnMainThread),
// the computation should occur on the worker thread. When the worker
// thread needs to invoke a main-thread-only operation such as
// PostMessage, it should use its WeakRefAnchor objecct to schedule a
// main thread callback and then wait on a condition variable for the
// operation to complete. The main thread callback can invoke the
// main-thread-only operation, then signal the condition variable to
// wake up the worker thread prior to returning. After the worker
// thread wakes up, it can use other synchronization methods to
// determine if the worker thread should continue to run or exit
// (e.g., if the worker thread is associated with the plugin instance,
// then if the main thread work result is NULL, the worker thread
// should probably Unref its anchor (and do other cleanup) and exit.
namespace nacl_ppapi {
template <typename R> class EventThreadWorkStateWrapper; // fwd
// the worker thread should own the EventThreadWorkState<R> object
template <typename R>
class EventThreadWorkState {
public:
EventThreadWorkState()
: done_(false),
result_(NULL) {
NaClXMutexCtor(&mu_);
NaClXCondVarCtor(&cv_);
}
virtual ~EventThreadWorkState() {
NaClMutexDtor(&mu_);
NaClCondVarDtor(&cv_);
}
// Pass ownership of result into the EventThreadWorkState. The value
// of result should be non-NULL to distinguish between
// completion/abandonment.
void SetResult(R *result) {
nacl::MutexLocker take(&mu_);
result_.reset(result);
}
// Returns result if callback completed, NULL if abandoned
R *WaitForCompletion() {
nacl::MutexLocker take(&mu_);
while (!done_) {
NaClXCondVarWait(&cv_, &mu_);
}
return result_.release();
}
private:
friend class EventThreadWorkStateWrapper<R>;
void EventThreadWorkDone() {
nacl::MutexLocker take(&mu_);
done_ = true;
NaClXCondVarBroadcast(&cv_);
}
NaClMutex mu_;
NaClCondVar cv_;
bool done_;
nacl::scoped_ptr<R> result_;
DISALLOW_COPY_AND_ASSIGN(EventThreadWorkState);
};
// Wrapper around EventThreadWorkState<R> or subclass thereof. The
// wrapper object should be created by a worker thread and the
// ownership passed into the COMT machinery, to be used and deleted on
// the main thread. This object is automatically deleted by the
// WeakRef machinery when the callback fires, and the dtor will just
// signal completion. If the anchor corresponding to the callback had
// not been abandoned, then the callback function should invoke
// SetResult before returning to pass ownership of a result object (R)
// from the main thread to the worker thread.
//
// Subclasses of EventThreadWorkStateWrapper may be used, so that
// contained input arguments are automatically deleted when the
// callback fires, or input arguments may be stashed in subclasses of
// EventThreadWorkState<R>.
template <typename R>
class EventThreadWorkStateWrapper {
public:
explicit EventThreadWorkStateWrapper(EventThreadWorkState<R> *ws):
ws_(ws) {}
virtual ~EventThreadWorkStateWrapper() {
ws_->EventThreadWorkDone();
};
void SetResult(R *result) {
ws_->SetResult(result);
}
private:
EventThreadWorkState<R> *ws_;
DISALLOW_COPY_AND_ASSIGN(EventThreadWorkStateWrapper);
};
class VoidResult;
extern VoidResult *const g_void_result;
class VoidResult {
public:
VoidResult() {}
void *operator new(size_t size) { return g_void_result; }
void operator delete(void *p) {}
private:
DISALLOW_COPY_AND_ASSIGN(VoidResult);
};
// Canonical pointer return value used with SetResult when the main
// thread operation does not return a result. The class declaration
// is private, so the compiler should refuse to allow the use of the
// delete operator.
// A plugin instance object should be referred to only from the main
// thread. Pointers to the anchor object can be given to worker
// thread so they can schedule work on the main thread via COMT.
class NaClPpapiPluginInstance : public pp::Instance {
public:
explicit NaClPpapiPluginInstance(PP_Instance instance);
virtual ~NaClPpapiPluginInstance();
nacl::WeakRefAnchor* anchor() const { return anchor_; }
protected:
nacl::WeakRefAnchor* anchor_;
DISALLOW_COPY_AND_ASSIGN(NaClPpapiPluginInstance);
};
} // namespace nacl_ppapi
#endif