c70834b4b6708b329ce0b70f48c21ba42c0229cb
[WebKit-https.git] / Source / WTF / wtf / glib / RunLoopGLib.cpp
1 /*
2  * Copyright (C) 2010 Apple Inc. All rights reserved.
3  * Portions Copyright (c) 2010 Motorola Mobility, Inc.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
15  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
16  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
18  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
24  * THE POSSIBILITY OF SUCH DAMAGE.
25  */
26
27 #include "config.h"
28 #include "RunLoop.h"
29
30 #include <glib.h>
31 #include <wtf/MainThread.h>
32
33 namespace WTF {
34
35 static GSourceFuncs runLoopSourceFunctions = {
36     nullptr, // prepare
37     nullptr, // check
38     // dispatch
39     [](GSource* source, GSourceFunc callback, gpointer userData) -> gboolean
40     {
41         if (g_source_get_ready_time(source) == -1)
42             return G_SOURCE_CONTINUE;
43         g_source_set_ready_time(source, -1);
44         return callback(userData);
45     },
46     nullptr, // finalize
47     nullptr, // closure_callback
48     nullptr, // closure_marshall
49 };
50
51 RunLoop::RunLoop()
52 {
53     m_mainContext = g_main_context_get_thread_default();
54     if (!m_mainContext)
55         m_mainContext = isMainThread() ? g_main_context_default() : adoptGRef(g_main_context_new());
56     ASSERT(m_mainContext);
57
58     GRefPtr<GMainLoop> innermostLoop = adoptGRef(g_main_loop_new(m_mainContext.get(), FALSE));
59     ASSERT(innermostLoop);
60     m_mainLoops.append(innermostLoop);
61
62     m_source = adoptGRef(g_source_new(&runLoopSourceFunctions, sizeof(GSource)));
63     g_source_set_name(m_source.get(), "[WebKit] RunLoop work");
64     g_source_set_can_recurse(m_source.get(), TRUE);
65     g_source_set_ready_time(m_source.get(), -1);
66     g_source_set_callback(m_source.get(), [](gpointer userData) -> gboolean {
67         static_cast<RunLoop*>(userData)->performWork();
68         return G_SOURCE_CONTINUE;
69     }, this, nullptr);
70     g_source_attach(m_source.get(), m_mainContext.get());
71 }
72
73 RunLoop::~RunLoop()
74 {
75     g_source_destroy(m_source.get());
76
77     for (int i = m_mainLoops.size() - 1; i >= 0; --i) {
78         if (!g_main_loop_is_running(m_mainLoops[i].get()))
79             continue;
80         g_main_loop_quit(m_mainLoops[i].get());
81     }
82 }
83
84 void RunLoop::run()
85 {
86     RunLoop& runLoop = RunLoop::current();
87     GMainContext* mainContext = runLoop.m_mainContext.get();
88
89     // The innermost main loop should always be there.
90     ASSERT(!runLoop.m_mainLoops.isEmpty());
91
92     GMainLoop* innermostLoop = runLoop.m_mainLoops[0].get();
93     if (!g_main_loop_is_running(innermostLoop)) {
94         g_main_context_push_thread_default(mainContext);
95         g_main_loop_run(innermostLoop);
96         g_main_context_pop_thread_default(mainContext);
97         return;
98     }
99
100     // Create and run a nested loop if the innermost one was already running.
101     GMainLoop* nestedMainLoop = g_main_loop_new(mainContext, FALSE);
102     runLoop.m_mainLoops.append(adoptGRef(nestedMainLoop));
103
104     g_main_context_push_thread_default(mainContext);
105     g_main_loop_run(nestedMainLoop);
106     g_main_context_pop_thread_default(mainContext);
107
108     runLoop.m_mainLoops.removeLast();
109 }
110
111 void RunLoop::stop()
112 {
113     g_source_set_ready_time(m_source.get(), -1);
114
115     // The innermost main loop should always be there.
116     ASSERT(!m_mainLoops.isEmpty());
117     GRefPtr<GMainLoop> lastMainLoop = m_mainLoops.last();
118     if (g_main_loop_is_running(lastMainLoop.get()))
119         g_main_loop_quit(lastMainLoop.get());
120 }
121
122 void RunLoop::wakeUp()
123 {
124     g_source_set_ready_time(m_source.get(), g_get_monotonic_time());
125 }
126
127 RunLoop::TimerBase::TimerBase(RunLoop& runLoop)
128     : m_runLoop(runLoop)
129     , m_source(adoptGRef(g_source_new(&runLoopSourceFunctions, sizeof(GSource))))
130 {
131     g_source_set_name(m_source.get(), "[WebKit] RunLoop::Timer work");
132     g_source_set_ready_time(m_source.get(), -1);
133     g_source_set_callback(m_source.get(), [](gpointer userData) -> gboolean {
134         RunLoop::TimerBase* timer = static_cast<RunLoop::TimerBase*>(userData);
135         timer->fired();
136         if (timer->m_isRepeating)
137             timer->updateReadyTime();
138         return G_SOURCE_CONTINUE;
139     }, this, nullptr);
140     g_source_attach(m_source.get(), m_runLoop.m_mainContext.get());
141 }
142
143 RunLoop::TimerBase::~TimerBase()
144 {
145     g_source_destroy(m_source.get());
146 }
147
148 void RunLoop::TimerBase::updateReadyTime()
149 {
150     g_source_set_ready_time(m_source.get(), m_fireInterval.count() ? g_get_monotonic_time() + m_fireInterval.count() : 0);
151 }
152
153 void RunLoop::TimerBase::start(double fireInterval, bool repeat)
154 {
155     m_fireInterval = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::duration<double>(fireInterval));
156     m_isRepeating = repeat;
157     updateReadyTime();
158 }
159
160 void RunLoop::TimerBase::stop()
161 {
162     g_source_set_ready_time(m_source.get(), -1);
163 }
164
165 bool RunLoop::TimerBase::isActive() const
166 {
167     return g_source_get_ready_time(m_source.get()) != -1;
168 }
169
170 } // namespace WTF