aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/core/hle/service/am/event_observer.cpp
blob: 5d1d303ed3c0abda6a80ae8e58cb449d1ae431a5 (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
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "core/core.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/service/am/applet.h"
#include "core/hle/service/am/event_observer.h"
#include "core/hle/service/am/window_system.h"

namespace Service::AM {

enum class UserDataTag : u32 {
    WakeupEvent,
    AppletProcess,
};

EventObserver::EventObserver(Core::System& system, WindowSystem& window_system)
    : m_system(system), m_context(system, "am:EventObserver"), m_window_system(window_system),
      m_wakeup_event(m_context), m_wakeup_holder(m_wakeup_event.GetHandle()) {
    m_window_system.SetEventObserver(this);
    m_wakeup_holder.SetUserData(static_cast<uintptr_t>(UserDataTag::WakeupEvent));
    m_wakeup_holder.LinkToMultiWait(std::addressof(m_multi_wait));
    m_thread = std::thread([&] { this->ThreadFunc(); });
}

EventObserver::~EventObserver() {
    // Signal thread and wait for processing to finish.
    m_stop_source.request_stop();
    m_wakeup_event.Signal();
    m_thread.join();

    // Free remaining owned sessions.
    auto it = m_process_holder_list.begin();
    while (it != m_process_holder_list.end()) {
        // Get the holder.
        auto* const holder = std::addressof(*it);

        // Remove from the list.
        it = m_process_holder_list.erase(it);

        // Free the holder.
        delete holder;
    }
}

void EventObserver::TrackAppletProcess(Applet& applet) {
    // Don't observe dummy processes.
    if (!applet.process->IsInitialized()) {
        return;
    }

    // Allocate new holder.
    auto* holder = new ProcessHolder(applet, *applet.process);
    holder->SetUserData(static_cast<uintptr_t>(UserDataTag::AppletProcess));

    // Insert into list.
    {
        std::scoped_lock lk{m_lock};
        m_process_holder_list.push_back(*holder);
        holder->LinkToMultiWait(std::addressof(m_deferred_wait_list));
    }

    // Signal wakeup.
    m_wakeup_event.Signal();
}

void EventObserver::RequestUpdate() {
    m_wakeup_event.Signal();
}

void EventObserver::LinkDeferred() {
    std::scoped_lock lk{m_lock};
    m_multi_wait.MoveAll(std::addressof(m_deferred_wait_list));
}

MultiWaitHolder* EventObserver::WaitSignaled() {
    while (true) {
        this->LinkDeferred();

        // If we're done, return before we start waiting.
        if (m_stop_source.stop_requested()) {
            return nullptr;
        }

        auto* selected = m_multi_wait.WaitAny(m_system.Kernel());
        if (selected != std::addressof(m_wakeup_holder)) {
            // Unlink the process.
            selected->UnlinkFromMultiWait();
        }

        return selected;
    }
}

void EventObserver::Process(MultiWaitHolder* holder) {
    switch (static_cast<UserDataTag>(holder->GetUserData())) {
    case UserDataTag::WakeupEvent:
        this->OnWakeupEvent(holder);
        break;
    case UserDataTag::AppletProcess:
        this->OnProcessEvent(static_cast<ProcessHolder*>(holder));
        break;
    default:
        UNREACHABLE();
    }
}

void EventObserver::OnWakeupEvent(MultiWaitHolder* holder) {
    m_wakeup_event.Clear();

    // Perform recalculation.
    m_window_system.Update();
}

void EventObserver::OnProcessEvent(ProcessHolder* holder) {
    // Check process state.
    auto& applet = holder->GetApplet();
    auto& process = holder->GetProcess();

    {
        std::scoped_lock lk{m_lock, applet.lock};
        if (process.IsTerminated()) {
            // Destroy the holder.
            this->DestroyAppletProcessHolderLocked(holder);
        } else {
            // Reset signaled state.
            process.ResetSignal();

            // Relink wakeup event.
            holder->LinkToMultiWait(std::addressof(m_deferred_wait_list));
        }

        // Set running.
        applet.is_process_running = process.IsRunning();
    }

    // Perform recalculation.
    m_window_system.Update();
}

void EventObserver::DestroyAppletProcessHolderLocked(ProcessHolder* holder) {
    // Remove from owned list.
    m_process_holder_list.erase(m_process_holder_list.iterator_to(*holder));

    // Destroy and free.
    delete holder;
}

void EventObserver::ThreadFunc() {
    Common::SetCurrentThreadName("am:EventObserver");

    while (true) {
        auto* signaled_holder = this->WaitSignaled();
        if (!signaled_holder) {
            break;
        }

        this->Process(signaled_holder);
    }
}

} // namespace Service::AM