2
* Copyright (C) 2013 Canonical Ltd
4
* This program is free software: you can redistribute it and/or modify
5
* it under the terms of the GNU General Public License version 3 as
6
* published by the Free Software Foundation.
8
* This program is distributed in the hope that it will be useful,
9
* but WITHOUT ANY WARRANTY; without even the implied warranty of
10
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
* GNU General Public License for more details.
13
* You should have received a copy of the GNU General Public License
14
* along with this program. If not, see <http://www.gnu.org/licenses/>.
16
* Authored by: Michi Henning <michi.henning@canonical.com>
19
#include <scopes/internal/RuntimeImpl.h>
20
#include <scopes/internal/ScopeLoader.h>
21
#include <scopes/internal/ThreadSafeQueue.h>
22
#include <scopes/internal/MWRegistry.h>
23
#include <scopes/ScopeExceptions.h>
24
#include <unity/UnityExceptions.h>
30
#include <unordered_map>
36
using namespace unity::api::scopes;
37
using namespace unity::api::scopes::internal;
42
char const* prog_name;
44
void error(string const& msg)
47
cerr << prog_name << ": " << msg << endl;
50
bool has_suffix(string const& s, string const& suffix)
52
auto s_len = s.length();
53
auto suffix_len = suffix.length();
54
if (s_len >= suffix_len)
56
return s.compare(s_len - suffix_len, suffix_len, suffix) == 0;
61
string strip_suffix(string const& s, string const& suffix)
63
auto s_len = s.length();
64
auto suffix_len = suffix.length();
65
if (s_len >= suffix_len)
67
if (s.compare(s_len - suffix_len, suffix_len, suffix) == 0)
69
return string(s, 0, s_len - suffix_len);
75
// One thread for each scope, plus a future that the thread sets when it finishes.
83
// Each thread provides its own ID on a queue when it finishes. That allows us to
84
// then locate the thread in the map. The promise is set by the thread so we can
85
// find out what happened to it and join with it. Unfortunately, we have to jump
86
// through these hoops because there is no way to wait on multiple futures in C++ 11.
88
unordered_map<thread::id, ThreadFuture> threads;
90
ThreadSafeQueue<thread::id> finished_threads;
92
// Scope thread start function.
94
void scope_thread(string const& runtime_config,
95
string const& scope_name,
96
string const& lib_dir,
97
promise<void> finished_promise)
101
// Instantiate the run time, create the middleware, load the scope from its
102
// shared library, and call the scope's start() method.
103
auto rt = RuntimeImpl::create(scope_name, runtime_config);
104
auto mw = rt->factory()->create(scope_name, "Zmq", "Zmq.Config"); // TODO: get middleware from config
105
ScopeLoader::SPtr loader = ScopeLoader::load(scope_name, lib_dir + "lib" + scope_name + ".so", rt->registry());
108
// Give a thread to the scope to do with as it likes. If the scope doesn't want to use it and
109
// immediately returns from run(), that's fine.
110
auto run_future = std::async(launch::async, [loader] { loader->scope_base()->run(); });
112
// Create a servant for the scope and register the servant.
113
auto scope = unique_ptr<ScopeObject>(new ScopeObject(rt.get(), loader->scope_base()));
114
auto proxy = mw->add_scope_object(scope_name, move(scope));
116
mw->wait_for_shutdown();
118
// Collect exit status from the run thread. If this throws, the ScopeLoader
119
// destructor will still call stop() on the scope.
122
finished_promise.set_value();
126
finished_promise.set_exception(current_exception());
129
finished_threads.push(this_thread::get_id());
132
// Run each of the scopes in config_files in a separate thread and wait for each thread to finish.
133
// Return the number of threads that did not terminate normally.
135
int run_scopes(string const& runtime_config, vector<string> config_files)
137
for (auto file : config_files)
139
string file_name = basename(const_cast<char*>(string(file).c_str())); // basename() modifies its argument
140
auto dir_len = file.size() - file_name.size();
141
string dir = file.substr(0, dir_len);
142
if (*dir.rbegin() != '/')
146
string scope_name = strip_suffix(file_name, ".ini");
148
// For each scope, create a thread that loads the scope and initializes it.
149
// Each thread gets a promise to indicate when it is finished. When a thread
150
// completes, it fulfils the promise, and pushes its ID onto the finished queue.
151
// We collect exit status from the thread via the future from each promise.
153
auto f = p.get_future();
154
thread t(scope_thread, runtime_config, scope_name, dir, move(p));
155
auto id = t.get_id();
156
threads[id] = ThreadFuture { move(t), move(f) };
159
// Now wait for the threads to finish (in any order).
161
for (int i = threads.size(); i > 0; --i)
165
auto id = finished_threads.wait_and_pop();
166
auto it = threads.find(id);
167
assert(it != threads.end());
169
it->second.f.get(); // This will throw if the thread terminated due to an exception
171
catch (unity::Exception const& e)
173
error(e.to_string());
176
catch (std::exception const& e)
183
error("unknown exception");
193
main(int argc, char* argv[])
195
prog_name = basename(argv[0]);
198
cerr << "usage: " << prog_name << " runtime.ini configfile.ini [configfile.ini ...]" << endl;
201
char const* const runtime_config = argv[1];
206
vector<string> config_files;
207
for (int i = 2; i < argc; ++i)
209
if (!has_suffix(argv[i], ".ini"))
211
throw ConfigException(string("invalid config file name: \"") + argv[i] + "\": missing .ini extension");
213
config_files.push_back(argv[i]);
216
exit_status = run_scopes(runtime_config, config_files);
218
catch (unity::Exception const& e)
220
error(e.to_string());
223
catch (std::exception const& e)
228
catch (string const& e)
230
error("fatal error: " + e);
233
catch (char const* e)
235
error(string("fatal error: ") + e);
240
error("terminated due to unknown exception");