Roll src/third_party/WebKit f36d5e0:68b67cd (svn 193299:193303)
[chromium-blink-merge.git] / components / nacl / renderer / plugin / pnacl_coordinator.cc
blob689188f2d1cac49d3b7900ff666d10a09e992029
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.
5 #include "components/nacl/renderer/plugin/pnacl_coordinator.h"
7 #include <algorithm>
8 #include <sstream>
9 #include <utility>
11 #include "components/nacl/renderer/plugin/plugin.h"
12 #include "components/nacl/renderer/plugin/plugin_error.h"
13 #include "components/nacl/renderer/plugin/pnacl_translate_thread.h"
14 #include "components/nacl/renderer/plugin/service_runtime.h"
15 #include "components/nacl/renderer/plugin/temporary_file.h"
16 #include "native_client/src/include/portability_io.h"
17 #include "native_client/src/shared/platform/nacl_check.h"
18 #include "native_client/src/trusted/service_runtime/include/sys/stat.h"
19 #include "ppapi/c/pp_bool.h"
20 #include "ppapi/c/pp_errors.h"
22 namespace plugin {
24 namespace {
26 std::string GetArchitectureAttributes(Plugin* plugin) {
27 pp::Var attrs_var(pp::PASS_REF,
28 plugin->nacl_interface()->GetCpuFeatureAttrs());
29 return attrs_var.AsString();
32 void DidCacheHit(void* user_data, PP_FileHandle nexe_file_handle) {
33 PnaclCoordinator* coordinator = static_cast<PnaclCoordinator*>(user_data);
34 coordinator->BitcodeStreamCacheHit(nexe_file_handle);
37 void DidCacheMiss(void* user_data, int64_t expected_pexe_size,
38 PP_FileHandle temp_nexe_file) {
39 PnaclCoordinator* coordinator = static_cast<PnaclCoordinator*>(user_data);
40 coordinator->BitcodeStreamCacheMiss(expected_pexe_size,
41 temp_nexe_file);
44 void DidStreamData(void* user_data, const void* stream_data, int32_t length) {
45 PnaclCoordinator* coordinator = static_cast<PnaclCoordinator*>(user_data);
46 coordinator->BitcodeStreamGotData(stream_data, length);
49 void DidFinishStream(void* user_data, int32_t pp_error) {
50 PnaclCoordinator* coordinator = static_cast<PnaclCoordinator*>(user_data);
51 coordinator->BitcodeStreamDidFinish(pp_error);
54 PPP_PexeStreamHandler kPexeStreamHandler = {
55 &DidCacheHit,
56 &DidCacheMiss,
57 &DidStreamData,
58 &DidFinishStream
61 } // namespace
63 PnaclCoordinator* PnaclCoordinator::BitcodeToNative(
64 Plugin* plugin,
65 const std::string& pexe_url,
66 const PP_PNaClOptions& pnacl_options,
67 const pp::CompletionCallback& translate_notify_callback) {
68 PLUGIN_PRINTF(("PnaclCoordinator::BitcodeToNative (plugin=%p, pexe=%s)\n",
69 static_cast<void*>(plugin), pexe_url.c_str()));
70 PnaclCoordinator* coordinator =
71 new PnaclCoordinator(plugin, pexe_url,
72 pnacl_options,
73 translate_notify_callback);
75 GetNaClInterface()->SetPNaClStartTime(plugin->pp_instance());
76 int cpus = plugin->nacl_interface()->GetNumberOfProcessors();
77 coordinator->num_threads_ = std::min(4, std::max(1, cpus));
78 if (pnacl_options.use_subzero) {
79 coordinator->split_module_count_ = 1;
80 } else {
81 coordinator->split_module_count_ = coordinator->num_threads_;
83 // First start a network request for the pexe, to tickle the component
84 // updater's On-Demand resource throttler, and to get Last-Modified/ETag
85 // cache information. We can cancel the request later if there's
86 // a bitcode->nexe cache hit.
87 coordinator->OpenBitcodeStream();
88 return coordinator;
91 PnaclCoordinator::PnaclCoordinator(
92 Plugin* plugin,
93 const std::string& pexe_url,
94 const PP_PNaClOptions& pnacl_options,
95 const pp::CompletionCallback& translate_notify_callback)
96 : translate_finish_error_(PP_OK),
97 plugin_(plugin),
98 translate_notify_callback_(translate_notify_callback),
99 translation_finished_reported_(false),
100 pexe_url_(pexe_url),
101 pnacl_options_(pnacl_options),
102 architecture_attributes_(GetArchitectureAttributes(plugin)),
103 split_module_count_(0),
104 num_threads_(0),
105 error_already_reported_(false),
106 pexe_size_(0),
107 pexe_bytes_compiled_(0),
108 expected_pexe_size_(-1) {
109 callback_factory_.Initialize(this);
112 PnaclCoordinator::~PnaclCoordinator() {
113 PLUGIN_PRINTF(("PnaclCoordinator::~PnaclCoordinator (this=%p, "
114 "translate_thread=%p\n",
115 static_cast<void*>(this), translate_thread_.get()));
116 // Stopping the translate thread will cause the translate thread to try to
117 // run translation_complete_callback_ on the main thread. This destructor is
118 // running from the main thread, and by the time it exits, callback_factory_
119 // will have been destroyed. This will result in the cancellation of
120 // translation_complete_callback_, so no notification will be delivered.
121 if (translate_thread_.get() != NULL)
122 translate_thread_->AbortSubprocesses();
123 if (!translation_finished_reported_) {
124 plugin_->nacl_interface()->ReportTranslationFinished(
125 plugin_->pp_instance(), PP_FALSE, pnacl_options_.opt_level,
126 pnacl_options_.use_subzero, 0, 0, 0);
128 // Force deleting the translate_thread now. It must be deleted
129 // before any scoped_* fields hanging off of PnaclCoordinator
130 // since the thread may be accessing those fields.
131 // It will also be accessing obj_files_.
132 translate_thread_.reset(NULL);
133 for (size_t i = 0; i < obj_files_.size(); i++)
134 delete obj_files_[i];
137 PP_FileHandle PnaclCoordinator::TakeTranslatedFileHandle() {
138 DCHECK(temp_nexe_file_ != NULL);
139 return temp_nexe_file_->TakeFileHandle();
142 void PnaclCoordinator::ReportNonPpapiError(PP_NaClError err_code,
143 const std::string& message) {
144 ErrorInfo error_info;
145 error_info.SetReport(err_code, message);
146 plugin_->ReportLoadError(error_info);
147 ExitWithError();
150 void PnaclCoordinator::ReportPpapiError(PP_NaClError err_code,
151 int32_t pp_error,
152 const std::string& message) {
153 std::stringstream ss;
154 ss << "PnaclCoordinator: " << message << " (pp_error=" << pp_error << ").";
155 ErrorInfo error_info;
156 error_info.SetReport(err_code, ss.str());
157 plugin_->ReportLoadError(error_info);
158 ExitWithError();
161 void PnaclCoordinator::ExitWithError() {
162 PLUGIN_PRINTF(("PnaclCoordinator::ExitWithError\n"));
163 // Free all the intermediate callbacks we ever created.
164 // Note: this doesn't *cancel* the callbacks from the factories attached
165 // to the various helper classes (e.g., pnacl_resources). Thus, those
166 // callbacks may still run asynchronously. We let those run but ignore
167 // any other errors they may generate so that they do not end up running
168 // translate_notify_callback_, which has already been freed.
169 callback_factory_.CancelAll();
170 if (!error_already_reported_) {
171 error_already_reported_ = true;
172 translation_finished_reported_ = true;
173 plugin_->nacl_interface()->ReportTranslationFinished(
174 plugin_->pp_instance(), PP_FALSE, pnacl_options_.opt_level,
175 pnacl_options_.use_subzero, 0, 0, 0);
176 translate_notify_callback_.Run(PP_ERROR_FAILED);
180 // Signal that Pnacl translation completed normally.
181 void PnaclCoordinator::TranslateFinished(int32_t pp_error) {
182 PLUGIN_PRINTF(("PnaclCoordinator::TranslateFinished (pp_error=%"
183 NACL_PRId32 ")\n", pp_error));
184 // Bail out if there was an earlier error (e.g., pexe load failure),
185 // or if there is an error from the translation thread.
186 if (translate_finish_error_ != PP_OK || pp_error != PP_OK) {
187 plugin_->ReportLoadError(error_info_);
188 ExitWithError();
189 return;
192 // Send out one last progress event, to finish up the progress events
193 // that were delayed (see the delay inserted in BitcodeGotCompiled).
194 if (expected_pexe_size_ != -1) {
195 pexe_bytes_compiled_ = expected_pexe_size_;
196 GetNaClInterface()->DispatchEvent(plugin_->pp_instance(),
197 PP_NACL_EVENT_PROGRESS,
198 pexe_url_.c_str(),
199 PP_TRUE,
200 pexe_bytes_compiled_,
201 expected_pexe_size_);
203 nacl_abi_off_t nexe_size = 0;
204 struct nacl_abi_stat stbuf;
205 struct NaClDesc* desc = temp_nexe_file_->read_wrapper()->desc();
206 if (0 == (*((struct NaClDescVtbl const *)desc->base.vtbl)->Fstat)(desc,
207 &stbuf)) {
208 nexe_size = stbuf.nacl_abi_st_size;
210 // The nexe is written to the temp_nexe_file_. We must Reset() the file
211 // pointer to be able to read it again from the beginning.
212 temp_nexe_file_->Reset();
214 // Report to the browser that translation finished. The browser will take
215 // care of storing the nexe in the cache.
216 translation_finished_reported_ = true;
217 plugin_->nacl_interface()->ReportTranslationFinished(
218 plugin_->pp_instance(), PP_TRUE, pnacl_options_.opt_level,
219 pnacl_options_.use_subzero, nexe_size, pexe_size_,
220 translate_thread_->GetCompileTime());
222 NexeReadDidOpen(PP_OK);
225 void PnaclCoordinator::NexeReadDidOpen(int32_t pp_error) {
226 PLUGIN_PRINTF(("PnaclCoordinator::NexeReadDidOpen (pp_error=%"
227 NACL_PRId32 ")\n", pp_error));
228 if (pp_error != PP_OK) {
229 if (pp_error == PP_ERROR_FILENOTFOUND) {
230 ReportPpapiError(PP_NACL_ERROR_PNACL_CACHE_FETCH_NOTFOUND,
231 pp_error,
232 "Failed to open translated nexe (not found).");
233 return;
235 if (pp_error == PP_ERROR_NOACCESS) {
236 ReportPpapiError(PP_NACL_ERROR_PNACL_CACHE_FETCH_NOACCESS,
237 pp_error,
238 "Failed to open translated nexe (no access).");
239 return;
241 ReportPpapiError(PP_NACL_ERROR_PNACL_CACHE_FETCH_OTHER,
242 pp_error,
243 "Failed to open translated nexe.");
244 return;
247 translate_notify_callback_.Run(PP_OK);
250 void PnaclCoordinator::OpenBitcodeStream() {
251 // Even though we haven't started downloading, create the translation
252 // thread object immediately. This ensures that any pieces of the file
253 // that get downloaded before the compilation thread is accepting
254 // SRPCs won't get dropped.
255 translate_thread_.reset(new PnaclTranslateThread());
256 if (translate_thread_ == NULL) {
257 ReportNonPpapiError(
258 PP_NACL_ERROR_PNACL_THREAD_CREATE,
259 "PnaclCoordinator: could not allocate translation thread.");
260 return;
263 GetNaClInterface()->StreamPexe(
264 plugin_->pp_instance(), pexe_url_.c_str(), pnacl_options_.opt_level,
265 pnacl_options_.use_subzero, &kPexeStreamHandler, this);
268 void PnaclCoordinator::BitcodeStreamCacheHit(PP_FileHandle handle) {
269 if (handle == PP_kInvalidFileHandle) {
270 ReportNonPpapiError(
271 PP_NACL_ERROR_PNACL_CREATE_TEMP,
272 std::string(
273 "PnaclCoordinator: Got bad temp file handle from GetNexeFd"));
274 BitcodeStreamDidFinish(PP_ERROR_FAILED);
275 return;
277 temp_nexe_file_.reset(new TempFile(plugin_, handle));
278 // Open it for reading as the cached nexe file.
279 NexeReadDidOpen(temp_nexe_file_->Open(false));
282 void PnaclCoordinator::BitcodeStreamCacheMiss(int64_t expected_pexe_size,
283 PP_FileHandle nexe_handle) {
284 // IMPORTANT: Make sure that PnaclResources::StartLoad() is only
285 // called after you receive a response to a request for a .pexe file.
287 // The component updater's resource throttles + OnDemand update/install
288 // should block the URL request until the compiler is present. Now we
289 // can load the resources (e.g. llc and ld nexes).
290 resources_.reset(new PnaclResources(plugin_, pnacl_options_.use_subzero));
291 CHECK(resources_ != NULL);
293 // The first step of loading resources: read the resource info file.
294 if (!resources_->ReadResourceInfo()) {
295 ExitWithError();
296 return;
299 // Second step of loading resources: call StartLoad to load pnacl-llc
300 // and pnacl-ld, based on the filenames found in the resource info file.
301 if (!resources_->StartLoad()) {
302 ReportNonPpapiError(
303 PP_NACL_ERROR_PNACL_RESOURCE_FETCH,
304 std::string("The Portable Native Client (pnacl) component is not "
305 "installed. Please consult chrome://components for more "
306 "information."));
307 return;
310 expected_pexe_size_ = expected_pexe_size;
312 for (int i = 0; i < split_module_count_; i++) {
313 PP_FileHandle obj_handle =
314 plugin_->nacl_interface()->CreateTemporaryFile(plugin_->pp_instance());
315 nacl::scoped_ptr<TempFile> temp_file(new TempFile(plugin_, obj_handle));
316 int32_t pp_error = temp_file->Open(true);
317 if (pp_error != PP_OK) {
318 ReportPpapiError(PP_NACL_ERROR_PNACL_CREATE_TEMP,
319 pp_error,
320 "Failed to open scratch object file.");
321 return;
322 } else {
323 obj_files_.push_back(temp_file.release());
326 invalid_desc_wrapper_.reset(plugin_->wrapper_factory()->MakeInvalid());
328 temp_nexe_file_.reset(new TempFile(plugin_, nexe_handle));
329 // Open the nexe file for connecting ld and sel_ldr.
330 // Start translation when done with this last step of setup!
331 RunTranslate(temp_nexe_file_->Open(true));
334 void PnaclCoordinator::BitcodeStreamGotData(const void* data, int32_t length) {
335 DCHECK(translate_thread_.get());
337 translate_thread_->PutBytes(data, length);
338 if (data && length > 0)
339 pexe_size_ += length;
342 void PnaclCoordinator::BitcodeStreamDidFinish(int32_t pp_error) {
343 PLUGIN_PRINTF(("PnaclCoordinator::BitcodeStreamDidFinish (pp_error=%"
344 NACL_PRId32 ")\n", pp_error));
345 if (pp_error != PP_OK) {
346 // Defer reporting the error and cleanup until after the translation
347 // thread returns, because it may be accessing the coordinator's
348 // objects or writing to the files.
349 translate_finish_error_ = pp_error;
350 if (pp_error == PP_ERROR_ABORTED) {
351 error_info_.SetReport(PP_NACL_ERROR_PNACL_PEXE_FETCH_ABORTED,
352 "PnaclCoordinator: pexe load failed (aborted).");
354 if (pp_error == PP_ERROR_NOACCESS) {
355 error_info_.SetReport(PP_NACL_ERROR_PNACL_PEXE_FETCH_NOACCESS,
356 "PnaclCoordinator: pexe load failed (no access).");
357 } else {
358 std::stringstream ss;
359 ss << "PnaclCoordinator: pexe load failed (pp_error=" << pp_error << ").";
360 error_info_.SetReport(PP_NACL_ERROR_PNACL_PEXE_FETCH_OTHER, ss.str());
363 if (translate_thread_->started())
364 translate_thread_->AbortSubprocesses();
365 else
366 TranslateFinished(pp_error);
367 } else {
368 // Compare download completion pct (100% now), to compile completion pct.
369 GetNaClInterface()->LogBytesCompiledVsDownloaded(
370 pnacl_options_.use_subzero, pexe_bytes_compiled_, pexe_size_);
371 translate_thread_->EndStream();
375 void PnaclCoordinator::BitcodeGotCompiled(int32_t pp_error,
376 int64_t bytes_compiled) {
377 DCHECK(pp_error == PP_OK);
378 pexe_bytes_compiled_ += bytes_compiled;
379 // Hold off reporting the last few bytes of progress, since we don't know
380 // when they are actually completely compiled. "bytes_compiled" only means
381 // that bytes were sent to the compiler.
382 if (expected_pexe_size_ != -1) {
383 if (!ShouldDelayProgressEvent()) {
384 GetNaClInterface()->DispatchEvent(plugin_->pp_instance(),
385 PP_NACL_EVENT_PROGRESS,
386 pexe_url_.c_str(),
387 PP_TRUE,
388 pexe_bytes_compiled_,
389 expected_pexe_size_);
391 } else {
392 GetNaClInterface()->DispatchEvent(plugin_->pp_instance(),
393 PP_NACL_EVENT_PROGRESS,
394 pexe_url_.c_str(),
395 PP_FALSE,
396 pexe_bytes_compiled_,
397 expected_pexe_size_);
401 pp::CompletionCallback PnaclCoordinator::GetCompileProgressCallback(
402 int64_t bytes_compiled) {
403 return callback_factory_.NewCallback(&PnaclCoordinator::BitcodeGotCompiled,
404 bytes_compiled);
407 void PnaclCoordinator::RunTranslate(int32_t pp_error) {
408 PLUGIN_PRINTF(("PnaclCoordinator::RunTranslate (pp_error=%"
409 NACL_PRId32 ")\n", pp_error));
410 // Invoke llc followed by ld off the main thread. This allows use of
411 // blocking RPCs that would otherwise block the JavaScript main thread.
412 pp::CompletionCallback report_translate_finished =
413 callback_factory_.NewCallback(&PnaclCoordinator::TranslateFinished);
415 CHECK(translate_thread_ != NULL);
416 translate_thread_->RunTranslate(report_translate_finished, &obj_files_,
417 num_threads_, temp_nexe_file_.get(),
418 invalid_desc_wrapper_.get(), &error_info_,
419 resources_.get(), &pnacl_options_,
420 architecture_attributes_, this, plugin_);
423 } // namespace plugin