Branch data Line data Source code
1 : : /* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
2 : : // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
3 : : // SPDX-FileCopyrightText: 2017 Endless Mobile, Inc.
4 : : // SPDX-FileCopyrightText: 2021 Canonical Ltd.
5 : : // SPDX-FileContributor: Authored by: Philip Chimento <philip@endlessm.com>
6 : : // SPDX-FileContributor: Philip Chimento <philip.chimento@gmail.com>
7 : : // SPDX-FileContributor: Marco Trevisan <marco.trevisan@canonical.com>
8 : :
9 : : #pragma once
10 : :
11 : : #include <config.h>
12 : :
13 : : #include <atomic>
14 : : #include <deque>
15 : : #include <thread>
16 : : #include <utility> // for pair
17 : :
18 : : #include <glib.h> // for gboolean
19 : :
20 : : class ObjectInstance;
21 : : namespace Gjs {
22 : : namespace Test {
23 : : struct ToggleQueue;
24 : : }
25 : : }
26 : :
27 : : /* Thread-safe queue for enqueueing toggle-up or toggle-down events on GObjects
28 : : * from any thread. For more information, see object.cpp, comments near
29 : : * wrapped_gobj_toggle_notify(). */
30 : : class ToggleQueue {
31 : : public:
32 : : enum Direction {
33 : : DOWN,
34 : : UP
35 : : };
36 : :
37 : : using Handler = void (*)(ObjectInstance*, Direction);
38 : :
39 : : private:
40 : : friend Gjs::Test::ToggleQueue;
41 : : struct Item {
42 : : Item() {}
43 : 62 : Item(ObjectInstance* o, Direction d) : object(o), direction(d) {}
44 : : ObjectInstance* object;
45 : : ToggleQueue::Direction direction;
46 : : };
47 : :
48 : : struct Locked {
49 : 9660 : explicit Locked(ToggleQueue* queue) { queue->lock(); }
50 : 9660 : ~Locked() { get_default_unlocked().maybe_unlock(); }
51 : 9608 : ToggleQueue* operator->() { return &get_default_unlocked(); }
52 : : };
53 : :
54 : : std::deque<Item> q;
55 : : std::atomic_bool m_shutdown = ATOMIC_VAR_INIT(false);
56 : :
57 : : unsigned m_idle_id = 0;
58 : : Handler m_toggle_handler = nullptr;
59 : : std::atomic<std::thread::id> m_holder = std::thread::id();
60 : : unsigned m_holder_ref_count = 0;
61 : :
62 : : void lock();
63 : : void maybe_unlock();
64 : : [[nodiscard]] bool is_locked() const {
65 : : return m_holder != std::thread::id();
66 : : }
67 : 19628 : [[nodiscard]] bool owns_lock() const {
68 : 19628 : return m_holder == std::this_thread::get_id();
69 : : }
70 : :
71 : : [[nodiscard]]
72 : : std::deque<Item>::iterator find_operation_locked(const ObjectInstance*,
73 : : Direction);
74 : :
75 : : [[nodiscard]]
76 : : std::deque<Item>::const_iterator find_operation_locked(
77 : : const ObjectInstance*, Direction) const;
78 : :
79 : : static gboolean idle_handle_toggle(void *data);
80 : : static void idle_destroy_notify(void *data);
81 : :
82 : 28873 : [[nodiscard]] static ToggleQueue& get_default_unlocked() {
83 [ + + + - ]: 28873 : static ToggleQueue the_singleton;
84 : 28873 : return the_singleton;
85 : : }
86 : :
87 : : public:
88 : : /* These two functions return a pair DOWN, UP signifying whether toggles
89 : : * are / were queued. is_queued() just checks and does not modify. */
90 : : [[nodiscard]] std::pair<bool, bool> is_queued(ObjectInstance*) const;
91 : : // Cancels pending toggles and returns whether any were queued.
92 : : std::pair<bool, bool> cancel(ObjectInstance*);
93 : :
94 : : /* Pops a toggle from the queue and processes it. Call this if you don't
95 : : * want to wait for it to be processed in idle time. Returns false if queue
96 : : * is empty. */
97 : : bool handle_toggle(Handler);
98 : : void handle_all_toggles(Handler);
99 : :
100 : : /* After calling this, the toggle queue won't accept any more toggles. Only
101 : : * intended for use when destroying the JSContext and breaking the
102 : : * associations between C and JS objects. */
103 : : void shutdown();
104 : :
105 : : // Queues a toggle to be processed in idle time.
106 : : void enqueue(ObjectInstance*, Direction, Handler);
107 : :
108 : 9605 : [[nodiscard]] static Locked get_default() {
109 : 9605 : return Locked(&get_default_unlocked());
110 : : }
111 : : };
|