From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 77720 invoked by alias); 12 Feb 2018 18:43:15 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Received: (qmail 77706 invoked by uid 89); 12 Feb 2018 18:43:15 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-25.1 required=5.0 tests=AWL,BAYES_00,GIT_PATCH_0,GIT_PATCH_1,GIT_PATCH_2,GIT_PATCH_3,KAM_SHORT,RCVD_IN_DNSWL_LOW,T_FILL_THIS_FORM_SHORT,T_RP_MATCHES_RCVD autolearn=ham version=3.3.2 spammy=ATTACH, 2079 X-HELO: mx1.redhat.com Received: from mx3-rdu2.redhat.com (HELO mx1.redhat.com) (66.187.233.73) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Mon, 12 Feb 2018 18:43:11 +0000 Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.rdu2.redhat.com [10.11.54.6]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 785EC4023141; Mon, 12 Feb 2018 18:43:09 +0000 (UTC) Received: from [127.0.0.1] (ovpn04.gateway.prod.ext.ams2.redhat.com [10.39.146.4]) by smtp.corp.redhat.com (Postfix) with ESMTP id AED0A2166BAE; Mon, 12 Feb 2018 18:43:08 +0000 (UTC) Subject: Re: [RFA] Convert observers to C++ To: Tom Tromey References: <20171105013039.9135-1-tom@tromey.com> <20f70b2b-bf93-a6b6-f2a4-82e14c807e05@redhat.com> <87zi4haeak.fsf@tromey.com> <87vaf5a4zq.fsf@tromey.com> Cc: gdb-patches@sourceware.org From: Pedro Alves Message-ID: <18b73858-b80f-4655-d33c-2be0dce650eb@redhat.com> Date: Mon, 12 Feb 2018 18:43:00 -0000 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Thunderbird/52.5.2 MIME-Version: 1.0 In-Reply-To: <87vaf5a4zq.fsf@tromey.com> Content-Type: multipart/mixed; boundary="------------11A85EAA43F49740899D7877" X-SW-Source: 2018-02/txt/msg00174.txt.bz2 This is a multi-part message in MIME format. --------------11A85EAA43F49740899D7877 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 7bit Content-length: 7098 On 02/10/2018 03:38 AM, Tom Tromey wrote: >>>>>> "Tom" == Tom Tromey writes: > > Tom> I'm running this through the buildbot again and when it is working I > Tom> will re-submit it. > > Here it is. Thanks again. I have a few comments / suggestions. See below, and the attached patches. Offhand, let me say that after playing with this today, I found that this being plain C++ instead of shell-based really makes it much easier to play around with the implementation. I like that, a lot. > +template > +class observable > +{ > +public: > + > + typedef std::function func_type; > + > + /* The return type of attach, which can be passed to detach to > + remove a listener. */ > + typedef size_t token_type; > + > + /* A "null" value of token_type that can be used as a > + sentinel. */ > + static constexpr const token_type null_token = size_t (-1); > + > + explicit observable (const char *name) > + : m_count (0), > + m_name (name) > + { > + } > + > + DISABLE_COPY_AND_ASSIGN (observable); > + > + /* Attach F as an observable to this observable. A token is > + returned which can be used to later remove F. */ > + token_type attach (const func_type &f) > + { > + m_observers.emplace_back (m_count, f); > + return m_count++; > + } The counter is a size_t, so in principle at least for 64-bit architectures, it shouldn't ever be a problem in practice (as in risk of overflow), but my pedantic self still wants to try to see if we can avoid forever-increasing counters by design. How about we replace the counter by key that is managed by the caller? Very much similar to the registry mechanism. In turn that allows registering multiple observers with the same key, meaning at places where we need to attach/detach a number of observers, we don't need to track a token per each observable. See patch #1 attached. > + > + /* Remove an observer from this observable. F is the token that was > + previously returned by "attach". */ > + void detach (const token_type &f) > + { > + auto iter = std::remove_if (m_observers.begin (), > + m_observers.end (), > + [=] (const std::pair &e) > + { > + return e.first == f; > + }); > + > + m_observers.erase (iter, m_observers.end ()); > + } > + > + /* Notify all observables that are attached to this observer. */ I think observable and observer are swapped in this comment. (see patch) > + void notify (T... args) const > + { > + if (observer_debug) > + fprintf_unfiltered (gdb_stdlog, "observer %s notify() called\n", > + m_name); > + for (auto &&e : m_observers) > + e.second (args...); > + } > + > +private: > + > + size_t m_count; > + std::vector> m_observers; > + const char *m_name; > +}; > + > +} /* namespace observers */ > + > +} /* namespace gdb */ > --- a/gdb/observer.c > +++ b/gdb/observer.c > @@ -17,199 +17,199 @@ > You should have received a copy of the GNU General Public License > along with this program. If not, see . */ > > -/* An observer is an entity who is interested in being notified when GDB > - reaches certain states, or certain events occur in GDB. The entity being > - observed is called the Subject. To receive notifications, the observer > - attaches a callback to the subject. One subject can have several > - observers. > - > - This file implements an internal generic low-level event notification > - mechanism based on the Observer paradigm described in the book "Design > - Patterns". This generic event notification mechansim is then re-used > - to implement the exported high-level notification management routines > - for all possible notifications. > - > - The current implementation of the generic observer provides support > - for contextual data. This contextual data is given to the subject > - when attaching the callback. In return, the subject will provide > - this contextual data back to the observer as a parameter of the > - callback. > - > - FIXME: The current support for the contextual data is only partial, > - as it lacks a mechanism that would deallocate this data when the > - callback is detached. This is not a problem so far, as this contextual > - data is only used internally to hold a function pointer. Later on, > - if a certain observer needs to provide support for user-level > - contextual data, then the generic notification mechanism will need > - need to be enhanced to allow the observer to provide a routine to > - deallocate the data when attaching the callback. > - > - This file is currently maintained by hand, but the long term plan > - if the number of different notifications starts growing is to create > - a new script (observer.sh) that would generate this file, and the > - associated documentation. */ > - > #include "defs.h" > #include "observer.h" > #include "command.h" > #include "gdbcmd.h" > +#include "selftest.h" > > -static unsigned int observer_debug; > -static void > -show_observer_debug (struct ui_file *file, int from_tty, > - struct cmd_list_element *c, const char *value) > +namespace gdb > { > - fprintf_filtered (file, _("Observer debugging is %s.\n"), value); > -} > - > -/* The internal generic observer. */ > > -typedef void (generic_observer_notification_ftype) (const void *data, > - const void *args); > - > -struct observer > +namespace observers > { > - generic_observer_notification_ftype *notify; > - /* No memory management needed for the following field for now. */ > - void *data; > -}; > - > -/* A list of observers, maintained by the subject. A subject is > - actually represented by its list of observers. */ > > -struct observer_list > +unsigned int observer_debug; > + > +#define DEFINE_OBSERVER(name) decltype (name) name (# name) Hmm, pedantically, I guess this should be called DEFINE_OBSERVABLE instead of DEFINE_OBSERVER? (see patch #3). > + > +DEFINE_OBSERVER (normal_stop); > +DEFINE_OBSERVER (signal_received); > +DEFINE_OBSERVER (end_stepping_range); ... > + > +#ifdef GDB_SELF_TEST > + > +namespace selftests > { > - struct observer_list *next; > - struct observer *observer; > -}; > > -/* Allocate a struct observer_list, intended to be used as a node > - in the list of observers maintained by a subject. */ > +/* This observer is used for internal testing. */ > +observable test_notification ("test_notification"); We have the gdb/unittests/ directory nowadays, so I think we could/should move the tests there. See patch #2. And with patches #2 and #3, I wonder whether gdb/observer.{h,c} is the right name for the files, given that they contain definitions of observables, not observers? Maybe they should be called gdb/observables.{h,c} instead (plural). Dunno, it's not a big deal. Fine with me as is, though I wonder whether that's because I'm used to the nomenclature; i.e., whether a newcomer would feel the same. WDYT? Thanks, Pedro Alves --------------11A85EAA43F49740899D7877 Content-Type: text/x-patch; name="0001-Use-address-indentify-tokens-instead-of-counters.-Fi.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename*0="0001-Use-address-indentify-tokens-instead-of-counters.-Fi.pa"; filename*1="tch" Content-length: 12263 >From 1c21617fc888261094851bd94b35fd1bbfaba650 Mon Sep 17 00:00:00 2001 From: Pedro Alves Date: Mon, 12 Feb 2018 12:36:16 +0000 Subject: [PATCH 1/3] Use address-indentify tokens instead of counters. Fix some typos. --- gdb/common/observable.h | 58 ++++++++++++++++++++++----------------- gdb/observer.c | 16 +++++------ gdb/record-btrace.c | 22 +++++---------- gdb/tui/tui-hooks.c | 72 ++++++++++++++++++++----------------------------- 4 files changed, 76 insertions(+), 92 deletions(-) diff --git a/gdb/common/observable.h b/gdb/common/observable.h index 118718871fe..f5666c14e5c 100644 --- a/gdb/common/observable.h +++ b/gdb/common/observable.h @@ -42,56 +42,65 @@ extern unsigned int observer_debug; particular, it is therefore not possible to call the attach or detach routines during a notification. */ +/* The type of a key that can be passed to attach, which can be passed + to detach to remove associated observers. Tokens have address + identity, and are thus usually const globals. */ +struct token +{ + token () = default; + + DISABLE_COPY_AND_ASSIGN (token); +}; + template class observable { public: - typedef std::function func_type; - - /* The return type of attach, which can be passed to detach to - remove a listener. */ - typedef size_t token_type; - - /* A "null" value of token_type that can be used as a - sentinel. */ - static constexpr const token_type null_token = size_t (-1); + typedef std::function func_type; explicit observable (const char *name) - : m_count (0), - m_name (name) + : m_name (name) { } DISABLE_COPY_AND_ASSIGN (observable); - /* Attach F as an observable to this observable. A token is - returned which can be used to later remove F. */ - token_type attach (const func_type &f) + /* Attach F as an observer to this observable. F cannot be + detached. */ + void attach (const func_type &f) + { + m_observers.emplace_back (nullptr, f); + } + + /* 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) { - m_observers.emplace_back (m_count, f); - return m_count++; + m_observers.emplace_back (&t, f); } - /* Remove an observer from this observable. F is the token that was - previously returned by "attach". */ - void detach (const token_type &f) + /* Remove observers associated with T from this observable. T is + the token that was previously passed to any number of "attach" + calls. */ + void detach (const token &t) { auto iter = std::remove_if (m_observers.begin (), m_observers.end (), - [=] (const std::pair &e) + [&] (const std::pair &e) { - return e.first == f; + return e.first == &t; }); m_observers.erase (iter, m_observers.end ()); } - /* Notify all observables that are attached to this observer. */ + /* Notify all observers that are attached to this observable. */ void notify (T... args) const { if (observer_debug) - fprintf_unfiltered (gdb_stdlog, "observer %s notify() called\n", + fprintf_unfiltered (gdb_stdlog, "observable %s notify() called\n", m_name); for (auto &&e : m_observers) e.second (args...); @@ -99,8 +108,7 @@ public: private: - size_t m_count; - std::vector> m_observers; + std::vector> m_observers; const char *m_name; }; diff --git a/gdb/observer.c b/gdb/observer.c index d39e3e7462e..7e44c8c0181 100644 --- a/gdb/observer.c +++ b/gdb/observer.c @@ -128,10 +128,10 @@ observer_self_tests () attached. */ notify_check_counters (0, 0, 0); - observable::token_type token1, token2, token3; + const gdb::observers::token token1, token2, token3; /* Now, attach one observer, and send a notification. */ - token2 = test_notification.attach (&test_second_notification_function); + test_notification.attach (&test_second_notification_function, token2); notify_check_counters (0, 1, 0); /* Remove the observer, and send a notification. */ @@ -139,15 +139,15 @@ observer_self_tests () notify_check_counters (0, 0, 0); /* With a new observer. */ - token1 = test_notification.attach (&test_first_notification_function); + test_notification.attach (&test_first_notification_function, token1); notify_check_counters (1, 0, 0); /* With 2 observers. */ - token2 = test_notification.attach (&test_second_notification_function); + test_notification.attach (&test_second_notification_function, token2); notify_check_counters (1, 1, 0); /* With 3 observers. */ - token3 = test_notification.attach (&test_third_notification_function); + test_notification.attach (&test_third_notification_function, token3); notify_check_counters (1, 1, 1); /* Remove middle observer. */ @@ -164,9 +164,9 @@ observer_self_tests () /* Go back to 3 observers, and remove them in a different order... */ - token1 = test_notification.attach (&test_first_notification_function); - token2 = test_notification.attach (&test_second_notification_function); - token3 = test_notification.attach (&test_third_notification_function); + test_notification.attach (&test_first_notification_function, token1); + test_notification.attach (&test_second_notification_function, token2); + test_notification.attach (&test_third_notification_function, token3); notify_check_counters (1, 1, 1); /* Remove the third observer. */ diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c index c0f36268eae..660ce407c96 100644 --- a/gdb/record-btrace.c +++ b/gdb/record-btrace.c @@ -44,9 +44,9 @@ /* The target_ops of record-btrace. */ static struct target_ops record_btrace_ops; -/* A new thread observer enabling branch tracing for the new thread. */ -static gdb::observers::observable::token_type - record_btrace_thread_observer; +/* Token associated with a new-thread observer enabling branch tracing + for the new thread. */ +static const gdb::observers::token record_btrace_thread_observer_token; /* Memory access types used in set/show record btrace replay-memory-access. */ static const char replay_memory_access_read_only[] = "read-only"; @@ -177,8 +177,8 @@ record_btrace_auto_enable (void) { DEBUG ("attach thread observer"); - record_btrace_thread_observer - = gdb::observers::new_thread.attach (record_btrace_enable_warn); + gdb::observers::new_thread.attach (record_btrace_enable_warn, + record_btrace_thread_observer_token); } /* Disable automatic tracing of new threads. */ @@ -186,14 +186,9 @@ record_btrace_auto_enable (void) static void record_btrace_auto_disable (void) { - /* The observer may have been detached, already. */ - if (record_btrace_thread_observer == gdb::observers::new_thread.null_token) - return; - DEBUG ("detach thread observer"); - gdb::observers::new_thread.detach (record_btrace_thread_observer); - record_btrace_thread_observer = gdb::observers::new_thread.null_token; + gdb::observers::new_thread.detach (record_btrace_thread_observer_token); } /* The record-btrace async event handler function. */ @@ -239,9 +234,6 @@ record_btrace_open (const char *args, int from_tty) if (!target_has_execution) error (_("The program is not being run.")); - gdb_assert (record_btrace_thread_observer - == gdb::observers::new_thread.null_token); - disable_chain = make_cleanup (null_cleanup, NULL); ALL_NON_EXITED_THREADS (tp) if (args == NULL || *args == 0 || number_is_in_list (args, tp->global_num)) @@ -3139,6 +3131,4 @@ to see the actual buffer size."), NULL, show_record_pt_buffer_size_value, record_btrace_conf.bts.size = 64 * 1024; record_btrace_conf.pt.size = 16 * 1024; - - record_btrace_thread_observer = gdb::observers::new_thread.null_token; } diff --git a/gdb/tui/tui-hooks.c b/gdb/tui/tui-hooks.c index ba87aec2e15..8d0bc8711a1 100644 --- a/gdb/tui/tui-hooks.c +++ b/gdb/tui/tui-hooks.c @@ -203,21 +203,32 @@ tui_normal_stop (struct bpstats *bs, int print_frame) tui_refresh_frame_and_register_information (/*registers_too_p=*/1); } -/* Observers created when installing TUI hooks. */ -static gdb::observers::observable::token_type - tui_bp_created_observer; -static gdb::observers::observable::token_type - tui_bp_deleted_observer; -static gdb::observers::observable::token_type - tui_bp_modified_observer; -static gdb::observers::observable::token_type - tui_inferior_exit_observer; -static gdb::observers::observable::token_type - tui_before_prompt_observer; -static gdb::observers::observable::token_type - tui_normal_stop_observer; -static gdb::observers::observable::token_type - tui_register_changed_observer; +/* Token associated with observers registered while TUI hooks are + installed. */ +static const gdb::observers::token tui_observers_token; + +/* Attach or detach TUI observers, according to ATTACH. */ + +static void +tui_attach_detach_observers (bool attach) +{ + auto ad = [&] (auto &observable, + const auto &func) + { + if (attach) + observable.attach (func, tui_observers_token); + else + observable.detach (tui_observers_token); + }; + + ad (gdb::observers::breakpoint_created, tui_event_create_breakpoint); + ad (gdb::observers::breakpoint_deleted, tui_event_delete_breakpoint); + ad (gdb::observers::breakpoint_modified, tui_event_modify_breakpoint); + ad (gdb::observers::inferior_exit, tui_inferior_exit); + ad (gdb::observers::before_prompt, tui_before_prompt); + ad (gdb::observers::normal_stop, tui_normal_stop); + ad (gdb::observers::register_changed, tui_register_changed); +} /* Install the TUI specific hooks. */ void @@ -231,20 +242,7 @@ tui_install_hooks (void) = tui_dummy_print_frame_info_listing_hook; /* Install the event hooks. */ - tui_bp_created_observer - = gdb::observers::breakpoint_created.attach (tui_event_create_breakpoint); - tui_bp_deleted_observer - = gdb::observers::breakpoint_deleted.attach (tui_event_delete_breakpoint); - tui_bp_modified_observer - = gdb::observers::breakpoint_modified.attach (tui_event_modify_breakpoint); - tui_inferior_exit_observer - = gdb::observers::inferior_exit.attach (tui_inferior_exit); - tui_before_prompt_observer - = gdb::observers::before_prompt.attach (tui_before_prompt); - tui_normal_stop_observer - = gdb::observers::normal_stop.attach (tui_normal_stop); - tui_register_changed_observer - = gdb::observers::register_changed.attach (tui_register_changed); + tui_attach_detach_observers (true); } /* Remove the TUI specific hooks. */ @@ -253,21 +251,9 @@ tui_remove_hooks (void) { deprecated_print_frame_info_listing_hook = 0; deprecated_query_hook = 0; + /* Remove our observers. */ - gdb::observers::breakpoint_created.detach (tui_bp_created_observer); - tui_bp_created_observer = gdb::observers::breakpoint_created.null_token; - gdb::observers::breakpoint_deleted.detach (tui_bp_deleted_observer); - tui_bp_deleted_observer = gdb::observers::breakpoint_deleted.null_token; - gdb::observers::breakpoint_modified.detach (tui_bp_modified_observer); - tui_bp_modified_observer = gdb::observers::breakpoint_modified.null_token; - gdb::observers::inferior_exit.detach (tui_inferior_exit_observer); - tui_inferior_exit_observer = gdb::observers::inferior_exit.null_token; - gdb::observers::before_prompt.detach (tui_before_prompt_observer); - tui_before_prompt_observer = gdb::observers::before_prompt.null_token; - gdb::observers::normal_stop.detach (tui_normal_stop_observer); - tui_normal_stop_observer = gdb::observers::normal_stop.null_token; - gdb::observers::register_changed.detach (tui_register_changed_observer); - tui_register_changed_observer = gdb::observers::register_changed.null_token; + tui_attach_detach_observers (false); } void -- 2.14.3 --------------11A85EAA43F49740899D7877 Content-Type: text/x-patch; name="0002-Move-unit-tests-to-gdb-unittests.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0002-Move-unit-tests-to-gdb-unittests.patch" Content-length: 8777 >From ac316fd054692670e426e8395f700091604d44f1 Mon Sep 17 00:00:00 2001 From: Pedro Alves Date: Mon, 12 Feb 2018 17:42:03 +0000 Subject: [PATCH 2/3] Move unit tests to gdb/unittests/ --- gdb/Makefile.in | 1 + gdb/observer.c | 116 ------------------------------- gdb/unittests/observer-selftests.c | 135 +++++++++++++++++++++++++++++++++++++ 3 files changed, 136 insertions(+), 116 deletions(-) create mode 100644 gdb/unittests/observer-selftests.c diff --git a/gdb/Makefile.in b/gdb/Makefile.in index 844f92d7cde..2b09850bd6c 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -421,6 +421,7 @@ SUBDIR_UNITTESTS_SRCS = \ unittests/memory-map-selftests.c \ unittests/memrange-selftests.c \ unittests/offset-type-selftests.c \ + unittests/observer-selftests.c \ unittests/optional-selftests.c \ unittests/ptid-selftests.c \ unittests/rsp-low-selftests.c \ diff --git a/gdb/observer.c b/gdb/observer.c index 7e44c8c0181..836b083125d 100644 --- a/gdb/observer.c +++ b/gdb/observer.c @@ -21,7 +21,6 @@ #include "observer.h" #include "command.h" #include "gdbcmd.h" -#include "selftest.h" namespace gdb { @@ -76,116 +75,6 @@ DEFINE_OBSERVER (inferior_call_post); DEFINE_OBSERVER (register_changed); DEFINE_OBSERVER (user_selected_context_changed); -#ifdef GDB_SELF_TEST - -namespace selftests -{ - -/* This observer is used for internal testing. */ -observable test_notification ("test_notification"); - -static int test_first_observer = 0; -static int test_second_observer = 0; -static int test_third_observer = 0; - -static void -test_first_notification_function (int arg) -{ - test_first_observer++; -} - -static void -test_second_notification_function (int arg) -{ - test_second_observer++; -} - -static void -test_third_notification_function (int arg) -{ - test_third_observer++; -} - -static void -notify_check_counters (int one, int two, int three) -{ - /* Reset. */ - test_first_observer = 0; - test_second_observer = 0; - test_third_observer = 0; - /* Notify. */ - test_notification.notify (0); - /* Check. */ - SELF_CHECK (one == test_first_observer); - SELF_CHECK (two == test_second_observer); - SELF_CHECK (three == test_third_observer); -} - -static void -observer_self_tests () -{ - /* First, try sending a notification without any observer - attached. */ - notify_check_counters (0, 0, 0); - - const gdb::observers::token token1, token2, token3; - - /* Now, attach one observer, and send a notification. */ - test_notification.attach (&test_second_notification_function, token2); - notify_check_counters (0, 1, 0); - - /* Remove the observer, and send a notification. */ - test_notification.detach (token2); - notify_check_counters (0, 0, 0); - - /* With a new observer. */ - test_notification.attach (&test_first_notification_function, token1); - notify_check_counters (1, 0, 0); - - /* With 2 observers. */ - test_notification.attach (&test_second_notification_function, token2); - notify_check_counters (1, 1, 0); - - /* With 3 observers. */ - test_notification.attach (&test_third_notification_function, token3); - notify_check_counters (1, 1, 1); - - /* Remove middle observer. */ - test_notification.detach (token2); - notify_check_counters (1, 0, 1); - - /* Remove first observer. */ - test_notification.detach (token1); - notify_check_counters (0, 0, 1); - - /* Remove last observer. */ - test_notification.detach (token3); - notify_check_counters (0, 0, 0); - - /* Go back to 3 observers, and remove them in a different - order... */ - test_notification.attach (&test_first_notification_function, token1); - test_notification.attach (&test_second_notification_function, token2); - test_notification.attach (&test_third_notification_function, token3); - notify_check_counters (1, 1, 1); - - /* Remove the third observer. */ - test_notification.detach (token3); - notify_check_counters (1, 1, 0); - - /* Remove the second observer. */ - test_notification.detach (token2); - notify_check_counters (1, 0, 0); - - /* Remove first observer, no more observers. */ - test_notification.detach (token1); - notify_check_counters (0, 0, 0); -} - -} /* namespace selftests */ - -#endif - } /* namespace observers */ } /* namespace gdb */ @@ -207,9 +96,4 @@ When non-zero, observer debugging is enabled."), NULL, show_observer_debug, &setdebuglist, &showdebuglist); - -#if GDB_SELF_TEST - selftests::register_test ("gdb::observers", - gdb::observers::selftests::observer_self_tests); -#endif } diff --git a/gdb/unittests/observer-selftests.c b/gdb/unittests/observer-selftests.c new file mode 100644 index 00000000000..6f2f7e8bccf --- /dev/null +++ b/gdb/unittests/observer-selftests.c @@ -0,0 +1,135 @@ +/* Self tests for gdb::observers, GDB notifications to observers. + + Copyright (C) 2003-2018 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 . */ + +#include "defs.h" +#include "selftest.h" +#include "common/observable.h" + +namespace selftests { +namespace observers { + +gdb::observers::observable test_notification ("test_notification"); + +static int test_first_observer = 0; +static int test_second_observer = 0; +static int test_third_observer = 0; + +static void +test_first_notification_function (int arg) +{ + test_first_observer++; +} + +static void +test_second_notification_function (int arg) +{ + test_second_observer++; +} + +static void +test_third_notification_function (int arg) +{ + test_third_observer++; +} + +static void +notify_check_counters (int one, int two, int three) +{ + /* Reset. */ + test_first_observer = 0; + test_second_observer = 0; + test_third_observer = 0; + /* Notify. */ + test_notification.notify (0); + /* Check. */ + SELF_CHECK (one == test_first_observer); + SELF_CHECK (two == test_second_observer); + SELF_CHECK (three == test_third_observer); +} + +static void +run_tests () +{ + /* First, try sending a notification without any observer + attached. */ + notify_check_counters (0, 0, 0); + + const gdb::observers::token token1, token2, token3; + + /* Now, attach one observer, and send a notification. */ + test_notification.attach (&test_second_notification_function, token2); + notify_check_counters (0, 1, 0); + + /* Remove the observer, and send a notification. */ + test_notification.detach (token2); + notify_check_counters (0, 0, 0); + + /* With a new observer. */ + test_notification.attach (&test_first_notification_function, token1); + notify_check_counters (1, 0, 0); + + /* With 2 observers. */ + test_notification.attach (&test_second_notification_function, token2); + notify_check_counters (1, 1, 0); + + /* With 3 observers. */ + test_notification.attach (&test_third_notification_function, token3); + notify_check_counters (1, 1, 1); + + /* Remove middle observer. */ + test_notification.detach (token2); + notify_check_counters (1, 0, 1); + + /* Remove first observer. */ + test_notification.detach (token1); + notify_check_counters (0, 0, 1); + + /* Remove last observer. */ + test_notification.detach (token3); + notify_check_counters (0, 0, 0); + + /* Go back to 3 observers, and remove them in a different + order... */ + test_notification.attach (&test_first_notification_function, token1); + test_notification.attach (&test_second_notification_function, token2); + test_notification.attach (&test_third_notification_function, token3); + notify_check_counters (1, 1, 1); + + /* Remove the third observer. */ + test_notification.detach (token3); + notify_check_counters (1, 1, 0); + + /* Remove the second observer. */ + test_notification.detach (token2); + notify_check_counters (1, 0, 0); + + /* Remove first observer, no more observers. */ + test_notification.detach (token1); + notify_check_counters (0, 0, 0); +} + +} /* namespace observers */ +} /* namespace selftests */ + +void +_initialize_observer_selftest () +{ + selftests::register_test ("gdb::observers", + selftests::observers::run_tests); +} -- 2.14.3 --------------11A85EAA43F49740899D7877 Content-Type: text/x-patch; name="0003-DEFINE_OBSERVER-DEFINE_OBSERVABLE.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0003-DEFINE_OBSERVER-DEFINE_OBSERVABLE.patch" Content-length: 3864 >From 5618a1c3d564387dff4799538e16849d2b1df816 Mon Sep 17 00:00:00 2001 From: Pedro Alves Date: Mon, 12 Feb 2018 17:55:14 +0000 Subject: [PATCH 3/3] DEFINE_OBSERVER -> DEFINE_OBSERVABLE --- gdb/observer.c | 86 +++++++++++++++++++++++++++++----------------------------- 1 file changed, 43 insertions(+), 43 deletions(-) diff --git a/gdb/observer.c b/gdb/observer.c index 836b083125d..d85ecd2cce7 100644 --- a/gdb/observer.c +++ b/gdb/observer.c @@ -30,50 +30,50 @@ namespace observers unsigned int observer_debug; -#define DEFINE_OBSERVER(name) decltype (name) name (# name) +#define DEFINE_OBSERVABLE(name) decltype (name) name (# name) -DEFINE_OBSERVER (normal_stop); -DEFINE_OBSERVER (signal_received); -DEFINE_OBSERVER (end_stepping_range); -DEFINE_OBSERVER (signal_exited); -DEFINE_OBSERVER (exited); -DEFINE_OBSERVER (no_history); -DEFINE_OBSERVER (sync_execution_done); -DEFINE_OBSERVER (command_error); -DEFINE_OBSERVER (target_changed); -DEFINE_OBSERVER (executable_changed); -DEFINE_OBSERVER (inferior_created); -DEFINE_OBSERVER (record_changed); -DEFINE_OBSERVER (solib_loaded); -DEFINE_OBSERVER (solib_unloaded); -DEFINE_OBSERVER (new_objfile); -DEFINE_OBSERVER (free_objfile); -DEFINE_OBSERVER (new_thread); -DEFINE_OBSERVER (thread_exit); -DEFINE_OBSERVER (thread_stop_requested); -DEFINE_OBSERVER (target_resumed); -DEFINE_OBSERVER (about_to_proceed); -DEFINE_OBSERVER (breakpoint_created); -DEFINE_OBSERVER (breakpoint_deleted); -DEFINE_OBSERVER (breakpoint_modified); -DEFINE_OBSERVER (traceframe_changed); -DEFINE_OBSERVER (architecture_changed); -DEFINE_OBSERVER (thread_ptid_changed); -DEFINE_OBSERVER (inferior_added); -DEFINE_OBSERVER (inferior_appeared); -DEFINE_OBSERVER (inferior_exit); -DEFINE_OBSERVER (inferior_removed); -DEFINE_OBSERVER (memory_changed); -DEFINE_OBSERVER (before_prompt); -DEFINE_OBSERVER (gdb_datadir_changed); -DEFINE_OBSERVER (command_param_changed); -DEFINE_OBSERVER (tsv_created); -DEFINE_OBSERVER (tsv_deleted); -DEFINE_OBSERVER (tsv_modified); -DEFINE_OBSERVER (inferior_call_pre); -DEFINE_OBSERVER (inferior_call_post); -DEFINE_OBSERVER (register_changed); -DEFINE_OBSERVER (user_selected_context_changed); +DEFINE_OBSERVABLE (normal_stop); +DEFINE_OBSERVABLE (signal_received); +DEFINE_OBSERVABLE (end_stepping_range); +DEFINE_OBSERVABLE (signal_exited); +DEFINE_OBSERVABLE (exited); +DEFINE_OBSERVABLE (no_history); +DEFINE_OBSERVABLE (sync_execution_done); +DEFINE_OBSERVABLE (command_error); +DEFINE_OBSERVABLE (target_changed); +DEFINE_OBSERVABLE (executable_changed); +DEFINE_OBSERVABLE (inferior_created); +DEFINE_OBSERVABLE (record_changed); +DEFINE_OBSERVABLE (solib_loaded); +DEFINE_OBSERVABLE (solib_unloaded); +DEFINE_OBSERVABLE (new_objfile); +DEFINE_OBSERVABLE (free_objfile); +DEFINE_OBSERVABLE (new_thread); +DEFINE_OBSERVABLE (thread_exit); +DEFINE_OBSERVABLE (thread_stop_requested); +DEFINE_OBSERVABLE (target_resumed); +DEFINE_OBSERVABLE (about_to_proceed); +DEFINE_OBSERVABLE (breakpoint_created); +DEFINE_OBSERVABLE (breakpoint_deleted); +DEFINE_OBSERVABLE (breakpoint_modified); +DEFINE_OBSERVABLE (traceframe_changed); +DEFINE_OBSERVABLE (architecture_changed); +DEFINE_OBSERVABLE (thread_ptid_changed); +DEFINE_OBSERVABLE (inferior_added); +DEFINE_OBSERVABLE (inferior_appeared); +DEFINE_OBSERVABLE (inferior_exit); +DEFINE_OBSERVABLE (inferior_removed); +DEFINE_OBSERVABLE (memory_changed); +DEFINE_OBSERVABLE (before_prompt); +DEFINE_OBSERVABLE (gdb_datadir_changed); +DEFINE_OBSERVABLE (command_param_changed); +DEFINE_OBSERVABLE (tsv_created); +DEFINE_OBSERVABLE (tsv_deleted); +DEFINE_OBSERVABLE (tsv_modified); +DEFINE_OBSERVABLE (inferior_call_pre); +DEFINE_OBSERVABLE (inferior_call_post); +DEFINE_OBSERVABLE (register_changed); +DEFINE_OBSERVABLE (user_selected_context_changed); } /* namespace observers */ } /* namespace gdb */ -- 2.14.3 --------------11A85EAA43F49740899D7877--