Commit 0ba65965 authored by nhiroki@chromium.org's avatar nhiroki@chromium.org

ServiceWorker: Add a function to abort all pending jobs in SWJobCoordinator

This will be used in the corruption recovery process.
The process try to shutdown ServiceWorkerCoordinator and abort pending jobs
retained by the coordinator to reject waiting promises.


BUG=371675
TEST=content_unittest --gtest_filter=ServiceWorkerJobTest.AbortAll_*

Review URL: https://codereview.chromium.org/345583002

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@278677 0039d316-1c4b-4281-b951-d872f2087c98
parent 971fda44
......@@ -41,6 +41,12 @@ void ServiceWorkerJobCoordinator::JobQueue::Pop(
jobs_.front()->Start();
}
void ServiceWorkerJobCoordinator::JobQueue::AbortAll() {
for (size_t i = 0; i < jobs_.size(); ++i)
jobs_[i]->Abort();
STLDeleteElements(&jobs_);
}
void ServiceWorkerJobCoordinator::JobQueue::ClearForShutdown() {
STLDeleteElements(&jobs_);
}
......@@ -52,14 +58,14 @@ ServiceWorkerJobCoordinator::ServiceWorkerJobCoordinator(
ServiceWorkerJobCoordinator::~ServiceWorkerJobCoordinator() {
if (!context_) {
for (RegistrationJobMap::iterator it = jobs_.begin(); it != jobs_.end();
++it) {
for (RegistrationJobMap::iterator it = job_queues_.begin();
it != job_queues_.end(); ++it) {
it->second.ClearForShutdown();
}
jobs_.clear();
job_queues_.clear();
}
DCHECK(jobs_.empty()) << "Destroying ServiceWorkerJobCoordinator with "
<< jobs_.size() << " job queues";
DCHECK(job_queues_.empty()) << "Destroying ServiceWorkerJobCoordinator with "
<< job_queues_.size() << " job queues";
}
void ServiceWorkerJobCoordinator::Register(
......@@ -70,7 +76,8 @@ void ServiceWorkerJobCoordinator::Register(
scoped_ptr<ServiceWorkerRegisterJobBase> job(
new ServiceWorkerRegisterJob(context_, pattern, script_url));
ServiceWorkerRegisterJob* queued_job =
static_cast<ServiceWorkerRegisterJob*>(jobs_[pattern].Push(job.Pass()));
static_cast<ServiceWorkerRegisterJob*>(
job_queues_[pattern].Push(job.Pass()));
queued_job->AddCallback(callback, source_process_id);
}
......@@ -80,17 +87,26 @@ void ServiceWorkerJobCoordinator::Unregister(
scoped_ptr<ServiceWorkerRegisterJobBase> job(
new ServiceWorkerUnregisterJob(context_, pattern));
ServiceWorkerUnregisterJob* queued_job =
static_cast<ServiceWorkerUnregisterJob*>(jobs_[pattern].Push(job.Pass()));
static_cast<ServiceWorkerUnregisterJob*>(
job_queues_[pattern].Push(job.Pass()));
queued_job->AddCallback(callback);
}
void ServiceWorkerJobCoordinator::AbortAll() {
for (RegistrationJobMap::iterator it = job_queues_.begin();
it != job_queues_.end(); ++it) {
it->second.AbortAll();
}
job_queues_.clear();
}
void ServiceWorkerJobCoordinator::FinishJob(const GURL& pattern,
ServiceWorkerRegisterJobBase* job) {
RegistrationJobMap::iterator pending_jobs = jobs_.find(pattern);
DCHECK(pending_jobs != jobs_.end()) << "Deleting non-existent job.";
RegistrationJobMap::iterator pending_jobs = job_queues_.find(pattern);
DCHECK(pending_jobs != job_queues_.end()) << "Deleting non-existent job.";
pending_jobs->second.Pop(job);
if (pending_jobs->second.empty())
jobs_.erase(pending_jobs);
job_queues_.erase(pending_jobs);
}
} // namespace content
......@@ -35,7 +35,11 @@ class CONTENT_EXPORT ServiceWorkerJobCoordinator {
const GURL& pattern,
const ServiceWorkerUnregisterJob::UnregistrationCallback& callback);
// Jobs are removed whenever they are finished or canceled.
// Calls ServiceWorkerRegisterJobBase::Abort() on all jobs and removes them.
void AbortAll();
// Removes the job. A job that was not aborted must call FinishJob when it is
// done.
void FinishJob(const GURL& pattern, ServiceWorkerRegisterJobBase* job);
private:
......@@ -55,6 +59,9 @@ class CONTENT_EXPORT ServiceWorkerJobCoordinator {
bool empty() { return jobs_.empty(); }
// Aborts all jobs in the queue and removes them.
void AbortAll();
// Marks that the browser is shutting down, so jobs may be destroyed before
// finishing.
void ClearForShutdown();
......@@ -68,7 +75,7 @@ class CONTENT_EXPORT ServiceWorkerJobCoordinator {
// The ServiceWorkerContextCore object should always outlive the
// job coordinator, the core owns the coordinator.
base::WeakPtr<ServiceWorkerContextCore> context_;
RegistrationJobMap jobs_;
RegistrationJobMap job_queues_;
DISALLOW_COPY_AND_ASSIGN(ServiceWorkerJobCoordinator);
};
......
......@@ -584,4 +584,119 @@ TEST_F(ServiceWorkerJobTest, ParallelUnreg) {
ASSERT_EQ(scoped_refptr<ServiceWorkerRegistration>(), registration);
}
TEST_F(ServiceWorkerJobTest, AbortAll_Register) {
GURL pattern1("http://www1.example.com/*");
GURL pattern2("http://www2.example.com/*");
GURL script_url1("http://www1.example.com/service_worker.js");
GURL script_url2("http://www2.example.com/service_worker.js");
bool registration_called1 = false;
scoped_refptr<ServiceWorkerRegistration> registration1;
job_coordinator()->Register(
pattern1,
script_url1,
render_process_id_,
SaveRegistration(SERVICE_WORKER_ERROR_ABORT,
&registration_called1, &registration1));
bool registration_called2 = false;
scoped_refptr<ServiceWorkerRegistration> registration2;
job_coordinator()->Register(
pattern2,
script_url2,
render_process_id_,
SaveRegistration(SERVICE_WORKER_ERROR_ABORT,
&registration_called2, &registration2));
ASSERT_FALSE(registration_called1);
ASSERT_FALSE(registration_called2);
job_coordinator()->AbortAll();
base::RunLoop().RunUntilIdle();
ASSERT_TRUE(registration_called1);
ASSERT_TRUE(registration_called2);
bool find_called1 = false;
storage()->FindRegistrationForPattern(
pattern1,
SaveFoundRegistration(
SERVICE_WORKER_ERROR_NOT_FOUND, &find_called1, &registration1));
bool find_called2 = false;
storage()->FindRegistrationForPattern(
pattern2,
SaveFoundRegistration(
SERVICE_WORKER_ERROR_NOT_FOUND, &find_called2, &registration2));
base::RunLoop().RunUntilIdle();
ASSERT_TRUE(find_called1);
ASSERT_TRUE(find_called2);
EXPECT_EQ(scoped_refptr<ServiceWorkerRegistration>(), registration1);
EXPECT_EQ(scoped_refptr<ServiceWorkerRegistration>(), registration2);
}
TEST_F(ServiceWorkerJobTest, AbortAll_Unregister) {
GURL pattern1("http://www1.example.com/*");
GURL pattern2("http://www2.example.com/*");
bool unregistration_called1 = false;
scoped_refptr<ServiceWorkerRegistration> registration1;
job_coordinator()->Unregister(
pattern1,
SaveUnregistration(SERVICE_WORKER_ERROR_ABORT,
&unregistration_called1));
bool unregistration_called2 = false;
job_coordinator()->Unregister(
pattern2,
SaveUnregistration(SERVICE_WORKER_ERROR_ABORT,
&unregistration_called2));
ASSERT_FALSE(unregistration_called1);
ASSERT_FALSE(unregistration_called2);
job_coordinator()->AbortAll();
base::RunLoop().RunUntilIdle();
ASSERT_TRUE(unregistration_called1);
ASSERT_TRUE(unregistration_called2);
}
TEST_F(ServiceWorkerJobTest, AbortAll_RegUnreg) {
GURL pattern("http://www.example.com/*");
GURL script_url("http://www.example.com/service_worker.js");
bool registration_called = false;
scoped_refptr<ServiceWorkerRegistration> registration;
job_coordinator()->Register(
pattern,
script_url,
render_process_id_,
SaveRegistration(SERVICE_WORKER_ERROR_ABORT,
&registration_called, &registration));
bool unregistration_called = false;
job_coordinator()->Unregister(
pattern,
SaveUnregistration(SERVICE_WORKER_ERROR_ABORT,
&unregistration_called));
ASSERT_FALSE(registration_called);
ASSERT_FALSE(unregistration_called);
job_coordinator()->AbortAll();
base::RunLoop().RunUntilIdle();
ASSERT_TRUE(registration_called);
ASSERT_TRUE(unregistration_called);
bool find_called = false;
storage()->FindRegistrationForPattern(
pattern,
SaveFoundRegistration(
SERVICE_WORKER_ERROR_NOT_FOUND, &find_called, &registration));
base::RunLoop().RunUntilIdle();
ASSERT_TRUE(find_called);
EXPECT_EQ(scoped_refptr<ServiceWorkerRegistration>(), registration);
}
} // namespace content
......@@ -38,7 +38,8 @@ ServiceWorkerRegisterJob::ServiceWorkerRegisterJob(
weak_factory_(this) {}
ServiceWorkerRegisterJob::~ServiceWorkerRegisterJob() {
DCHECK(!context_ || phase_ == INITIAL || phase_ == COMPLETE)
DCHECK(!context_ ||
phase_ == INITIAL || phase_ == COMPLETE || phase_ == ABORT)
<< "Jobs should only be interrupted during shutdown.";
}
......@@ -64,6 +65,13 @@ void ServiceWorkerRegisterJob::Start() {
weak_factory_.GetWeakPtr()));
}
void ServiceWorkerRegisterJob::Abort() {
SetPhase(ABORT);
CompleteInternal(SERVICE_WORKER_ERROR_ABORT);
// Don't have to call FinishJob() because the caller takes care of removing
// the jobs from the queue.
}
bool ServiceWorkerRegisterJob::Equals(ServiceWorkerRegisterJobBase* job) {
if (job->GetType() != GetType())
return false;
......@@ -131,6 +139,8 @@ void ServiceWorkerRegisterJob::SetPhase(Phase phase) {
case COMPLETE:
DCHECK(phase_ != INITIAL && phase_ != COMPLETE) << phase_;
break;
case ABORT:
break;
}
phase_ = phase;
}
......@@ -359,6 +369,12 @@ void ServiceWorkerRegisterJob::OnActivateFinished(
}
void ServiceWorkerRegisterJob::Complete(ServiceWorkerStatusCode status) {
CompleteInternal(status);
context_->job_coordinator()->FinishJob(pattern_, this);
}
void ServiceWorkerRegisterJob::CompleteInternal(
ServiceWorkerStatusCode status) {
SetPhase(COMPLETE);
if (status != SERVICE_WORKER_OK) {
if (registration() && registration()->waiting_version()) {
......@@ -380,7 +396,6 @@ void ServiceWorkerRegisterJob::Complete(ServiceWorkerStatusCode status) {
context_->storage()->NotifyDoneInstallingRegistration(
registration(), pending_version(), status);
}
context_->job_coordinator()->FinishJob(pattern_, this);
}
void ServiceWorkerRegisterJob::ResolvePromise(
......
......@@ -53,6 +53,7 @@ class ServiceWorkerRegisterJob : public ServiceWorkerRegisterJobBase {
// ServiceWorkerRegisterJobBase implementation:
virtual void Start() OVERRIDE;
virtual void Abort() OVERRIDE;
virtual bool Equals(ServiceWorkerRegisterJobBase* job) OVERRIDE;
virtual RegistrationJobType GetType() OVERRIDE;
......@@ -70,7 +71,8 @@ class ServiceWorkerRegisterJob : public ServiceWorkerRegisterJobBase {
INSTALL,
STORE,
ACTIVATE,
COMPLETE
COMPLETE,
ABORT,
};
// Holds internal state of ServiceWorkerRegistrationJob, to compel use of the
......@@ -103,6 +105,7 @@ class ServiceWorkerRegisterJob : public ServiceWorkerRegisterJobBase {
void ActivateAndContinue();
void OnActivateFinished(ServiceWorkerStatusCode status);
void Complete(ServiceWorkerStatusCode status);
void CompleteInternal(ServiceWorkerStatusCode status);
void ResolvePromise(ServiceWorkerStatusCode status,
ServiceWorkerRegistration* registration,
......
......@@ -18,6 +18,10 @@ class ServiceWorkerRegisterJobBase {
// Starts the job. This method should be called once and only once per job.
virtual void Start() = 0;
// Aborts the job. This method should be called once and only once per job.
// It can be called regardless of whether Start() was called.
virtual void Abort() = 0;
// Returns true if this job is identical to |job| for the purpose of
// collapsing them together in a ServiceWorkerJobCoordinator queue.
// Registration jobs are equal if they are for the same pattern and script
......
......@@ -34,6 +34,10 @@ void ServiceWorkerUnregisterJob::Start() {
weak_factory_.GetWeakPtr()));
}
void ServiceWorkerUnregisterJob::Abort() {
CompleteInternal(SERVICE_WORKER_ERROR_ABORT);
}
bool ServiceWorkerUnregisterJob::Equals(ServiceWorkerRegisterJobBase* job) {
if (job->GetType() != GetType())
return false;
......@@ -70,12 +74,17 @@ void ServiceWorkerUnregisterJob::DeleteExistingRegistration(
}
void ServiceWorkerUnregisterJob::Complete(ServiceWorkerStatusCode status) {
CompleteInternal(status);
context_->job_coordinator()->FinishJob(pattern_, this);
}
void ServiceWorkerUnregisterJob::CompleteInternal(
ServiceWorkerStatusCode status) {
for (std::vector<UnregistrationCallback>::iterator it = callbacks_.begin();
it != callbacks_.end();
++it) {
it->Run(status);
}
context_->job_coordinator()->FinishJob(pattern_, this);
}
} // namespace content
......@@ -40,6 +40,7 @@ class ServiceWorkerUnregisterJob : public ServiceWorkerRegisterJobBase {
// ServiceWorkerRegisterJobBase implementation:
virtual void Start() OVERRIDE;
virtual void Abort() OVERRIDE;
virtual bool Equals(ServiceWorkerRegisterJobBase* job) OVERRIDE;
virtual RegistrationJobType GetType() OVERRIDE;
......@@ -48,6 +49,7 @@ class ServiceWorkerUnregisterJob : public ServiceWorkerRegisterJobBase {
ServiceWorkerStatusCode status,
const scoped_refptr<ServiceWorkerRegistration>& registration);
void Complete(ServiceWorkerStatusCode status);
void CompleteInternal(ServiceWorkerStatusCode status);
// The ServiceWorkerStorage object should always outlive this.
base::WeakPtr<ServiceWorkerContextCore> context_;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment