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

Side by Side Diff: net/base/directory_lister.cc

Issue 5710002: Create base::WorkerPoolJob. Use it for HostResolverImpl and DirectoryLister. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Address cancellation. Created 10 years 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 | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2009 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 "net/base/directory_lister.h" 5 #include "net/base/directory_lister.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 #include <vector> 8 #include <vector>
9 9
10 #include "base/file_util.h" 10 #include "base/file_util.h"
11 #include "base/i18n/file_util_icu.h" 11 #include "base/i18n/file_util_icu.h"
12 #include "base/message_loop.h" 12 #include "base/logging.h"
13 #include "base/platform_thread.h" 13 #include "base/waitable_event.h"
14 #include "base/thread_restrictions.h" 14 #include "base/worker_pool_job.h"
15 #include "build/build_config.h"
15 #include "net/base/net_errors.h" 16 #include "net/base/net_errors.h"
16 17
17 namespace net { 18 namespace net {
18 19
19 static const int kFilesPerEvent = 8; 20 namespace {
20 21
21 // A task which is used to signal the delegate asynchronously. 22 const int kFilesPerEvent = 8;
22 class DirectoryDataEvent : public Task {
23 public:
24 explicit DirectoryDataEvent(DirectoryLister* d) : lister(d), error(0) {
25 // Allocations of the FindInfo aren't super cheap, so reserve space.
26 data.reserve(64);
27 }
28
29 void Run() {
30 if (data.empty()) {
31 lister->OnDone(error);
32 return;
33 }
34 lister->OnReceivedData(&data[0], static_cast<int>(data.size()));
35 }
36
37 scoped_refptr<DirectoryLister> lister;
38 std::vector<DirectoryLister::DirectoryListerData> data;
39 int error;
40 };
41 23
42 // Comparator for sorting lister results. This uses the locale aware filename 24 // Comparator for sorting lister results. This uses the locale aware filename
43 // comparison function on the filenames for sorting in the user's locale. 25 // comparison function on the filenames for sorting in the user's locale.
44 // Static. 26 bool CompareAlphaDirsFirst(const DirectoryLister::Data& a,
45 bool DirectoryLister::CompareAlphaDirsFirst(const DirectoryListerData& a, 27 const DirectoryLister::Data& b) {
46 const DirectoryListerData& b) {
47 // Parent directory before all else. 28 // Parent directory before all else.
48 if (file_util::IsDotDot(file_util::FileEnumerator::GetFilename(a.info))) 29 if (file_util::IsDotDot(file_util::FileEnumerator::GetFilename(a.info)))
49 return true; 30 return true;
50 if (file_util::IsDotDot(file_util::FileEnumerator::GetFilename(b.info))) 31 if (file_util::IsDotDot(file_util::FileEnumerator::GetFilename(b.info)))
51 return false; 32 return false;
52 33
53 // Directories before regular files. 34 // Directories before regular files.
54 bool a_is_directory = file_util::FileEnumerator::IsDirectory(a.info); 35 bool a_is_directory = file_util::FileEnumerator::IsDirectory(a.info);
55 bool b_is_directory = file_util::FileEnumerator::IsDirectory(b.info); 36 bool b_is_directory = file_util::FileEnumerator::IsDirectory(b.info);
56 if (a_is_directory != b_is_directory) 37 if (a_is_directory != b_is_directory)
57 return a_is_directory; 38 return a_is_directory;
58 39
59 return file_util::LocaleAwareCompareFilenames( 40 return file_util::LocaleAwareCompareFilenames(
60 file_util::FileEnumerator::GetFilename(a.info), 41 file_util::FileEnumerator::GetFilename(a.info),
61 file_util::FileEnumerator::GetFilename(b.info)); 42 file_util::FileEnumerator::GetFilename(b.info));
62 } 43 }
63 44
64 // Static. 45 bool CompareDate(const DirectoryLister::Data& a,
65 bool DirectoryLister::CompareDate(const DirectoryListerData& a, 46 const DirectoryLister::Data& b) {
66 const DirectoryListerData& b) {
67 // Parent directory before all else. 47 // Parent directory before all else.
68 if (file_util::IsDotDot(file_util::FileEnumerator::GetFilename(a.info))) 48 if (file_util::IsDotDot(file_util::FileEnumerator::GetFilename(a.info)))
69 return true; 49 return true;
70 if (file_util::IsDotDot(file_util::FileEnumerator::GetFilename(b.info))) 50 if (file_util::IsDotDot(file_util::FileEnumerator::GetFilename(b.info)))
71 return false; 51 return false;
72 52
73 // Directories before regular files. 53 // Directories before regular files.
74 bool a_is_directory = file_util::FileEnumerator::IsDirectory(a.info); 54 bool a_is_directory = file_util::FileEnumerator::IsDirectory(a.info);
75 bool b_is_directory = file_util::FileEnumerator::IsDirectory(b.info); 55 bool b_is_directory = file_util::FileEnumerator::IsDirectory(b.info);
76 if (a_is_directory != b_is_directory) 56 if (a_is_directory != b_is_directory)
77 return a_is_directory; 57 return a_is_directory;
78 #if defined(OS_POSIX) 58 #if defined(OS_POSIX)
79 return a.info.stat.st_mtime > b.info.stat.st_mtime; 59 return a.info.stat.st_mtime > b.info.stat.st_mtime;
80 #elif defined(OS_WIN) 60 #elif defined(OS_WIN)
81 if (a.info.ftLastWriteTime.dwHighDateTime == 61 if (a.info.ftLastWriteTime.dwHighDateTime ==
82 b.info.ftLastWriteTime.dwHighDateTime) { 62 b.info.ftLastWriteTime.dwHighDateTime) {
83 return a.info.ftLastWriteTime.dwLowDateTime > 63 return a.info.ftLastWriteTime.dwLowDateTime >
84 b.info.ftLastWriteTime.dwLowDateTime; 64 b.info.ftLastWriteTime.dwLowDateTime;
85 } else { 65 } else {
86 return a.info.ftLastWriteTime.dwHighDateTime > 66 return a.info.ftLastWriteTime.dwHighDateTime >
87 b.info.ftLastWriteTime.dwHighDateTime; 67 b.info.ftLastWriteTime.dwHighDateTime;
88 } 68 }
89 #endif 69 #endif
90 } 70 }
91 71
92 // Comparator for sorting find result by paths. This uses the locale-aware 72 // Comparator for sorting find result by paths. This uses the locale-aware
93 // comparison function on the filenames for sorting in the user's locale. 73 // comparison function on the filenames for sorting in the user's locale.
94 // Static. 74 bool CompareFullPath(const DirectoryLister::Data& a,
95 bool DirectoryLister::CompareFullPath(const DirectoryListerData& a, 75 const DirectoryLister::Data& b) {
96 const DirectoryListerData& b) {
97 return file_util::LocaleAwareCompareFilenames(a.path, b.path); 76 return file_util::LocaleAwareCompareFilenames(a.path, b.path);
98 } 77 }
99 78
100 DirectoryLister::DirectoryLister(const FilePath& dir, 79 } // namespace
101 DirectoryListerDelegate* delegate) 80
81 class DirectoryLister::Job : public base::WorkerPoolJob {
82 public:
83 Job(const FilePath& dir,
84 bool recursive,
85 SORT_TYPE sort,
86 Delegate* delegate);
87
88 void Start() { StartJob(); }
89 void Cancel() {
90 CancelJob();
91 }
92
93 using WorkerPoolJob::is_running;
94
95 private:
96 friend class base::RefCountedThreadSafe<Job>;
97
98 virtual ~Job();
99
100 virtual void RunJob();
101 virtual void CompleteJob();
102
103 // Construction parameters.
104 const FilePath dir_;
105 const bool recursive_;
106 const SORT_TYPE sort_;
107 Delegate* const delegate_;
108
109 // Job state to pass to delegate.
110 int error_;
111 std::vector<Data> data_;
112 };
113
114 DirectoryLister::Job::Job(const FilePath& dir,
115 bool recursive,
116 SORT_TYPE sort,
117 Delegate* delegate)
102 : dir_(dir), 118 : dir_(dir),
103 recursive_(false), 119 recursive_(recursive),
120 sort_(sort),
104 delegate_(delegate), 121 delegate_(delegate),
105 sort_(ALPHA_DIRS_FIRST), 122 error_(OK) {
106 message_loop_(NULL),
107 thread_(kNullThreadHandle) {
108 DCHECK(!dir.value().empty()); 123 DCHECK(!dir.value().empty());
124 // Allocations of the FindInfo aren't super cheap, so reserve space.
125 data_.reserve(64);
109 } 126 }
110 127
111 DirectoryLister::DirectoryLister(const FilePath& dir, 128 DirectoryLister::Job::~Job() {}
112 bool recursive,
113 SORT_TYPE sort,
114 DirectoryListerDelegate* delegate)
115 : dir_(dir),
116 recursive_(recursive),
117 delegate_(delegate),
118 sort_(sort),
119 message_loop_(NULL),
120 thread_(kNullThreadHandle) {
121 DCHECK(!dir.value().empty());
122 }
123 129
124 DirectoryLister::~DirectoryLister() { 130 void DirectoryLister::Job::RunJob() {
125 if (thread_) {
126 // This is a bug and we should stop joining this thread.
127 // http://crbug.com/65331
128 base::ThreadRestrictions::ScopedAllowIO allow_io;
129 PlatformThread::Join(thread_);
130 }
131 }
132
133 bool DirectoryLister::Start() {
134 // spawn a thread to enumerate the specified directory
135
136 // pass events back to the current thread
137 message_loop_ = MessageLoop::current();
138 DCHECK(message_loop_) << "calling thread must have a message loop";
139
140 AddRef(); // the thread will release us when it is done
141
142 if (!PlatformThread::Create(0, this, &thread_)) {
143 Release();
144 return false;
145 }
146
147 return true;
148 }
149
150 void DirectoryLister::Cancel() {
151 canceled_.Set();
152
153 if (thread_) {
154 // This is a bug and we should stop joining this thread.
155 // http://crbug.com/65331
156 base::ThreadRestrictions::ScopedAllowIO allow_io;
157 PlatformThread::Join(thread_);
158 thread_ = kNullThreadHandle;
159 }
160 }
161
162 void DirectoryLister::ThreadMain() {
163 DirectoryDataEvent* e = new DirectoryDataEvent(this);
164
165 if (!file_util::DirectoryExists(dir_)) { 131 if (!file_util::DirectoryExists(dir_)) {
166 e->error = net::ERR_FILE_NOT_FOUND; 132 error_ = net::ERR_FILE_NOT_FOUND;
167 message_loop_->PostTask(FROM_HERE, e);
168 Release();
169 return; 133 return;
170 } 134 }
171 135
172 int types = file_util::FileEnumerator::FILES | 136 int types = file_util::FileEnumerator::FILES |
173 file_util::FileEnumerator::DIRECTORIES; 137 file_util::FileEnumerator::DIRECTORIES;
174 if (!recursive_) 138 if (!recursive_)
175 types |= file_util::FileEnumerator::INCLUDE_DOT_DOT; 139 types |= file_util::FileEnumerator::INCLUDE_DOT_DOT;
176 140
177 file_util::FileEnumerator file_enum(dir_, recursive_, 141 file_util::FileEnumerator file_enum(dir_, recursive_,
178 static_cast<file_util::FileEnumerator::FILE_TYPE>(types)); 142 static_cast<file_util::FileEnumerator::FILE_TYPE>(types));
179 143
180 FilePath path; 144 FilePath path;
181 while (!canceled_.IsSet() && !(path = file_enum.Next()).empty()) { 145 while (!canceled() && !(path = file_enum.Next()).empty()) {
182 DirectoryListerData data; 146 Data data;
183 file_enum.GetFindInfo(&data.info); 147 file_enum.GetFindInfo(&data.info);
184 data.path = path; 148 data.path = path;
185 e->data.push_back(data); 149 data_.push_back(data);
186 150
187 /* TODO(brettw) bug 24107: It would be nice to send incremental updates. 151 /* TODO(brettw) bug 24107: It would be nice to send incremental updates.
188 We gather them all so they can be sorted, but eventually the sorting 152 We gather them all so they can be sorted, but eventually the sorting
189 should be done from JS to give more flexibility in the page. When we do 153 should be done from JS to give more flexibility in the page. When we do
190 that, we can uncomment this to send incremental updates to the page. 154 that, we can uncomment this to send incremental updates to the page.
191 if (++e->count == kFilesPerEvent) { 155 if (++e->count == kFilesPerEvent) {
192 message_loop_->PostTask(FROM_HERE, e); 156 message_loop_->PostTask(FROM_HERE, e);
193 e = new DirectoryDataEvent(this); 157 e = new DirectoryDataEvent(this);
194 } 158 }
195 */ 159 */
196 } 160 }
197 161
198 if (!e->data.empty()) { 162 if (!data_.empty()) {
199 // Sort the results. See the TODO above (this sort should be removed and we 163 // Sort the results. See the TODO above (this sort should be removed and we
200 // should do it from JS). 164 // should do it from JS).
201 if (sort_ == DATE) 165 if (sort_ == DATE)
202 std::sort(e->data.begin(), e->data.end(), CompareDate); 166 std::sort(data_.begin(), data_.end(), CompareDate);
203 else if (sort_ == FULL_PATH) 167 else if (sort_ == FULL_PATH)
204 std::sort(e->data.begin(), e->data.end(), CompareFullPath); 168 std::sort(data_.begin(), data_.end(), CompareFullPath);
205 else if (sort_ == ALPHA_DIRS_FIRST) 169 else if (sort_ == ALPHA_DIRS_FIRST)
206 std::sort(e->data.begin(), e->data.end(), CompareAlphaDirsFirst); 170 std::sort(data_.begin(), data_.end(), CompareAlphaDirsFirst);
207 else 171 else
208 DCHECK_EQ(NO_SORT, sort_); 172 DCHECK_EQ(NO_SORT, sort_);
173 }
174 }
209 175
210 message_loop_->PostTask(FROM_HERE, e); 176 void DirectoryLister::Job::CompleteJob() {
211 e = new DirectoryDataEvent(this); 177 for (size_t i = 0; i < data_.size(); ++i) {
178 delegate_->OnListFile(data_[i]);
179 if (canceled())
180 return;
212 } 181 }
213 182
214 // Notify done 183 delegate_->OnListDone(error_);
215 Release();
216 message_loop_->PostTask(FROM_HERE, e);
217 } 184 }
218 185
219 void DirectoryLister::OnReceivedData(const DirectoryListerData* data, 186 DirectoryLister::DirectoryLister(const FilePath& dir,
220 int count) { 187 Delegate* delegate)
221 // Since the delegate can clear itself during the OnListFile callback, we 188 : job_handle_(new Job(dir, false, ALPHA_DIRS_FIRST, delegate)) {}
222 // need to null check it during each iteration of the loop. Similarly, it is 189
223 // necessary to check the canceled_ flag to avoid sending data to a delegate 190 DirectoryLister::DirectoryLister(const FilePath& dir,
224 // who doesn't want anymore. 191 bool recursive,
225 for (int i = 0; !canceled_.IsSet() && delegate_ && i < count; ++i) 192 SORT_TYPE sort,
226 delegate_->OnListFile(data[i]); 193 Delegate* delegate)
194 : job_handle_(new Job(dir, recursive, sort, delegate)) {}
195
196 DirectoryLister::~DirectoryLister() {}
197
198 void DirectoryLister::Start() {
199 job_handle_.job()->Start();
227 } 200 }
228 201
229 void DirectoryLister::OnDone(int error) { 202 void DirectoryLister::Cancel() {
230 // If canceled is set, we need to report some kind of error, 203 job_handle_.job()->Cancel();
231 // but don't overwrite the error condition if it is already set.
232 if (!error && canceled_.IsSet())
233 error = net::ERR_ABORTED;
234
235 if (delegate_)
236 delegate_->OnListDone(error);
237 } 204 }
238 205
239 } // namespace net 206 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698