~unity-api-team/unity-scopes-api/child-scopes-option

« back to all changes in this revision

Viewing changes to scoperunner/scoperunner.cpp

  • Committer: Michi Henning
  • Date: 2013-11-19 02:51:46 UTC
  • mto: This revision was merged to the branch mainline in revision 62.
  • Revision ID: michi.henning@canonical.com-20131119025146-kglcalpphl4ozzk7
Fixed scoperegistry to use new scoperunner and to figure out which scopes to run from config files.
Still to do:
- deal with overrides and OEM scopes, particularly the grouping aspect.
- SignalThread needs to invoke a callback for clean shut-down on receipt of SIGINT.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (C) 2013 Canonical Ltd
 
3
 *
 
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.
 
7
 *
 
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.
 
12
 *
 
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/>.
 
15
 *
 
16
 * Authored by: Michi Henning <michi.henning@canonical.com>
 
17
 */
 
18
 
 
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>
 
25
 
 
26
#include <cassert>
 
27
#include <future>
 
28
#include <iostream>
 
29
#include <string>
 
30
#include <unordered_map>
 
31
#include <vector>
 
32
 
 
33
#include <libgen.h>
 
34
 
 
35
using namespace std;
 
36
using namespace unity::api::scopes;
 
37
using namespace unity::api::scopes::internal;
 
38
 
 
39
namespace
 
40
{
 
41
 
 
42
char const* prog_name;
 
43
 
 
44
void error(string const& msg)
 
45
{
 
46
    assert(!msg.empty());
 
47
    cerr << prog_name << ": " << msg << endl;
 
48
}
 
49
 
 
50
bool has_suffix(string const& s, string const& suffix)
 
51
{
 
52
    auto s_len = s.length();
 
53
    auto suffix_len = suffix.length();
 
54
    if (s_len >= suffix_len)
 
55
    {
 
56
        return s.compare(s_len - suffix_len, suffix_len, suffix) == 0;
 
57
    }
 
58
    return false;
 
59
}
 
60
 
 
61
string strip_suffix(string const& s, string const& suffix)
 
62
{
 
63
    auto s_len = s.length();
 
64
    auto suffix_len = suffix.length();
 
65
    if (s_len >= suffix_len)
 
66
    {
 
67
        if (s.compare(s_len - suffix_len, suffix_len, suffix) == 0)
 
68
        {
 
69
            return string(s, 0, s_len - suffix_len);
 
70
        }
 
71
    }
 
72
    return s;
 
73
}
 
74
 
 
75
// One thread for each scope, plus a future that the thread sets when it finishes.
 
76
 
 
77
struct ThreadFuture
 
78
{
 
79
    thread t;
 
80
    std::future<void> f;
 
81
};
 
82
 
 
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.
 
87
 
 
88
unordered_map<thread::id, ThreadFuture> threads;
 
89
 
 
90
ThreadSafeQueue<thread::id> finished_threads;
 
91
 
 
92
// Scope thread start function.
 
93
 
 
94
void scope_thread(string const& runtime_config,
 
95
                  string const& scope_name,
 
96
                  string const& lib_dir,
 
97
                  promise<void> finished_promise)
 
98
{
 
99
    try
 
100
    {
 
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());
 
106
        loader->start();
 
107
 
 
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(); });
 
111
 
 
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));
 
115
 
 
116
        mw->wait_for_shutdown();
 
117
 
 
118
        // Collect exit status from the run thread. If this throws, the ScopeLoader
 
119
        // destructor will still call stop() on the scope.
 
120
        run_future.get();
 
121
 
 
122
        finished_promise.set_value();
 
123
    }
 
124
    catch (...)
 
125
    {
 
126
        finished_promise.set_exception(current_exception());
 
127
    }
 
128
 
 
129
    finished_threads.push(this_thread::get_id());
 
130
}
 
131
 
 
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.
 
134
 
 
135
int run_scopes(string const& runtime_config, vector<string> config_files)
 
136
{
 
137
    for (auto file : config_files)
 
138
    {
 
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() != '/')
 
143
        {
 
144
            dir += "/";
 
145
        }
 
146
        string scope_name = strip_suffix(file_name, ".ini");
 
147
 
 
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.
 
152
        promise<void> p;
 
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) };
 
157
    }
 
158
 
 
159
    // Now wait for the threads to finish (in any order).
 
160
    int num_errors = 0;
 
161
    for (int i = threads.size(); i > 0; --i)
 
162
    {
 
163
        try
 
164
        {
 
165
            auto id = finished_threads.wait_and_pop();
 
166
            auto it = threads.find(id);
 
167
            assert(it != threads.end());
 
168
            it->second.t.join();
 
169
            it->second.f.get();             // This will throw if the thread terminated due to an exception
 
170
        }
 
171
        catch (unity::Exception const& e)
 
172
        {
 
173
            error(e.to_string());
 
174
            ++num_errors;
 
175
        }
 
176
        catch (std::exception const& e)
 
177
        {
 
178
            error(e.what());
 
179
            ++num_errors;
 
180
        }
 
181
        catch (...)
 
182
        {
 
183
            error("unknown exception");
 
184
            ++num_errors;
 
185
        }
 
186
    }
 
187
    return num_errors;
 
188
}
 
189
 
 
190
} // namespace
 
191
 
 
192
int
 
193
main(int argc, char* argv[])
 
194
{
 
195
    prog_name = basename(argv[0]);
 
196
    if (argc < 3)
 
197
    {
 
198
        cerr << "usage: " << prog_name << " runtime.ini configfile.ini [configfile.ini ...]" << endl;
 
199
        return 2;
 
200
    }
 
201
    char const* const runtime_config = argv[1];
 
202
 
 
203
    int exit_status = 0;
 
204
    try
 
205
    {
 
206
        vector<string> config_files;
 
207
        for (int i = 2; i < argc; ++i)
 
208
        {
 
209
            if (!has_suffix(argv[i], ".ini"))
 
210
            {
 
211
                throw ConfigException(string("invalid config file name: \"") + argv[i] + "\": missing .ini extension");
 
212
            }
 
213
            config_files.push_back(argv[i]);
 
214
        }
 
215
 
 
216
        exit_status = run_scopes(runtime_config, config_files);
 
217
    }
 
218
    catch (unity::Exception const& e)
 
219
    {
 
220
        error(e.to_string());
 
221
        exit_status = 1;
 
222
    }
 
223
    catch (std::exception const& e)
 
224
    {
 
225
        error(e.what());
 
226
        exit_status = 1;
 
227
    }
 
228
    catch (string const& e)
 
229
    {
 
230
        error("fatal error: " + e);
 
231
        exit_status = 1;
 
232
    }
 
233
    catch (char const* e)
 
234
    {
 
235
        error(string("fatal error: ") + e);
 
236
        exit_status = 1;
 
237
    }
 
238
    catch (...)
 
239
    {
 
240
        error("terminated due to unknown exception");
 
241
        exit_status = 1;
 
242
    }
 
243
 
 
244
    return exit_status;
 
245
}