|  | /* Run a function on the main thread | 
|  | Copyright (C) 2019-2025 Free Software Foundation, Inc. | 
|  |  | 
|  | This file is part of GDB. | 
|  |  | 
|  | This program is free software; you can redistribute it and/or modify | 
|  | it under the terms of the GNU General Public License as published by | 
|  | the Free Software Foundation; either version 3 of the License, or | 
|  | (at your option) any later version. | 
|  |  | 
|  | This program is distributed in the hope that it will be useful, | 
|  | but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | GNU General Public License for more details. | 
|  |  | 
|  | You should have received a copy of the GNU General Public License | 
|  | along with this program.  If not, see <http://www.gnu.org/licenses/>.  */ | 
|  |  | 
|  | #include "run-on-main-thread.h" | 
|  | #include "ser-event.h" | 
|  | #if CXX_STD_THREAD | 
|  | #include <thread> | 
|  | #include <mutex> | 
|  | #endif | 
|  | #include "gdbsupport/cleanups.h" | 
|  | #include "gdbsupport/event-loop.h" | 
|  |  | 
|  | /* The serial event used when posting runnables.  */ | 
|  |  | 
|  | static struct serial_event *runnable_event; | 
|  |  | 
|  | /* Runnables that have been posted.  */ | 
|  |  | 
|  | static std::vector<std::function<void ()>> runnables; | 
|  |  | 
|  | #if CXX_STD_THREAD | 
|  |  | 
|  | /* Mutex to hold when handling RUNNABLE_EVENT or RUNNABLES.  */ | 
|  |  | 
|  | static std::mutex runnable_mutex; | 
|  |  | 
|  | /* The main thread's thread id.  */ | 
|  |  | 
|  | static std::thread::id main_thread_id; | 
|  |  | 
|  | #endif | 
|  |  | 
|  | /* Run all the queued runnables.  */ | 
|  |  | 
|  | static void | 
|  | run_events (int error, gdb_client_data client_data) | 
|  | { | 
|  | std::vector<std::function<void ()>> local; | 
|  |  | 
|  | /* Hold the lock while changing the globals, but not while running | 
|  | the runnables.  */ | 
|  | { | 
|  | #if CXX_STD_THREAD | 
|  | std::lock_guard<std::mutex> lock (runnable_mutex); | 
|  | #endif | 
|  |  | 
|  | /* Clear the event fd.  Do this before flushing the events list, | 
|  | so that any new event post afterwards is sure to re-awaken the | 
|  | event loop.  */ | 
|  | serial_event_clear (runnable_event); | 
|  |  | 
|  | /* Move the vector in case running a runnable pushes a new | 
|  | runnable.  */ | 
|  | local = std::move (runnables); | 
|  | } | 
|  |  | 
|  | for (auto &item : local) | 
|  | { | 
|  | try | 
|  | { | 
|  | item (); | 
|  | } | 
|  | catch (const gdb_exception_forced_quit &e) | 
|  | { | 
|  | /* GDB is terminating, so: | 
|  | - make sure this is propagated, and | 
|  | - no need to keep running things, so propagate immediately.  */ | 
|  | throw; | 
|  | } | 
|  | catch (const gdb_exception_quit &e) | 
|  | { | 
|  | /* Should cancellation of a runnable event cancel the execution of | 
|  | the following one?  The answer is not clear, so keep doing what | 
|  | we've done so far: ignore this exception.  */ | 
|  | } | 
|  | catch (const gdb_exception &) | 
|  | { | 
|  | /* Ignore exceptions in the callback.  */ | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /* See run-on-main-thread.h.  */ | 
|  |  | 
|  | void | 
|  | run_on_main_thread (std::function<void ()> &&func) | 
|  | { | 
|  | #if CXX_STD_THREAD | 
|  | std::lock_guard<std::mutex> lock (runnable_mutex); | 
|  | #endif | 
|  | runnables.emplace_back (std::move (func)); | 
|  | serial_event_set (runnable_event); | 
|  | } | 
|  |  | 
|  | #if CXX_STD_THREAD | 
|  | static bool main_thread_id_initialized = false; | 
|  | #endif | 
|  |  | 
|  | /* See run-on-main-thread.h.  */ | 
|  |  | 
|  | bool | 
|  | is_main_thread () | 
|  | { | 
|  | #if CXX_STD_THREAD | 
|  | /* Initialize main_thread_id on first use of is_main_thread.  */ | 
|  | if (!main_thread_id_initialized) | 
|  | { | 
|  | main_thread_id_initialized = true; | 
|  |  | 
|  | main_thread_id = std::this_thread::get_id (); | 
|  | } | 
|  |  | 
|  | return std::this_thread::get_id () == main_thread_id; | 
|  | #else | 
|  | return true; | 
|  | #endif | 
|  | } | 
|  |  | 
|  | INIT_GDB_FILE (run_on_main_thread) | 
|  | { | 
|  | #if CXX_STD_THREAD | 
|  | /* The variable main_thread_id should be initialized when entering main, or | 
|  | at an earlier use, so it should already be initialized here.  */ | 
|  | gdb_assert (main_thread_id_initialized); | 
|  |  | 
|  | /* Assume that we execute this in the main thread.  */ | 
|  | gdb_assert (is_main_thread ()); | 
|  | #endif | 
|  | runnable_event = make_serial_event (); | 
|  | add_file_handler (serial_event_fd (runnable_event), run_events, nullptr, | 
|  | "run-on-main-thread"); | 
|  |  | 
|  | /* A runnable may refer to an extension language.  So, we want to | 
|  | make sure any pending ones have been deleted before the extension | 
|  | languages are shut down.  */ | 
|  | add_final_cleanup ([] () | 
|  | { | 
|  | #if CXX_STD_THREAD | 
|  | std::lock_guard lock (runnable_mutex); | 
|  | #endif | 
|  | runnables.clear (); | 
|  | }); | 
|  | } |