1 : // Copyright 2014 Google Inc. All Rights Reserved.
2 : //
3 : // Licensed under the Apache License, Version 2.0 (the "License");
4 : // you may not use this file except in compliance with the License.
5 : // You may obtain a copy of the License at
6 : //
7 : // http://www.apache.org/licenses/LICENSE-2.0
8 : //
9 : // Unless required by applicable law or agreed to in writing, software
10 : // distributed under the License is distributed on an "AS IS" BASIS,
11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : // See the License for the specific language governing permissions and
13 : // limitations under the License.
14 :
15 : #include "syzygy/kasko/upload_thread.h"
16 :
17 : #include <windows.h>
18 : #include "base/logging.h"
19 : #include "base/files/file_path.h"
20 : #include "base/strings/string16.h"
21 : #include "base/strings/string_util.h"
22 : #include "syzygy/kasko/waitable_timer.h"
23 :
24 : namespace kasko {
25 :
26 : // static
27 : scoped_ptr<UploadThread> UploadThread::Create(
28 : const base::FilePath& exclusive_path,
29 : scoped_ptr<WaitableTimer> waitable_timer,
30 E : const base::Closure& uploader) {
31 E : scoped_ptr<UploadThread> instance;
32 :
33 : // '\' is the only character not permitted in mutex names.
34 E : base::string16 escaped_path;
35 E : base::ReplaceChars(exclusive_path.value(), L"\\", L"/", &escaped_path);
36 E : base::string16 mutex_name = L"Local\\kasko_uploader_mutex_" + escaped_path;
37 : base::string16 wake_event_name =
38 E : L"Local\\kasko_uploader_wake_event_" + escaped_path;
39 E : base::win::ScopedHandle mutex(::CreateMutex(NULL, FALSE, mutex_name.c_str()));
40 E : DPCHECK(mutex.Get());
41 E : base::win::ScopedHandle stop_event(::CreateEvent(NULL, TRUE, FALSE, NULL));
42 E : DPCHECK(stop_event.Get());
43 : base::win::ScopedHandle wake_event(
44 E : ::CreateEvent(NULL, FALSE, FALSE, wake_event_name.c_str()));
45 E : DPCHECK(wake_event.Get());
46 E : if (mutex.Get() && stop_event.Get() && wake_event.Get()) {
47 : instance.reset(new UploadThread(mutex.Pass(), stop_event.Pass(),
48 : wake_event.Pass(), waitable_timer.Pass(),
49 E : uploader));
50 : }
51 :
52 E : return instance.Pass();
53 E : }
54 :
55 E : UploadThread::~UploadThread() {
56 : // It's a bad idea to shut down without stopping the service. It's also a bad
57 : // idea to block unexpectedly in our destructor.
58 E : CHECK(!thread_impl_.HasBeenStarted() || thread_impl_.HasBeenJoined());
59 E : }
60 :
61 E : void UploadThread::Start() {
62 E : thread_impl_.Start();
63 E : }
64 :
65 E : void UploadThread::Stop() {
66 E : BOOL result = ::SetEvent(stop_event_.Get());
67 E : PCHECK(result)
68 : << "Failed to signal stop event. Terminating to avoid deadlock.";
69 E : }
70 :
71 E : void UploadThread::Join() {
72 E : Stop();
73 E : thread_impl_.Join();
74 E : }
75 :
76 E : void UploadThread::UploadOneNowAsync() {
77 E : BOOL result = ::SetEvent(wake_event_.Get());
78 E : DPCHECK(result);
79 E : }
80 :
81 : UploadThread::ThreadImpl::ThreadImpl(UploadThread* owner)
82 E : : base::SimpleThread("upload_thread"), owner_(owner) {
83 E : }
84 :
85 E : UploadThread::ThreadImpl::~ThreadImpl(){}
86 :
87 E : void UploadThread::ThreadImpl::Run() {
88 : HANDLE handles_pre_mutex[] = {
89 : owner_->mutex_.Get(),
90 : owner_->stop_event_.Get()
91 E : };
92 : DWORD wait_result = ::WaitForMultipleObjects(
93 E : arraysize(handles_pre_mutex), handles_pre_mutex, FALSE, INFINITE);
94 E : switch (wait_result) {
95 : case WAIT_OBJECT_0:
96 : case WAIT_ABANDONED_0:
97 : // mutex_
98 E : break;
99 : case WAIT_OBJECT_0 + 1:
100 : // stop_event_
101 i : return;
102 : default:
103 i : DPLOG(ERROR) << "WaitForMultipleObjects failed.";
104 i : return;
105 : }
106 :
107 : // We have the mutex now. We will wait on the wake event, the stop event, and
108 : // the timer.
109 : HANDLE handles_post_mutex[] = {owner_->wake_event_.Get(),
110 : owner_->stop_event_.Get(),
111 E : owner_->waitable_timer_->GetHANDLE()};
112 :
113 E : while (true) {
114 E : owner_->waitable_timer_->Start();
115 : wait_result = ::WaitForMultipleObjects(arraysize(handles_post_mutex),
116 E : handles_post_mutex, FALSE, INFINITE);
117 E : switch (wait_result) {
118 : case WAIT_OBJECT_0:
119 : // wake_event_
120 E : break;
121 : case WAIT_OBJECT_0 + 1:
122 : // stop_event_
123 E : return;
124 : case WAIT_OBJECT_0 + 2:
125 : // waitable_timer_
126 E : break;
127 : default:
128 i : DPLOG(ERROR) << "WaitForMultipleObjects failed.";
129 i : return;
130 : }
131 E : owner_->uploader_.Run();
132 E : }
133 E : }
134 :
135 : UploadThread::UploadThread(base::win::ScopedHandle mutex,
136 : base::win::ScopedHandle stop_event,
137 : base::win::ScopedHandle wake_event,
138 : scoped_ptr<WaitableTimer> waitable_timer,
139 : const base::Closure& uploader)
140 : : mutex_(mutex.Take()),
141 : stop_event_(stop_event.Take()),
142 : wake_event_(wake_event.Take()),
143 : waitable_timer_(waitable_timer.Pass()),
144 : uploader_(uploader),
145 E : thread_impl_(this) {
146 E : }
147 :
148 : } // namespace kasko
|