Skip to content

Commit cdcec35

Browse files
committed
add CancelableTaskTracker from Browser for password store
1 parent 5dc1aef commit cdcec35

File tree

3 files changed

+300
-0
lines changed

3 files changed

+300
-0
lines changed

nw.gypi

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,8 @@
229229
'src/chrome_breakpad_client.cc',
230230
'src/chrome_breakpad_client.h',
231231
'src/chrome_breakpad_client_mac.mm',
232+
'src/common/cancelable_task_tracker.cc',
233+
'src/common/cancelable_task_tracker.h',
232234
'src/common/print_messages.cc',
233235
'src/common/print_messages.h',
234236
'src/common/shell_switches.cc',

src/common/cancelable_task_tracker.cc

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
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 "content/nw/src/common/cancelable_task_tracker.h"
6+
7+
#include <utility>
8+
9+
#include "base/bind.h"
10+
#include "base/bind_helpers.h"
11+
#include "base/compiler_specific.h"
12+
#include "base/location.h"
13+
#include "base/memory/ref_counted.h"
14+
#include "base/message_loop/message_loop_proxy.h"
15+
#include "base/synchronization/cancellation_flag.h"
16+
#include "base/task_runner.h"
17+
18+
using base::Bind;
19+
using base::CancellationFlag;
20+
using base::Closure;
21+
using base::hash_map;
22+
using base::TaskRunner;
23+
24+
namespace {
25+
26+
void RunIfNotCanceled(const CancellationFlag* flag, const Closure& task) {
27+
if (!flag->IsSet())
28+
task.Run();
29+
}
30+
31+
void RunIfNotCanceledThenUntrack(const CancellationFlag* flag,
32+
const Closure& task,
33+
const Closure& untrack) {
34+
RunIfNotCanceled(flag, task);
35+
untrack.Run();
36+
}
37+
38+
bool IsCanceled(const CancellationFlag* flag,
39+
base::ScopedClosureRunner* cleanup_runner) {
40+
return flag->IsSet();
41+
}
42+
43+
void RunAndDeleteFlag(const Closure& closure, const CancellationFlag* flag) {
44+
closure.Run();
45+
delete flag;
46+
}
47+
48+
void RunOrPostToTaskRunner(TaskRunner* task_runner, const Closure& closure) {
49+
if (task_runner->RunsTasksOnCurrentThread())
50+
closure.Run();
51+
else
52+
task_runner->PostTask(FROM_HERE, closure);
53+
}
54+
55+
} // namespace
56+
57+
// static
58+
const CancelableTaskTracker::TaskId CancelableTaskTracker::kBadTaskId = 0;
59+
60+
CancelableTaskTracker::CancelableTaskTracker()
61+
: weak_factory_(this),
62+
next_id_(1) {}
63+
64+
CancelableTaskTracker::~CancelableTaskTracker() {
65+
DCHECK(thread_checker_.CalledOnValidThread());
66+
67+
TryCancelAll();
68+
}
69+
70+
CancelableTaskTracker::TaskId CancelableTaskTracker::PostTask(
71+
TaskRunner* task_runner,
72+
const tracked_objects::Location& from_here,
73+
const Closure& task) {
74+
DCHECK(thread_checker_.CalledOnValidThread());
75+
76+
return PostTaskAndReply(task_runner, from_here, task, Bind(&base::DoNothing));
77+
}
78+
79+
CancelableTaskTracker::TaskId CancelableTaskTracker::PostTaskAndReply(
80+
TaskRunner* task_runner,
81+
const tracked_objects::Location& from_here,
82+
const Closure& task,
83+
const Closure& reply) {
84+
DCHECK(thread_checker_.CalledOnValidThread());
85+
86+
// We need a MessageLoop to run reply.
87+
DCHECK(base::MessageLoopProxy::current().get());
88+
89+
// Owned by reply callback below.
90+
CancellationFlag* flag = new CancellationFlag();
91+
92+
TaskId id = next_id_;
93+
next_id_++; // int64 is big enough that we ignore the potential overflow.
94+
95+
const Closure& untrack_closure = Bind(
96+
&CancelableTaskTracker::Untrack, weak_factory_.GetWeakPtr(), id);
97+
bool success = task_runner->PostTaskAndReply(
98+
from_here,
99+
Bind(&RunIfNotCanceled, flag, task),
100+
Bind(&RunIfNotCanceledThenUntrack,
101+
base::Owned(flag), reply, untrack_closure));
102+
103+
if (!success)
104+
return kBadTaskId;
105+
106+
Track(id, flag);
107+
return id;
108+
}
109+
110+
CancelableTaskTracker::TaskId CancelableTaskTracker::NewTrackedTaskId(
111+
IsCanceledCallback* is_canceled_cb) {
112+
DCHECK(thread_checker_.CalledOnValidThread());
113+
DCHECK(base::MessageLoopProxy::current().get());
114+
115+
TaskId id = next_id_;
116+
next_id_++; // int64 is big enough that we ignore the potential overflow.
117+
118+
// Will be deleted by |untrack_and_delete_flag| after Untrack().
119+
CancellationFlag* flag = new CancellationFlag();
120+
121+
Closure untrack_and_delete_flag = Bind(
122+
&RunAndDeleteFlag,
123+
Bind(&CancelableTaskTracker::Untrack, weak_factory_.GetWeakPtr(), id),
124+
flag);
125+
126+
// Will always run |untrack_and_delete_flag| on current MessageLoop.
127+
base::ScopedClosureRunner* untrack_and_delete_flag_runner =
128+
new base::ScopedClosureRunner(
129+
Bind(&RunOrPostToTaskRunner,
130+
base::MessageLoopProxy::current(),
131+
untrack_and_delete_flag));
132+
133+
*is_canceled_cb = Bind(
134+
&IsCanceled, flag, base::Owned(untrack_and_delete_flag_runner));
135+
136+
Track(id, flag);
137+
return id;
138+
}
139+
140+
void CancelableTaskTracker::TryCancel(TaskId id) {
141+
DCHECK(thread_checker_.CalledOnValidThread());
142+
143+
hash_map<TaskId, CancellationFlag*>::const_iterator it = task_flags_.find(id);
144+
if (it == task_flags_.end()) {
145+
// Two possibilities:
146+
//
147+
// 1. The task has already been untracked.
148+
// 2. The TaskId is bad or unknown.
149+
//
150+
// Since this function is best-effort, it's OK to ignore these.
151+
return;
152+
}
153+
it->second->Set();
154+
}
155+
156+
void CancelableTaskTracker::TryCancelAll() {
157+
DCHECK(thread_checker_.CalledOnValidThread());
158+
159+
for (hash_map<TaskId, CancellationFlag*>::const_iterator it =
160+
task_flags_.begin();
161+
it != task_flags_.end();
162+
++it) {
163+
it->second->Set();
164+
}
165+
}
166+
167+
bool CancelableTaskTracker::HasTrackedTasks() const {
168+
DCHECK(thread_checker_.CalledOnValidThread());
169+
return !task_flags_.empty();
170+
}
171+
172+
void CancelableTaskTracker::Track(TaskId id, CancellationFlag* flag) {
173+
DCHECK(thread_checker_.CalledOnValidThread());
174+
175+
bool success = task_flags_.insert(std::make_pair(id, flag)).second;
176+
DCHECK(success);
177+
}
178+
179+
void CancelableTaskTracker::Untrack(TaskId id) {
180+
DCHECK(thread_checker_.CalledOnValidThread());
181+
size_t num = task_flags_.erase(id);
182+
DCHECK_EQ(1u, num);
183+
}

