Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / test / data / nacl / manifest_file / pm_manifest_file_test.cc
blob6198af0d73bd17af2d931270804b0532a9631a8a
1 /*
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.
5 */
7 //
8 // Post-message based test for simple rpc based access to name services.
9 //
11 #include <string>
13 #include <assert.h>
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <inttypes.h>
17 #include <sys/fcntl.h>
18 #include <string.h>
19 #include <unistd.h>
20 #include <pthread.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> {
43 public:
44 PostStringMessageWrapper(nacl_ppapi::EventThreadWorkState<
45 nacl_ppapi::VoidResult>
46 *state,
47 const std::string &msg)
48 : nacl_ppapi::EventThreadWorkStateWrapper<nacl_ppapi::VoidResult>(
49 state),
50 msg_(msg) {}
51 ~PostStringMessageWrapper();
52 const std::string &msg() const { return msg_; }
53 private:
54 std::string msg_;
56 DISALLOW_COPY_AND_ASSIGN(PostStringMessageWrapper);
59 // ---------------------------------------------------------------------------
61 class MyInstance;
63 struct WorkRequest {
64 explicit WorkRequest(const std::string &message)
65 : msg(message),
66 next(reinterpret_cast<WorkRequest *>(NULL)) {}
67 std::string msg; // copied from HandleMessage
68 WorkRequest *next;
69 private:
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*).
83 class Worker {
84 public:
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
91 // RunToCompletion().
92 void RunToCompletion();
94 WorkRequest *Dequeue();
95 void Enqueue(WorkRequest *req);
97 void Initialize(nacl::StringBuffer *sb);
98 void NameServiceDump(nacl::StringBuffer *sb);
99 void ManifestListTest(nacl::StringBuffer *sb);
100 void ManifestOpenTest(nacl::StringBuffer *sb);
102 // Called on the event thread as part of the instance shutdown.
103 // Automatically unreferences the Worker object, so the event thread
104 // should stop using the Worker pointer after invoking ShouldExit.
105 void ShouldExit();
107 void Unref(); // used only for error cleanup, e.g., when the worker
108 // thread did not launch.
110 bool InitializeChannel(nacl::StringBuffer *sb);
112 protected:
113 // Event thread operation(s):
115 // In order for a test method to send reply messages, it should use
116 // this PostStringMessage method, since (currently) the PostMessage
117 // interface is event thread-only and not thread-safe. Returns true
118 // if successful and the thread should continue to do work, false
119 // otherwise (anchor has been abandoned).
120 bool PostStringMessage(const std::string &msg);
121 // ... more Event thread operations here.
123 private:
124 MyInstance *instance_; // cannot use directly from test worker thread!
125 nacl::WeakRefAnchor *anchor_;
126 // must copy out and Ref in ctor, since instance_ might go bad at any time.
128 NaClMutex mu_;
129 NaClCondVar cv_; // queue not empty or should_exit_
131 int ref_count_;
132 WorkRequest *queue_head_;
133 WorkRequest **queue_insert_;
135 bool should_exit_;
137 ~Worker();
139 WorkRequest *Dequeue_mu();
140 void Enqueue_mu(WorkRequest *req);
142 struct DispatchTable {
143 char const *op_name;
144 void (Worker::*mfunc)(nacl::StringBuffer *sb);
147 bool ns_channel_initialized_;
148 NaClSrpcChannel ns_channel_;
150 static DispatchTable const kDispatch[]; // null terminated
152 DISALLOW_COPY_AND_ASSIGN(Worker);
155 // This object represents one time the page says <embed>.
156 class MyInstance : public nacl_ppapi::NaClPpapiPluginInstance {
157 public:
158 explicit MyInstance(PP_Instance instance);
159 virtual ~MyInstance();
160 virtual void HandleMessage(const pp::Var& message_data);
162 Worker *worker() { return worker_; }
164 // used with plugin::WeakRefCompletionCallback
165 void PostStringMessage_EventThread(PostStringMessageWrapper *msg_wrapper,
166 int32_t err);
167 private:
168 Worker *worker_;
170 DISALLOW_COPY_AND_ASSIGN(MyInstance);
173 // ---------------------------------------------------------------------------
175 // success/fail
176 bool EnumerateNames(NaClSrpcChannel *nschan, nacl::StringBuffer *sb) {
177 char *buffer;
178 uint32_t nbytes = 4;
179 uint32_t in_out_nbytes;
180 char *new_buffer;
182 buffer = reinterpret_cast<char *>(malloc(nbytes));
183 if (NULL == buffer) {
184 sb->Printf("EnumerateNames: initial malloc failed\n");
185 return false;
188 for (;;) {
189 in_out_nbytes = nbytes;
190 if (NACL_SRPC_RESULT_OK != NaClSrpcInvokeBySignature(nschan,
191 NACL_NAME_SERVICE_LIST,
192 &in_out_nbytes,
193 buffer)) {
194 sb->Printf("NaClSrpcInvokeBySignature failed\n");
195 return false;
197 sb->Printf("EnumerateNames: in_out_nbytes %d\n", in_out_nbytes);
198 if (in_out_nbytes < nbytes) {
199 break;
201 nbytes *= 2;
202 new_buffer = reinterpret_cast<char *>(realloc(buffer, nbytes));
203 if (NULL == new_buffer) {
204 sb->Printf("EnumerateNames: out of memory during realloc\n");
205 free(buffer);
206 return false;
208 buffer = new_buffer;
209 new_buffer = NULL;
211 nbytes = in_out_nbytes;
212 sb->Printf("nbytes = %u\n", (size_t) nbytes);
213 if (nbytes == sizeof buffer) {
214 sb->Printf("Insufficent space for namespace enumeration\n");
215 return false;
217 size_t name_len;
218 for (char *p = buffer;
219 static_cast<size_t>(p - buffer) < nbytes;
220 p += name_len) {
221 name_len = strlen(p) + 1;
222 sb->Printf("%s\n", p);
224 free(buffer);
225 return true;
228 // ---------------------------------------------------------------------------
230 PostStringMessageWrapper::~PostStringMessageWrapper() {}
232 // ---------------------------------------------------------------------------
234 MyInstance::MyInstance(PP_Instance instance)
235 : nacl_ppapi::NaClPpapiPluginInstance(instance),
236 worker_(new Worker(this)) {
239 MyInstance::~MyInstance() {
240 worker_->ShouldExit();
243 void MyInstance::PostStringMessage_EventThread(
244 PostStringMessageWrapper *msg_wrapper,
245 int32_t err) {
246 PostMessage(msg_wrapper->msg());
247 msg_wrapper->SetResult(nacl_ppapi::g_void_result);
250 // ---------------------------------------------------------------------------
252 Worker::Worker(MyInstance *instance)
253 : instance_(instance),
254 anchor_(instance->anchor()->Ref()),
255 ref_count_(2), // one for the master and one for the dame...
256 queue_head_(NULL),
257 queue_insert_(&queue_head_),
258 should_exit_(false),
259 ns_channel_initialized_(false) {
260 NaClXMutexCtor(&mu_);
261 NaClXCondVarCtor(&cv_);
264 void Worker::Unref() {
265 bool do_delete;
266 do {
267 nacl::MutexLocker take(&mu_);
268 do_delete = (--ref_count_ == 0);
269 } while (0);
270 // dropped lock before invoking dtor
271 if (do_delete) {
272 delete this;
276 Worker::~Worker() {
277 anchor_->Unref();
279 WorkRequest *req;
280 while ((req = Dequeue_mu()) != NULL) {
281 delete req;
284 NaClMutexDtor(&mu_);
285 NaClCondVarDtor(&cv_);
288 void Worker::ShouldExit() {
289 do {
290 nacl::MutexLocker take(&mu_);
291 should_exit_ = true;
292 NaClXCondVarBroadcast(&cv_);
293 } while (0);
294 Unref();
297 WorkRequest *Worker::Dequeue_mu() {
298 WorkRequest *head = queue_head_;
300 if (head != NULL) {
301 queue_head_ = head->next;
302 if (queue_head_ == NULL) {
303 queue_insert_ = &queue_head_;
306 return head;
309 void Worker::Enqueue_mu(WorkRequest *req) {
310 req->next = NULL;
311 *queue_insert_ = req;
312 queue_insert_ = &req->next;
315 WorkRequest *Worker::Dequeue() {
316 nacl::MutexLocker take(&mu_);
317 return Dequeue_mu();
320 void Worker::Enqueue(WorkRequest *req) {
321 nacl::MutexLocker take(&mu_);
322 Enqueue_mu(req);
323 NaClXCondVarBroadcast(&cv_);
326 Worker::DispatchTable const Worker::kDispatch[] = {
327 { "init", &Worker::Initialize },
328 { "name_dump", &Worker::NameServiceDump },
329 { "manifest_list", &Worker::ManifestListTest },
330 { "manifest_open", &Worker::ManifestOpenTest },
331 { reinterpret_cast<char const *>(NULL), NULL }
334 bool Worker::PostStringMessage(const std::string &msg) {
335 nacl_ppapi::EventThreadWorkState<nacl_ppapi::VoidResult> state;
336 plugin::WeakRefCallOnMainThread(anchor_,
337 0 /* mS */,
338 instance_,
339 &MyInstance::PostStringMessage_EventThread,
340 new PostStringMessageWrapper(&state, msg));
341 if (NULL == state.WaitForCompletion()) {
342 // anchor_ has been abandoned, so the plugin instance went away.
343 // we should drop our ref to the anchor, then shut down the worker
344 // thread.
345 nacl::MutexLocker take(&mu_);
346 should_exit_ = true;
347 // There's no need to condvar broadcast, since it is the worker
348 // thread that will look at the work queue and the should_exit_ to
349 // act on this. Unfortunately every worker thread must test the
350 // return value of PostStringMessage to determine if it should do
351 // early exit (if the worker needs to do multiple event-thread
352 // operations).
353 return false;
355 return true;
358 void Worker::RunToCompletion() {
359 for (;;) {
360 WorkRequest *req;
361 do {
362 nacl::MutexLocker take(&mu_);
363 for (;;) {
364 if (should_exit_) {
365 // drop the lock and drop the reference count to this
366 goto break_x3;
368 fprintf(stderr, "RunToCompletion: Dequeuing...\n");
369 if ((req = Dequeue_mu()) != NULL) {
370 fprintf(stderr, "RunToCompletion: found work %p\n",
371 reinterpret_cast<void *>(req));
372 break;
374 fprintf(stderr, "RunToCompletion: waiting\n");
375 NaClXCondVarWait(&cv_, &mu_);
376 fprintf(stderr, "RunToCompletion: woke up\n");
378 } while (0);
380 // Do the work, without holding the lock. The work function
381 // should reacquire mu_ as needed.
383 nacl::StringBuffer sb;
385 // scan dispatch table for op_name
386 fprintf(stderr, "RunToCompletion: scanning for %s\n", req->msg.c_str());
387 for (size_t ix = 0; kDispatch[ix].op_name != NULL; ++ix) {
388 fprintf(stderr,
389 "RunToCompletion: comparing against %s\n", kDispatch[ix].op_name);
390 if (req->msg == kDispatch[ix].op_name) {
391 if (InitializeChannel(&sb)) {
392 fprintf(stderr, "RunToCompletion: invoking table entry %u\n", ix);
393 (this->*(kDispatch[ix].mfunc))(&sb);
395 break;
398 // always post a reply, even if it is the empty string
399 fprintf(stderr,
400 "RunToCompletion: posting reply %s\n", sb.ToString().c_str());
401 if (!PostStringMessage(sb.ToString())) {
402 break;
405 break_x3:
406 fprintf(stderr, "RunToCompletion: exiting\n");
407 Unref();
410 bool Worker::InitializeChannel(nacl::StringBuffer *sb) {
411 if (ns_channel_initialized_) {
412 return true;
414 int ns = -1;
415 nacl_nameservice(&ns);
416 printf("ns = %d\n", ns);
417 assert(-1 != ns);
418 int connected_socket = imc_connect(ns);
419 assert(-1 != connected_socket);
420 if (!NaClSrpcClientCtor(&ns_channel_, connected_socket)) {
421 sb->Printf("Srpc client channel ctor failed\n");
422 close(ns);
423 return false;
425 sb->Printf("NaClSrpcClientCtor succeeded\n");
426 close(ns);
427 ns_channel_initialized_ = true;
428 return true;
431 void Worker::Initialize(nacl::StringBuffer *sb) {
432 // we just want the log output from the InitializeChannel
433 return;
436 // return name service output in sb
437 void Worker::NameServiceDump(nacl::StringBuffer *sb) {
438 (void) EnumerateNames(&ns_channel_, sb);
441 void Worker::ManifestListTest(nacl::StringBuffer *sb) {
442 int status;
443 int manifest;
444 // name service lookup for the manifest service descriptor
445 if (NACL_SRPC_RESULT_OK !=
446 NaClSrpcInvokeBySignature(&ns_channel_, NACL_NAME_SERVICE_LOOKUP,
447 "ManifestNameService", O_RDWR,
448 &status, &manifest) ||
449 NACL_NAME_SERVICE_SUCCESS != status) {
450 sb->Printf("nameservice lookup failed, status %d\n", status);
452 sb->Printf("Got manifest descriptor %d\n", manifest);
453 if (-1 == manifest) {
454 return;
457 // connect to manifest name server
458 int manifest_conn = imc_connect(manifest);
459 close(manifest);
460 sb->Printf("got manifest connection %d\n", manifest_conn);
461 if (-1 == manifest_conn) {
462 sb->Printf("could not connect\n");
463 return;
466 // build the SRPC connection (do service discovery)
467 struct NaClSrpcChannel manifest_channel;
468 if (!NaClSrpcClientCtor(&manifest_channel, manifest_conn)) {
469 sb->Printf("could not build srpc client\n");
470 return;
472 sb->Printf("ManifestListTest: basic connectivity ok\n");
474 // list manifest service contents
475 char buffer[1024];
476 uint32_t nbytes = sizeof buffer;
478 if (NACL_SRPC_RESULT_OK !=
479 NaClSrpcInvokeBySignature(&manifest_channel, NACL_NAME_SERVICE_LIST,
480 &nbytes, buffer)) {
481 sb->Printf("manifest list RPC failed\n");
482 NaClSrpcDtor(&manifest_channel);
483 return;
486 sb->DiscardOutput();
487 sb->Printf("Manifest Contents:\n");
488 size_t name_len;
489 // Should we explicitly sort the names? This would ensure that the
490 // test output is easy to compare with expected results. Currently,
491 // the manifest uses a set to hold the names, so the results will be
492 // sorted anyway, but this is not a guarantee of the API.
493 for (char *p = buffer;
494 static_cast<size_t>(p - buffer) < nbytes;
495 p += name_len + 1) {
496 name_len = strlen(p);
497 sb->Printf("%.*s\n", (int) name_len, p);
499 NaClSrpcDtor(&manifest_channel);
500 return;
503 void Worker::ManifestOpenTest(nacl::StringBuffer *sb) {
504 int status = -1;
505 int manifest;
506 struct NaClSrpcChannel manifest_channel;
508 // name service lookup for the manifest service descriptor
509 if (NACL_SRPC_RESULT_OK !=
510 NaClSrpcInvokeBySignature(&ns_channel_, NACL_NAME_SERVICE_LOOKUP,
511 "ManifestNameService", O_RDWR,
512 &status, &manifest) ||
513 NACL_NAME_SERVICE_SUCCESS != status) {
514 sb->Printf("nameservice lookup failed, status %d\n", status);
515 return;
517 sb->Printf("Got manifest descriptor %d\n", manifest);
518 if (-1 == manifest) {
519 return;
522 // connect to manifest name server
523 int manifest_conn = imc_connect(manifest);
524 close(manifest);
525 sb->Printf("got manifest connection %d\n", manifest_conn);
526 if (-1 == manifest_conn) {
527 sb->Printf("could not connect\n");
528 return;
531 // build the SRPC connection (do service discovery)
532 if (!NaClSrpcClientCtor(&manifest_channel, manifest_conn)) {
533 sb->Printf("could not build srpc client\n");
534 return;
537 int desc;
539 sb->Printf("Invoking name service lookup\n");
540 if (NACL_SRPC_RESULT_OK !=
541 NaClSrpcInvokeBySignature(&manifest_channel,
542 NACL_NAME_SERVICE_LOOKUP,
543 "files/test_file", O_RDONLY,
544 &status, &desc)) {
545 sb->Printf("manifest lookup RPC failed\n");
546 NaClSrpcDtor(&manifest_channel);
547 return;
550 sb->DiscardOutput();
551 sb->Printf("File Contents:\n");
553 char buffer[4096];
554 int len;
555 while ((len = read(desc, buffer, sizeof buffer - 1)) > 0) {
556 // NB: fgets does not discard the newline nor any carriage return
557 // character before that.
559 // Note that CR LF is the default end-of-line style for Windows.
560 // Furthermore, when the test_file (input data, which happens to
561 // be the nmf file) is initially created in a change list, the
562 // patch is sent to our try bots as text. This means that when
563 // the file arrives, it has CR LF endings instead of the original
564 // LF line endings. Since the expected or golden data is
565 // (manually) encoded in the HTML file's JavaScript, there will be
566 // a mismatch. After submission, the svn property svn:eol-style
567 // will be set to LF, so a clean check out should have LF and not
568 // CR LF endings, and the tests will pass without CR removal.
569 // However -- and there's always a however in long discourses --
570 // if the nmf file is edited, say, because the test is being
571 // modified, and the modification is being done on a Windows
572 // machine, then it is likely that the editor used by the
573 // programmer will convert the file to CR LF endings. Which,
574 // unfortunatly, implies that the test will mysteriously fail
575 // again.
577 // To defend against such nonsense, we weaken the test slighty,
578 // and just strip the CR if it is present.
579 if (len >= 2 && buffer[len-1] == '\n' && buffer[len-2] == '\r') {
580 buffer[len-2] = '\n';
581 buffer[len-1] = '\0';
583 // Null terminate.
584 buffer[len] = 0;
585 sb->Printf("%s", buffer);
587 NaClSrpcDtor(&manifest_channel);
588 return;
591 // HandleMessage gets invoked when postMessage is called on the DOM
592 // element associated with this plugin instance. In this case, if we
593 // are given a string, we'll post a message back to JavaScript with a
594 // reply -- essentially treating this as a string-based RPC.
595 void MyInstance::HandleMessage(const pp::Var& message) {
596 if (message.is_string()) {
597 fprintf(stderr,
598 "HandleMessage: enqueuing %s\n", message.AsString().c_str());
599 fflush(NULL);
600 worker_->Enqueue(new WorkRequest(message.AsString()));
601 } else {
602 fprintf(stderr, "HandleMessage: message is not a string\n");
603 fflush(NULL);
607 void *worker_thread_start(void *arg) {
608 Worker *worker = reinterpret_cast<Worker *>(arg);
610 fprintf(stderr, "Sleeping...\n"); fflush(stderr);
611 sleep(1);
612 fprintf(stderr, "worker_thread_start: worker %p\n",
613 reinterpret_cast<void *>(worker));
614 fflush(NULL);
615 worker->RunToCompletion();
616 worker = NULL; // RunToCompletion automatically Unrefs
617 return reinterpret_cast<void *>(NULL);
620 // This object is the global object representing this plugin library as long
621 // as it is loaded.
622 class MyModule : public pp::Module {
623 public:
624 MyModule() : pp::Module() {}
625 virtual ~MyModule() {}
627 // Override CreateInstance to create your customized Instance object.
628 virtual pp::Instance *CreateInstance(PP_Instance instance);
630 DISALLOW_COPY_AND_ASSIGN(MyModule);
633 pp::Instance *MyModule::CreateInstance(PP_Instance pp_instance) {
634 MyInstance *instance = new MyInstance(pp_instance);
635 // spawn worker thread associated with this instance
636 pthread_t thread;
638 fprintf(stderr, "CreateInstance invoked\n"); fflush(NULL);
639 if (0 != pthread_create(&thread,
640 reinterpret_cast<pthread_attr_t *>(NULL),
641 worker_thread_start,
642 reinterpret_cast<void *>(instance->worker()))) {
643 // Remove the reference the ownership of which should have been
644 // passed to the worker thread.
645 instance->worker()->Unref();
646 delete instance;
647 instance = NULL;
648 fprintf(stderr, "pthread_create failed\n"); fflush(NULL);
649 } else {
650 fprintf(stderr, "CreateInstance: Worker thread started\n");
651 fprintf(stderr, "CreateInstance: worker thread object %p\n",
652 reinterpret_cast<void *>(instance->worker()));
653 (void) pthread_detach(thread);
655 fprintf(stderr, "CreateInstance: returning instance %p\n",
656 reinterpret_cast<void *>(instance));
658 return instance;
661 namespace pp {
663 // Factory function for your specialization of the Module object.
664 Module* CreateModule() {
665 fprintf(stderr, "CreateModule invoked\n"); fflush(NULL);
666 return new MyModule();
669 } // namespace pp