2 * Copyright 2014 The Chromium Authors. All rights reserved.
3 * Use of this source code is governed by a BSD-style license that can be
4 * found in the LICENSE file.
8 // Post-message based test for simple rpc based access to name services.
17 #include <sys/fcntl.h>
22 #include "native_client/src/include/nacl_base.h"
23 #include "native_client/src/public/imc_syscalls.h"
24 #include "native_client/src/public/name_service.h"
25 #include "native_client/src/shared/platform/nacl_sync.h"
26 #include "native_client/src/shared/platform/nacl_sync_checked.h"
27 #include "native_client/src/shared/platform/nacl_sync_raii.h"
28 #include "native_client/src/shared/srpc/nacl_srpc.h"
30 // TODO(bsy): move weak_ref module to the shared directory
31 #include "native_client/src/trusted/weak_ref/weak_ref.h"
33 #include "ppapi/cpp/instance.h"
34 #include "ppapi/cpp/module.h"
35 #include "ppapi/cpp/var.h"
37 #include "ppapi/native_client/src/trusted/weak_ref/call_on_main_thread.h"
38 #include "ppapi/native_client/src/untrusted/nacl_ppapi_util/nacl_ppapi_util.h"
39 #include "ppapi/native_client/src/untrusted/nacl_ppapi_util/string_buffer.h"
41 class PostStringMessageWrapper
42 : public nacl_ppapi::EventThreadWorkStateWrapper<nacl_ppapi::VoidResult> {
44 PostStringMessageWrapper(nacl_ppapi::EventThreadWorkState<
45 nacl_ppapi::VoidResult>
47 const std::string &msg)
48 : nacl_ppapi::EventThreadWorkStateWrapper<nacl_ppapi::VoidResult>(
51 ~PostStringMessageWrapper();
52 const std::string &msg() const { return msg_; }
56 DISALLOW_COPY_AND_ASSIGN(PostStringMessageWrapper);
59 // ---------------------------------------------------------------------------
64 explicit WorkRequest(const std::string &message)
66 next(reinterpret_cast<WorkRequest *>(NULL)) {}
67 std::string msg; // copied from HandleMessage
70 DISALLOW_COPY_AND_ASSIGN(WorkRequest);
73 // A Worker object is associated with a single worker thread and a
74 // plugin instance (which may be associated with multiple Worker
75 // objects/threads). It is created by a plugin instance (on the event
76 // handler thread) with a refcount of 2, with the expectation that one
77 // reference will be immediately handed off to its associated worker
78 // thread. When the plugin instance is about to be destroyed in the
79 // event handler thread, the event handler should invoke the
80 // ShouldExit member function, which automatically decrements the
81 // reference associated with the event handler thread (i.e., the event
82 // handler thread should no longer use the Worker*).
85 explicit Worker(MyInstance *instance);
87 // RunToCompletion should be invoked in the worker thread. It
88 // returns when the plugin instance went away, and will
89 // automatically unref the Worker object, so the worker thread
90 // should no longer use the Worker object pointer after invoking
92 void RunToCompletion();
94 WorkRequest *Dequeue();
95 void Enqueue(WorkRequest *req);
97 void Initialize(nacl::StringBuffer *sb);
98 void ManifestListTest(nacl::StringBuffer *sb);
99 void ManifestOpenTest(nacl::StringBuffer *sb);
101 // Called on the event thread as part of the instance shutdown.
102 // Automatically unreferences the Worker object, so the event thread
103 // should stop using the Worker pointer after invoking ShouldExit.
106 void Unref(); // used only for error cleanup, e.g., when the worker
107 // thread did not launch.
109 bool InitializeChannel(nacl::StringBuffer *sb);
112 // Event thread operation(s):
114 // In order for a test method to send reply messages, it should use
115 // this PostStringMessage method, since (currently) the PostMessage
116 // interface is event thread-only and not thread-safe. Returns true
117 // if successful and the thread should continue to do work, false
118 // otherwise (anchor has been abandoned).
119 bool PostStringMessage(const std::string &msg);
120 // ... more Event thread operations here.
123 MyInstance *instance_; // cannot use directly from test worker thread!
124 nacl::WeakRefAnchor *anchor_;
125 // must copy out and Ref in ctor, since instance_ might go bad at any time.
128 NaClCondVar cv_; // queue not empty or should_exit_
131 WorkRequest *queue_head_;
132 WorkRequest **queue_insert_;
138 WorkRequest *Dequeue_mu();
139 void Enqueue_mu(WorkRequest *req);
141 struct DispatchTable {
143 void (Worker::*mfunc)(nacl::StringBuffer *sb);
146 bool ns_channel_initialized_;
147 NaClSrpcChannel ns_channel_;
149 static DispatchTable const kDispatch[]; // null terminated
151 DISALLOW_COPY_AND_ASSIGN(Worker);
154 // This object represents one time the page says <embed>.
155 class MyInstance : public nacl_ppapi::NaClPpapiPluginInstance {
157 explicit MyInstance(PP_Instance instance);
158 virtual ~MyInstance();
159 virtual void HandleMessage(const pp::Var& message_data);
161 Worker *worker() { return worker_; }
163 // used with plugin::WeakRefCompletionCallback
164 void PostStringMessage_EventThread(PostStringMessageWrapper *msg_wrapper,
169 DISALLOW_COPY_AND_ASSIGN(MyInstance);
172 // ---------------------------------------------------------------------------
174 PostStringMessageWrapper::~PostStringMessageWrapper() {}
176 // ---------------------------------------------------------------------------
178 MyInstance::MyInstance(PP_Instance instance)
179 : nacl_ppapi::NaClPpapiPluginInstance(instance),
180 worker_(new Worker(this)) {
183 MyInstance::~MyInstance() {
184 worker_->ShouldExit();
187 void MyInstance::PostStringMessage_EventThread(
188 PostStringMessageWrapper *msg_wrapper,
190 PostMessage(msg_wrapper->msg());
191 msg_wrapper->SetResult(nacl_ppapi::g_void_result);
194 // ---------------------------------------------------------------------------
196 Worker::Worker(MyInstance *instance)
197 : instance_(instance),
198 anchor_(instance->anchor()->Ref()),
199 ref_count_(2), // one for the master and one for the dame...
201 queue_insert_(&queue_head_),
203 ns_channel_initialized_(false) {
204 NaClXMutexCtor(&mu_);
205 NaClXCondVarCtor(&cv_);
208 void Worker::Unref() {
211 nacl::MutexLocker take(&mu_);
212 do_delete = (--ref_count_ == 0);
214 // dropped lock before invoking dtor
224 while ((req = Dequeue_mu()) != NULL) {
229 NaClCondVarDtor(&cv_);
232 void Worker::ShouldExit() {
234 nacl::MutexLocker take(&mu_);
236 NaClXCondVarBroadcast(&cv_);
241 WorkRequest *Worker::Dequeue_mu() {
242 WorkRequest *head = queue_head_;
245 queue_head_ = head->next;
246 if (queue_head_ == NULL) {
247 queue_insert_ = &queue_head_;
253 void Worker::Enqueue_mu(WorkRequest *req) {
255 *queue_insert_ = req;
256 queue_insert_ = &req->next;
259 WorkRequest *Worker::Dequeue() {
260 nacl::MutexLocker take(&mu_);
264 void Worker::Enqueue(WorkRequest *req) {
265 nacl::MutexLocker take(&mu_);
267 NaClXCondVarBroadcast(&cv_);
270 Worker::DispatchTable const Worker::kDispatch[] = {
271 { "init", &Worker::Initialize },
272 { "manifest_open", &Worker::ManifestOpenTest },
273 { reinterpret_cast<char const *>(NULL), NULL }
276 bool Worker::PostStringMessage(const std::string &msg) {
277 nacl_ppapi::EventThreadWorkState<nacl_ppapi::VoidResult> state;
278 plugin::WeakRefCallOnMainThread(anchor_,
281 &MyInstance::PostStringMessage_EventThread,
282 new PostStringMessageWrapper(&state, msg));
283 if (NULL == state.WaitForCompletion()) {
284 // anchor_ has been abandoned, so the plugin instance went away.
285 // we should drop our ref to the anchor, then shut down the worker
287 nacl::MutexLocker take(&mu_);
289 // There's no need to condvar broadcast, since it is the worker
290 // thread that will look at the work queue and the should_exit_ to
291 // act on this. Unfortunately every worker thread must test the
292 // return value of PostStringMessage to determine if it should do
293 // early exit (if the worker needs to do multiple event-thread
300 void Worker::RunToCompletion() {
304 nacl::MutexLocker take(&mu_);
307 // drop the lock and drop the reference count to this
310 fprintf(stderr, "RunToCompletion: Dequeuing...\n");
311 if ((req = Dequeue_mu()) != NULL) {
312 fprintf(stderr, "RunToCompletion: found work %p\n",
313 reinterpret_cast<void *>(req));
316 fprintf(stderr, "RunToCompletion: waiting\n");
317 NaClXCondVarWait(&cv_, &mu_);
318 fprintf(stderr, "RunToCompletion: woke up\n");
322 // Do the work, without holding the lock. The work function
323 // should reacquire mu_ as needed.
325 nacl::StringBuffer sb;
327 // scan dispatch table for op_name
328 fprintf(stderr, "RunToCompletion: scanning for %s\n", req->msg.c_str());
329 for (size_t ix = 0; kDispatch[ix].op_name != NULL; ++ix) {
331 "RunToCompletion: comparing against %s\n", kDispatch[ix].op_name);
332 if (req->msg == kDispatch[ix].op_name) {
333 if (InitializeChannel(&sb)) {
334 fprintf(stderr, "RunToCompletion: invoking table entry %u\n", ix);
335 (this->*(kDispatch[ix].mfunc))(&sb);
340 // always post a reply, even if it is the empty string
342 "RunToCompletion: posting reply %s\n", sb.ToString().c_str());
343 if (!PostStringMessage(sb.ToString())) {
348 fprintf(stderr, "RunToCompletion: exiting\n");
352 bool Worker::InitializeChannel(nacl::StringBuffer *sb) {
353 if (ns_channel_initialized_) {
357 nacl_nameservice(&ns);
358 printf("ns = %d\n", ns);
360 int connected_socket = imc_connect(ns);
361 assert(-1 != connected_socket);
362 if (!NaClSrpcClientCtor(&ns_channel_, connected_socket)) {
363 sb->Printf("Srpc client channel ctor failed\n");
367 sb->Printf("NaClSrpcClientCtor succeeded\n");
369 ns_channel_initialized_ = true;
373 void Worker::Initialize(nacl::StringBuffer *sb) {
374 // we just want the log output from the InitializeChannel
378 void Worker::ManifestOpenTest(nacl::StringBuffer *sb) {
381 struct NaClSrpcChannel manifest_channel;
383 // name service lookup for the manifest service descriptor
384 if (NACL_SRPC_RESULT_OK !=
385 NaClSrpcInvokeBySignature(&ns_channel_, NACL_NAME_SERVICE_LOOKUP,
386 "ManifestNameService", O_RDWR,
387 &status, &manifest) ||
388 NACL_NAME_SERVICE_SUCCESS != status) {
389 sb->Printf("nameservice lookup failed, status %d\n", status);
392 sb->Printf("Got manifest descriptor %d\n", manifest);
393 if (-1 == manifest) {
397 // connect to manifest name server
398 int manifest_conn = imc_connect(manifest);
400 sb->Printf("got manifest connection %d\n", manifest_conn);
401 if (-1 == manifest_conn) {
402 sb->Printf("could not connect\n");
406 // build the SRPC connection (do service discovery)
407 if (!NaClSrpcClientCtor(&manifest_channel, manifest_conn)) {
408 sb->Printf("could not build srpc client\n");
414 sb->Printf("Invoking name service lookup\n");
415 if (NACL_SRPC_RESULT_OK !=
416 NaClSrpcInvokeBySignature(&manifest_channel,
417 NACL_NAME_SERVICE_LOOKUP,
418 "files/test_file", O_RDONLY,
420 sb->Printf("manifest lookup RPC failed\n");
421 NaClSrpcDtor(&manifest_channel);
426 sb->Printf("File Contents:\n");
430 while ((len = read(desc, buffer, sizeof buffer - 1)) > 0) {
433 sb->Printf("%s", buffer);
435 NaClSrpcDtor(&manifest_channel);
439 // HandleMessage gets invoked when postMessage is called on the DOM
440 // element associated with this plugin instance. In this case, if we
441 // are given a string, we'll post a message back to JavaScript with a
442 // reply -- essentially treating this as a string-based RPC.
443 void MyInstance::HandleMessage(const pp::Var& message) {
444 if (message.is_string()) {
446 "HandleMessage: enqueuing %s\n", message.AsString().c_str());
448 worker_->Enqueue(new WorkRequest(message.AsString()));
450 fprintf(stderr, "HandleMessage: message is not a string\n");
455 void *worker_thread_start(void *arg) {
456 Worker *worker = reinterpret_cast<Worker *>(arg);
458 fprintf(stderr, "Sleeping...\n"); fflush(stderr);
460 fprintf(stderr, "worker_thread_start: worker %p\n",
461 reinterpret_cast<void *>(worker));
463 worker->RunToCompletion();
464 worker = NULL; // RunToCompletion automatically Unrefs
465 return reinterpret_cast<void *>(NULL);
468 // This object is the global object representing this plugin library as long
470 class MyModule : public pp::Module {
472 MyModule() : pp::Module() {}
473 virtual ~MyModule() {}
475 // Override CreateInstance to create your customized Instance object.
476 virtual pp::Instance *CreateInstance(PP_Instance instance);
478 DISALLOW_COPY_AND_ASSIGN(MyModule);
481 pp::Instance *MyModule::CreateInstance(PP_Instance pp_instance) {
482 MyInstance *instance = new MyInstance(pp_instance);
483 // spawn worker thread associated with this instance
486 fprintf(stderr, "CreateInstance invoked\n"); fflush(NULL);
487 if (0 != pthread_create(&thread,
488 reinterpret_cast<pthread_attr_t *>(NULL),
490 reinterpret_cast<void *>(instance->worker()))) {
491 // Remove the reference the ownership of which should have been
492 // passed to the worker thread.
493 instance->worker()->Unref();
496 fprintf(stderr, "pthread_create failed\n"); fflush(NULL);
498 fprintf(stderr, "CreateInstance: Worker thread started\n");
499 fprintf(stderr, "CreateInstance: worker thread object %p\n",
500 reinterpret_cast<void *>(instance->worker()));
501 (void) pthread_detach(thread);
503 fprintf(stderr, "CreateInstance: returning instance %p\n",
504 reinterpret_cast<void *>(instance));
511 // Factory function for your specialization of the Module object.
512 Module* CreateModule() {
513 fprintf(stderr, "CreateModule invoked\n"); fflush(NULL);
514 return new MyModule();