| OLD | NEW |
| 1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2006-2008 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 <process.h> | |
| 6 | |
| 7 #include "net/base/directory_lister.h" | 5 #include "net/base/directory_lister.h" |
| 8 | 6 |
| 7 #include "base/file_util.h" |
| 9 #include "base/message_loop.h" | 8 #include "base/message_loop.h" |
| 9 #include "base/platform_thread.h" |
| 10 #include "net/base/net_errors.h" |
| 10 | 11 |
| 11 namespace net { | 12 namespace net { |
| 12 | 13 |
| 13 static const int kFilesPerEvent = 8; | 14 static const int kFilesPerEvent = 8; |
| 14 | 15 |
| 15 class DirectoryDataEvent : public Task { | 16 class DirectoryDataEvent : public Task { |
| 16 public: | 17 public: |
| 17 explicit DirectoryDataEvent(DirectoryLister* d) | 18 explicit DirectoryDataEvent(DirectoryLister* d) |
| 18 : lister(d), count(0), error(0) { | 19 : lister(d), count(0), error(0) { |
| 19 } | 20 } |
| 20 | 21 |
| 21 void Run() { | 22 void Run() { |
| 22 if (count) { | 23 if (count) { |
| 23 lister->OnReceivedData(data, count); | 24 lister->OnReceivedData(data, count); |
| 24 } else { | 25 } else { |
| 25 lister->OnDone(error); | 26 lister->OnDone(error); |
| 26 } | 27 } |
| 27 } | 28 } |
| 28 | 29 |
| 29 scoped_refptr<DirectoryLister> lister; | 30 scoped_refptr<DirectoryLister> lister; |
| 30 WIN32_FIND_DATA data[kFilesPerEvent]; | 31 file_util::FileEnumerator::FindInfo data[kFilesPerEvent]; |
| 31 int count; | 32 int count, error; |
| 32 DWORD error; | |
| 33 }; | 33 }; |
| 34 | 34 |
| 35 /*static*/ | 35 DirectoryLister::DirectoryLister(const std::wstring& dir, |
| 36 unsigned __stdcall DirectoryLister::ThreadFunc(void* param) { | 36 DirectoryListerDelegate* delegate) |
| 37 DirectoryLister* self = reinterpret_cast<DirectoryLister*>(param); | |
| 38 | |
| 39 std::wstring pattern = self->directory(); | |
| 40 if (pattern[pattern.size()-1] != '\\') { | |
| 41 pattern.append(L"\\*"); | |
| 42 } else { | |
| 43 pattern.append(L"*"); | |
| 44 } | |
| 45 | |
| 46 DirectoryDataEvent* e = new DirectoryDataEvent(self); | |
| 47 | |
| 48 HANDLE handle = FindFirstFile(pattern.c_str(), &e->data[e->count]); | |
| 49 if (handle == INVALID_HANDLE_VALUE) { | |
| 50 e->error = GetLastError(); | |
| 51 self->message_loop_->PostTask(FROM_HERE, e); | |
| 52 e = NULL; | |
| 53 } else { | |
| 54 do { | |
| 55 if (++e->count == kFilesPerEvent) { | |
| 56 self->message_loop_->PostTask(FROM_HERE, e); | |
| 57 e = new DirectoryDataEvent(self); | |
| 58 } | |
| 59 } while (!self->was_canceled() && FindNextFile(handle, &e->data[e->count])); | |
| 60 | |
| 61 FindClose(handle); | |
| 62 | |
| 63 if (e->count > 0) { | |
| 64 self->message_loop_->PostTask(FROM_HERE, e); | |
| 65 e = NULL; | |
| 66 } | |
| 67 | |
| 68 // Notify done | |
| 69 e = new DirectoryDataEvent(self); | |
| 70 self->message_loop_->PostTask(FROM_HERE, e); | |
| 71 } | |
| 72 | |
| 73 self->Release(); | |
| 74 return 0; | |
| 75 } | |
| 76 | |
| 77 DirectoryLister::DirectoryLister(const std::wstring& dir, Delegate* delegate) | |
| 78 : dir_(dir), | 37 : dir_(dir), |
| 38 delegate_(delegate), |
| 79 message_loop_(NULL), | 39 message_loop_(NULL), |
| 80 delegate_(delegate), | |
| 81 thread_(NULL), | 40 thread_(NULL), |
| 82 canceled_(false) { | 41 canceled_(false) { |
| 83 DCHECK(!dir.empty()); | 42 DCHECK(!dir.empty()); |
| 84 } | 43 } |
| 85 | 44 |
| 86 DirectoryLister::~DirectoryLister() { | 45 DirectoryLister::~DirectoryLister() { |
| 87 if (thread_) | 46 if (thread_) { |
| 88 CloseHandle(thread_); | 47 PlatformThread::Join(thread_); |
| 48 } |
| 89 } | 49 } |
| 90 | 50 |
| 91 bool DirectoryLister::Start() { | 51 bool DirectoryLister::Start() { |
| 92 // spawn a thread to enumerate the specified directory | 52 // spawn a thread to enumerate the specified directory |
| 93 | 53 |
| 94 // pass events back to the current thread | 54 // pass events back to the current thread |
| 95 message_loop_ = MessageLoop::current(); | 55 message_loop_ = MessageLoop::current(); |
| 96 DCHECK(message_loop_) << "calling thread must have a message loop"; | 56 DCHECK(message_loop_) << "calling thread must have a message loop"; |
| 97 | 57 |
| 98 AddRef(); // the thread will release us when it is done | 58 AddRef(); // the thread will release us when it is done |
| 99 | 59 |
| 100 unsigned thread_id; | 60 if (!PlatformThread::Create(0, this, &thread_)) { |
| 101 thread_ = reinterpret_cast<HANDLE>( | |
| 102 _beginthreadex(NULL, 0, DirectoryLister::ThreadFunc, this, 0, | |
| 103 &thread_id)); | |
| 104 | |
| 105 if (!thread_) { | |
| 106 Release(); | 61 Release(); |
| 107 return false; | 62 return false; |
| 108 } | 63 } |
| 109 | 64 |
| 110 return true; | 65 return true; |
| 111 } | 66 } |
| 112 | 67 |
| 113 void DirectoryLister::Cancel() { | 68 void DirectoryLister::Cancel() { |
| 114 canceled_ = true; | 69 canceled_ = true; |
| 115 | 70 |
| 116 if (thread_) { | 71 if (thread_) { |
| 117 WaitForSingleObject(thread_, INFINITE); | 72 PlatformThread::Join(thread_); |
| 118 CloseHandle(thread_); | |
| 119 thread_ = NULL; | 73 thread_ = NULL; |
| 120 } | 74 } |
| 121 } | 75 } |
| 122 | 76 |
| 123 void DirectoryLister::OnReceivedData(const WIN32_FIND_DATA* data, int count) { | 77 void DirectoryLister::ThreadMain() { |
| 78 DirectoryDataEvent* e = new DirectoryDataEvent(this); |
| 79 |
| 80 if (!file_util::DirectoryExists(directory())) { |
| 81 e->error = net::ERR_FILE_NOT_FOUND; |
| 82 message_loop_->PostTask(FROM_HERE, e); |
| 83 Release(); |
| 84 return; |
| 85 } |
| 86 |
| 87 file_util::FileEnumerator file_enum(directory(), false, |
| 88 file_util::FileEnumerator::FILES_AND_DIRECTORIES); |
| 89 |
| 90 std::wstring filename; |
| 91 while (!was_canceled() && !(filename = file_enum.Next()).empty()) { |
| 92 file_enum.GetFindInfo(&e->data[e->count]); |
| 93 |
| 94 if (++e->count == kFilesPerEvent) { |
| 95 message_loop_->PostTask(FROM_HERE, e); |
| 96 e = new DirectoryDataEvent(this); |
| 97 } |
| 98 } |
| 99 |
| 100 if (e->count > 0) { |
| 101 message_loop_->PostTask(FROM_HERE, e); |
| 102 e = NULL; |
| 103 } |
| 104 |
| 105 // Notify done |
| 106 e = new DirectoryDataEvent(this); |
| 107 message_loop_->PostTask(FROM_HERE, e); |
| 108 |
| 109 Release(); |
| 110 } |
| 111 |
| 112 void DirectoryLister::OnReceivedData( |
| 113 const file_util::FileEnumerator::FindInfo* data, int count) { |
| 124 // Since the delegate can clear itself during the OnListFile callback, we | 114 // Since the delegate can clear itself during the OnListFile callback, we |
| 125 // need to null check it during each iteration of the loop. Similarly, it is | 115 // need to null check it during each iteration of the loop. Similarly, it is |
| 126 // necessary to check the canceled_ flag to avoid sending data to a delegate | 116 // necessary to check the canceled_ flag to avoid sending data to a delegate |
| 127 // who doesn't want anymore. | 117 // who doesn't want anymore. |
| 128 for (int i = 0; !canceled_ && delegate_ && i < count; ++i) | 118 for (int i = 0; !canceled_ && delegate_ && i < count; ++i) |
| 129 delegate_->OnListFile(data[i]); | 119 delegate_->OnListFile(data[i]); |
| 130 } | 120 } |
| 131 | 121 |
| 132 void DirectoryLister::OnDone(int error) { | 122 void DirectoryLister::OnDone(int error) { |
| 133 // If canceled, we need to report some kind of error, but don't overwrite the | 123 // If canceled, we need to report some kind of error, but don't overwrite the |
| 134 // error condition if it is already set. | 124 // error condition if it is already set. |
| 135 if (!error && canceled_) | 125 if (!error && canceled_) |
| 136 error = ERROR_OPERATION_ABORTED; | 126 error = net::ERR_ABORTED; |
| 137 | 127 |
| 138 if (delegate_) | 128 if (delegate_) |
| 139 delegate_->OnListDone(error); | 129 delegate_->OnListDone(error); |
| 140 } | 130 } |
| 141 | 131 |
| 142 } // namespace net | 132 } // namespace net |
| 143 | 133 |
| OLD | NEW |