Refactor complaint thread-safety approach
This patch changes the way complaint works in a background thread. The new approach requires installing a complaint interceptor in each worker, and then the resulting complaints are treated as one of the results of the computation. This change is needed for a subsequent patch, where installing a complaint interceptor around a parallel-for is no longer a viable approach.
This commit is contained in:
parent
da0e2ac4f7
commit
54b815ddb4
5 changed files with 60 additions and 36 deletions
|
@ -21,6 +21,7 @@
|
|||
#include "complaints.h"
|
||||
#include "command.h"
|
||||
#include "gdbcmd.h"
|
||||
#include "run-on-main-thread.h"
|
||||
#include "gdbsupport/selftest.h"
|
||||
#include <unordered_map>
|
||||
#include <mutex>
|
||||
|
@ -78,17 +79,14 @@ clear_complaints ()
|
|||
|
||||
/* See complaints.h. */
|
||||
|
||||
complaint_interceptor *complaint_interceptor::g_complaint_interceptor;
|
||||
thread_local complaint_interceptor *complaint_interceptor::g_complaint_interceptor;
|
||||
|
||||
/* See complaints.h. */
|
||||
|
||||
complaint_interceptor::complaint_interceptor ()
|
||||
: m_saved_warning_hook (deprecated_warning_hook)
|
||||
: m_saved_warning_hook (&deprecated_warning_hook, issue_complaint),
|
||||
m_saved_complaint_interceptor (&g_complaint_interceptor, this)
|
||||
{
|
||||
/* These cannot be stacked. */
|
||||
gdb_assert (g_complaint_interceptor == nullptr);
|
||||
g_complaint_interceptor = this;
|
||||
deprecated_warning_hook = issue_complaint;
|
||||
}
|
||||
|
||||
/* A helper that wraps a warning hook. */
|
||||
|
@ -104,19 +102,19 @@ wrap_warning_hook (void (*hook) (const char *, va_list), ...)
|
|||
|
||||
/* See complaints.h. */
|
||||
|
||||
complaint_interceptor::~complaint_interceptor ()
|
||||
void
|
||||
re_emit_complaints (const complaint_collection &complaints)
|
||||
{
|
||||
for (const std::string &str : m_complaints)
|
||||
gdb_assert (is_main_thread ());
|
||||
|
||||
for (const std::string &str : complaints)
|
||||
{
|
||||
if (m_saved_warning_hook)
|
||||
wrap_warning_hook (m_saved_warning_hook, str.c_str ());
|
||||
if (deprecated_warning_hook)
|
||||
wrap_warning_hook (deprecated_warning_hook, str.c_str ());
|
||||
else
|
||||
gdb_printf (gdb_stderr, _("During symbol reading: %s\n"),
|
||||
str.c_str ());
|
||||
}
|
||||
|
||||
g_complaint_interceptor = nullptr;
|
||||
deprecated_warning_hook = m_saved_warning_hook;
|
||||
}
|
||||
|
||||
/* See complaints.h. */
|
||||
|
|
|
@ -57,29 +57,45 @@ have_complaint ()
|
|||
|
||||
extern void clear_complaints ();
|
||||
|
||||
/* Type of collected complaints. */
|
||||
|
||||
typedef std::unordered_set<std::string> complaint_collection;
|
||||
|
||||
/* A class that can handle calls to complaint from multiple threads.
|
||||
When this is instantiated, it hooks into the complaint mechanism,
|
||||
so the 'complaint' macro can continue to be used. When it is
|
||||
destroyed, it issues all the complaints that have been stored. It
|
||||
should only be instantiated in the main thread. */
|
||||
so the 'complaint' macro can continue to be used. It collects all
|
||||
the complaints (and warnings) emitted while it is installed, and
|
||||
these can be retrieved before the object is destroyed. This is
|
||||
intended for use from worker threads (though it will also work on
|
||||
the main thread). Messages can be re-emitted on the main thread
|
||||
using re_emit_complaints, see below. */
|
||||
|
||||
class complaint_interceptor
|
||||
{
|
||||
public:
|
||||
|
||||
complaint_interceptor ();
|
||||
~complaint_interceptor ();
|
||||
~complaint_interceptor () = default;
|
||||
|
||||
DISABLE_COPY_AND_ASSIGN (complaint_interceptor);
|
||||
|
||||
complaint_collection &&release ()
|
||||
{
|
||||
return std::move (m_complaints);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/* The issued complaints. */
|
||||
std::unordered_set<std::string> m_complaints;
|
||||
complaint_collection m_complaints;
|
||||
|
||||
typedef void (*saved_warning_hook_ftype) (const char *, va_list);
|
||||
|
||||
/* The saved value of deprecated_warning_hook. */
|
||||
void (*m_saved_warning_hook) (const char *, va_list)
|
||||
ATTRIBUTE_FPTR_PRINTF (1,0);
|
||||
scoped_restore_tmpl<saved_warning_hook_ftype> m_saved_warning_hook;
|
||||
|
||||
/* The saved value of g_complaint_interceptor. */
|
||||
scoped_restore_tmpl<complaint_interceptor *> m_saved_complaint_interceptor;
|
||||
|
||||
/* A helper function that is used by the 'complaint' implementation
|
||||
to issue a complaint. */
|
||||
|
@ -87,7 +103,12 @@ private:
|
|||
ATTRIBUTE_PRINTF (1, 0);
|
||||
|
||||
/* This object. Used by the static callback function. */
|
||||
static complaint_interceptor *g_complaint_interceptor;
|
||||
static thread_local complaint_interceptor *g_complaint_interceptor;
|
||||
};
|
||||
|
||||
/* Re-emit complaints that were collected by complaint_interceptor.
|
||||
This can only be called on the main thread. */
|
||||
|
||||
extern void re_emit_complaints (const complaint_collection &);
|
||||
|
||||
#endif /* !defined (COMPLAINTS_H) */
|
||||
|
|
|
@ -562,7 +562,7 @@ extern void (*deprecated_print_frame_info_listing_hook) (struct symtab * s,
|
|||
int noerror);
|
||||
extern int (*deprecated_query_hook) (const char *, va_list)
|
||||
ATTRIBUTE_FPTR_PRINTF(1,0);
|
||||
extern void (*deprecated_warning_hook) (const char *, va_list)
|
||||
extern thread_local void (*deprecated_warning_hook) (const char *, va_list)
|
||||
ATTRIBUTE_FPTR_PRINTF(1,0);
|
||||
extern void (*deprecated_readline_begin_hook) (const char *, ...)
|
||||
ATTRIBUTE_FPTR_PRINTF_1;
|
||||
|
|
|
@ -4930,9 +4930,6 @@ dwarf2_build_psymtabs_hard (dwarf2_per_objfile *per_objfile)
|
|||
index_storage.get_addrmap ());
|
||||
|
||||
{
|
||||
/* Ensure that complaints are handled correctly. */
|
||||
complaint_interceptor complaint_handler;
|
||||
|
||||
using iter_type = decltype (per_bfd->all_units.begin ());
|
||||
|
||||
auto task_size_ = [] (iter_type iter)
|
||||
|
@ -4942,18 +4939,23 @@ dwarf2_build_psymtabs_hard (dwarf2_per_objfile *per_objfile)
|
|||
};
|
||||
auto task_size = gdb::make_function_view (task_size_);
|
||||
|
||||
/* Each thread returns a pair holding a cooked index, and a vector
|
||||
of errors that should be printed. The latter is done because
|
||||
GDB's I/O system is not thread-safe. run_on_main_thread could be
|
||||
used, but that would mean the messages are printed after the
|
||||
prompt, which looks weird. */
|
||||
using result_type = std::pair<std::unique_ptr<cooked_index_shard>,
|
||||
std::vector<gdb_exception>>;
|
||||
/* Each thread returns a tuple holding a cooked index, any
|
||||
collected complaints, and a vector of errors that should be
|
||||
printed. The latter is done because GDB's I/O system is not
|
||||
thread-safe. run_on_main_thread could be used, but that would
|
||||
mean the messages are printed after the prompt, which looks
|
||||
weird. */
|
||||
using result_type = std::tuple<std::unique_ptr<cooked_index_shard>,
|
||||
complaint_collection,
|
||||
std::vector<gdb_exception>>;
|
||||
std::vector<result_type> results
|
||||
= gdb::parallel_for_each (1, per_bfd->all_units.begin (),
|
||||
per_bfd->all_units.end (),
|
||||
[=] (iter_type iter, iter_type end)
|
||||
{
|
||||
/* Ensure that complaints are handled correctly. */
|
||||
complaint_interceptor complaint_handler;
|
||||
|
||||
std::vector<gdb_exception> errors;
|
||||
cooked_index_storage thread_storage;
|
||||
for (; iter != end; ++iter)
|
||||
|
@ -4969,15 +4971,18 @@ dwarf2_build_psymtabs_hard (dwarf2_per_objfile *per_objfile)
|
|||
errors.push_back (std::move (except));
|
||||
}
|
||||
}
|
||||
return result_type (thread_storage.release (), std::move (errors));
|
||||
return result_type (thread_storage.release (),
|
||||
complaint_handler.release (),
|
||||
std::move (errors));
|
||||
}, task_size);
|
||||
|
||||
/* Only show a given exception a single time. */
|
||||
std::unordered_set<gdb_exception> seen_exceptions;
|
||||
for (auto &one_result : results)
|
||||
{
|
||||
indexes.push_back (std::move (one_result.first));
|
||||
for (auto &one_exc : one_result.second)
|
||||
indexes.push_back (std::move (std::get<0> (one_result)));
|
||||
re_emit_complaints (std::get<1> (one_result));
|
||||
for (auto &one_exc : std::get<2> (one_result))
|
||||
if (seen_exceptions.insert (one_exc).second)
|
||||
exception_print (gdb_stderr, one_exc);
|
||||
}
|
||||
|
|
|
@ -221,7 +221,7 @@ int (*deprecated_query_hook) (const char *, va_list);
|
|||
|
||||
/* Replaces most of warning. */
|
||||
|
||||
void (*deprecated_warning_hook) (const char *, va_list);
|
||||
thread_local void (*deprecated_warning_hook) (const char *, va_list);
|
||||
|
||||
/* These three functions support getting lines of text from the user.
|
||||
They are used in sequence. First deprecated_readline_begin_hook is
|
||||
|
|
Loading…
Add table
Reference in a new issue