public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
From: Michael Weghorn <m.weghorn@posteo.de>
To: gdb-patches@sourceware.org
Subject: [PING 2] [PATCH v3] gdb: Do autoload before notifying Python side in new_objfile event
Date: Tue, 20 Apr 2021 11:38:19 +0000	[thread overview]
Message-ID: <8ef2bf68-b2c8-57bd-750a-2bd3b831bc6b@posteo.de> (raw)
In-Reply-To: <c7924256-d7b1-59b0-cf4b-afad8a5fd0d4@posteo.de>

Ping.

On 12/04/2021 15.37, Michael Weghorn via Gdb-patches wrote:
> Ping.
> 
> On 26/03/2021 09.29, Michael Weghorn via Gdb-patches wrote:
>> Previously, the observers attached to the 'gdb::observers::new_objfile'
>> observable were always notified in the order in which they had been
>> attached.
>>
>> The new_objfile observer callback to auto-load scripts is attached in
>> '_initialize_auto_load'.
>> The new_objfile observer callback that propagates the new_objfile event
>> to the Python side is attached in 'gdbpy_initialize_inferior', which is
>> called via '_initialize_python'.
>> With '_initialize_python' happening before '_initialize_auto_load',
>> the consequence was that the new_objfile event was emitted on the Python
>> side before autoloaded scripts had been executed when a new objfile was
>> loaded.
>> As a result, trying to access the objfile's pretty printers (defined in
>> the autoloaded script) from a handler for the Python-side
>> 'new_objfile' event would fail. Those would only be initialized later on
>> (when the 'auto_load_new_objfile' callback was called).
>>
>> To make sure that the objfile passed to the Python event handler
>> is properly initialized (including its 'pretty_printers' member),
>> make sure that the 'auto_load_new_objfile' observer is notified
>> before the 'python_new_objfile' one that propagates the event
>> to the Python side.
>>
>> To do this, extend the 'observable' class to allow explicitly
>> specifying dependencies when attaching observers, by adding
>> the possibility to specify a key for the observer, and the
>> keys for other observers it depends on, and make use of that
>> mechanism to specify a dependency from 'python_new_objfile'
>> on 'auto_load_new_objfile'.
>>
>> To make sure dependencies are notified before observers
>> depending on them, the vector holding the observers is sorted in
>> a way that dependencies come before observers depending on them.
>> The current implementation for sorting uses the depth-first search
>> algorithm for topological sorting as described at [1].
>>
>> Add a corresponding testcase that involves a test library with an autoloaded
>> Python script and a handler for the Python 'new_objfile' event.
>>
>> (The real world use case where I came across this issue was in an attempt
>> to extend handling for GDB pretty printers for dynamically loaded
>> objfiles in the Qt Creator IDE, s. [2] and [3] for more background.)
>>
>> [1] https://en.wikipedia.org/wiki/Topological_sorting#Depth-first_search
>> [2] https://bugreports.qt.io/browse/QTCREATORBUG-25339
>> [3] https://codereview.qt-project.org/c/qt-creator/qt-creator/+/333857/1
>>
>> Tested on x86_64-linux (Debian testing).
>>
>> gdb/ChangeLog:
>>
>>         * auto-load.c (_initialize_auto_load): Specify key when
>>         attaching the 'auto_load_new_objfile' observer, so
>>         other observers can specify it as a dependency.
>>         * auto-load.h (AUTO_LOAD_H): Declare
>>         'auto_load_new_objfile_observer' as key to be used
>>         for the 'auto_load_new_objfile' observer.
>>         * python/py-inferior.c (gdbpy_initialize_inferior): Make
>>         'python_new_objfile' observer depend on 'auto_load_new_objfile'
>>         observer, so it gets notified after the latter.
>>
>> gdb/testsuite/ChangeLog:
>>
>>         * gdb.python/libpy-autoloaded-pretty-printers-in-newobjfile-event.so-gdb.py: New test.
>>         * gdb.python/py-autoloaded-pretty-printers-in-newobjfile-event-lib.cc: New test.
>>         * gdb.python/py-autoloaded-pretty-printers-in-newobjfile-event-lib.h: New test.
>>         * gdb.python/py-autoloaded-pretty-printers-in-newobjfile-event-main.cc: New test.
>>         * gdb.python/py-autoloaded-pretty-printers-in-newobjfile-event.exp: New test.
>>         * gdb.python/py-autoloaded-pretty-printers-in-newobjfile-event.py: New test.
>>
>> gdbsupport/ChangeLog:
>>         * observable.h (struct observer_key): New struct that can be
>>         used as a key when attaching observers, and specyfing
>>         dependencies to other observers
>>         (class observable): Extend to allow specifying dependencies
>>         between observers, keep vector holding observers sorted
>>         that dependencies are notified before observers depending
>>         on them.
>> ---
>>  gdb/auto-load.c                               |   5 +-
>>  gdb/auto-load.h                               |  11 ++
>>  gdb/python/py-inferior.c                      |   6 +-
>>  ...tty-printers-in-newobjfile-event.so-gdb.py |  44 ++++++
>>  ...pretty-printers-in-newobjfile-event-lib.cc |  26 ++++
>>  ...-pretty-printers-in-newobjfile-event-lib.h |  31 +++++
>>  ...retty-printers-in-newobjfile-event-main.cc |  23 ++++
>>  ...ed-pretty-printers-in-newobjfile-event.exp |  96 +++++++++++++
>>  ...ded-pretty-printers-in-newobjfile-event.py |  34 +++++
>>  gdbsupport/observable.h                       | 127 ++++++++++++++++--
>>  10 files changed, 389 insertions(+), 14 deletions(-)
>>  create mode 100644 gdb/testsuite/gdb.python/libpy-autoloaded-pretty-printers-in-newobjfile-event.so-gdb.py
>>  create mode 100644 gdb/testsuite/gdb.python/py-autoloaded-pretty-printers-in-newobjfile-event-lib.cc
>>  create mode 100644 gdb/testsuite/gdb.python/py-autoloaded-pretty-printers-in-newobjfile-event-lib.h
>>  create mode 100644 gdb/testsuite/gdb.python/py-autoloaded-pretty-printers-in-newobjfile-event-main.cc
>>  create mode 100644 gdb/testsuite/gdb.python/py-autoloaded-pretty-printers-in-newobjfile-event.exp
>>  create mode 100644 gdb/testsuite/gdb.python/py-autoloaded-pretty-printers-in-newobjfile-event.py
>>
>> diff --git a/gdb/auto-load.c b/gdb/auto-load.c
>> index 1dfcf21eeeb..4ecfdd28f35 100644
>> --- a/gdb/auto-load.c
>> +++ b/gdb/auto-load.c
>> @@ -1494,6 +1494,8 @@ found and/or loaded."),
>>    return &retval;
>>  }
>>  
>> +gdb::observers::observer_key auto_load_new_objfile_observer;
>> +
>>  void _initialize_auto_load ();
>>  void
>>  _initialize_auto_load ()
>> @@ -1503,7 +1505,8 @@ _initialize_auto_load ()
>>    char *guile_name_help;
>>    const char *suffix;
>>  
>> -  gdb::observers::new_objfile.attach (auto_load_new_objfile);
>> +  gdb::observers::new_objfile.attach (auto_load_new_objfile,
>> +                                      &auto_load_new_objfile_observer);
>>  
>>    add_setshow_boolean_cmd ("gdb-scripts", class_support,
>>  			   &auto_load_gdb_scripts, _("\
>> diff --git a/gdb/auto-load.h b/gdb/auto-load.h
>> index f726126c554..e599b862ff9 100644
>> --- a/gdb/auto-load.h
>> +++ b/gdb/auto-load.h
>> @@ -25,6 +25,13 @@ struct program_space;
>>  struct auto_load_pspace_info;
>>  struct extension_language_defn;
>>  
>> +namespace gdb
>> +{
>> +namespace observers {
>> +struct observer_key;
>> +}
>> +}
>> +
>>  /* Value of the 'set debug auto-load' configuration variable.  */
>>  
>>  extern bool debug_auto_load;
>> @@ -40,6 +47,10 @@ extern bool auto_load_local_gdbinit;
>>  extern char *auto_load_local_gdbinit_pathname;
>>  extern bool auto_load_local_gdbinit_loaded;
>>  
>> +/* Key used for the auto_load_new_objfile observer, so other observers can
>> + * specify it as a dependency. */
>> +extern gdb::observers::observer_key auto_load_new_objfile_observer;
>> +
>>  extern struct auto_load_pspace_info *
>>    get_auto_load_pspace_data_for_loading (struct program_space *pspace);
>>  extern void auto_load_objfile_script (struct objfile *objfile,
>> diff --git a/gdb/python/py-inferior.c b/gdb/python/py-inferior.c
>> index a3d5952a10b..6ec3f1599ca 100644
>> --- a/gdb/python/py-inferior.c
>> +++ b/gdb/python/py-inferior.c
>> @@ -18,6 +18,7 @@
>>     along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
>>  
>>  #include "defs.h"
>> +#include "auto-load.h"
>>  #include "gdbcore.h"
>>  #include "gdbthread.h"
>>  #include "inferior.h"
>> @@ -913,7 +914,10 @@ gdbpy_initialize_inferior (void)
>>    gdb::observers::memory_changed.attach (python_on_memory_change);
>>    gdb::observers::register_changed.attach (python_on_register_change);
>>    gdb::observers::inferior_exit.attach (python_inferior_exit);
>> -  gdb::observers::new_objfile.attach (python_new_objfile);
>> +  // needs to run after auto_load_new_objfile observer, so autoloaded
>> +  // pretty-printers are available
>> +  gdb::observers::new_objfile.attach (python_new_objfile, nullptr,
>> +                                      { &auto_load_new_objfile_observer });
>>    gdb::observers::inferior_added.attach (python_new_inferior);
>>    gdb::observers::inferior_removed.attach (python_inferior_deleted);
>>  
>> diff --git a/gdb/testsuite/gdb.python/libpy-autoloaded-pretty-printers-in-newobjfile-event.so-gdb.py b/gdb/testsuite/gdb.python/libpy-autoloaded-pretty-printers-in-newobjfile-event.so-gdb.py
>> new file mode 100644
>> index 00000000000..2327e4a7384
>> --- /dev/null
>> +++ b/gdb/testsuite/gdb.python/libpy-autoloaded-pretty-printers-in-newobjfile-event.so-gdb.py
>> @@ -0,0 +1,44 @@
>> +# Copyright (C) 2021 Free Software Foundation, Inc.
>> +
>> +# 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/>.
>> +
>> +# This file is part of the GDB testsuite. It tests that python pretty
>> +# printers defined in a python script that is autoloaded have been
>> +# registered when a custom event handler for the new_objfile event
>> +# is called.
>> +
>> +import gdb.printing
>> +
>> +
>> +class MyClassTestLibPrinter(object):
>> +    "Print a MyClassTestLib"
>> +
>> +    def __init__(self, val):
>> +        self.val = val
>> +
>> +    def to_string(self):
>> +        return "MyClassTestLib object, id: {}".format(self.val['id'])
>> +
>> +    def display_hint(self):
>> +        return "string"
>> +
>> +
>> +def build_pretty_printer():
>> +    pp = gdb.printing.RegexpCollectionPrettyPrinter(
>> +        "my_library")
>> +    pp.add_printer("MyClassTestLib", "^MyClassTestLib$", MyClassTestLibPrinter)
>> +    return pp
>> +
>> +
>> +gdb.printing.register_pretty_printer(gdb.current_objfile(), build_pretty_printer())
>> diff --git a/gdb/testsuite/gdb.python/py-autoloaded-pretty-printers-in-newobjfile-event-lib.cc b/gdb/testsuite/gdb.python/py-autoloaded-pretty-printers-in-newobjfile-event-lib.cc
>> new file mode 100644
>> index 00000000000..7e06cf3903f
>> --- /dev/null
>> +++ b/gdb/testsuite/gdb.python/py-autoloaded-pretty-printers-in-newobjfile-event-lib.cc
>> @@ -0,0 +1,26 @@
>> +/* This testcase is part of GDB, the GNU debugger.
>> +
>> +   Copyright 2021 Free Software Foundation, Inc.
>> +
>> +   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 "py-autoloaded-pretty-printers-in-newobjfile-event-lib.h"
>> +
>> +MyClassTestLib::MyClassTestLib(int theId) {
>> +    id = theId;
>> +}
>> +
>> +int MyClassTestLib::getId() {
>> +    return id;
>> +}
>> diff --git a/gdb/testsuite/gdb.python/py-autoloaded-pretty-printers-in-newobjfile-event-lib.h b/gdb/testsuite/gdb.python/py-autoloaded-pretty-printers-in-newobjfile-event-lib.h
>> new file mode 100644
>> index 00000000000..fa2501bf6de
>> --- /dev/null
>> +++ b/gdb/testsuite/gdb.python/py-autoloaded-pretty-printers-in-newobjfile-event-lib.h
>> @@ -0,0 +1,31 @@
>> +/* This testcase is part of GDB, the GNU debugger.
>> +
>> +   Copyright 2021 Free Software Foundation, Inc.
>> +
>> +   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/>.  */
>> +
>> +#ifndef TESTLIBRARY_H
>> +#define TESTLIBRARY_H
>> +
>> +class MyClassTestLib {
>> +
>> +public:
>> +    explicit MyClassTestLib(int theId);
>> +    int getId();
>> +
>> +private:
>> +    int id;
>> +};
>> +
>> +#endif // TESTLIBRARY_H
>> diff --git a/gdb/testsuite/gdb.python/py-autoloaded-pretty-printers-in-newobjfile-event-main.cc b/gdb/testsuite/gdb.python/py-autoloaded-pretty-printers-in-newobjfile-event-main.cc
>> new file mode 100644
>> index 00000000000..6e66bbe3d43
>> --- /dev/null
>> +++ b/gdb/testsuite/gdb.python/py-autoloaded-pretty-printers-in-newobjfile-event-main.cc
>> @@ -0,0 +1,23 @@
>> +/* This testcase is part of GDB, the GNU debugger.
>> +
>> +   Copyright 2021 Free Software Foundation, Inc.
>> +
>> +   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 "py-autoloaded-pretty-printers-in-newobjfile-event-lib.h"
>> +
>> +int main() {
>> +    MyClassTestLib test(1);
>> +    return 0; /* break to inspect */
>> +}
>> diff --git a/gdb/testsuite/gdb.python/py-autoloaded-pretty-printers-in-newobjfile-event.exp b/gdb/testsuite/gdb.python/py-autoloaded-pretty-printers-in-newobjfile-event.exp
>> new file mode 100644
>> index 00000000000..ed1c3230835
>> --- /dev/null
>> +++ b/gdb/testsuite/gdb.python/py-autoloaded-pretty-printers-in-newobjfile-event.exp
>> @@ -0,0 +1,96 @@
>> +# Copyright (C) 2021 Free Software Foundation, Inc.
>> +
>> +# 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/>.
>> +
>> +# This file is part of the GDB testsuite. It tests that python pretty
>> +# printers defined in a python script that is autoloaded have been
>> +# registered when a custom event handler for the new_objfile event
>> +# is called.
>> +
>> +if [use_gdb_stub] {
>> +    return 0
>> +}
>> +
>> +load_lib gdb-python.exp
>> +
>> +set test "py-autoloaded-pretty-printers-in-newobjfile-event"
>> +set srcfile_main "${test}-main.cc"
>> +set executable_main ${test}-test
>> +set binfile_main [standard_output_file ${executable_main}]
>> +set srcfile_lib "${test}-lib.cc"
>> +set libname "lib${test}"
>> +set pyscriptfile_lib "${libname}.so-gdb.py"
>> +set binfile_lib [standard_output_file ${libname}.so]
>> +
>> +
>> +# Start with a fresh gdb.
>> +gdb_exit
>> +gdb_start
>> +
>> +# Skip all tests if Python scripting is not enabled.
>> +if { [skip_python_tests] } { continue }
>> +
>> +if {[gdb_compile_shlib ${srcdir}/${subdir}/${srcfile_lib} ${binfile_lib} \
>> +                       [list debug c++ -Wl,-soname,${libname}.so]] != ""} {
>> +    return -1
>> +}
>> +
>> +if {[gdb_compile "${srcdir}/${subdir}/${srcfile_main}" "${binfile_main}.o" \
>> +                 object [list debug c++]] != ""} {
>> +    return -1
>> +}
>> +
>> +set testobjdir [standard_output_file {}]
>> +if {[gdb_compile "${binfile_main}.o" "${binfile_main}" executable \
>> +                 [list debug "additional_flags=-L$testobjdir -l${test} \
>> +                              -Wl,-rpath=$testobjdir"]] != ""} {
>> +    return -1
>> +}
>> +
>> +# Start with a fresh gdb.
>> +gdb_exit
>> +gdb_start
>> +gdb_reinitialize_dir $srcdir/$subdir
>> +
>> +# Make the -gdb.py script available to gdb, it is automatically loaded by
>> +# gdb if it is put in the same directory as the library.
>> +set remote_python_file_autoload [gdb_remote_download host \
>> +                            ${srcdir}/${subdir}/${pyscriptfile_lib}]
>> +
>> +gdb_test_no_output "set auto-load safe-path ${remote_python_file_autoload}" \
>> +                   "set auto-load safe-path"
>> +
>> +# load the python file that defines a handler for the new_objfile event,
>> +# which will generate the output to check later
>> +# (prints information on available pretty-printers for objfile)
>> +set remote_python_file [gdb_remote_download host \
>> +                            ${srcdir}/${subdir}/${test}.py]
>> +gdb_test_no_output "source ${remote_python_file}" "load python file"
>> +
>> +gdb_load ${binfile_main}
>> +
>> +gdb_test_no_output "set print pretty on"
>> +
>> +gdb_breakpoint [gdb_get_line_number "break to inspect" ${srcfile_main}]
>> +
>> +# check that the handler prints output when test library is loaded
>> +# and that the pretty printer from autoloaded python file has been registered
>> +gdb_test "run" "new_objfile event for test library.*
>> +.*number of pretty printers: 1.*
>> +.*name of the first pretty printer: my_library.*"
>> +
>> +# check that pretty printer actually works
>> +gdb_test "print test" " .*MyClassTestLib object, id: 1.*"
>> +
>> +gdb_continue_to_end
>> diff --git a/gdb/testsuite/gdb.python/py-autoloaded-pretty-printers-in-newobjfile-event.py b/gdb/testsuite/gdb.python/py-autoloaded-pretty-printers-in-newobjfile-event.py
>> new file mode 100644
>> index 00000000000..924f304fd33
>> --- /dev/null
>> +++ b/gdb/testsuite/gdb.python/py-autoloaded-pretty-printers-in-newobjfile-event.py
>> @@ -0,0 +1,34 @@
>> +# Copyright (C) 2021 Free Software Foundation, Inc.
>> +
>> +# 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/>.
>> +
>> +# This file is part of the GDB testsuite. It tests that python pretty
>> +# printers defined in a python script that is autoloaded have been
>> +# registered when a custom event handler for the new_objfile event
>> +# is called.
>> +
>> +import gdb
>> +import os
>> +
>> +
>> +def new_objfile_handler(event):
>> +    assert(isinstance(event, gdb.NewObjFileEvent))
>> +    # only observe the custom test library
>> +    if os.path.basename(event.new_objfile.filename).find("libpy-autoloaded-pretty-printers-in-newobjfile-event") == 0:
>> +        print("new_objfile event for test library")
>> +        assert(len(event.new_objfile.pretty_printers) == 1)
>> +        print("number of pretty printers: {}".format(len(event.new_objfile.pretty_printers)))
>> +        print("name of the first pretty printer: {}".format(event.new_objfile.pretty_printers[0].name))
>> +
>> +gdb.events.new_objfile.connect(new_objfile_handler)
>> diff --git a/gdbsupport/observable.h b/gdbsupport/observable.h
>> index 1a9e767ba18..524f98e5a78 100644
>> --- a/gdbsupport/observable.h
>> +++ b/gdbsupport/observable.h
>> @@ -52,6 +52,13 @@ struct token
>>    DISABLE_COPY_AND_ASSIGN (token);
>>  };
>>  
>> +struct observer_key
>> +{
>> +  observer_key () = default;
>> +
>> +  DISABLE_COPY_AND_ASSIGN (observer_key);
>> +};
>> +
>>  template<typename... T>
>>  class observable
>>  {
>> @@ -59,6 +66,22 @@ class observable
>>  
>>    typedef std::function<void (T...)> func_type;
>>  
>> +private:
>> +  struct observer_entry
>> +  {
>> +    const token *tok;
>> +    func_type func;
>> +    observer_key *key;
>> +    std::vector<observer_key *> deps;
>> +
>> +    observer_entry (const token *t, func_type f, observer_key *k,
>> +                    std::vector<observer_key *> d)
>> +        : tok (t), func (f), key (k), deps (d)
>> +    {
>> +    }
>> +  };
>> +
>> +public:
>>    explicit observable (const char *name)
>>      : m_name (name)
>>    {
>> @@ -67,17 +90,29 @@ class observable
>>    DISABLE_COPY_AND_ASSIGN (observable);
>>  
>>    /* Attach F as an observer to this observable.  F cannot be
>> -     detached.  */
>> -  void attach (const func_type &f)
>> +     detached.
>> +     Optional:
>> +     * key that can be used to specify a dependency on
>> +       the attached observer
>> +     * keys for other observers this observer depends on
>> +     Dependencies are notified before observers depending on them. */
>> +  void attach (const func_type &f, observer_key *key = nullptr,
>> +               std::vector<observer_key *> dependencies = {})
>>    {
>> -    m_observers.emplace_back (nullptr, f);
>> +    attach (f, nullptr, key, dependencies);
>>    }
>>  
>>    /* Attach F as an observer to this observable.  T is a reference to
>> -     a token that can be used to later remove F.  */
>> -  void attach (const func_type &f, const token &t)
>> +     a token that can be used to later remove F.
>> +     Optional:
>> +     * key that can be used to specify a dependency on
>> +       the attached observer
>> +     * keys for other observers this observer depends on
>> +     Dependencies are notified before observers depending on them. */
>> +  void attach (const func_type &f, const token &t, observer_key *key = nullptr,
>> +               std::vector<observer_key *> dependencies = {})
>>    {
>> -    m_observers.emplace_back (&t, f);
>> +    attach (f, &t, key, dependencies);
>>    }
>>  
>>    /* Remove observers associated with T from this observable.  T is
>> @@ -87,10 +122,9 @@ class observable
>>    {
>>      auto iter = std::remove_if (m_observers.begin (),
>>  				m_observers.end (),
>> -				[&] (const std::pair<const token *,
>> -				     func_type> &e)
>> +                                [&] (const observer_entry &e)
>>  				{
>> -				  return e.first == &t;
>> +				  return e.tok == &t;
>>  				});
>>  
>>      m_observers.erase (iter, m_observers.end ());
>> @@ -103,13 +137,82 @@ class observable
>>        fprintf_unfiltered (gdb_stdlog, "observable %s notify() called\n",
>>  			  m_name);
>>      for (auto &&e : m_observers)
>> -      e.second (args...);
>> +      e.func (args...);
>>    }
>>  
>>  private:
>> -
>> -  std::vector<std::pair<const token *, func_type>> m_observers;
>> +  std::vector<observer_entry> m_observers;
>>    const char *m_name;
>> +
>> +  /* used for sorting algorithm */
>> +  enum class mark
>> +  {
>> +    NONE,
>> +    PERMANENT,
>> +    TEMPORARY
>> +  };
>> +
>> +  /* Helper method for topological sort using depth-first search algorithm */
>> +  void visit_for_sorting (std::vector<observer_entry> &sorted_elems,
>> +         std::vector<mark> &marks, int index)
>> +  {
>> +    if (marks[index] == mark::PERMANENT)
>> +      return;
>> +    if (marks[index] == mark::TEMPORARY)
>> +        error (_("Cyclic dependencies in observers."));
>> +
>> +    marks[index] = mark::TEMPORARY;
>> +
>> +    for (observer_key *dep : m_observers[index].deps)
>> +      {
>> +        auto it_dep
>> +            = std::find_if (m_observers.begin (), m_observers.end (),
>> +                            [&] (observer_entry e) { return e.key == dep; });
>> +        if (it_dep != m_observers.end ())
>> +          {
>> +            int i = std::distance (m_observers.begin (), it_dep);
>> +            visit_for_sorting (sorted_elems, marks, i);
>> +          }
>> +      }
>> +
>> +    marks[index] = mark::PERMANENT;
>> +    sorted_elems.push_back (m_observers[index]);
>> +  }
>> +
>> +  /* Sorts the elements, so that dependencies come before observers
>> +   * depending on them.
>> +   *
>> +   * Currently uses depth-first search algorithm for topological sorting,
>> +   * s. https://en.wikipedia.org/wiki/Topological_sorting#Depth-first_search . */
>> +  void sort_elements ()
>> +  {
>> +    std::vector<observer_entry> sorted_elems;
>> +    std::vector<mark> marks (m_observers.size (), mark::NONE);
>> +
>> +    auto it = marks.begin();
>> +    while (it != marks.end ())
>> +      {
>> +        int index = std::distance (marks.begin (), it);
>> +        visit_for_sorting (sorted_elems, marks, index);
>> +
>> +        it = std::find_if (marks.begin (), marks.end (),
>> +                           [] (mark m) { return m == mark::NONE; });
>> +      }
>> +    // assign sorted result
>> +    m_observers = sorted_elems;
>> +  }
>> +
>> +  void attach (const func_type &f, const token *t, observer_key *key,
>> +          std::vector<observer_key *> dependencies)
>> +  {
>> +    m_observers.emplace_back (t, f, key, dependencies);
>> +
>> +    if (key != nullptr)
>> +      {
>> +        // other observers might depend on this one -> sort anew
>> +        sort_elements ();
>> +      }
>> +  };
>>  };
>>  
>>  } /* namespace observers */
>>

  reply	other threads:[~2021-04-20 11:38 UTC|newest]

Thread overview: 23+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-02-10 15:40 [PATCH] gdb: Change init order so pretty printers are set " Michael Weghorn
2021-02-11  9:42 ` Andrew Burgess
2021-02-11 11:23   ` Michael Weghorn
2021-03-02  7:18     ` Michael Weghorn
2021-03-23 18:55   ` Simon Marchi
2021-03-24  9:45     ` Andrew Burgess
2021-03-24 13:51       ` Simon Marchi
2021-03-26  8:33         ` Michael Weghorn
2021-02-11 11:22 ` [PATCH v2] " Michael Weghorn
2021-03-23  8:01   ` [PING] " Michael Weghorn
2021-03-26  8:29 ` [PATCH v3] gdb: Do autoload before notifying Python side " Michael Weghorn
2021-04-12 13:37   ` [PING] " Michael Weghorn
2021-04-20 11:38     ` Michael Weghorn [this message]
2021-04-20 21:57   ` Simon Marchi
2021-04-22 15:46     ` Michael Weghorn
2021-04-22 16:06       ` Simon Marchi
2021-04-23  6:41         ` Michael Weghorn
2021-04-23 10:48           ` Simon Marchi
2021-04-22 15:44 ` [PATCH v4 0/2] Make sure autoload happens " Michael Weghorn
2021-04-22 15:44   ` [PATCH v4 1/2] gdbsupport: Allow to specify dependencies between observers Michael Weghorn
2021-04-22 15:44   ` [PATCH v4 2/2] gdb: Do autoload before notifying Python side in new_objfile event Michael Weghorn
2021-04-25  1:46   ` [PATCH v4 0/2] Make sure autoload happens " Simon Marchi
2021-04-26  8:18     ` Michael Weghorn

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=8ef2bf68-b2c8-57bd-750a-2bd3b831bc6b@posteo.de \
    --to=m.weghorn@posteo.de \
    --cc=gdb-patches@sourceware.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).