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