2
* Copyright © 2015 Canonical Ltd.
4
* This program is free software: you can redistribute it and/or modify it
5
* under the terms of the GNU General Public License version 3, as published
6
* by the Free Software Foundation.
8
* This program is distributed in the hope that it will be useful, but
9
* WITHOUT ANY WARRANTY; without even the implied warranties of
10
* MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11
* PURPOSE. See the GNU General Public License for more details.
13
* You should have received a copy of the GNU General Public License along
14
* with this program. If not, see <http://www.gnu.org/licenses/>.
17
* Ted Gould <ted.gould@canonical.com>
20
#include "glib-thread.h"
25
ContextThread::ContextThread(std::function<void()> beforeLoop, std::function<void()> afterLoop)
27
_cancel = std::shared_ptr<GCancellable>(g_cancellable_new(), [](GCancellable* cancel) {
28
if (cancel != nullptr)
30
g_cancellable_cancel(cancel);
31
g_object_unref(cancel);
34
std::promise<std::pair<std::shared_ptr<GMainContext>, std::shared_ptr<GMainLoop>>> context_promise;
36
/* NOTE: We copy afterLoop but reference beforeLoop. We're blocking so we
37
know that beforeLoop will stay valid long enough, but we can't say the
39
_thread = std::thread([&context_promise, &beforeLoop, afterLoop, this]() {
40
/* Build up the context and loop for the async events and a place
41
for GDBus to send its events back to */
42
auto context = std::shared_ptr<GMainContext>(
43
g_main_context_new(), [](GMainContext* context) { g_clear_pointer(&context, g_main_context_unref); });
44
auto loop = std::shared_ptr<GMainLoop>(g_main_loop_new(context.get(), FALSE),
45
[](GMainLoop* loop) { g_clear_pointer(&loop, g_main_loop_unref); });
47
g_main_context_push_thread_default(context.get());
51
/* Free's the constructor to continue */
52
auto pair = std::pair<std::shared_ptr<GMainContext>, std::shared_ptr<GMainLoop>>(context, loop);
53
context_promise.set_value(pair);
55
if (!g_cancellable_is_cancelled(_cancel.get()))
57
g_main_loop_run(loop.get());
63
/* We need to have the context and the mainloop ready before
64
other functions on this object can work properly. So we wait
65
for them and set them on this thread. */
66
auto context_future = context_promise.get_future();
67
context_future.wait();
68
auto context_value = context_future.get();
70
_context = context_value.first;
71
_loop = context_value.second;
73
if (!_context || !_loop)
75
throw std::runtime_error("Unable to create GLib Thread");
79
ContextThread::~ContextThread()
84
void ContextThread::quit()
86
g_cancellable_cancel(_cancel.get()); /* Force the cancellation on ongoing tasks */
89
g_main_loop_quit(_loop.get()); /* Quit the loop */
92
/* Joining here because we want to ensure that the final afterLoop()
93
function is run before returning */
94
if (std::this_thread::get_id() != _thread.get_id())
96
if (_thread.joinable())
103
bool ContextThread::isCancelled()
105
return g_cancellable_is_cancelled(_cancel.get()) == TRUE;
108
std::shared_ptr<GCancellable> ContextThread::getCancellable()
113
void ContextThread::simpleSource(std::function<GSource*()> srcBuilder, std::function<void()> work)
117
throw std::runtime_error("Trying to execute work on a GLib thread that is shutting down.");
120
/* Copy the work so that we can reuse it */
121
/* Lifecycle is handled with the source pointer when we attach
122
it to the context. */
123
auto heapWork = new std::function<void()>(work);
125
auto source = std::shared_ptr<GSource>(srcBuilder(), [](GSource* src) { g_clear_pointer(&src, g_source_unref); });
126
g_source_set_callback(source.get(),
128
auto heapWork = static_cast<std::function<void()>*>(data);
130
return G_SOURCE_REMOVE;
134
auto heapWork = static_cast<std::function<void()>*>(data);
138
g_source_attach(source.get(), _context.get());
141
void ContextThread::executeOnThread(std::function<void()> work)
143
simpleSource(g_idle_source_new, work);
146
void ContextThread::timeout(const std::chrono::milliseconds& length, std::function<void()> work)
148
simpleSource([length]() { return g_timeout_source_new(length.count()); }, work);
151
void ContextThread::timeoutSeconds(const std::chrono::seconds& length, std::function<void()> work)
153
simpleSource([length]() { return g_timeout_source_new_seconds(length.count()); }, work);