https://gcc.gnu.org/g:b34dad6d185a2d1b332e07ce307938bd7a0d68d3
commit r16-6663-gb34dad6d185a2d1b332e07ce307938bd7a0d68d3 Author: David Malcolm <[email protected]> Date: Fri Jan 9 15:54:15 2026 -0500 Add pub-sub.{h,cc} This patch introduces a publish/subscribe mechanism, allowing for loosely-coupled senders and receivers, with strongly-typed messages passing between them. For example, a GCC subsystem could publish messages about events, and a plugin could subscribe to them. An example can be seen in the selftests. gcc/ChangeLog: * Makefile.in (OBJS-libcommon): Add pub-sub.o. * pub-sub.cc: New file. * pub-sub.h: New file. * selftest-run-tests.cc (selftest::run_tests): Call selftest::pub_sub_cc_tests. * selftest.h (selftest::pub_sub_cc_tests): New decl. Signed-off-by: David Malcolm <[email protected]> Diff: --- gcc/Makefile.in | 1 + gcc/pub-sub.cc | 138 ++++++++++++++++++++++++++++++++++++++++++++++ gcc/pub-sub.h | 68 +++++++++++++++++++++++ gcc/selftest-run-tests.cc | 1 + gcc/selftest.h | 1 + 5 files changed, 209 insertions(+) diff --git a/gcc/Makefile.in b/gcc/Makefile.in index 41b689971725..8878eaa9c84b 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -1899,6 +1899,7 @@ OBJS-libcommon = \ graphviz.o pex.o \ pretty-print.o intl.o \ json.o json-parsing.o \ + pub-sub.o \ xml.o \ sbitmap.o \ vec.o input.o hash-table.o ggc-none.o memory-block.o \ diff --git a/gcc/pub-sub.cc b/gcc/pub-sub.cc new file mode 100644 index 000000000000..192cfa59fd3d --- /dev/null +++ b/gcc/pub-sub.cc @@ -0,0 +1,138 @@ +/* Loosely-coupled notifications via the Publish-Subscribe pattern. + Copyright (C) 2025 Free Software Foundation, Inc. + Contributed by David Malcolm <[email protected]>. + +This file is part of GCC. + +GCC 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, or (at your option) any later +version. + +GCC 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 GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + +#define INCLUDE_LIST +#define INCLUDE_STRING +#include "config.h" + +#include "system.h" +#include "coretypes.h" + +#include "pub-sub.h" + + +#if CHECKING_P + +#include "selftest.h" + +namespace selftest { + +/* Selftests. */ + +// A topic for use in selftests + +namespace snafu { + +struct paper_jam {}; + +struct out_of_paper +{ + int tray; +}; + +struct ink_low +{ + std::string color; +}; + +struct subscriber +{ + virtual void on_message (const paper_jam &m) = 0; + virtual void on_message (const out_of_paper &m) = 0; + virtual void on_message (const ink_low &m) = 0; +}; + +} // namespace snafu + +static void +test_example () +{ + struct logger : public snafu::subscriber + { + void on_message (const snafu::paper_jam &) final override + { + m_log += "paper jam\n"; + } + void on_message (const snafu::out_of_paper &m) final override + { + m_log += "out of paper (tray " + std::to_string (m.tray) + ")\n"; + } + void on_message (const snafu::ink_low &m) final override + { + m_log += "ink low: " + m.color + "\n"; + } + + std::string m_log; + }; + + pub_sub::channel<snafu::subscriber> printer_a; + pub_sub::channel<snafu::subscriber> printer_b; + pub_sub::channel<snafu::subscriber> printer_c; + + // No subscribers yet + ASSERT_EQ (printer_a.get_if_active (), nullptr); + ASSERT_EQ (printer_b.get_if_active (), nullptr); + ASSERT_EQ (printer_c.get_if_active (), nullptr); + + // Subscribers to individual channels + logger log_a; + logger log_b; + logger log_c; + printer_a.add_subscriber (log_a); + printer_b.add_subscriber (log_b); + printer_c.add_subscriber (log_c); + + // A subscriber to all channels + logger log_all; + printer_a.add_subscriber (log_all); + printer_b.add_subscriber (log_all); + printer_c.add_subscriber (log_all); + + // The channels now have subscribers + ASSERT_EQ (printer_a.get_if_active (), &printer_a); + ASSERT_EQ (printer_b.get_if_active (), &printer_b); + ASSERT_EQ (printer_c.get_if_active (), &printer_c); + + // Publish a message to each channel + printer_a.publish (snafu::paper_jam {}); + printer_b.publish (snafu::out_of_paper {1}); + printer_c.publish (snafu::ink_low {"cyan"}); + + // Verify that the subscribers got the messages they were meant to + ASSERT_EQ (log_a.m_log, "paper jam\n"); + ASSERT_EQ (log_b.m_log, "out of paper (tray 1)\n"); + ASSERT_EQ (log_c.m_log, "ink low: cyan\n"); + ASSERT_EQ (log_all.m_log, + "paper jam\n" + "out of paper (tray 1)\n" + "ink low: cyan\n"); +} + +/* Run all of the selftests within this file. */ + +void +pub_sub_cc_tests () +{ + test_example (); +} + +} // namespace selftest + +#endif /* #if CHECKING_P */ diff --git a/gcc/pub-sub.h b/gcc/pub-sub.h new file mode 100644 index 000000000000..30837c4c8521 --- /dev/null +++ b/gcc/pub-sub.h @@ -0,0 +1,68 @@ +/* Loosely-coupled notifications via the Publish-Subscribe pattern. + Copyright (C) 2025 Free Software Foundation, Inc. + Contributed by David Malcolm <[email protected]>. + +This file is part of GCC. + +GCC 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, or (at your option) any later +version. + +GCC 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 GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + +#ifndef GCC_PUB_SUB_H +#define GCC_PUB_SUB_H + +namespace pub_sub { + +template <typename Subscriber> +class channel +{ +public: + using subscriber = Subscriber; + + // A node within the std::list + using subscription = typename std::list<subscriber *>::iterator; + + /* Return this if this channel has subscribers, or nullptr if + there are none. */ + const channel * + get_if_active () const + { + if (m_subscribers.empty ()) + return nullptr; + return this; + } + + template <typename Message> + void publish (const Message &m) const + { + for (auto sub : m_subscribers) + sub->on_message (m); + } + + subscription + add_subscriber (subscriber &s) + { + return m_subscribers.insert (m_subscribers.end (), &s); + } + void unsubscribe (subscription s) + { + m_subscribers.remove (s); + } + +private: + std::list<subscriber *> m_subscribers; +}; + +} // namespace pub_sub + +#endif /* GCC_PUB_SUB_H */ diff --git a/gcc/selftest-run-tests.cc b/gcc/selftest-run-tests.cc index d29dcbf04fb2..7ead010360f9 100644 --- a/gcc/selftest-run-tests.cc +++ b/gcc/selftest-run-tests.cc @@ -83,6 +83,7 @@ selftest::run_tests () splay_tree_cc_tests (); xml_cc_tests (); graphviz_cc_tests (); + pub_sub_cc_tests (); /* Mid-level data structures. */ input_cc_tests (); diff --git a/gcc/selftest.h b/gcc/selftest.h index f2130e5ecd87..462786dab2f5 100644 --- a/gcc/selftest.h +++ b/gcc/selftest.h @@ -246,6 +246,7 @@ extern void ordered_hash_map_tests_cc_tests (); extern void path_coverage_cc_tests (); extern void predict_cc_tests (); extern void pretty_print_cc_tests (); +extern void pub_sub_cc_tests (); extern void range_op_tests (); extern void range_tests (); extern void read_rtl_function_cc_tests ();
