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

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

Powered by Google App Engine
This is Rietveld 408576698