1 // Copyright 2010-2018, Google Inc.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 // * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above
11 // copyright notice, this list of conditions and the following disclaimer
12 // in the documentation and/or other materials provided with the
13 // distribution.
14 // * Neither the name of Google Inc. nor the names of its
15 // contributors may be used to endorse or promote products derived from
16 // this software without specific prior written permission.
17 //
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30 #include "engine/engine_builder.h"
31
32 #include <memory>
33 #include <utility>
34
35 #include "base/file_util.h"
36 #include "base/logging.h"
37 #include "base/thread.h"
38 #include "data_manager/data_manager.h"
39 #include "engine/engine.h"
40 #include "protocol/engine_builder.pb.h"
41
42 namespace mozc {
43 namespace {
44
ConvertStatus(DataManager::Status status)45 EngineReloadResponse::Status ConvertStatus(DataManager::Status status) {
46 switch (status) {
47 case DataManager::Status::ENGINE_VERSION_MISMATCH:
48 return EngineReloadResponse::ENGINE_VERSION_MISMATCH;
49 case DataManager::Status::DATA_MISSING:
50 return EngineReloadResponse::DATA_MISSING;
51 case DataManager::Status::DATA_BROKEN:
52 return EngineReloadResponse::DATA_BROKEN;
53 case DataManager::Status::MMAP_FAILURE:
54 return EngineReloadResponse::MMAP_FAILURE;
55 case DataManager::Status::UNKNOWN:
56 return EngineReloadResponse::UNKNOWN_ERROR;
57 default:
58 LOG(DFATAL) << "Should not reach here";
59 }
60 return EngineReloadResponse::UNKNOWN_ERROR;
61 }
62
63 } // namespace
64
65 class EngineBuilder::Preparator : public Thread {
66 public:
Preparator(const EngineReloadRequest & request)67 explicit Preparator(const EngineReloadRequest &request) {
68 *response_.mutable_request() = request;
69 }
70
71 ~Preparator() override = default;
72
Run()73 void Run() override {
74 const EngineReloadRequest &request = response_.request();
75
76 std::unique_ptr<DataManager> tmp_data_manager(new DataManager());
77 const DataManager::Status status = InitDataManager(request,
78 tmp_data_manager.get());
79 if (status != DataManager::Status::OK) {
80 LOG(ERROR) << "Failed to load data [" << status << "] "
81 << request.Utf8DebugString();
82 response_.set_status(ConvertStatus(status));
83 return;
84 }
85
86 if (request.has_install_location() &&
87 !FileUtil::AtomicRename(request.file_path(),
88 request.install_location())) {
89 LOG(ERROR) << "Atomic rename faild: " << request.Utf8DebugString();
90 response_.set_status(EngineReloadResponse::INSTALL_FAILURE);
91 return;
92 }
93
94 response_.set_status(EngineReloadResponse::RELOAD_READY);
95 data_manager_ = std::move(tmp_data_manager);
96 }
97
98 private:
InitDataManager(const EngineReloadRequest & request,DataManager * data_manager)99 DataManager::Status InitDataManager(const EngineReloadRequest &request,
100 DataManager *data_manager) {
101 if (request.has_magic_number()) {
102 return data_manager->InitFromFile(request.file_path(),
103 request.magic_number());
104 }
105 return data_manager->InitFromFile(request.file_path());
106 }
107
108 friend class EngineBuilder;
109 EngineReloadResponse response_;
110 std::unique_ptr<DataManager> data_manager_;
111 };
112
113 EngineBuilder::EngineBuilder() = default;
114 EngineBuilder::~EngineBuilder() = default;
115
PrepareAsync(const EngineReloadRequest & request,EngineReloadResponse * response)116 void EngineBuilder::PrepareAsync(const EngineReloadRequest &request,
117 EngineReloadResponse *response) {
118 *response->mutable_request() = request;
119 if (preparator_) {
120 if (preparator_->IsRunning()) {
121 response->set_status(EngineReloadResponse::ALREADY_RUNNING);
122 return;
123 }
124 preparator_->Join();
125 VLOG(1) << "Previously loaded data is discarded";
126 }
127 preparator_.reset(new Preparator(request));
128 preparator_->SetJoinable(true);
129 preparator_->Start("EngineBuilder::Preparator");
130 response->set_status(EngineReloadResponse::ACCEPTED);
131 }
132
Wait()133 void EngineBuilder::Wait() {
134 if (preparator_) {
135 preparator_->Join();
136 }
137 }
138
HasResponse() const139 bool EngineBuilder::HasResponse() const {
140 return preparator_ && !preparator_->IsRunning();
141 }
142
GetResponse(EngineReloadResponse * response) const143 void EngineBuilder::GetResponse(EngineReloadResponse *response) const {
144 if (!HasResponse()) {
145 return;
146 }
147 *response = preparator_->response_;
148 }
149
BuildFromPreparedData()150 std::unique_ptr<EngineInterface> EngineBuilder::BuildFromPreparedData() {
151 std::unique_ptr<EngineInterface> engine;
152
153 if (!HasResponse() ||
154 !preparator_->data_manager_ ||
155 preparator_->response_.status() != EngineReloadResponse::RELOAD_READY) {
156 LOG(ERROR) << "Build() is called in invalid state";
157 return engine;
158 }
159
160 switch (preparator_->response_.request().engine_type()) {
161 case EngineReloadRequest::DESKTOP:
162 engine =
163 Engine::CreateDesktopEngine(std::move(preparator_->data_manager_));
164 break;
165 case EngineReloadRequest::MOBILE:
166 engine =
167 Engine::CreateMobileEngine(std::move(preparator_->data_manager_));
168 break;
169 default:
170 LOG(DFATAL) << "Should not reach here";
171 break;
172 }
173
174 return engine;
175 }
176
Clear()177 void EngineBuilder::Clear() {
178 if (!preparator_) {
179 return;
180 }
181 preparator_->Join();
182 preparator_.reset();
183 }
184
185 } // namespace mozc
186