summaryrefslogtreecommitdiff
path: root/Userland/Libraries/LibCore/EventLoop.h
blob: a8519507b452042c76adf3cd43931503dfdcf866 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
/*
 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
 * Copyright (c) 2022, kleines Filmröllchen <filmroellchen@serenityos.org>
 * Copyright (c) 2022, the SerenityOS developers.
 *
 * SPDX-License-Identifier: BSD-2-Clause
 */

#pragma once

#include <AK/Forward.h>
#include <AK/Function.h>
#include <AK/HashMap.h>
#include <AK/Noncopyable.h>
#include <AK/NonnullOwnPtr.h>
#include <AK/NonnullRefPtr.h>
#include <AK/Time.h>
#include <AK/Vector.h>
#include <AK/WeakPtr.h>
#include <LibCore/DeferredInvocationContext.h>
#include <LibCore/Event.h>
#include <LibCore/Forward.h>
#include <LibThreading/MutexProtected.h>
#include <sys/time.h>
#include <sys/types.h>

namespace Core {

// The event loop enables asynchronous (not parallel or multi-threaded) computing by efficiently handling events from various sources.
// Event loops are most important for GUI programs, where the various GUI updates and action callbacks run on the EventLoop,
// as well as services, where asynchronous remote procedure calls of multiple clients are handled.
// Event loops, through select(), allow programs to "go to sleep" for most of their runtime until some event happens.
// EventLoop is too expensive to use in realtime scenarios (read: audio) where even the time required by a single select() system call is too large and unpredictable.
//
// There is at most one running event loop per thread.
// Another event loop can be started while another event loop is already running; that new event loop will take over for the other event loop.
// This is mainly used in LibGUI, where each modal window stacks another event loop until it is closed.
// However, that means you need to be careful with storing the current event loop, as it might already be gone at the time of use.
// Event loops currently handle these kinds of events:
// - Deferred invocations caused by various objects. These are just a generic way of telling the EventLoop to run some function as soon as possible at a later point.
// - Timers, which repeatedly (or once after a delay) run a function on the EventLoop. Note that timers are not super accurate.
// - Filesystem notifications, i.e. whenever a file is read from, written to, etc.
// - POSIX signals, which allow the event loop to act as a signal handler and dispatch those signals in a more user-friendly way.
// - Fork events, because the child process event loop needs to clear its events and handlers.
// - Quit events, i.e. the event loop should exit.
// Any event that the event loop needs to wait on or needs to repeatedly handle is stored in a handle, e.g. s_timers.
//
// EventLoop has one final responsibility: Handling the InspectorServer connection and processing requests to the Object hierarchy.
class EventLoop {
    friend struct EventLoopPusher;

public:
    enum class MakeInspectable {
        No,
        Yes,
    };

    enum class ShouldWake {
        No,
        Yes
    };

    enum class WaitMode {
        WaitForEvents,
        PollForEvents,
    };

    explicit EventLoop(MakeInspectable = MakeInspectable::No);
    ~EventLoop();

    static void initialize_wake_pipes();
    static bool has_been_instantiated();

    // Pump the event loop until its exit is requested.
    int exec();

    // Process events, generally called by exec() in a loop.
    // This should really only be used for integrating with other event loops.
    // The wait mode determines whether pump() uses select() to wait for the next event.
    size_t pump(WaitMode = WaitMode::WaitForEvents);

    // Pump the event loop until some condition is met.
    void spin_until(Function<bool()>);

    // Post an event to this event loop and possibly wake the loop.
    void post_event(Object& receiver, NonnullOwnPtr<Event>&&, ShouldWake = ShouldWake::No);
    void wake_once(Object& receiver, int custom_event_type);

    void add_job(NonnullRefPtr<Promise<NonnullRefPtr<Object>>> job_promise);

    void deferred_invoke(Function<void()> invokee)
    {
        auto context = DeferredInvocationContext::construct();
        post_event(context, make<Core::DeferredInvocationEvent>(context, move(invokee)));
    }

    void wake();

    void quit(int);
    void unquit();
    bool was_exit_requested() const { return m_exit_requested; }

    // The registration functions act upon the current loop of the current thread.
    static int register_timer(Object&, int milliseconds, bool should_reload, TimerShouldFireWhenNotVisible);
    static bool unregister_timer(int timer_id);

    static void register_notifier(Badge<Notifier>, Notifier&);
    static void unregister_notifier(Badge<Notifier>, Notifier&);

    static int register_signal(int signo, Function<void(int)> handler);
    static void unregister_signal(int handler_id);

    // Note: Boost uses Parent/Child/Prepare, but we don't really have anything
    //       interesting to do in the parent or before forking.
    enum class ForkEvent {
        Child,
    };
    static void notify_forked(ForkEvent);

    void take_pending_events_from(EventLoop& other)
    {
        m_queued_events.extend(move(other.m_queued_events));
    }

    static EventLoop& current();

    static void wake_current();

private:
    void wait_for_event(WaitMode);
    Optional<Time> get_next_timer_expiration();
    static void dispatch_signal(int);
    static void handle_signal(int);

    struct QueuedEvent {
        AK_MAKE_NONCOPYABLE(QueuedEvent);

    public:
        QueuedEvent(Object& receiver, NonnullOwnPtr<Event>);
        QueuedEvent(QueuedEvent&&);
        ~QueuedEvent() = default;

        WeakPtr<Object> receiver;
        NonnullOwnPtr<Event> event;
    };

    Vector<QueuedEvent, 64> m_queued_events;
    Vector<NonnullRefPtr<Promise<NonnullRefPtr<Object>>>> m_pending_promises;
    static pid_t s_pid;

    bool m_exit_requested { false };
    int m_exit_code { 0 };

    static thread_local int s_wake_pipe_fds[2];
    static thread_local bool s_wake_pipe_initialized;

    // The wake pipe of this event loop needs to be accessible from other threads.
    int (*m_wake_pipe_fds)[2];

    struct Private;
    NonnullOwnPtr<Private> m_private;
};

inline void deferred_invoke(Function<void()> invokee)
{
    EventLoop::current().deferred_invoke(move(invokee));
}

}