1 /* Run a function on the main thread
2    Copyright (C) 2019-2021 Free Software Foundation, Inc.
3 
4    This file is part of GDB.
5 
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10 
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15 
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
18 
19 #include "defs.h"
20 #include "run-on-main-thread.h"
21 #include "ser-event.h"
22 #if CXX_STD_THREAD
23 #include <mutex>
24 #endif
25 #include "gdbsupport/event-loop.h"
26 
27 /* The serial event used when posting runnables.  */
28 
29 static struct serial_event *runnable_event;
30 
31 /* Runnables that have been posted.  */
32 
33 static std::vector<std::function<void ()>> runnables;
34 
35 #if CXX_STD_THREAD
36 
37 /* Mutex to hold when handling RUNNABLE_EVENT or RUNNABLES.  */
38 
39 static std::mutex runnable_mutex;
40 
41 #endif
42 
43 /* Run all the queued runnables.  */
44 
45 static void
run_events(int error,gdb_client_data client_data)46 run_events (int error, gdb_client_data client_data)
47 {
48   std::vector<std::function<void ()>> local;
49 
50   /* Hold the lock while changing the globals, but not while running
51      the runnables.  */
52   {
53 #if CXX_STD_THREAD
54     std::lock_guard<std::mutex> lock (runnable_mutex);
55 #endif
56 
57     /* Clear the event fd.  Do this before flushing the events list,
58        so that any new event post afterwards is sure to re-awaken the
59        event loop.  */
60     serial_event_clear (runnable_event);
61 
62     /* Move the vector in case running a runnable pushes a new
63        runnable.  */
64     local = std::move (runnables);
65   }
66 
67   for (auto &item : local)
68     {
69       try
70 	{
71 	  item ();
72 	}
73       catch (...)
74 	{
75 	  /* Ignore exceptions in the callback.  */
76 	}
77     }
78 }
79 
80 /* See run-on-main-thread.h.  */
81 
82 void
run_on_main_thread(std::function<void ()> && func)83 run_on_main_thread (std::function<void ()> &&func)
84 {
85 #if CXX_STD_THREAD
86   std::lock_guard<std::mutex> lock (runnable_mutex);
87 #endif
88   runnables.emplace_back (std::move (func));
89   serial_event_set (runnable_event);
90 }
91 
92 void _initialize_run_on_main_thread ();
93 void
_initialize_run_on_main_thread()94 _initialize_run_on_main_thread ()
95 {
96   runnable_event = make_serial_event ();
97   add_file_handler (serial_event_fd (runnable_event), run_events, nullptr,
98 		    "run-on-main-thread");
99 }
100