~ci-train-bot/ubuntu-app-launch/ubuntu-app-launch-ubuntu-xenial-landing-058

« back to all changes in this revision

Viewing changes to libubuntu-app-launch/glib-thread.cpp

  • Committer: CI Train Bot
  • Author(s): Michael Terry, Stephen M. Webb, Ted Gould
  • Date: 2016-03-04 12:42:53 UTC
  • mfrom: (144.5.115 app-object)
  • Revision ID: ci-train-bot@canonical.com-20160304124253-tfw4er4b2jm2tyst
fix build deps

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright © 2015 Canonical Ltd.
 
3
 *
 
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.
 
7
 *
 
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.
 
12
 *
 
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/>.
 
15
 *
 
16
 * Authors:
 
17
 *   Ted Gould <ted.gould@canonical.com>
 
18
 */
 
19
 
 
20
#include "glib-thread.h"
 
21
 
 
22
namespace GLib
 
23
{
 
24
 
 
25
ContextThread::ContextThread(std::function<void()> beforeLoop, std::function<void()> afterLoop)
 
26
{
 
27
    _cancel = std::shared_ptr<GCancellable>(g_cancellable_new(), [](GCancellable* cancel) {
 
28
        if (cancel != nullptr)
 
29
        {
 
30
            g_cancellable_cancel(cancel);
 
31
            g_object_unref(cancel);
 
32
        }
 
33
    });
 
34
    std::promise<std::pair<std::shared_ptr<GMainContext>, std::shared_ptr<GMainLoop>>> context_promise;
 
35
 
 
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
 
38
       same for afterLoop */
 
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); });
 
46
 
 
47
        g_main_context_push_thread_default(context.get());
 
48
 
 
49
        beforeLoop();
 
50
 
 
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);
 
54
 
 
55
        if (!g_cancellable_is_cancelled(_cancel.get()))
 
56
        {
 
57
            g_main_loop_run(loop.get());
 
58
        }
 
59
 
 
60
        afterLoop();
 
61
    });
 
62
 
 
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();
 
69
 
 
70
    _context = context_value.first;
 
71
    _loop = context_value.second;
 
72
 
 
73
    if (!_context || !_loop)
 
74
    {
 
75
        throw std::runtime_error("Unable to create GLib Thread");
 
76
    }
 
77
}
 
78
 
 
79
ContextThread::~ContextThread()
 
80
{
 
81
    quit();
 
82
}
 
83
 
 
84
void ContextThread::quit()
 
85
{
 
86
    g_cancellable_cancel(_cancel.get()); /* Force the cancellation on ongoing tasks */
 
87
    if (_loop)
 
88
    {
 
89
        g_main_loop_quit(_loop.get()); /* Quit the loop */
 
90
    }
 
91
 
 
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())
 
95
    {
 
96
        if (_thread.joinable())
 
97
        {
 
98
            _thread.join();
 
99
        }
 
100
    }
 
101
}
 
102
 
 
103
bool ContextThread::isCancelled()
 
104
{
 
105
    return g_cancellable_is_cancelled(_cancel.get()) == TRUE;
 
106
}
 
107
 
 
108
std::shared_ptr<GCancellable> ContextThread::getCancellable()
 
109
{
 
110
    return _cancel;
 
111
}
 
112
 
 
113
void ContextThread::simpleSource(std::function<GSource*()> srcBuilder, std::function<void()> work)
 
114
{
 
115
    if (isCancelled())
 
116
    {
 
117
        throw std::runtime_error("Trying to execute work on a GLib thread that is shutting down.");
 
118
    }
 
119
 
 
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);
 
124
 
 
125
    auto source = std::shared_ptr<GSource>(srcBuilder(), [](GSource* src) { g_clear_pointer(&src, g_source_unref); });
 
126
    g_source_set_callback(source.get(),
 
127
                          [](gpointer data) {
 
128
                              auto heapWork = static_cast<std::function<void()>*>(data);
 
129
                              (*heapWork)();
 
130
                              return G_SOURCE_REMOVE;
 
131
                          },
 
132
                          heapWork,
 
133
                          [](gpointer data) {
 
134
                              auto heapWork = static_cast<std::function<void()>*>(data);
 
135
                              delete heapWork;
 
136
                          });
 
137
 
 
138
    g_source_attach(source.get(), _context.get());
 
139
}
 
140
 
 
141
void ContextThread::executeOnThread(std::function<void()> work)
 
142
{
 
143
    simpleSource(g_idle_source_new, work);
 
144
}
 
145
 
 
146
void ContextThread::timeout(const std::chrono::milliseconds& length, std::function<void()> work)
 
147
{
 
148
    simpleSource([length]() { return g_timeout_source_new(length.count()); }, work);
 
149
}
 
150
 
 
151
void ContextThread::timeoutSeconds(const std::chrono::seconds& length, std::function<void()> work)
 
152
{
 
153
    simpleSource([length]() { return g_timeout_source_new_seconds(length.count()); }, work);
 
154
}
 
155
 
 
156
}  // ns GLib