- add sources.
[platform/framework/web/crosswalk.git] / src / ppapi / native_client / src / trusted / plugin / pnacl_translate_thread.cc
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "ppapi/native_client/src/trusted/plugin/pnacl_translate_thread.h"
6
7 #include "native_client/src/trusted/desc/nacl_desc_wrapper.h"
8 #include "ppapi/native_client/src/trusted/plugin/plugin.h"
9 #include "ppapi/native_client/src/trusted/plugin/plugin_error.h"
10 #include "ppapi/native_client/src/trusted/plugin/pnacl_resources.h"
11 #include "ppapi/native_client/src/trusted/plugin/srpc_params.h"
12 #include "ppapi/native_client/src/trusted/plugin/temporary_file.h"
13 #include "ppapi/native_client/src/trusted/plugin/utility.h"
14
15 namespace plugin {
16
17 PnaclTranslateThread::PnaclTranslateThread() : llc_subprocess_active_(false),
18                                                ld_subprocess_active_(false),
19                                                done_(false),
20                                                time_stats_(),
21                                                manifest_(NULL),
22                                                obj_file_(NULL),
23                                                nexe_file_(NULL),
24                                                coordinator_error_info_(NULL),
25                                                resources_(NULL),
26                                                coordinator_(NULL),
27                                                plugin_(NULL) {
28   NaClXMutexCtor(&subprocess_mu_);
29   NaClXMutexCtor(&cond_mu_);
30   NaClXCondVarCtor(&buffer_cond_);
31 }
32
33 void PnaclTranslateThread::RunTranslate(
34     const pp::CompletionCallback& finish_callback,
35     const Manifest* manifest,
36     TempFile* obj_file,
37     TempFile* nexe_file,
38     ErrorInfo* error_info,
39     PnaclResources* resources,
40     PnaclOptions* pnacl_options,
41     PnaclCoordinator* coordinator,
42     Plugin* plugin) {
43   PLUGIN_PRINTF(("PnaclStreamingTranslateThread::RunTranslate)\n"));
44   manifest_ = manifest;
45   obj_file_ = obj_file;
46   nexe_file_ = nexe_file;
47   coordinator_error_info_ = error_info;
48   resources_ = resources;
49   pnacl_options_ = pnacl_options;
50   coordinator_ = coordinator;
51   plugin_ = plugin;
52
53   // Invoke llc followed by ld off the main thread.  This allows use of
54   // blocking RPCs that would otherwise block the JavaScript main thread.
55   report_translate_finished_ = finish_callback;
56   translate_thread_.reset(new NaClThread);
57   if (translate_thread_ == NULL) {
58     TranslateFailed(ERROR_PNACL_THREAD_CREATE,
59                     "could not allocate thread struct.");
60     return;
61   }
62   const int32_t kArbitraryStackSize = 128 * 1024;
63   if (!NaClThreadCreateJoinable(translate_thread_.get(),
64                                 DoTranslateThread,
65                                 this,
66                                 kArbitraryStackSize)) {
67     TranslateFailed(ERROR_PNACL_THREAD_CREATE,
68                     "could not create thread.");
69     translate_thread_.reset(NULL);
70   }
71 }
72
73 // Called from main thread to send bytes to the translator.
74 void PnaclTranslateThread::PutBytes(std::vector<char>* bytes,
75                                              int count) {
76   PLUGIN_PRINTF(("PutBytes (this=%p, bytes=%p, size=%" NACL_PRIuS
77                  ", count=%d)\n",
78                  this, bytes, bytes ? bytes->size() : 0, count));
79   size_t buffer_size = 0;
80   // If we are done (error or not), Signal the translation thread to stop.
81   if (count <= PP_OK) {
82     NaClXMutexLock(&cond_mu_);
83     done_ = true;
84     NaClXCondVarSignal(&buffer_cond_);
85     NaClXMutexUnlock(&cond_mu_);
86     return;
87   }
88
89   CHECK(bytes != NULL);
90   // Ensure that the buffer we send to the translation thread is the right size
91   // (count can be < the buffer size). This can be done without the lock.
92   buffer_size = bytes->size();
93   bytes->resize(count);
94
95   NaClXMutexLock(&cond_mu_);
96
97   data_buffers_.push_back(std::vector<char>());
98   bytes->swap(data_buffers_.back()); // Avoid copying the buffer data.
99
100   NaClXCondVarSignal(&buffer_cond_);
101   NaClXMutexUnlock(&cond_mu_);
102
103   // Ensure the buffer we send back to the coordinator is the expected size
104   bytes->resize(buffer_size);
105 }
106
107 NaClSubprocess* PnaclTranslateThread::StartSubprocess(
108     const nacl::string& url_for_nexe,
109     const Manifest* manifest,
110     ErrorInfo* error_info) {
111   PLUGIN_PRINTF(("PnaclTranslateThread::StartSubprocess (url_for_nexe=%s)\n",
112                  url_for_nexe.c_str()));
113   nacl::DescWrapper* wrapper = resources_->WrapperForUrl(url_for_nexe);
114   nacl::scoped_ptr<NaClSubprocess> subprocess(
115       plugin_->LoadHelperNaClModule(wrapper, manifest, error_info));
116   if (subprocess.get() == NULL) {
117     PLUGIN_PRINTF((
118         "PnaclTranslateThread::StartSubprocess: subprocess creation failed\n"));
119     return NULL;
120   }
121   return subprocess.release();
122 }
123
124 void WINAPI PnaclTranslateThread::DoTranslateThread(void* arg) {
125   PnaclTranslateThread* translator =
126       reinterpret_cast<PnaclTranslateThread*>(arg);
127   translator->DoTranslate();
128 }
129
130 void PnaclTranslateThread::DoTranslate() {
131   ErrorInfo error_info;
132   SrpcParams params;
133   nacl::DescWrapper* llc_out_file = obj_file_->write_wrapper();
134
135   {
136     nacl::MutexLocker ml(&subprocess_mu_);
137     int64_t llc_start_time = NaClGetTimeOfDayMicroseconds();
138     llc_subprocess_.reset(
139       StartSubprocess(resources_->GetLlcUrl(), manifest_, &error_info));
140     if (llc_subprocess_ == NULL) {
141       TranslateFailed(ERROR_PNACL_LLC_SETUP,
142                       "Compile process could not be created: " +
143                       error_info.message());
144       return;
145     }
146     llc_subprocess_active_ = true;
147     time_stats_.pnacl_llc_load_time =
148         (NaClGetTimeOfDayMicroseconds() - llc_start_time);
149     // Run LLC.
150     PluginReverseInterface* llc_reverse =
151         llc_subprocess_->service_runtime()->rev_interface();
152     llc_reverse->AddTempQuotaManagedFile(obj_file_->identifier());
153   }
154
155   int64_t compile_start_time = NaClGetTimeOfDayMicroseconds();
156   bool init_success;
157   std::vector<char> options = pnacl_options_->GetOptCommandline();
158   init_success = llc_subprocess_->InvokeSrpcMethod(
159       "StreamInitWithOverrides",
160       "hC",
161       &params,
162       llc_out_file->desc(),
163       &options[0],
164       options.size());
165
166   if (!init_success) {
167     if (llc_subprocess_->srpc_client()->GetLastError() ==
168         NACL_SRPC_RESULT_APP_ERROR) {
169       // The error message is only present if the error was returned from llc
170       TranslateFailed(ERROR_PNACL_LLC_INTERNAL,
171                       nacl::string("Stream init failed: ") +
172                       nacl::string(params.outs()[0]->arrays.str));
173     } else {
174       TranslateFailed(ERROR_PNACL_LLC_INTERNAL,
175                       "Stream init internal error");
176     }
177     return;
178   }
179
180   PLUGIN_PRINTF(("PnaclCoordinator: StreamInit successful\n"));
181   pp::Core* core = pp::Module::Get()->core();
182
183   // llc process is started.
184   while(!done_ || data_buffers_.size() > 0) {
185     NaClXMutexLock(&cond_mu_);
186     while(!done_ && data_buffers_.size() == 0) {
187       NaClXCondVarWait(&buffer_cond_, &cond_mu_);
188     }
189     PLUGIN_PRINTF(("PnaclTranslateThread awake (done=%d, size=%" NACL_PRIuS
190                    ")\n",
191                    done_, data_buffers_.size()));
192     if (data_buffers_.size() > 0) {
193       std::vector<char> data;
194       data.swap(data_buffers_.front());
195       data_buffers_.pop_front();
196       NaClXMutexUnlock(&cond_mu_);
197       PLUGIN_PRINTF(("StreamChunk\n"));
198       if (!llc_subprocess_->InvokeSrpcMethod("StreamChunk",
199                                              "C",
200                                              &params,
201                                              &data[0],
202                                              data.size())) {
203         if (llc_subprocess_->srpc_client()->GetLastError() !=
204             NACL_SRPC_RESULT_APP_ERROR) {
205           // If the error was reported by the translator, then we fall through
206           // and call StreamEnd, which returns a string describing the error,
207           // which we can then send to the Javascript console. Otherwise just
208           // fail here, since the translator has probably crashed or asserted.
209           TranslateFailed(ERROR_PNACL_LLC_INTERNAL,
210                           "Compile stream chunk failed. "
211                           "The PNaCl translator has probably crashed.");
212           return;
213         }
214         break;
215       } else {
216         PLUGIN_PRINTF(("StreamChunk Successful\n"));
217         core->CallOnMainThread(
218             0,
219             coordinator_->GetCompileProgressCallback(data.size()),
220             PP_OK);
221       }
222     } else {
223       NaClXMutexUnlock(&cond_mu_);
224     }
225   }
226   PLUGIN_PRINTF(("PnaclTranslateThread done with chunks\n"));
227   // Finish llc.
228   if (!llc_subprocess_->InvokeSrpcMethod("StreamEnd", std::string(), &params)) {
229     PLUGIN_PRINTF(("PnaclTranslateThread StreamEnd failed\n"));
230     if (llc_subprocess_->srpc_client()->GetLastError() ==
231         NACL_SRPC_RESULT_APP_ERROR) {
232       // The error string is only present if the error was sent back from llc.
233       TranslateFailed(ERROR_PNACL_LLC_INTERNAL,
234                       params.outs()[3]->arrays.str);
235     } else {
236       TranslateFailed(ERROR_PNACL_LLC_INTERNAL,
237                       "Compile StreamEnd internal error");
238     }
239     return;
240   }
241   time_stats_.pnacl_compile_time =
242       (NaClGetTimeOfDayMicroseconds() - compile_start_time);
243
244   // LLC returns values that are used to determine how linking is done.
245   int is_shared_library = (params.outs()[0]->u.ival != 0);
246   nacl::string soname = params.outs()[1]->arrays.str;
247   nacl::string lib_dependencies = params.outs()[2]->arrays.str;
248   PLUGIN_PRINTF(("PnaclCoordinator: compile (translator=%p) succeeded"
249                  " is_shared_library=%d, soname='%s', lib_dependencies='%s')\n",
250                  this, is_shared_library, soname.c_str(),
251                  lib_dependencies.c_str()));
252
253   // Shut down the llc subprocess.
254   NaClXMutexLock(&subprocess_mu_);
255   llc_subprocess_active_ = false;
256   llc_subprocess_.reset(NULL);
257   NaClXMutexUnlock(&subprocess_mu_);
258
259   if(!RunLdSubprocess(is_shared_library, soname, lib_dependencies)) {
260     return;
261   }
262   core->CallOnMainThread(0, report_translate_finished_, PP_OK);
263 }
264
265 bool PnaclTranslateThread::RunLdSubprocess(int is_shared_library,
266                                            const nacl::string& soname,
267                                            const nacl::string& lib_dependencies
268                                            ) {
269   ErrorInfo error_info;
270   SrpcParams params;
271   // Reset object file for reading first.
272   if (!obj_file_->Reset()) {
273     TranslateFailed(ERROR_PNACL_LD_SETUP,
274                     "Link process could not reset object file");
275     return false;
276   }
277   nacl::DescWrapper* ld_in_file = obj_file_->read_wrapper();
278   nacl::DescWrapper* ld_out_file = nexe_file_->write_wrapper();
279
280   {
281     // Create LD process
282     nacl::MutexLocker ml(&subprocess_mu_);
283     int64_t ld_start_time = NaClGetTimeOfDayMicroseconds();
284     ld_subprocess_.reset(
285       StartSubprocess(resources_->GetLdUrl(), manifest_, &error_info));
286     if (ld_subprocess_ == NULL) {
287       TranslateFailed(ERROR_PNACL_LD_SETUP,
288                       "Link process could not be created: " +
289                       error_info.message());
290       return false;
291     }
292     ld_subprocess_active_ = true;
293     time_stats_.pnacl_ld_load_time =
294         (NaClGetTimeOfDayMicroseconds() - ld_start_time);
295     PluginReverseInterface* ld_reverse =
296         ld_subprocess_->service_runtime()->rev_interface();
297     ld_reverse->AddTempQuotaManagedFile(nexe_file_->identifier());
298   }
299
300   int64_t link_start_time = NaClGetTimeOfDayMicroseconds();
301   // Run LD.
302   if (!ld_subprocess_->InvokeSrpcMethod("RunWithDefaultCommandLine",
303                                        "hhiss",
304                                        &params,
305                                        ld_in_file->desc(),
306                                        ld_out_file->desc(),
307                                        is_shared_library,
308                                        soname.c_str(),
309                                        lib_dependencies.c_str())) {
310     TranslateFailed(ERROR_PNACL_LD_INTERNAL,
311                     "link failed.");
312     return false;
313   }
314   time_stats_.pnacl_link_time =
315       NaClGetTimeOfDayMicroseconds() - link_start_time;
316   PLUGIN_PRINTF(("PnaclCoordinator: link (translator=%p) succeeded\n",
317                  this));
318   // Shut down the ld subprocess.
319   NaClXMutexLock(&subprocess_mu_);
320   ld_subprocess_active_ = false;
321   ld_subprocess_.reset(NULL);
322   NaClXMutexUnlock(&subprocess_mu_);
323   return true;
324 }
325
326 void PnaclTranslateThread::TranslateFailed(
327     enum PluginErrorCode err_code,
328     const nacl::string& error_string) {
329   PLUGIN_PRINTF(("PnaclTranslateThread::TranslateFailed (error_string='%s')\n",
330                  error_string.c_str()));
331   pp::Core* core = pp::Module::Get()->core();
332   if (coordinator_error_info_->message().empty()) {
333     // Only use our message if one hasn't already been set by the coordinator
334     // (e.g. pexe load failed).
335     coordinator_error_info_->SetReport(err_code,
336                                        nacl::string("PnaclCoordinator: ") +
337                                        error_string);
338   }
339   core->CallOnMainThread(0, report_translate_finished_, PP_ERROR_FAILED);
340 }
341
342 void PnaclTranslateThread::AbortSubprocesses() {
343   PLUGIN_PRINTF(("PnaclTranslateThread::AbortSubprocesses\n"));
344   NaClXMutexLock(&subprocess_mu_);
345   if (llc_subprocess_ != NULL && llc_subprocess_active_) {
346     llc_subprocess_->service_runtime()->Shutdown();
347     llc_subprocess_active_ = false;
348   }
349   if (ld_subprocess_ != NULL && ld_subprocess_active_) {
350     ld_subprocess_->service_runtime()->Shutdown();
351     ld_subprocess_active_ = false;
352   }
353   NaClXMutexUnlock(&subprocess_mu_);
354   nacl::MutexLocker ml(&cond_mu_);
355   done_ = true;
356   // Free all buffered bitcode chunks
357   data_buffers_.clear();
358   NaClXCondVarSignal(&buffer_cond_);
359 }
360
361 PnaclTranslateThread::~PnaclTranslateThread() {
362   PLUGIN_PRINTF(("~PnaclTranslateThread (translate_thread=%p)\n", this));
363   AbortSubprocesses();
364   if (translate_thread_ != NULL)
365     NaClThreadJoin(translate_thread_.get());
366   PLUGIN_PRINTF(("~PnaclTranslateThread joined\n"));
367   NaClCondVarDtor(&buffer_cond_);
368   NaClMutexDtor(&cond_mu_);
369   NaClMutexDtor(&subprocess_mu_);
370 }
371
372 } // namespace plugin