Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(225)

Side by Side Diff: content/browser/service_worker/service_worker_version.cc

Issue 962543005: Service Worker: Add metrics and timeout for starting a Service Worker. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: sync again Created 5 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2013 The Chromium Authors. All rights reserved. 1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "content/browser/service_worker/service_worker_version.h" 5 #include "content/browser/service_worker/service_worker_version.h"
6 6
7 #include "base/command_line.h" 7 #include "base/command_line.h"
8 #include "base/memory/ref_counted.h" 8 #include "base/memory/ref_counted.h"
9 #include "base/metrics/histogram_macros.h"
9 #include "base/stl_util.h" 10 #include "base/stl_util.h"
10 #include "base/strings/string16.h" 11 #include "base/strings/string16.h"
11 #include "base/strings/utf_string_conversions.h" 12 #include "base/strings/utf_string_conversions.h"
12 #include "base/time/time.h" 13 #include "base/time/time.h"
13 #include "content/browser/message_port_message_filter.h" 14 #include "content/browser/message_port_message_filter.h"
14 #include "content/browser/message_port_service.h" 15 #include "content/browser/message_port_service.h"
15 #include "content/browser/service_worker/embedded_worker_instance.h" 16 #include "content/browser/service_worker/embedded_worker_instance.h"
16 #include "content/browser/service_worker/embedded_worker_registry.h" 17 #include "content/browser/service_worker/embedded_worker_registry.h"
17 #include "content/browser/service_worker/service_worker_context_core.h" 18 #include "content/browser/service_worker/service_worker_context_core.h"
18 #include "content/browser/service_worker/service_worker_context_wrapper.h" 19 #include "content/browser/service_worker/service_worker_context_wrapper.h"
(...skipping 10 matching lines...) Expand all
29 #include "content/public/browser/web_contents_observer.h" 30 #include "content/public/browser/web_contents_observer.h"
30 #include "content/public/common/child_process_host.h" 31 #include "content/public/common/child_process_host.h"
31 #include "content/public/common/content_client.h" 32 #include "content/public/common/content_client.h"
32 #include "content/public/common/content_switches.h" 33 #include "content/public/common/content_switches.h"
33 #include "content/public/common/result_codes.h" 34 #include "content/public/common/result_codes.h"
34 #include "net/http/http_response_info.h" 35 #include "net/http/http_response_info.h"
35 36
36 namespace content { 37 namespace content {
37 38
38 typedef ServiceWorkerVersion::StatusCallback StatusCallback; 39 typedef ServiceWorkerVersion::StatusCallback StatusCallback;
39 typedef ServiceWorkerVersion::MessageCallback MessageCallback;
40 40
41 class ServiceWorkerVersion::GetClientDocumentsCallback 41 class ServiceWorkerVersion::GetClientDocumentsCallback
42 : public base::RefCounted<GetClientDocumentsCallback> { 42 : public base::RefCounted<GetClientDocumentsCallback> {
43 public: 43 public:
44 GetClientDocumentsCallback(int request_id, 44 GetClientDocumentsCallback(int request_id,
45 ServiceWorkerVersion* version) 45 ServiceWorkerVersion* version)
46 : request_id_(request_id), 46 : request_id_(request_id),
47 version_(version) { 47 version_(version) {
48 DCHECK(version_); 48 DCHECK(version_);
49 } 49 }
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
84 84
85 // Default delay for scheduled update. 85 // Default delay for scheduled update.
86 const int kUpdateDelaySeconds = 1; 86 const int kUpdateDelaySeconds = 1;
87 87
88 // Delay between sending pings to the worker. 88 // Delay between sending pings to the worker.
89 const int kPingIntervalTime = 10; // 10 secs. 89 const int kPingIntervalTime = 10; // 10 secs.
90 90
91 // Timeout for waiting for a response to a ping. 91 // Timeout for waiting for a response to a ping.
92 const int kPingTimeoutTime = 30; // 30 secs. 92 const int kPingTimeoutTime = 30; // 30 secs.
93 93
94 // Timeout for the worker to start.
95 const int kStartWorkerTimeoutMinutes = 5;
96
97 // If the SW was destructed while starting up, how many seconds it
98 // had to start up for this to be considered a timeout occurrence.
99 const int kDestructedStartingWorkerTimeoutThresholdSeconds = 5; // 5 secs.
100
94 const char kClaimClientsStateErrorMesage[] = 101 const char kClaimClientsStateErrorMesage[] =
95 "Only the active worker can claim clients."; 102 "Only the active worker can claim clients.";
96 103
97 const char kClaimClientsShutdownErrorMesage[] = 104 const char kClaimClientsShutdownErrorMesage[] =
98 "Failed to claim clients due to Service Worker system shutdown."; 105 "Failed to claim clients due to Service Worker system shutdown.";
99 106
100 void RunSoon(const base::Closure& callback) { 107 void RunSoon(const base::Closure& callback) {
101 if (!callback.is_null()) 108 if (!callback.is_null())
102 base::MessageLoop::current()->PostTask(FROM_HERE, callback); 109 base::MessageLoop::current()->PostTask(FROM_HERE, callback);
103 } 110 }
(...skipping 196 matching lines...) Expand 10 before | Expand all | Expand 10 after
300 if (registration) { 307 if (registration) {
301 registration_id_ = registration->id(); 308 registration_id_ = registration->id();
302 scope_ = registration->pattern(); 309 scope_ = registration->pattern();
303 } 310 }
304 context_->AddLiveVersion(this); 311 context_->AddLiveVersion(this);
305 embedded_worker_ = context_->embedded_worker_registry()->CreateWorker(); 312 embedded_worker_ = context_->embedded_worker_registry()->CreateWorker();
306 embedded_worker_->AddListener(this); 313 embedded_worker_->AddListener(this);
307 } 314 }
308 315
309 ServiceWorkerVersion::~ServiceWorkerVersion() { 316 ServiceWorkerVersion::~ServiceWorkerVersion() {
317 // The user may have closed the tab waiting for SW to start up.
318 if (start_worker_timeout_timer_.IsRunning() && !start_timing_.is_null()) {
319 if ((base::TimeTicks::Now() - start_timing_) >
320 base::TimeDelta::FromSeconds(
321 kDestructedStartingWorkerTimeoutThresholdSeconds)) {
322 RecordStartWorkerResult(SERVICE_WORKER_ERROR_TIMEOUT);
323 }
324 }
325
310 embedded_worker_->RemoveListener(this); 326 embedded_worker_->RemoveListener(this);
311 if (context_) 327 if (context_)
312 context_->RemoveLiveVersion(version_id_); 328 context_->RemoveLiveVersion(version_id_);
313 // EmbeddedWorker's dtor sends StopWorker if it's still running. 329 // EmbeddedWorker's dtor sends StopWorker if it's still running.
314 } 330 }
315 331
316 void ServiceWorkerVersion::SetStatus(Status status) { 332 void ServiceWorkerVersion::SetStatus(Status status) {
317 if (status_ == status) 333 if (status_ == status)
318 return; 334 return;
319 335
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
361 RunSoon(base::Bind(callback, SERVICE_WORKER_ERROR_START_WORKER_FAILED)); 377 RunSoon(base::Bind(callback, SERVICE_WORKER_ERROR_START_WORKER_FAILED));
362 return; 378 return;
363 } 379 }
364 switch (running_status()) { 380 switch (running_status()) {
365 case RUNNING: 381 case RUNNING:
366 RunSoon(base::Bind(callback, SERVICE_WORKER_OK)); 382 RunSoon(base::Bind(callback, SERVICE_WORKER_OK));
367 return; 383 return;
368 case STOPPING: 384 case STOPPING:
369 case STOPPED: 385 case STOPPED:
370 case STARTING: 386 case STARTING:
387 if (!start_worker_timeout_timer_.IsRunning())
388 ScheduleStartWorkerTimeout();
371 start_callbacks_.push_back(callback); 389 start_callbacks_.push_back(callback);
372 if (running_status() == STOPPED) { 390 if (running_status() == STOPPED) {
373 DCHECK(!cache_listener_.get()); 391 DCHECK(!cache_listener_.get());
374 cache_listener_.reset(new ServiceWorkerCacheListener(this, context_)); 392 cache_listener_.reset(new ServiceWorkerCacheListener(this, context_));
375 embedded_worker_->Start( 393 embedded_worker_->Start(
376 version_id_, 394 version_id_, scope_, script_url_, pause_after_download,
377 scope_, 395 base::Bind(&ServiceWorkerVersion::OnStartSentAndScriptEvaluated,
378 script_url_,
379 pause_after_download,
380 base::Bind(&ServiceWorkerVersion::OnStartMessageSent,
381 weak_factory_.GetWeakPtr())); 396 weak_factory_.GetWeakPtr()));
382 } 397 }
383 return; 398 return;
384 } 399 }
385 } 400 }
386 401
387 void ServiceWorkerVersion::StopWorker(const StatusCallback& callback) { 402 void ServiceWorkerVersion::StopWorker(const StatusCallback& callback) {
388 if (running_status() == STOPPED) { 403 if (running_status() == STOPPED) {
389 RunSoon(base::Bind(callback, SERVICE_WORKER_OK)); 404 RunSoon(base::Bind(callback, SERVICE_WORKER_OK));
390 return; 405 return;
(...skipping 370 matching lines...) Expand 10 before | Expand all | Expand 10 after
761 void ServiceWorkerVersion::Doom() { 776 void ServiceWorkerVersion::Doom() {
762 if (is_doomed_) 777 if (is_doomed_)
763 return; 778 return;
764 is_doomed_ = true; 779 is_doomed_ = true;
765 if (!HasControllee()) 780 if (!HasControllee())
766 DoomInternal(); 781 DoomInternal();
767 } 782 }
768 783
769 void ServiceWorkerVersion::SetDevToolsAttached(bool attached) { 784 void ServiceWorkerVersion::SetDevToolsAttached(bool attached) {
770 embedded_worker()->set_devtools_attached(attached); 785 embedded_worker()->set_devtools_attached(attached);
771 if (attached) 786 if (attached) {
787 // Set to null time so we don't record the startup time metric.
788 start_timing_ = base::TimeTicks();
772 return; 789 return;
790 }
773 // If devtools is detached try scheduling the timers for stopping the worker 791 // If devtools is detached try scheduling the timers for stopping the worker
774 // now. 792 // now.
775 if (!stop_worker_timer_.IsRunning()) 793 if (!stop_worker_timer_.IsRunning())
776 ScheduleStopWorker(); 794 ScheduleStopWorker();
777 if (!ping_worker_timer_.IsRunning()) 795 if (!ping_worker_timer_.IsRunning())
778 StartPingWorker(); 796 StartPingWorker();
797 if (!start_worker_timeout_timer_.IsRunning() && !start_callbacks_.empty())
798 ScheduleStartWorkerTimeout();
779 } 799 }
780 800
781 void ServiceWorkerVersion::SetMainScriptHttpResponseInfo( 801 void ServiceWorkerVersion::SetMainScriptHttpResponseInfo(
782 const net::HttpResponseInfo& http_info) { 802 const net::HttpResponseInfo& http_info) {
783 main_script_http_info_.reset(new net::HttpResponseInfo(http_info)); 803 main_script_http_info_.reset(new net::HttpResponseInfo(http_info));
784 } 804 }
785 805
786 const net::HttpResponseInfo* 806 const net::HttpResponseInfo*
787 ServiceWorkerVersion::GetMainScriptHttpResponseInfo() { 807 ServiceWorkerVersion::GetMainScriptHttpResponseInfo() {
788 return main_script_http_info_.get(); 808 return main_script_http_info_.get();
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after
852 872
853 FOR_EACH_OBSERVER(Listener, listeners_, OnWorkerStopped(this)); 873 FOR_EACH_OBSERVER(Listener, listeners_, OnWorkerStopped(this));
854 874
855 // There should be no more communication from/to a stopped worker. Deleting 875 // There should be no more communication from/to a stopped worker. Deleting
856 // the listener prevents any pending completion callbacks from causing 876 // the listener prevents any pending completion callbacks from causing
857 // messages to be sent to the stopped worker. 877 // messages to be sent to the stopped worker.
858 cache_listener_.reset(); 878 cache_listener_.reset();
859 879
860 // Restart worker if we have any start callbacks and the worker isn't doomed. 880 // Restart worker if we have any start callbacks and the worker isn't doomed.
861 if (should_restart) { 881 if (should_restart) {
882 start_worker_timeout_timer_.Reset();
862 cache_listener_.reset(new ServiceWorkerCacheListener(this, context_)); 883 cache_listener_.reset(new ServiceWorkerCacheListener(this, context_));
863 embedded_worker_->Start( 884 embedded_worker_->Start(
864 version_id_, scope_, script_url_, false /* pause_after_download */, 885 version_id_, scope_, script_url_, false /* pause_after_download */,
865 base::Bind(&ServiceWorkerVersion::OnStartMessageSent, 886 base::Bind(&ServiceWorkerVersion::OnStartSentAndScriptEvaluated,
866 weak_factory_.GetWeakPtr())); 887 weak_factory_.GetWeakPtr()));
867 } 888 }
868 } 889 }
869 890
870 void ServiceWorkerVersion::OnReportException( 891 void ServiceWorkerVersion::OnReportException(
871 const base::string16& error_message, 892 const base::string16& error_message,
872 int line_number, 893 int line_number,
873 int column_number, 894 int column_number,
874 const GURL& source_url) { 895 const GURL& source_url) {
875 FOR_EACH_OBSERVER( 896 FOR_EACH_OBSERVER(
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after
928 IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_SkipWaiting, 949 IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_SkipWaiting,
929 OnSkipWaiting) 950 OnSkipWaiting)
930 IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_ClaimClients, 951 IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_ClaimClients,
931 OnClaimClients) 952 OnClaimClients)
932 IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_Pong, OnPongFromWorker) 953 IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_Pong, OnPongFromWorker)
933 IPC_MESSAGE_UNHANDLED(handled = false) 954 IPC_MESSAGE_UNHANDLED(handled = false)
934 IPC_END_MESSAGE_MAP() 955 IPC_END_MESSAGE_MAP()
935 return handled; 956 return handled;
936 } 957 }
937 958
938 void ServiceWorkerVersion::OnStartMessageSent( 959 void ServiceWorkerVersion::OnStartSentAndScriptEvaluated(
939 ServiceWorkerStatusCode status) { 960 ServiceWorkerStatusCode status) {
940 if (status != SERVICE_WORKER_OK) 961 if (status != SERVICE_WORKER_OK)
941 RunCallbacks(this, &start_callbacks_, status); 962 RunCallbacks(this, &start_callbacks_, status);
942 } 963 }
943 964
944 void ServiceWorkerVersion::DispatchInstallEventAfterStartWorker( 965 void ServiceWorkerVersion::DispatchInstallEventAfterStartWorker(
945 const StatusCallback& callback) { 966 const StatusCallback& callback) {
946 DCHECK_EQ(RUNNING, running_status()) 967 DCHECK_EQ(RUNNING, running_status())
947 << "Worker stopped too soon after it was started."; 968 << "Worker stopped too soon after it was started.";
948 969
(...skipping 480 matching lines...) Expand 10 before | Expand all | Expand 10 after
1429 base::TimeDelta::FromSeconds(kPingIntervalTime), 1450 base::TimeDelta::FromSeconds(kPingIntervalTime),
1430 base::Bind(&ServiceWorkerVersion::PingWorker, 1451 base::Bind(&ServiceWorkerVersion::PingWorker,
1431 weak_factory_.GetWeakPtr())); 1452 weak_factory_.GetWeakPtr()));
1432 } 1453 }
1433 1454
1434 void ServiceWorkerVersion::OnPingTimeout() { 1455 void ServiceWorkerVersion::OnPingTimeout() {
1435 if (running_status() != STARTING && running_status() != RUNNING) 1456 if (running_status() != STARTING && running_status() != RUNNING)
1436 return; 1457 return;
1437 ping_timed_out_ = true; 1458 ping_timed_out_ = true;
1438 // TODO(falken): Show a message to the developer that the SW was stopped due 1459 // TODO(falken): Show a message to the developer that the SW was stopped due
1439 // to timeout (crbug.com/457968). 1460 // to timeout (crbug.com/457968). Also, change the error code to
1461 // SERVICE_WORKER_ERROR_TIMEOUT.
1440 StopWorkerIfIdle(); 1462 StopWorkerIfIdle();
1441 } 1463 }
1442 1464
1443 void ServiceWorkerVersion::ScheduleStopWorker() { 1465 void ServiceWorkerVersion::ScheduleStopWorker() {
1444 if (running_status() != RUNNING) 1466 if (running_status() != RUNNING)
1445 return; 1467 return;
1446 stop_worker_timer_.Stop(); 1468 stop_worker_timer_.Stop();
1447 stop_worker_timer_.Start( 1469 stop_worker_timer_.Start(
1448 FROM_HERE, base::TimeDelta::FromSeconds( 1470 FROM_HERE, base::TimeDelta::FromSeconds(
1449 is_doomed_ ? kStopDoomedWorkerDelay : kStopWorkerDelay), 1471 is_doomed_ ? kStopDoomedWorkerDelay : kStopWorkerDelay),
(...skipping 23 matching lines...) Expand all
1473 !install_callbacks_.IsEmpty() || 1495 !install_callbacks_.IsEmpty() ||
1474 !fetch_callbacks_.IsEmpty() || 1496 !fetch_callbacks_.IsEmpty() ||
1475 !sync_callbacks_.IsEmpty() || 1497 !sync_callbacks_.IsEmpty() ||
1476 !notification_click_callbacks_.IsEmpty() || 1498 !notification_click_callbacks_.IsEmpty() ||
1477 !push_callbacks_.IsEmpty() || 1499 !push_callbacks_.IsEmpty() ||
1478 !geofencing_callbacks_.IsEmpty() || 1500 !geofencing_callbacks_.IsEmpty() ||
1479 !cross_origin_connect_callbacks_.IsEmpty() || 1501 !cross_origin_connect_callbacks_.IsEmpty() ||
1480 !streaming_url_request_jobs_.empty(); 1502 !streaming_url_request_jobs_.empty();
1481 } 1503 }
1482 1504
1505 void ServiceWorkerVersion::ScheduleStartWorkerTimeout() {
1506 DCHECK(!start_worker_timeout_timer_.IsRunning());
1507 start_timing_ = embedded_worker_->devtools_attached()
1508 ? base::TimeTicks()
1509 : base::TimeTicks::Now();
1510 start_callbacks_.push_back(
1511 base::Bind(&ServiceWorkerVersion::RecordStartWorkerResult,
1512 weak_factory_.GetWeakPtr()));
1513 start_worker_timeout_timer_.Start(
1514 FROM_HERE, base::TimeDelta::FromMinutes(kStartWorkerTimeoutMinutes),
1515 base::Bind(&ServiceWorkerVersion::OnStartWorkerTimeout,
1516 weak_factory_.GetWeakPtr()));
1517 }
1518
1519 void ServiceWorkerVersion::OnStartWorkerTimeout() {
1520 DCHECK(running_status() == STARTING || running_status() == STOPPING)
1521 << running_status();
1522
1523 if (embedded_worker_->devtools_attached()) {
1524 start_worker_timeout_timer_.Stop();
1525 return;
1526 }
1527
1528 scoped_refptr<ServiceWorkerVersion> protect(this);
1529 RunCallbacks(this, &start_callbacks_, SERVICE_WORKER_ERROR_TIMEOUT);
1530 if (running_status() == STARTING)
1531 embedded_worker_->Stop();
1532 }
1533
1534 void ServiceWorkerVersion::RecordStartWorkerResult(
1535 ServiceWorkerStatusCode status) {
1536 start_worker_timeout_timer_.Stop();
1537
1538 UMA_HISTOGRAM_ENUMERATION("ServiceWorker.StartWorker.Status", status,
1539 SERVICE_WORKER_ERROR_MAX_VALUE);
1540 if (status == SERVICE_WORKER_OK && !start_timing_.is_null()) {
1541 UMA_HISTOGRAM_MEDIUM_TIMES("ServiceWorker.StartWorker.Time",
1542 base::TimeTicks::Now() - start_timing_);
1543 }
1544
1545 if (status != SERVICE_WORKER_ERROR_TIMEOUT)
1546 return;
1547 EmbeddedWorkerInstance::StartingPhase phase =
1548 EmbeddedWorkerInstance::NOT_STARTING;
1549 EmbeddedWorkerInstance::Status running_status = embedded_worker_->status();
1550 // Build an artifical JavaScript exception to show in the ServiceWorker
1551 // log for developers; it's not user-facing so it's not a localized resource.
1552 std::string message = "ServiceWorker startup timed out. ";
1553 if (running_status != EmbeddedWorkerInstance::STARTING) {
1554 message.append("The worker had unexpected status: ");
1555 message.append(EmbeddedWorkerInstance::StatusToString(running_status));
1556 } else {
1557 phase = embedded_worker_->starting_phase();
1558 message.append("The worker was in startup phase: ");
1559 message.append(EmbeddedWorkerInstance::StartingPhaseToString(phase));
1560 }
1561 message.append(".");
1562 OnReportException(base::UTF8ToUTF16(message), -1, -1, GURL());
1563 DVLOG(1) << message;
1564 UMA_HISTOGRAM_ENUMERATION("ServiceWorker.StartWorker.TimeoutPhase",
1565 phase,
1566 EmbeddedWorkerInstance::STARTING_PHASE_MAX_VALUE);
1567 }
1568
1483 void ServiceWorkerVersion::DoomInternal() { 1569 void ServiceWorkerVersion::DoomInternal() {
1484 DCHECK(is_doomed_); 1570 DCHECK(is_doomed_);
1485 DCHECK(!HasControllee()); 1571 DCHECK(!HasControllee());
1486 SetStatus(REDUNDANT); 1572 SetStatus(REDUNDANT);
1487 StopWorkerIfIdle(); 1573 StopWorkerIfIdle();
1488 if (!context_) 1574 if (!context_)
1489 return; 1575 return;
1490 std::vector<ServiceWorkerDatabase::ResourceRecord> resources; 1576 std::vector<ServiceWorkerDatabase::ResourceRecord> resources;
1491 script_cache_map_.GetResources(&resources); 1577 script_cache_map_.GetResources(&resources);
1492 context_->storage()->PurgeResources(resources); 1578 context_->storage()->PurgeResources(resources);
1493 } 1579 }
1494 1580
1495 template <typename IDMAP> 1581 template <typename IDMAP>
1496 void ServiceWorkerVersion::RemoveCallbackAndStopIfDoomed( 1582 void ServiceWorkerVersion::RemoveCallbackAndStopIfDoomed(
1497 IDMAP* callbacks, 1583 IDMAP* callbacks,
1498 int request_id) { 1584 int request_id) {
1499 callbacks->Remove(request_id); 1585 callbacks->Remove(request_id);
1500 if (is_doomed_) { 1586 if (is_doomed_) {
1501 // The stop should be already scheduled, but try to stop immediately, in 1587 // The stop should be already scheduled, but try to stop immediately, in
1502 // order to release worker resources soon. 1588 // order to release worker resources soon.
1503 StopWorkerIfIdle(); 1589 StopWorkerIfIdle();
1504 } 1590 }
1505 } 1591 }
1506 1592
1507 } // namespace content 1593 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698