src/common/cancelable_task_tracker.h

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
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+
// CancelableTaskTracker posts tasks (in the form of a Closure) to a TaskRunner,
6+
// and is able to cancel the task later if it's not needed anymore. On
7+
// destruction, CancelableTaskTracker will cancel all tracked tasks.
8+
//
9+
// Each cancelable task can be associated with a reply (also a Closure). After
10+
// the task is run on the TaskRunner, |reply| will be posted back to originating
11+
// TaskRunner.
12+
//
13+
// NOTE:
14+
//
15+
// CancelableCallback (base/cancelable_callback.h) and WeakPtr binding are
16+
// preferred solutions for canceling a task. However, they don't support
17+
// cancelation from another thread. This is sometimes a performance critical
18+
// requirement. E.g. We need to cancel database lookup task on DB thread when
19+
// user changes inputed text. If it is performance critical to do a best effort
20+
// cancelation of a task, then CancelableTaskTracker is appropriate, otherwise
21+
// use one of the other mechanisms.
22+
//
23+
// THREAD-SAFETY:
24+
//
25+
// 1. CancelableTaskTracker objects are not thread safe. They must be created,
26+
// used, and destroyed on the originating thread that posts the task. It's safe
27+
// to destroy a CancelableTaskTracker while there are outstanding tasks. This is
28+
// commonly used to cancel all outstanding tasks.
29+
//
30+
// 2. Both task and reply are deleted on the originating thread.
31+
//
32+
// 3. IsCanceledCallback is thread safe and can be run or deleted on any thread.
33+
34+
#ifndef NW_COMMON_CANCELABLE_TASK_TRACKER_H_
35+
#define NW_COMMON_CANCELABLE_TASK_TRACKER_H_
36+
37+
#include "base/basictypes.h"
38+
#include "base/callback.h"
39+
#include "base/containers/hash_tables.h"
40+
#include "base/memory/weak_ptr.h"
41+
#include "base/threading/thread_checker.h"
42+
43+
namespace base {
44+
class CancellationFlag;
45+
class TaskRunner;
46+
} // namespace base
47+
48+
namespace tracked_objects {
49+
class Location;
50+
} // namespace tracked_objects
51+
52+
class CancelableTaskTracker {
53+
public:
54+
// All values except kBadTaskId are valid.
55+
typedef int64 TaskId;
56+
static const TaskId kBadTaskId;
57+
58+
typedef base::Callback<bool()> IsCanceledCallback;
59+
60+
CancelableTaskTracker();
61+
62+
// Cancels all tracked tasks.
63+
~CancelableTaskTracker();
64+
65+
TaskId PostTask(base::TaskRunner* task_runner,
66+
const tracked_objects::Location& from_here,
67+
const base::Closure& task);
68+
69+
TaskId PostTaskAndReply(base::TaskRunner* task_runner,
70+
const tracked_objects::Location& from_here,
71+
const base::Closure& task,
72+
const base::Closure& reply);
73+
74+
// Creates a tracked TaskId and an associated IsCanceledCallback. Client can
75+
// later call TryCancel() with the returned TaskId, and run |is_canceled_cb|
76+
// from any thread to check whether the TaskId is canceled.
77+
//
78+
// The returned task ID is tracked until the last copy of
79+
// |is_canceled_cb| is destroyed.
80+
//
81+
// Note. This function is used to address some special cancelation requirement
82+
// in existing code. You SHOULD NOT need this function in new code.
83+
TaskId NewTrackedTaskId(IsCanceledCallback* is_canceled_cb);
84+
85+
// After calling this function, |task| and |reply| will not run. If the
86+
// cancelation happens when |task| is running or has finished running, |reply|
87+
// will not run. If |reply| is running or has finished running, cancellation
88+
// is a noop.
89+
//
90+
// Note. It's OK to cancel a |task| for more than once. The later calls are
91+
// noops.
92+
void TryCancel(TaskId id);
93+
94+
// It's OK to call this function for more than once. The later calls are
95+
// noops.
96+
void TryCancelAll();
97+
98+
// Returns true iff there are in-flight tasks that are still being
99+
// tracked.
100+
bool HasTrackedTasks() const;
101+
102+
private:
103+
void Track(TaskId id, base::CancellationFlag* flag);
104+
void Untrack(TaskId id);
105+
106+
base::hash_map<TaskId, base::CancellationFlag*> task_flags_;
107+
base::WeakPtrFactory<CancelableTaskTracker> weak_factory_;
108+
109+
TaskId next_id_;
110+
base::ThreadChecker thread_checker_;
111+
112+
DISALLOW_COPY_AND_ASSIGN(CancelableTaskTracker);
113+
};
114+
115+
#endif // CHROME_COMMON_CANCELABLE_TASK_TRACKER_H_

0 commit comments

Comments
 (0)