aboutsummaryrefslogtreecommitdiff
path: root/src/tests/core/priority_queue.cpp
blob: 0d7174f7932c679eeeb5e450c73771b9893a006f (plain) (blame)
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
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later

#include <algorithm>
#include <array>
#include <random>
#include <vector>
#include <catch2/catch_test_macros.hpp>
#include "core/hle/kernel/k_priority_queue.h"

namespace {
struct Affinity {
    u64 GetAffinityMask() const {
        return 3;
    }
    void SetAffinityMask(u64) {}
    bool GetAffinity(s32) const {
        return true;
    }
    void SetAffinity(s32, bool) {}
    void SetAll() {}
};

struct Member {
    struct QueueEntry {
        Member* prev{};
        Member* next{};
        void Initialize() {
            prev = next = nullptr;
        }
        void SetPrev(Member* value) {
            prev = value;
        }
        void SetNext(Member* value) {
            next = value;
        }
        Member* GetNext() const {
            return next;
        }
        Member* GetPrev() const {
            return prev;
        }
    };
    std::array<QueueEntry, 2> entries{};
    s32 priority{};
    QueueEntry& GetPriorityQueueEntry(s32 core) {
        return entries[core];
    }
    const QueueEntry& GetPriorityQueueEntry(s32 core) const {
        return entries[core];
    }
    Affinity GetAffinityMask() const {
        return {};
    }
    s32 GetActiveCore() const {
        return 0;
    }
    s32 GetPriority() const {
        return priority;
    }
    bool IsDummyThread() const {
        return false;
    }
};

template <size_t Priorities>
void CheckQueue() {
    using Queue = Kernel::KPriorityQueue<Member, 2, Priorities - 1, 0>;
    typename Queue::KPriorityQueueImpl queue;
    std::array<Member, 160> members{};
    std::array<std::array<bool, 2>, 160> present{};
    std::array<std::array<std::vector<Member*>, Priorities>, 2> model{};
    std::mt19937 random{6152};
    for (size_t i = 0; i < members.size(); ++i) {
        members[i].priority = static_cast<s32>(i % Priorities);
    }
    REQUIRE(queue.GetFront(0) == nullptr);
    for (u32 operation = 0; operation < 3000; ++operation) {
        const size_t index = random() % members.size();
        const s32 core = random() % 2;
        auto& member = members[index];
        auto& expected = model[core][member.priority];
        if (present[index][core]) {
            queue.Remove(member.priority, core, &member);
            std::erase(expected, &member);
        } else if ((random() & 1) != 0) {
            queue.PushFront(member.priority, core, &member);
            expected.insert(expected.begin(), &member);
        } else {
            queue.PushBack(member.priority, core, &member);
            expected.push_back(&member);
        }
        present[index][core] = !present[index][core];
        for (s32 checked_core = 0; checked_core < 2; ++checked_core) {
            Member* actual = queue.GetFront(checked_core);
            for (const auto& priority : model[checked_core]) {
                for (Member* wanted : priority) {
                    REQUIRE(actual == wanted);
                    actual = queue.GetNext(checked_core, actual);
                }
            }
            REQUIRE(actual == nullptr);
        }
    }
}
} // namespace

TEST_CASE("Priority queue bitmap traversal preserves thread order", "[core]") {
    CheckQueue<64>();
    CheckQueue<128>();
}