[GLIB] Remove delete on destroy GMainLoopSources
[WebKit-https.git] / Source / WTF / wtf / glib / GMainLoopSource.cpp
1 /*
2  * Copyright (C) 2014 Igalia S.L.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "GMainLoopSource.h"
28
29 #if USE(GLIB)
30
31 #include <gio/gio.h>
32
33 namespace WTF {
34
35 GMainLoopSource::~GMainLoopSource()
36 {
37     cancel();
38 }
39
40 bool GMainLoopSource::isScheduled() const
41 {
42     return m_status == Scheduled;
43 }
44
45 bool GMainLoopSource::isActive() const
46 {
47     return m_status != Ready;
48 }
49
50 void GMainLoopSource::cancel()
51 {
52     // A valid context should only be present if GMainLoopSource is in the Scheduled or Dispatching state.
53     ASSERT(!m_context.source || m_status == Scheduled || m_status == Dispatching);
54
55     m_status = Ready;
56
57     g_cancellable_cancel(m_context.socketCancellable.get());
58
59     if (!m_context.source)
60         return;
61
62     Context context;
63     context = WTF::move(m_context);
64     context.destroySource();
65 }
66
67 void GMainLoopSource::scheduleIdleSource(const char* name, GSourceFunc sourceFunction, int priority, GMainContext* context)
68 {
69     ASSERT(m_status == Ready);
70     m_status = Scheduled;
71
72     g_source_set_name(m_context.source.get(), name);
73     if (priority != G_PRIORITY_DEFAULT_IDLE)
74         g_source_set_priority(m_context.source.get(), priority);
75     g_source_set_callback(m_context.source.get(), sourceFunction, this, nullptr);
76     g_source_attach(m_context.source.get(), context);
77 }
78
79 void GMainLoopSource::schedule(const char* name, std::function<void ()>&& function, int priority, std::function<void ()>&& destroyFunction, GMainContext* context)
80 {
81     cancel();
82
83     ASSERT(!m_context.source);
84     m_context = {
85         adoptGRef(g_idle_source_new()),
86         nullptr, // cancellable
87         nullptr, // socketCancellable
88         WTF::move(function),
89         nullptr, // boolCallback
90         nullptr, // socketCallback
91         WTF::move(destroyFunction)
92     };
93     scheduleIdleSource(name, reinterpret_cast<GSourceFunc>(voidSourceCallback), priority, context);
94 }
95
96 void GMainLoopSource::schedule(const char* name, std::function<bool ()>&& function, int priority, std::function<void ()>&& destroyFunction, GMainContext* context)
97 {
98     cancel();
99
100     ASSERT(!m_context.source);
101     m_context = {
102         adoptGRef(g_idle_source_new()),
103         nullptr, // cancellable
104         nullptr, // socketCancellable
105         nullptr, // voidCallback
106         WTF::move(function),
107         nullptr, // socketCallback
108         WTF::move(destroyFunction)
109     };
110     scheduleIdleSource(name, reinterpret_cast<GSourceFunc>(boolSourceCallback), priority, context);
111 }
112
113 void GMainLoopSource::schedule(const char* name, std::function<bool (GIOCondition)>&& function, GSocket* socket, GIOCondition condition, std::function<void ()>&& destroyFunction, GMainContext* context)
114 {
115     cancel();
116
117     ASSERT(!m_context.source);
118     GCancellable* socketCancellable = g_cancellable_new();
119     m_context = {
120         adoptGRef(g_socket_create_source(socket, condition, socketCancellable)),
121         nullptr, // cancellable
122         adoptGRef(socketCancellable),
123         nullptr, // voidCallback
124         nullptr, // boolCallback
125         WTF::move(function),
126         WTF::move(destroyFunction)
127     };
128
129     ASSERT(m_status == Ready);
130     m_status = Scheduled;
131     g_source_set_name(m_context.source.get(), name);
132     g_source_set_callback(m_context.source.get(), reinterpret_cast<GSourceFunc>(socketSourceCallback), this, nullptr);
133     g_source_attach(m_context.source.get(), context);
134 }
135
136 void GMainLoopSource::scheduleTimeoutSource(const char* name, GSourceFunc sourceFunction, int priority, GMainContext* context)
137 {
138     ASSERT(m_status == Ready);
139     m_status = Scheduled;
140
141     g_source_set_name(m_context.source.get(), name);
142     if (priority != G_PRIORITY_DEFAULT)
143         g_source_set_priority(m_context.source.get(), priority);
144     g_source_set_callback(m_context.source.get(), sourceFunction, this, nullptr);
145     g_source_attach(m_context.source.get(), context);
146 }
147
148 void GMainLoopSource::scheduleAfterDelay(const char* name, std::function<void ()>&& function, std::chrono::milliseconds delay, int priority, std::function<void ()>&& destroyFunction, GMainContext* context)
149 {
150     cancel();
151
152     ASSERT(!m_context.source);
153     m_context = {
154         adoptGRef(g_timeout_source_new(delay.count())),
155         nullptr, // cancellable
156         nullptr, // socketCancellable
157         WTF::move(function),
158         nullptr, // boolCallback
159         nullptr, // socketCallback
160         WTF::move(destroyFunction)
161     };
162     scheduleTimeoutSource(name, reinterpret_cast<GSourceFunc>(voidSourceCallback), priority, context);
163 }
164
165 void GMainLoopSource::scheduleAfterDelay(const char* name, std::function<bool ()>&& function, std::chrono::milliseconds delay, int priority, std::function<void ()>&& destroyFunction, GMainContext* context)
166 {
167     cancel();
168
169     ASSERT(!m_context.source);
170     m_context = {
171         adoptGRef(g_timeout_source_new(delay.count())),
172         nullptr, // cancellable
173         nullptr, // socketCancellable
174         nullptr, // voidCallback
175         WTF::move(function),
176         nullptr, // socketCallback
177         WTF::move(destroyFunction)
178     };
179     scheduleTimeoutSource(name, reinterpret_cast<GSourceFunc>(boolSourceCallback), priority, context);
180 }
181
182 void GMainLoopSource::scheduleAfterDelay(const char* name, std::function<void ()>&& function, std::chrono::seconds delay, int priority, std::function<void ()>&& destroyFunction, GMainContext* context)
183 {
184     cancel();
185
186     ASSERT(!m_context.source);
187     m_context = {
188         adoptGRef(g_timeout_source_new_seconds(delay.count())),
189         nullptr, // cancellable
190         nullptr, // socketCancellable
191         WTF::move(function),
192         nullptr, // boolCallback
193         nullptr, // socketCallback
194         WTF::move(destroyFunction)
195     };
196     scheduleTimeoutSource(name, reinterpret_cast<GSourceFunc>(voidSourceCallback), priority, context);
197 }
198
199 void GMainLoopSource::scheduleAfterDelay(const char* name, std::function<bool ()>&& function, std::chrono::seconds delay, int priority, std::function<void ()>&& destroyFunction, GMainContext* context)
200 {
201     cancel();
202
203     ASSERT(!m_context.source);
204     m_context = {
205         adoptGRef(g_timeout_source_new_seconds(delay.count())),
206         nullptr, // cancellable
207         nullptr, // socketCancellable
208         nullptr, // voidCallback
209         WTF::move(function),
210         nullptr, // socketCallback
211         WTF::move(destroyFunction)
212     };
213     scheduleTimeoutSource(name, reinterpret_cast<GSourceFunc>(boolSourceCallback), priority, context);
214 }
215
216 struct MicrosecondsTimeoutSource {
217     GSource source;
218     uint64_t delay;
219 };
220
221 static GSourceFuncs microsecondsTimeoutSourceFunctions = {
222     nullptr, // prepare
223     nullptr, // check
224     // dispatch
225     [](GSource* source, GSourceFunc callback, gpointer userData) -> gboolean
226     {
227         bool repeat = callback(userData);
228         if (repeat)
229             g_source_set_ready_time(source, g_source_get_time(source) + reinterpret_cast<MicrosecondsTimeoutSource*>(source)->delay);
230         return repeat;
231     },
232     nullptr, // finalize
233     nullptr, // closure_callback
234     nullptr // closure_marshall
235 };
236
237 static GSource* createMicrosecondsTimeoutSource(uint64_t delay)
238 {
239     GSource* source = g_source_new(&microsecondsTimeoutSourceFunctions, sizeof(MicrosecondsTimeoutSource));
240     reinterpret_cast<MicrosecondsTimeoutSource*>(source)->delay = delay;
241     g_source_set_ready_time(source, g_get_monotonic_time() + delay);
242     return source;
243 }
244
245 void GMainLoopSource::scheduleAfterDelay(const char* name, std::function<void ()>&& function, std::chrono::microseconds delay, int priority, std::function<void ()>&& destroyFunction, GMainContext* context)
246 {
247     cancel();
248
249     ASSERT(!m_context.source);
250     m_context = {
251         adoptGRef(createMicrosecondsTimeoutSource(delay.count())),
252         nullptr, // cancellable
253         nullptr, // socketCancellable
254         WTF::move(function),
255         nullptr, // boolCallback
256         nullptr, // socketCallback
257         WTF::move(destroyFunction)
258     };
259     scheduleTimeoutSource(name, reinterpret_cast<GSourceFunc>(voidSourceCallback), priority, context);
260 }
261
262 void GMainLoopSource::scheduleAfterDelay(const char* name, std::function<bool ()>&& function, std::chrono::microseconds delay, int priority, std::function<void ()>&& destroyFunction, GMainContext* context)
263 {
264     cancel();
265
266     ASSERT(!m_context.source);
267     m_context = {
268         adoptGRef(createMicrosecondsTimeoutSource(delay.count())),
269         nullptr, // cancellable
270         nullptr, // socketCancellable
271         nullptr, // voidCallback
272         WTF::move(function),
273         nullptr, // socketCallback
274         WTF::move(destroyFunction)
275     };
276     scheduleTimeoutSource(name, reinterpret_cast<GSourceFunc>(boolSourceCallback), priority, context);
277 }
278
279 bool GMainLoopSource::prepareVoidCallback(Context& context)
280 {
281     if (!m_context.source)
282         return false;
283
284     context = WTF::move(m_context);
285
286     ASSERT(context.voidCallback);
287     ASSERT(m_status == Scheduled);
288     m_status = Dispatching;
289
290     return true;
291 }
292
293 void GMainLoopSource::finishVoidCallback()
294 {
295     m_status = Ready;
296 }
297
298 void GMainLoopSource::voidCallback()
299 {
300     Context context;
301     if (!prepareVoidCallback(context))
302         return;
303
304     context.voidCallback();
305     if (m_status != Ready && !m_context.source) {
306         // Switch to Ready if it hasn't been re-scheduled or cancelled.
307         finishVoidCallback();
308     }
309
310     context.destroySource();
311 }
312
313 bool GMainLoopSource::prepareBoolCallback(Context& context)
314 {
315     if (!m_context.source)
316         return false;
317
318     context = WTF::move(m_context);
319
320     ASSERT(context.boolCallback);
321     ASSERT(m_status == Scheduled || m_status == Dispatching);
322     m_status = Dispatching;
323     return true;
324 }
325
326 void GMainLoopSource::finishBoolCallback(bool retval, Context& context)
327 {
328     // m_status should reflect whether the GMainLoopSource has been rescheduled during dispatch.
329     ASSERT((!m_context.source && m_status == Dispatching) || m_status == Scheduled);
330     if (retval && !m_context.source)
331         m_context = WTF::move(context);
332     else if (!retval)
333         m_status = Ready;
334 }
335
336 bool GMainLoopSource::boolCallback()
337 {
338     Context context;
339     if (!prepareBoolCallback(context))
340         return Stop;
341
342     bool retval = context.boolCallback();
343     if (m_status != Ready && !m_context.source) {
344         // Prepare for a new iteration or switch to Ready if it hasn't been re-scheduled or cancelled.
345         finishBoolCallback(retval, context);
346     }
347
348     if (context.source)
349         context.destroySource();
350
351     return retval;
352 }
353
354 bool GMainLoopSource::socketCallback(GIOCondition condition)
355 {
356     if (!m_context.source)
357         return Stop;
358
359     Context context;
360     context = WTF::move(m_context);
361
362     ASSERT(context.socketCallback);
363     ASSERT(m_status == Scheduled || m_status == Dispatching);
364     m_status = Dispatching;
365
366     if (g_cancellable_is_cancelled(context.socketCancellable.get())) {
367         context.destroySource();
368         return Stop;
369     }
370
371     bool retval = context.socketCallback(condition);
372
373     if (m_status != Ready && !m_context.source) {
374         // m_status should reflect whether the GMainLoopSource has been rescheduled during dispatch.
375         ASSERT((!m_context.source && m_status == Dispatching) || m_status == Scheduled);
376         if (retval && !m_context.source)
377             m_context = WTF::move(context);
378         else if (!retval)
379             m_status = Ready;
380     }
381
382     if (context.source)
383         context.destroySource();
384
385     return retval;
386 }
387
388 gboolean GMainLoopSource::voidSourceCallback(GMainLoopSource* source)
389 {
390     source->voidCallback();
391     return G_SOURCE_REMOVE;
392 }
393
394 gboolean GMainLoopSource::boolSourceCallback(GMainLoopSource* source)
395 {
396     return source->boolCallback() == Continue;
397 }
398
399 gboolean GMainLoopSource::socketSourceCallback(GSocket*, GIOCondition condition, GMainLoopSource* source)
400 {
401     return source->socketCallback(condition) == Continue;
402 }
403
404 void GMainLoopSource::Context::destroySource()
405 {
406     g_source_destroy(source.get());
407     if (destroyCallback)
408         destroyCallback();
409 }
410
411 } // namespace WTF
412
413 #endif // USE(GLIB)