summaryrefslogtreecommitdiff
path: root/gdb/run-on-main-thread.c
blob: 2d40048de56bab6a02dfbd55b4ee312a62eb299e (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
/* Run a function on the main thread
   Copyright (C) 2019-2024 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 "defs.h"
#include "run-on-main-thread.h"
#include "ser-event.h"
#if CXX_STD_THREAD
#include <thread>
#include <mutex>
#endif
#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 (...)
	{
	  /* 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
}

void _initialize_run_on_main_thread ();
void
_initialize_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");
}