On Fri, Nov 14, 2025 at 10:31 AM David Malcolm <[email protected]> wrote:
>
> 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.

LGTM. Also really printer events; I had a laugh at least.

Thanks,
Andrew

>
> 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.
> ---
>  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(+)
>  create mode 100644 gcc/pub-sub.cc
>  create mode 100644 gcc/pub-sub.h
>
> diff --git a/gcc/Makefile.in b/gcc/Makefile.in
> index 5c24a9aab00a5..f09915780192c 100644
> --- a/gcc/Makefile.in
> +++ b/gcc/Makefile.in
> @@ -1892,6 +1892,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 0000000000000..192cfa59fd3d6
> --- /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 0000000000000..30837c4c85216
> --- /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 c68d3e79a3109..d0e210492f414 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 4501d34181c7c..dc0b080de210e 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 ();
> --
> 2.26.3
>

Reply via email to