Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / chrome / test / data / nacl / manifest_file / pm_manifest_file_test.cc
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  */
6
7 //
8 // Post-message based test for simple rpc based access to name services.
9 //
10
11 #include <string>
12
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>
21
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"
29
30 // TODO(bsy): move weak_ref module to the shared directory
31 #include "native_client/src/trusted/weak_ref/weak_ref.h"
32
33 #include "ppapi/cpp/instance.h"
34 #include "ppapi/cpp/module.h"
35 #include "ppapi/cpp/var.h"
36
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"
40
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_;
55
56   DISALLOW_COPY_AND_ASSIGN(PostStringMessageWrapper);
57 };
58
59 // ---------------------------------------------------------------------------
60
61 class MyInstance;
62
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);
71 };
72
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);
86
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();
93
94   WorkRequest *Dequeue();
95   void Enqueue(WorkRequest *req);
96
97   void Initialize(nacl::StringBuffer *sb);
98   void ManifestListTest(nacl::StringBuffer *sb);
99   void ManifestOpenTest(nacl::StringBuffer *sb);
100
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.
104   void ShouldExit();
105
106   void Unref();  // used only for error cleanup, e.g., when the worker
107                  // thread did not launch.
108
109   bool InitializeChannel(nacl::StringBuffer *sb);
110
111  protected:
112   // Event thread operation(s):
113   //
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.
121
122  private:
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.
126
127   NaClMutex mu_;
128   NaClCondVar cv_;  // queue not empty or should_exit_
129
130   int ref_count_;
131   WorkRequest *queue_head_;
132   WorkRequest **queue_insert_;
133
134   bool should_exit_;
135
136   ~Worker();
137
138   WorkRequest *Dequeue_mu();
139   void Enqueue_mu(WorkRequest *req);
140
141   struct DispatchTable {
142     char const *op_name;
143     void (Worker::*mfunc)(nacl::StringBuffer *sb);
144   };
145
146   bool            ns_channel_initialized_;
147   NaClSrpcChannel ns_channel_;
148
149   static DispatchTable const kDispatch[];  // null terminated
150
151   DISALLOW_COPY_AND_ASSIGN(Worker);
152 };
153
154 // This object represents one time the page says <embed>.
155 class MyInstance : public nacl_ppapi::NaClPpapiPluginInstance {
156  public:
157   explicit MyInstance(PP_Instance instance);
158   virtual ~MyInstance();
159   virtual void HandleMessage(const pp::Var& message_data);
160
161   Worker *worker() { return worker_; }
162
163   // used with plugin::WeakRefCompletionCallback
164   void PostStringMessage_EventThread(PostStringMessageWrapper *msg_wrapper,
165                                      int32_t err);
166  private:
167   Worker *worker_;
168
169   DISALLOW_COPY_AND_ASSIGN(MyInstance);
170 };
171
172 // ---------------------------------------------------------------------------
173
174 PostStringMessageWrapper::~PostStringMessageWrapper() {}
175
176 // ---------------------------------------------------------------------------
177
178 MyInstance::MyInstance(PP_Instance instance)
179     : nacl_ppapi::NaClPpapiPluginInstance(instance),
180       worker_(new Worker(this)) {
181 }
182
183 MyInstance::~MyInstance() {
184   worker_->ShouldExit();
185 }
186
187 void MyInstance::PostStringMessage_EventThread(
188     PostStringMessageWrapper *msg_wrapper,
189     int32_t err) {
190   PostMessage(msg_wrapper->msg());
191   msg_wrapper->SetResult(nacl_ppapi::g_void_result);
192 }
193
194 // ---------------------------------------------------------------------------
195
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...
200       queue_head_(NULL),
201       queue_insert_(&queue_head_),
202       should_exit_(false),
203       ns_channel_initialized_(false) {
204   NaClXMutexCtor(&mu_);
205   NaClXCondVarCtor(&cv_);
206 }
207
208 void Worker::Unref() {
209   bool do_delete;
210   do {
211     nacl::MutexLocker take(&mu_);
212     do_delete = (--ref_count_ == 0);
213   } while (0);
214   // dropped lock before invoking dtor
215   if (do_delete) {
216     delete this;
217   }
218 }
219
220 Worker::~Worker() {
221   anchor_->Unref();
222
223   WorkRequest *req;
224   while ((req = Dequeue_mu()) != NULL) {
225     delete req;
226   }
227
228   NaClMutexDtor(&mu_);
229   NaClCondVarDtor(&cv_);
230 }
231
232 void Worker::ShouldExit() {
233   do {
234     nacl::MutexLocker take(&mu_);
235     should_exit_ = true;
236     NaClXCondVarBroadcast(&cv_);
237   } while (0);
238   Unref();
239 }
240
241 WorkRequest *Worker::Dequeue_mu() {
242   WorkRequest *head = queue_head_;
243
244   if (head != NULL) {
245     queue_head_ = head->next;
246     if (queue_head_ == NULL) {
247       queue_insert_ = &queue_head_;
248     }
249   }
250   return head;
251 }
252
253 void Worker::Enqueue_mu(WorkRequest *req) {
254   req->next = NULL;
255   *queue_insert_ = req;
256   queue_insert_ = &req->next;
257 }
258
259 WorkRequest *Worker::Dequeue() {
260   nacl::MutexLocker take(&mu_);
261   return Dequeue_mu();
262 }
263
264 void Worker::Enqueue(WorkRequest *req) {
265   nacl::MutexLocker take(&mu_);
266   Enqueue_mu(req);
267   NaClXCondVarBroadcast(&cv_);
268 }
269
270 Worker::DispatchTable const Worker::kDispatch[] = {
271   { "init", &Worker::Initialize },
272   { "manifest_open", &Worker::ManifestOpenTest },
273   { reinterpret_cast<char const *>(NULL), NULL }
274 };
275
276 bool Worker::PostStringMessage(const std::string &msg) {
277   nacl_ppapi::EventThreadWorkState<nacl_ppapi::VoidResult> state;
278   plugin::WeakRefCallOnMainThread(anchor_,
279                                   0  /* mS */,
280                                   instance_,
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
286     // thread.
287     nacl::MutexLocker take(&mu_);
288     should_exit_ = true;
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
294     // operations).
295     return false;
296   }
297   return true;
298 }
299
300 void Worker::RunToCompletion() {
301   for (;;) {
302     WorkRequest *req;
303     do {
304       nacl::MutexLocker take(&mu_);
305       for (;;) {
306         if (should_exit_) {
307           // drop the lock and drop the reference count to this
308           goto break_x3;
309         }
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));
314           break;
315         }
316         fprintf(stderr, "RunToCompletion: waiting\n");
317         NaClXCondVarWait(&cv_, &mu_);
318         fprintf(stderr, "RunToCompletion: woke up\n");
319       }
320     } while (0);
321
322     // Do the work, without holding the lock.  The work function
323     // should reacquire mu_ as needed.
324
325     nacl::StringBuffer sb;
326
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) {
330       fprintf(stderr,
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);
336         }
337         break;
338       }
339     }
340     // always post a reply, even if it is the empty string
341     fprintf(stderr,
342             "RunToCompletion: posting reply %s\n", sb.ToString().c_str());
343     if (!PostStringMessage(sb.ToString())) {
344       break;
345     }
346   }
347  break_x3:
348   fprintf(stderr, "RunToCompletion: exiting\n");
349   Unref();
350 }
351
352 bool Worker::InitializeChannel(nacl::StringBuffer *sb) {
353   if (ns_channel_initialized_) {
354     return true;
355   }
356   int ns = -1;
357   nacl_nameservice(&ns);
358   printf("ns = %d\n", ns);
359   assert(-1 != 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");
364     close(ns);
365     return false;
366   }
367   sb->Printf("NaClSrpcClientCtor succeeded\n");
368   close(ns);
369   ns_channel_initialized_ = true;
370   return true;
371 }
372
373 void Worker::Initialize(nacl::StringBuffer *sb) {
374   // we just want the log output from the InitializeChannel
375   return;
376 }
377
378 void Worker::ManifestOpenTest(nacl::StringBuffer *sb) {
379   int                     status = -1;
380   int                     manifest;
381   struct NaClSrpcChannel  manifest_channel;
382
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);
390     return;
391   }
392   sb->Printf("Got manifest descriptor %d\n", manifest);
393   if (-1 == manifest) {
394     return;
395   }
396
397   // connect to manifest name server
398   int manifest_conn = imc_connect(manifest);
399   close(manifest);
400   sb->Printf("got manifest connection %d\n", manifest_conn);
401   if (-1 == manifest_conn) {
402     sb->Printf("could not connect\n");
403     return;
404   }
405
406   // build the SRPC connection (do service discovery)
407   if (!NaClSrpcClientCtor(&manifest_channel, manifest_conn)) {
408     sb->Printf("could not build srpc client\n");
409     return;
410   }
411
412   int desc;
413
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,
419                                 &status, &desc)) {
420     sb->Printf("manifest lookup RPC failed\n");
421     NaClSrpcDtor(&manifest_channel);
422     return;
423   }
424
425   sb->DiscardOutput();
426   sb->Printf("File Contents:\n");
427
428   char buffer[4096];
429   int len;
430   while ((len = read(desc, buffer, sizeof buffer - 1)) > 0) {
431     // NB: fgets does not discard the newline nor any carriage return
432     // character before that.
433     //
434     // Note that CR LF is the default end-of-line style for Windows.
435     // Furthermore, when the test_file (input data, which happens to
436     // be the nmf file) is initially created in a change list, the
437     // patch is sent to our try bots as text.  This means that when
438     // the file arrives, it has CR LF endings instead of the original
439     // LF line endings.  Since the expected or golden data is
440     // (manually) encoded in the HTML file's JavaScript, there will be
441     // a mismatch.  After submission, the svn property svn:eol-style
442     // will be set to LF, so a clean check out should have LF and not
443     // CR LF endings, and the tests will pass without CR removal.
444     // However -- and there's always a however in long discourses --
445     // if the nmf file is edited, say, because the test is being
446     // modified, and the modification is being done on a Windows
447     // machine, then it is likely that the editor used by the
448     // programmer will convert the file to CR LF endings.  Which,
449     // unfortunatly, implies that the test will mysteriously fail
450     // again.
451     //
452     // To defend against such nonsense, we weaken the test slighty,
453     // and just strip the CR if it is present.
454     if (len >= 2 && buffer[len-1] == '\n' && buffer[len-2] == '\r') {
455       buffer[len-2] = '\n';
456       buffer[len-1] = '\0';
457     }
458     // Null terminate.
459     buffer[len] = 0;
460     sb->Printf("%s", buffer);
461   }
462   NaClSrpcDtor(&manifest_channel);
463   return;
464 }
465
466 // HandleMessage gets invoked when postMessage is called on the DOM
467 // element associated with this plugin instance.  In this case, if we
468 // are given a string, we'll post a message back to JavaScript with a
469 // reply -- essentially treating this as a string-based RPC.
470 void MyInstance::HandleMessage(const pp::Var& message) {
471   if (message.is_string()) {
472     fprintf(stderr,
473             "HandleMessage: enqueuing %s\n", message.AsString().c_str());
474     fflush(NULL);
475     worker_->Enqueue(new WorkRequest(message.AsString()));
476   } else {
477     fprintf(stderr, "HandleMessage: message is not a string\n");
478     fflush(NULL);
479   }
480 }
481
482 void *worker_thread_start(void *arg) {
483   Worker *worker = reinterpret_cast<Worker *>(arg);
484
485   fprintf(stderr, "Sleeping...\n"); fflush(stderr);
486   sleep(1);
487   fprintf(stderr, "worker_thread_start: worker %p\n",
488           reinterpret_cast<void *>(worker));
489   fflush(NULL);
490   worker->RunToCompletion();
491   worker = NULL;  // RunToCompletion automatically Unrefs
492   return reinterpret_cast<void *>(NULL);
493 }
494
495 // This object is the global object representing this plugin library as long
496 // as it is loaded.
497 class MyModule : public pp::Module {
498  public:
499   MyModule() : pp::Module() {}
500   virtual ~MyModule() {}
501
502   // Override CreateInstance to create your customized Instance object.
503   virtual pp::Instance *CreateInstance(PP_Instance instance);
504
505   DISALLOW_COPY_AND_ASSIGN(MyModule);
506 };
507
508 pp::Instance *MyModule::CreateInstance(PP_Instance pp_instance) {
509   MyInstance *instance = new MyInstance(pp_instance);
510   // spawn worker thread associated with this instance
511   pthread_t thread;
512
513   fprintf(stderr, "CreateInstance invoked\n"); fflush(NULL);
514   if (0 != pthread_create(&thread,
515                           reinterpret_cast<pthread_attr_t *>(NULL),
516                           worker_thread_start,
517                           reinterpret_cast<void *>(instance->worker()))) {
518     // Remove the reference the ownership of which should have been
519     // passed to the worker thread.
520     instance->worker()->Unref();
521     delete instance;
522     instance = NULL;
523     fprintf(stderr, "pthread_create failed\n"); fflush(NULL);
524   } else {
525     fprintf(stderr, "CreateInstance: Worker thread started\n");
526     fprintf(stderr, "CreateInstance: worker thread object %p\n",
527             reinterpret_cast<void *>(instance->worker()));
528     (void) pthread_detach(thread);
529   }
530   fprintf(stderr, "CreateInstance: returning instance %p\n",
531           reinterpret_cast<void *>(instance));
532
533   return instance;
534 }
535
536 namespace pp {
537
538 // Factory function for your specialization of the Module object.
539 Module* CreateModule() {
540   fprintf(stderr, "CreateModule invoked\n"); fflush(NULL);
541   return new MyModule();
542 }
543
544 }  // namespace pp