From: Maksym Mamontov <madf@madf.info>
Date: Tue, 23 Aug 2022 15:35:03 +0000 (+0300)
Subject: Start replacing notifiers with subscriptions.
X-Git-Url: https://git.stg.codes/stg.git/commitdiff_plain/ee1709cd231588fe672d0bd2546ef69ee87ff88c?hp=c59911ca3cd38cf4ab36d2cc62686f97395899f9

Start replacing notifiers with subscriptions.
---

diff --git a/include/stg/subscriptions.h b/include/stg/subscriptions.h
index 1486e62d..853cebe1 100644
--- a/include/stg/subscriptions.h
+++ b/include/stg/subscriptions.h
@@ -7,6 +7,48 @@
 namespace STG
 {
 
+class Connection
+{
+    public:
+        Connection() noexcept : m_connected(false) {}
+
+        Connection(const Connection&) = delete;
+        Connection& operator=(const Connection&) = delete;
+        Connection(Connection&&) = default;
+        Connection& operator=(Connection&&) = default;
+
+        Connection(const std::function<void ()>& f) noexcept : m_disconnect(f), m_connected(true) {}
+        void disconnect() noexcept
+        {
+            if (!m_connected)
+                return;
+            m_disconnect();
+            m_connected = false;
+        }
+    private:
+        std::function<void ()> m_disconnect;
+        bool m_connected;
+};
+
+class ScopedConnection
+{
+    public:
+        ScopedConnection() = default;
+
+        ScopedConnection(const ScopedConnection&) = delete;
+        ScopedConnection& operator=(const ScopedConnection&) = delete;
+        ScopedConnection(ScopedConnection&&) = default;
+        ScopedConnection& operator=(ScopedConnection&&) = default;
+
+        ScopedConnection(Connection c) noexcept : m_conn(std::move(c)) {}
+        ~ScopedConnection() { disconnect(); }
+
+        void disconnect() noexcept { m_conn.disconnect(); }
+
+    private:
+        Connection m_conn;
+};
+
 template <typename... Ts>
 class Subscriptions
 {
@@ -14,35 +56,16 @@ class Subscriptions
         using Callback = std::function<void (Ts...)>;
         using Callbacks = std::list<Callback>;
 
-        class Connection
+        Connection makeConn(typename Callbacks::iterator i) noexcept
         {
-            public:
-                Connection(Subscriptions& s, typename Callbacks::iterator i) noexcept
-                    : m_subscriptions(s), m_iterator(i), m_connected(true)
-                {}
-                ~Connection()
-                {
-                    disconnect();
-                }
-
-                void disconnect() noexcept
-                {
-                    if (!m_connected)
-                        return;
-                    m_subscriptions.remove(m_iterator);
-                    m_connected = false;
-                }
-            private:
-                Subscriptions& m_subscriptions;
-                typename Callbacks::iterator m_iterator;
-                bool m_connected;
-        };
+            return Connection([this, i](){ remove(i); });
+        }
 
         template <typename F>
         Connection add(F&& f)
         {
             std::lock_guard lock(m_mutex);
-            return Connection(*this, m_callbacks.insert(m_callbacks.end(), Callback(std::forward<F>(f))));
+            return makeConn(m_callbacks.insert(m_callbacks.end(), Callback(std::forward<F>(f))));
         }
 
         template <typename C, typename... T2s>
diff --git a/include/stg/users.h b/include/stg/users.h
index dfc60b64..a8d4eb60 100644
--- a/include/stg/users.h
+++ b/include/stg/users.h
@@ -20,7 +20,7 @@
 
 #pragma once
 
-#include "notifer.h"
+#include "subscriptions.h"
 
 #include <string>
 
@@ -31,46 +31,51 @@ struct Admin;
 struct User;
 struct Auth;
 
-struct Users {
-    virtual ~Users() = default;
+class Users
+{
+    public:
+        virtual ~Users() = default;
 
-    using UserPtr = User*;
-    using ConstUserPtr = const User*;
+        using UserPtr = User*;
+        using ConstUserPtr = const User*;
 
-    virtual int  FindByName(const std::string& login, UserPtr* user) = 0;
-    virtual int  FindByName(const std::string& login, ConstUserPtr* user) const = 0;
-    virtual bool Exists(const std::string& login) const = 0;
+        virtual int  FindByName(const std::string& login, UserPtr* user) = 0;
+        virtual int  FindByName(const std::string& login, ConstUserPtr* user) const = 0;
+        virtual bool Exists(const std::string& login) const = 0;
 
-    virtual bool TariffInUse(const std::string& tariffName) const = 0;
+        virtual bool TariffInUse(const std::string& tariffName) const = 0;
 
-    virtual void AddNotifierUserAdd(NotifierBase<User*>* notifier) = 0;
-    virtual void DelNotifierUserAdd(NotifierBase<User*>* notifier) = 0;
+        template <typename F>
+        auto onUserAdd(F&& f) { return m_onAddCallbacks.add(std::forward<F>(f)); }
+        template <typename F>
+        auto onUserDel(F&& f) { return m_onDelCallbacks.add(std::forward<F>(f)); }
 
-    virtual void AddNotifierUserDel(NotifierBase<User*>* notifier) = 0;
-    virtual void DelNotifierUserDel(NotifierBase<User*>* notifier) = 0;
+        virtual int  Add(const std::string& login, const Admin* admin) = 0;
+        virtual void Del(const std::string& login, const Admin* admin) = 0;
 
-    virtual int  Add(const std::string& login, const Admin* admin) = 0;
-    virtual void Del(const std::string& login, const Admin* admin) = 0;
+        virtual bool Authorize(const std::string& login, uint32_t ip,
+                               uint32_t enabledDirs, const Auth* auth) = 0;
+        virtual bool Unauthorize(const std::string& login,
+                                 const Auth* auth,
+                                 const std::string& reason = {}) = 0;
 
-    virtual bool Authorize(const std::string& login, uint32_t ip,
-                           uint32_t enabledDirs, const Auth* auth) = 0;
-    virtual bool Unauthorize(const std::string& login,
-                             const Auth* auth,
-                             const std::string& reason = {}) = 0;
+        virtual int  ReadUsers() = 0;
+        virtual size_t Count() const = 0;
 
-    virtual int  ReadUsers() = 0;
-    virtual size_t Count() const = 0;
+        virtual int  FindByIPIdx(uint32_t ip, User** user) const = 0;
+        virtual bool IsIPInIndex(uint32_t ip) const = 0;
+        virtual bool IsIPInUse(uint32_t ip, const std::string & login, const User** user) const = 0;
 
-    virtual int  FindByIPIdx(uint32_t ip, User** user) const = 0;
-    virtual bool IsIPInIndex(uint32_t ip) const = 0;
-    virtual bool IsIPInUse(uint32_t ip, const std::string & login, const User** user) const = 0;
+        virtual unsigned int  OpenSearch() = 0;
+        virtual int  SearchNext(int handle, User** u) = 0;
+        virtual int  CloseSearch(int handle) = 0;
 
-    virtual unsigned int  OpenSearch() = 0;
-    virtual int  SearchNext(int handle, User** u) = 0;
-    virtual int  CloseSearch(int handle) = 0;
+        virtual int  Start() = 0;
+        virtual int  Stop() = 0;
 
-    virtual int  Start() = 0;
-    virtual int  Stop() = 0;
+    protected:
+        Subscriptions<UserPtr> m_onAddCallbacks;
+        Subscriptions<UserPtr> m_onDelCallbacks;
 };
 
 }
diff --git a/projects/stargazer/plugins/authorization/ao/ao.cpp b/projects/stargazer/plugins/authorization/ao/ao.cpp
index 1145d679..d0be8dd5 100644
--- a/projects/stargazer/plugins/authorization/ao/ao.cpp
+++ b/projects/stargazer/plugins/authorization/ao/ao.cpp
@@ -48,8 +48,6 @@ return "Always Online authorizator v.1.0";
 AUTH_AO::AUTH_AO()
     : users(NULL),
       isRunning(false),
-      onAddUserNotifier(*this),
-      onDelUserNotifier(*this),
       logger(STG::PluginLogger::get("auth_ao"))
 {
 }
@@ -59,8 +57,8 @@ int AUTH_AO::Start()
 printfd(__FILE__, "AUTH_AO::Start()\n");
 GetUsers();
 
-users->AddNotifierUserAdd(&onAddUserNotifier);
-users->AddNotifierUserDel(&onDelUserNotifier);
+m_onAddUserConn = users->onUserAdd([this](auto user){ AddUser(user); });
+m_onDelUserConn = users->onUserDel([this](auto user){ DelUser(user); });
 
 std::for_each(userList.begin(), userList.end(), [this](auto user){ UpdateUserAuthorization(user); });
 
@@ -75,8 +73,8 @@ printfd(__FILE__, "AUTH_AO::Stop()\n");
 if (!isRunning)
     return 0;
 
-users->DelNotifierUserAdd(&onAddUserNotifier);
-users->DelNotifierUserDel(&onDelUserNotifier);
+m_onAddUserConn.disconnect();
+m_onDelUserConn.disconnect();
 
 auto it = userList.begin();
 while (it != userList.end())
diff --git a/projects/stargazer/plugins/authorization/ao/ao.h b/projects/stargazer/plugins/authorization/ao/ao.h
index a97a0918..18b41429 100644
--- a/projects/stargazer/plugins/authorization/ao/ao.h
+++ b/projects/stargazer/plugins/authorization/ao/ao.h
@@ -24,6 +24,7 @@
 #include "stg/module_settings.h"
 #include "stg/store.h"
 #include "stg/notifer.h"
+#include "stg/subscriptions.h"
 #include "stg/user_ips.h"
 #include "stg/user.h"
 #include "stg/logger.h"
@@ -121,31 +122,8 @@ private:
     std::list<CHG_BEFORE_NOTIFIER<STG::UserIPs> > BeforeChgIPNotifierList;
     std::list<CHG_AFTER_NOTIFIER<STG::UserIPs> >  AfterChgIPNotifierList;
 
-    class ADD_USER_NONIFIER: public STG::NotifierBase<UserPtr> {
-    public:
-        explicit ADD_USER_NONIFIER(AUTH_AO & a) : auth(a) {}
-        virtual ~ADD_USER_NONIFIER() {}
-        void notify(const UserPtr & user) override { auth.AddUser(user); }
-
-    private:
-        ADD_USER_NONIFIER(const ADD_USER_NONIFIER & rvalue);
-        ADD_USER_NONIFIER & operator=(const ADD_USER_NONIFIER & rvalue);
-
-        AUTH_AO & auth;
-    } onAddUserNotifier;
-
-    class DEL_USER_NONIFIER: public STG::NotifierBase<UserPtr> {
-    public:
-        explicit DEL_USER_NONIFIER(AUTH_AO & a) : auth(a) {}
-        virtual ~DEL_USER_NONIFIER() {}
-        void notify(const UserPtr & user) override { auth.DelUser(user); }
-
-    private:
-        DEL_USER_NONIFIER(const DEL_USER_NONIFIER & rvalue);
-        DEL_USER_NONIFIER & operator=(const DEL_USER_NONIFIER & rvalue);
-
-        AUTH_AO & auth;
-    } onDelUserNotifier;
+    STG::ScopedConnection m_onAddUserConn;
+    STG::ScopedConnection m_onDelUserConn;
 
     STG::PluginLogger logger;
 
diff --git a/projects/stargazer/plugins/authorization/inetaccess/inetaccess.cpp b/projects/stargazer/plugins/authorization/inetaccess/inetaccess.cpp
index 01162875..3c6a687c 100644
--- a/projects/stargazer/plugins/authorization/inetaccess/inetaccess.cpp
+++ b/projects/stargazer/plugins/authorization/inetaccess/inetaccess.cpp
@@ -290,7 +290,6 @@ AUTH_IA::AUTH_IA()
       stgSettings(NULL),
       listenSocket(-1),
       enabledDirs(0xFFffFFff),
-      onDelUserNotifier(*this),
       logger(STG::PluginLogger::get("auth_ia"))
 {
 InitContext("pr7Hhen", 7, &ctxS);
@@ -341,12 +340,10 @@ AUTH_IA::~AUTH_IA()
 //-----------------------------------------------------------------------------
 int AUTH_IA::Start()
 {
-users->AddNotifierUserDel(&onDelUserNotifier);
+m_onDelUserConn = users->onUserDel([this](auto user){ DelUser(user); });
 
 if (PrepareNet())
-    {
     return -1;
-    }
 
 if (!m_thread.joinable())
     m_thread = std::jthread([this](auto token){ Run(std::move(token)); });
@@ -394,7 +391,7 @@ if (isRunningRunTimeouter)
         }
     }
 
-users->DelNotifierUserDel(&onDelUserNotifier);
+m_onDelUserConn.disconnect();
 
 if (isRunningRun)
     m_thread.detach();
diff --git a/projects/stargazer/plugins/authorization/inetaccess/inetaccess.h b/projects/stargazer/plugins/authorization/inetaccess/inetaccess.h
index f9137c20..d620f392 100644
--- a/projects/stargazer/plugins/authorization/inetaccess/inetaccess.h
+++ b/projects/stargazer/plugins/authorization/inetaccess/inetaccess.h
@@ -213,21 +213,7 @@ private:
 class AUTH_IA;
 using UserPtr = STG::User*;
 //-----------------------------------------------------------------------------
-class DEL_USER_NOTIFIER: public STG::NotifierBase<UserPtr> {
-public:
-    explicit DEL_USER_NOTIFIER(AUTH_IA & a) : auth(a) {}
-    virtual ~DEL_USER_NOTIFIER() {}
-
-    void notify(const UserPtr & user) override;
-private:
-    DEL_USER_NOTIFIER(const DEL_USER_NOTIFIER & rvalue);
-    DEL_USER_NOTIFIER & operator=(const DEL_USER_NOTIFIER & rvalue);
-
-    AUTH_IA & auth;
-};
-//-----------------------------------------------------------------------------
 class AUTH_IA : public STG::Auth {
-friend class DEL_USER_NOTIFIER;
 public:
                         AUTH_IA();
                         ~AUTH_IA() override;
@@ -350,7 +336,7 @@ private:
 
     uint32_t            enabledDirs;
 
-    DEL_USER_NOTIFIER   onDelUserNotifier;
+    STG::ScopedConnection m_onDelUserConn;
 
     STG::PluginLogger   logger;
 
@@ -370,9 +356,3 @@ class UnauthorizeUser : std::unary_function<const std::pair<uint32_t, IA_USER> &
 
         AUTH_IA * auth;
 };
-//-----------------------------------------------------------------------------
-inline
-void DEL_USER_NOTIFIER::notify(const UserPtr & user)
-{
-    auth.DelUser(user);
-}
diff --git a/projects/stargazer/plugins/other/ping/ping.cpp b/projects/stargazer/plugins/other/ping/ping.cpp
index 35237120..65dd3454 100644
--- a/projects/stargazer/plugins/other/ping/ping.cpp
+++ b/projects/stargazer/plugins/other/ping/ping.cpp
@@ -63,8 +63,6 @@ return 0;
 PING::PING()
     : users(nullptr),
       isRunning(false),
-      onAddUserNotifier(*this),
-      onDelUserNotifier(*this),
       logger(STG::PluginLogger::get("ping"))
 {
 }
@@ -81,8 +79,8 @@ int PING::Start()
 {
 GetUsers();
 
-users->AddNotifierUserAdd(&onAddUserNotifier);
-users->AddNotifierUserDel(&onDelUserNotifier);
+m_onAddUserConn = users->onUserAdd([this](auto user){ AddUser(user); });
+m_onDelUserConn = users->onUserDel([this](auto user){ DelUser(user); });
 
 pinger.SetDelayTime(pingSettings.GetPingDelay());
 pinger.Start();
@@ -111,8 +109,8 @@ for (int i = 0; i < 25; i++)
     nanosleep(&ts, nullptr);
     }
 
-users->DelNotifierUserAdd(&onAddUserNotifier);
-users->DelNotifierUserDel(&onDelUserNotifier);
+m_onAddUserConn.disconnect();
+m_onDelUserConn.disconnect();
 
 std::list<UserPtr>::iterator users_iter;
 users_iter = usersList.begin();
@@ -306,14 +304,3 @@ if (oldIPS.onlyOneIP())
 if (newIPS.onlyOneIP())
     ping.pinger.AddIP(newIPS[0].ip);
 }
-//-----------------------------------------------------------------------------
-void ADD_USER_NONIFIER_PING::notify(const UserPtr & user)
-{
-ping.AddUser(user);
-}
-//-----------------------------------------------------------------------------
-void DEL_USER_NONIFIER_PING::notify(const UserPtr & user)
-{
-ping.DelUser(user);
-}
-//-----------------------------------------------------------------------------
diff --git a/projects/stargazer/plugins/other/ping/ping.h b/projects/stargazer/plugins/other/ping/ping.h
index ff66a1c2..68d79794 100644
--- a/projects/stargazer/plugins/other/ping/ping.h
+++ b/projects/stargazer/plugins/other/ping/ping.h
@@ -3,6 +3,7 @@
 #include "stg/plugin.h"
 #include "stg/module_settings.h"
 #include "stg/notifer.h"
+#include "stg/subscriptions.h"
 #include "stg/user_ips.h"
 #include "stg/pinger.h"
 #include "stg/users.h"
@@ -55,30 +56,6 @@ private:
     const PING & ping;
 };
 //-----------------------------------------------------------------------------
-class ADD_USER_NONIFIER_PING: public STG::NotifierBase<UserPtr> {
-public:
-    explicit ADD_USER_NONIFIER_PING(PING & p) : ping(p) {}
-    void notify(const UserPtr & user) override;
-
-private:
-    ADD_USER_NONIFIER_PING(const ADD_USER_NONIFIER_PING &);
-    ADD_USER_NONIFIER_PING & operator=(const ADD_USER_NONIFIER_PING &);
-
-    PING & ping;
-};
-//-----------------------------------------------------------------------------
-class DEL_USER_NONIFIER_PING: public STG::NotifierBase<UserPtr> {
-public:
-    explicit DEL_USER_NONIFIER_PING(PING & p) : ping(p) {}
-    void notify(const UserPtr & user) override;
-
-private:
-    DEL_USER_NONIFIER_PING(const DEL_USER_NONIFIER_PING &);
-    DEL_USER_NONIFIER_PING & operator=(const DEL_USER_NONIFIER_PING &);
-
-    PING & ping;
-};
-//-----------------------------------------------------------------------------
 class PING_SETTINGS {
 public:
     PING_SETTINGS() : pingDelay(0) {}
@@ -136,8 +113,8 @@ private:
     std::list<CHG_CURRIP_NOTIFIER_PING> ChgCurrIPNotifierList;
     std::list<CHG_IPS_NOTIFIER_PING> ChgIPNotifierList;
 
-    ADD_USER_NONIFIER_PING onAddUserNotifier;
-    DEL_USER_NONIFIER_PING onDelUserNotifier;
+    STG::ScopedConnection m_onAddUserConn;
+    STG::ScopedConnection m_onDelUserConn;
 
     STG::PluginLogger logger;
 };
diff --git a/projects/stargazer/plugins/other/rscript/rscript.cpp b/projects/stargazer/plugins/other/rscript/rscript.cpp
index 7f0f4bc4..d54c800e 100644
--- a/projects/stargazer/plugins/other/rscript/rscript.cpp
+++ b/projects/stargazer/plugins/other/rscript/rscript.cpp
@@ -160,8 +160,6 @@ REMOTE_SCRIPT::REMOTE_SCRIPT()
       isRunning(false),
       users(nullptr),
       sock(0),
-      onAddUserNotifier(*this),
-      onDelUserNotifier(*this),
       logger(STG::PluginLogger::get("rscript"))
 {
 }
@@ -201,8 +199,8 @@ netRouters = rsSettings.GetSubnetsMap();
 
 InitEncrypt(rsSettings.GetPassword());
 
-users->AddNotifierUserAdd(&onAddUserNotifier);
-users->AddNotifierUserDel(&onDelUserNotifier);
+m_onAddUserConn = users->onUserAdd([this](auto user){ AddUser(user); });
+m_onDelUserConn = users->onUserDel([this](auto user){ DelUser(user); });
 
 if (GetUsers())
     return -1;
@@ -242,9 +240,6 @@ if (isRunning)
         }
     }
 
-users->DelNotifierUserDel(&onDelUserNotifier);
-users->DelNotifierUserAdd(&onAddUserNotifier);
-
 if (isRunning)
     {
     logger("Cannot stop thread.");
diff --git a/projects/stargazer/plugins/other/rscript/rscript.h b/projects/stargazer/plugins/other/rscript/rscript.h
index fc957108..d0bf1e7e 100644
--- a/projects/stargazer/plugins/other/rscript/rscript.h
+++ b/projects/stargazer/plugins/other/rscript/rscript.h
@@ -23,6 +23,7 @@
 
 #include "stg/plugin.h"
 #include "stg/module_settings.h"
+#include "stg/subscriptions.h"
 #include "stg/notifer.h"
 #include "stg/user.h"
 #include "stg/blowfish.h"
@@ -58,32 +59,6 @@ class DisconnectUser;
 
 using UserPtr = STG::User*;
 
-//-----------------------------------------------------------------------------
-class ADD_USER_NONIFIER: public STG::NotifierBase<UserPtr> {
-public:
-    explicit ADD_USER_NONIFIER(REMOTE_SCRIPT & r)
-        : rs(r) {}
-    void notify(const UserPtr & user) override;
-
-private:
-    ADD_USER_NONIFIER(const ADD_USER_NONIFIER & rhs);
-    ADD_USER_NONIFIER & operator=(const ADD_USER_NONIFIER);
-
-    REMOTE_SCRIPT & rs;
-};
-//-----------------------------------------------------------------------------
-class DEL_USER_NONIFIER: public STG::NotifierBase<UserPtr> {
-public:
-    explicit DEL_USER_NONIFIER(REMOTE_SCRIPT & r)
-        : rs(r) {}
-    void notify(const UserPtr & user) override;
-
-private:
-    DEL_USER_NONIFIER(const DEL_USER_NONIFIER & rhs);
-    DEL_USER_NONIFIER & operator=(const DEL_USER_NONIFIER);
-
-    REMOTE_SCRIPT & rs;
-};
 //-----------------------------------------------------------------------------
 class IP_NOTIFIER: public STG::PropertyNotifierBase<uint32_t> {
 public:
@@ -243,8 +218,8 @@ private:
 
     int                 sock;
 
-    ADD_USER_NONIFIER onAddUserNotifier;
-    DEL_USER_NONIFIER onDelUserNotifier;
+    STG::ScopedConnection m_onAddUserConn;
+    STG::ScopedConnection m_onDelUserConn;
 
     STG::PluginLogger       logger;
 
@@ -264,15 +239,5 @@ class DisconnectUser : public std::unary_function<std::pair<const uint32_t, USER
         REMOTE_SCRIPT & rscript;
 };
 //-----------------------------------------------------------------------------
-inline void ADD_USER_NONIFIER::notify(const UserPtr & user)
-{
-rs.AddUser(user);
-}
-//-----------------------------------------------------------------------------
-inline void DEL_USER_NONIFIER::notify(const UserPtr & user)
-{
-rs.DelUser(user);
-}
-//-----------------------------------------------------------------------------
 
 } // namespace RS
diff --git a/projects/stargazer/plugins/other/smux/smux.cpp b/projects/stargazer/plugins/other/smux/smux.cpp
index f79085df..8d5079bd 100644
--- a/projects/stargazer/plugins/other/smux/smux.cpp
+++ b/projects/stargazer/plugins/other/smux/smux.cpp
@@ -100,8 +100,6 @@ SMUX::SMUX()
       lastReconnectTry(0),
       reconnectTimeout(1),
       sock(-1),
-      addUserNotifier(*this),
-      delUserNotifier(*this),
       addDelTariffNotifier(*this),
       logger(STG::PluginLogger::get("smux"))
 {
@@ -444,8 +442,14 @@ while (users->SearchNext(h, &u) == 0)
 
 users->CloseSearch(h);
 
-users->AddNotifierUserAdd(&addUserNotifier);
-users->AddNotifierUserDel(&delUserNotifier);
+m_onAddUserConn = users->onUserAdd([this](auto user){
+    SetNotifier(user);
+    UpdateTables();
+});
+m_onDelUserConn = users->onUserDel([this](auto user){
+    UnsetNotifier(user);
+    UpdateTables();
+});
 
 tariffs->AddNotifierAdd(&addDelTariffNotifier);
 tariffs->AddNotifierDel(&addDelTariffNotifier);
@@ -456,8 +460,8 @@ void SMUX::ResetNotifiers()
 tariffs->DelNotifierDel(&addDelTariffNotifier);
 tariffs->DelNotifierAdd(&addDelTariffNotifier);
 
-users->DelNotifierUserDel(&delUserNotifier);
-users->DelNotifierUserAdd(&addUserNotifier);
+m_onAddUserConn.disconnect();
+m_onDelUserConn.disconnect();
 
 auto it = notifiers.begin();
 while (it != notifiers.end())
diff --git a/projects/stargazer/plugins/other/smux/smux.h b/projects/stargazer/plugins/other/smux/smux.h
index f45beffc..1986d28b 100644
--- a/projects/stargazer/plugins/other/smux/smux.h
+++ b/projects/stargazer/plugins/other/smux/smux.h
@@ -1,28 +1,28 @@
-#ifndef __SMUX_H__
-#define __SMUX_H__
+#pragma once
 
-#include <string>
-#include <map>
-#include <list>
-#include <mutex>
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wshadow"
-#include <jthread.hpp>
-#pragma GCC diagnostic pop
-#include <cstdint>
+#include "sensors.h"
+#include "tables.h"
+#include "types.h"
 
 #include "stg/SMUX-PDUs.h"
 #include "stg/ObjectSyntax.h"
 
 #include "stg/plugin.h"
 #include "stg/module_settings.h"
+#include "stg/subscriptions.h"
 #include "stg/notifer.h"
 #include "stg/noncopyable.h"
 #include "stg/logger.h"
 
-#include "sensors.h"
-#include "tables.h"
-#include "types.h"
+#include <string>
+#include <map>
+#include <list>
+#include <mutex>
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wshadow"
+#include <jthread.hpp>
+#pragma GCC diagnostic pop
+#include <cstdint>
 
 namespace STG
 {
@@ -87,24 +87,6 @@ public:
              : STG::NotifierBase<STG::TariffData>(), smux(s) {}
     void notify(const STG::TariffData &) override;
 
-private:
-    SMUX & smux;
-};
-//-----------------------------------------------------------------------------
-class ADD_USER_NOTIFIER : public STG::NotifierBase<UserPtr> {
-public:
-    explicit ADD_USER_NOTIFIER(SMUX & s) : STG::NotifierBase<STG::User*>(), smux(s) {}
-    void notify(const UserPtr &) override;
-
-private:
-    SMUX & smux;
-};
-//-----------------------------------------------------------------------------
-class DEL_USER_NOTIFIER : public STG::NotifierBase<UserPtr> {
-public:
-    explicit DEL_USER_NOTIFIER(SMUX & s) : STG::NotifierBase<UserPtr>(), smux(s) {}
-    void notify(const UserPtr &) override;
-
 private:
     SMUX & smux;
 };
@@ -186,9 +168,10 @@ private:
     Sensors sensors;
     Tables tables;
 
+    STG::ScopedConnection m_onAddUserConn;
+    STG::ScopedConnection m_onDelUserConn;
+
     std::list<CHG_AFTER_NOTIFIER> notifiers;
-    ADD_USER_NOTIFIER addUserNotifier;
-    DEL_USER_NOTIFIER delUserNotifier;
     ADD_DEL_TARIFF_NOTIFIER addDelTariffNotifier;
 
     STG::PluginLogger logger;
@@ -206,19 +189,3 @@ void ADD_DEL_TARIFF_NOTIFIER::notify(const STG::TariffData &)
 {
 smux.UpdateTables();
 }
-
-inline
-void ADD_USER_NOTIFIER::notify(const UserPtr & userPtr)
-{
-smux.SetNotifier(userPtr);
-smux.UpdateTables();
-}
-
-inline
-void DEL_USER_NOTIFIER::notify(const UserPtr & userPtr)
-{
-smux.UnsetNotifier(userPtr);
-smux.UpdateTables();
-}
-
-#endif
diff --git a/projects/stargazer/traffcounter_impl.cpp b/projects/stargazer/traffcounter_impl.cpp
index ed0f93d5..aa156c9d 100644
--- a/projects/stargazer/traffcounter_impl.cpp
+++ b/projects/stargazer/traffcounter_impl.cpp
@@ -53,8 +53,6 @@
 using STG::TraffCounterImpl;
 using STG::TRF_IP_BEFORE;
 using STG::TRF_IP_AFTER;
-using STG::ADD_USER_NONIFIER;
-using STG::DEL_USER_NONIFIER;
 
 namespace AsyncPoolST = STG::AsyncPoolST;
 
@@ -73,17 +71,20 @@ TraffCounterImpl::TraffCounterImpl(UsersImpl * u, const std::string & fn)
       monitoring(false),
       touchTimeP(stgTime - MONITOR_TIME_DELAY_SEC),
       users(u),
-      stopped(true),
-      addUserNotifier(*this),
-      delUserNotifier(*this)
+      stopped(true)
 {
 for (int i = 0; i < DIR_NUM; i++)
     strprintf(&dirName[i], "DIR%d", i);
 
 dirName[DIR_NUM] = "NULL";
 
-users->AddNotifierUserAdd(&addUserNotifier);
-users->AddNotifierUserDel(&delUserNotifier);
+m_onAddUserConn = users->onUserImplAdd([this](auto user){
+    AsyncPoolST::enqueue([this, user](){ SetUserNotifiers(user); });
+});
+m_onDelUserConn = users->onUserImplDel([this](auto user){
+    AsyncPoolST::enqueue([this, user](){ UnSetUserNotifiers(user); });
+    AsyncPoolST::enqueue([this, user](){ DelUser(user->GetCurrIP()); });
+});
 }
 //-----------------------------------------------------------------------------
 TraffCounterImpl::~TraffCounterImpl()
@@ -868,14 +869,3 @@ if (!newValue)
 AsyncPoolST::enqueue([this](){ traffCnt.AddUser(user); });
 }
 //-----------------------------------------------------------------------------
-void ADD_USER_NONIFIER::notify(const UserImplPtr & user)
-{
-AsyncPoolST::enqueue([this, user](){ traffCnt.SetUserNotifiers(user); });
-}
-//-----------------------------------------------------------------------------
-void DEL_USER_NONIFIER::notify(const UserImplPtr & user)
-{
-AsyncPoolST::enqueue([this, user](){ traffCnt.UnSetUserNotifiers(user); });
-AsyncPoolST::enqueue([this, user](){ traffCnt.DelUser(user->GetCurrIP()); });
-}
-//-----------------------------------------------------------------------------
diff --git a/projects/stargazer/traffcounter_impl.h b/projects/stargazer/traffcounter_impl.h
index 7fd06309..474b6694 100644
--- a/projects/stargazer/traffcounter_impl.h
+++ b/projects/stargazer/traffcounter_impl.h
@@ -23,7 +23,7 @@
 #include "stg/traffcounter.h"
 #include "stg/logger.h"
 #include "stg/raw_ip_packet.h"
-#include "stg/noncopyable.h"
+#include "stg/subscriptions.h"
 #include "stg/notifer.h"
 #include "user_impl.h"
 
@@ -130,41 +130,7 @@ private:
 
 using UserImplPtr = UserImpl*;
 //-----------------------------------------------------------------------------
-class ADD_USER_NONIFIER: public NotifierBase<UserImplPtr> {
-public:
-            explicit ADD_USER_NONIFIER(TraffCounterImpl & t) :
-                NotifierBase<UserImplPtr>(),
-                traffCnt(t)
-            {}
-    virtual ~ADD_USER_NONIFIER() {}
-    void    notify(const UserImplPtr & user) override;
-
-private:
-    ADD_USER_NONIFIER(const ADD_USER_NONIFIER & rvalue);
-    ADD_USER_NONIFIER & operator=(const ADD_USER_NONIFIER & rvalue);
-
-    TraffCounterImpl & traffCnt;
-};
-//-----------------------------------------------------------------------------
-class DEL_USER_NONIFIER: public NotifierBase<UserImplPtr> {
-public:
-            explicit DEL_USER_NONIFIER(TraffCounterImpl & t) :
-                NotifierBase<UserImplPtr>(),
-                traffCnt(t)
-            {}
-    virtual ~DEL_USER_NONIFIER() {}
-    void    notify(const UserImplPtr & user) override;
-
-private:
-    DEL_USER_NONIFIER(const DEL_USER_NONIFIER & rvalue);
-    DEL_USER_NONIFIER & operator=(const DEL_USER_NONIFIER & rvalue);
-
-    TraffCounterImpl & traffCnt;
-};
-//-----------------------------------------------------------------------------
 class TraffCounterImpl : public TraffCounter {
-    friend class ADD_USER_NONIFIER;
-    friend class DEL_USER_NONIFIER;
     friend class TRF_IP_BEFORE;
     friend class TRF_IP_AFTER;
     public:
@@ -228,11 +194,11 @@ class TraffCounterImpl : public TraffCounter {
         std::mutex               m_mutex;
         std::jthread             m_thread;
 
+        ScopedConnection m_onAddUserConn;
+        ScopedConnection m_onDelUserConn;
+
         std::list<TRF_IP_BEFORE> ipBeforeNotifiers;
         std::list<TRF_IP_AFTER>  ipAfterNotifiers;
-
-        ADD_USER_NONIFIER        addUserNotifier;
-        DEL_USER_NONIFIER        delUserNotifier;
 };
 
 }
diff --git a/projects/stargazer/users_impl.cpp b/projects/stargazer/users_impl.cpp
index cfd692c5..5bf082ec 100644
--- a/projects/stargazer/users_impl.cpp
+++ b/projects/stargazer/users_impl.cpp
@@ -148,26 +148,8 @@ u.OnAdd();
 users.push_front(u);
 
 AddUserIntoIndexes(users.begin());
-
-    {
-    // Fire all "on add" notifiers
-    auto ni = onAddNotifiers.begin();
-    while (ni != onAddNotifiers.end())
-        {
-        (*ni)->notify(&users.front());
-        ++ni;
-        }
-    }
-
-    {
-    // Fire all "on add" implementation notifiers
-    auto ni = onAddNotifiersImpl.begin();
-    while (ni != onAddNotifiersImpl.end())
-        {
-        (*ni)->notify(&users.front());
-        ++ni;
-        }
-    }
+m_onAddCallbacks.notify(&users.front());
+m_onAddImplCallbacks.notify(&users.front());
 
 return 0;
 }
@@ -199,23 +181,8 @@ if (priv.userAddDel == 0)
     u->SetDeleted();
     }
 
-    {
-    auto ni = onDelNotifiers.begin();
-    while (ni != onDelNotifiers.end())
-        {
-        (*ni)->notify(&(*u));
-        ++ni;
-        }
-    }
-
-    {
-    auto ni = onDelNotifiersImpl.begin();
-    while (ni != onDelNotifiersImpl.end())
-        {
-        (*ni)->notify(&(*u));
-        ++ni;
-        }
-    }
+    m_onDelCallbacks.notify(&(*u));
+    m_onDelImplCallbacks.notify(&(*u));
 
     {
     std::lock_guard<std::mutex> lock(m_mutex);
@@ -664,54 +631,6 @@ while (iter != users.end())
 return false;
 }
 //-----------------------------------------------------------------------------
-void UsersImpl::AddNotifierUserAdd(NotifierBase<UserPtr> * n)
-{
-std::lock_guard<std::mutex> lock(m_mutex);
-onAddNotifiers.insert(n);
-}
-//-----------------------------------------------------------------------------
-void UsersImpl::DelNotifierUserAdd(NotifierBase<UserPtr> * n)
-{
-std::lock_guard<std::mutex> lock(m_mutex);
-onAddNotifiers.erase(n);
-}
-//-----------------------------------------------------------------------------
-void UsersImpl::AddNotifierUserDel(NotifierBase<UserPtr> * n)
-{
-std::lock_guard<std::mutex> lock(m_mutex);
-onDelNotifiers.insert(n);
-}
-//-----------------------------------------------------------------------------
-void UsersImpl::DelNotifierUserDel(NotifierBase<UserPtr> * n)
-{
-std::lock_guard<std::mutex> lock(m_mutex);
-onDelNotifiers.erase(n);
-}
-//-----------------------------------------------------------------------------
-void UsersImpl::AddNotifierUserAdd(NotifierBase<UserImplPtr> * n)
-{
-std::lock_guard<std::mutex> lock(m_mutex);
-onAddNotifiersImpl.insert(n);
-}
-//-----------------------------------------------------------------------------
-void UsersImpl::DelNotifierUserAdd(NotifierBase<UserImplPtr> * n)
-{
-std::lock_guard<std::mutex> lock(m_mutex);
-onAddNotifiersImpl.erase(n);
-}
-//-----------------------------------------------------------------------------
-void UsersImpl::AddNotifierUserDel(NotifierBase<UserImplPtr> * n)
-{
-std::lock_guard<std::mutex> lock(m_mutex);
-onDelNotifiersImpl.insert(n);
-}
-//-----------------------------------------------------------------------------
-void UsersImpl::DelNotifierUserDel(NotifierBase<UserImplPtr> * n)
-{
-std::lock_guard<std::mutex> lock(m_mutex);
-onDelNotifiersImpl.erase(n);
-}
-//-----------------------------------------------------------------------------
 unsigned int UsersImpl::OpenSearch()
 {
 std::lock_guard<std::mutex> lock(m_mutex);
diff --git a/projects/stargazer/users_impl.h b/projects/stargazer/users_impl.h
index cd7d2932..b5a37063 100644
--- a/projects/stargazer/users_impl.h
+++ b/projects/stargazer/users_impl.h
@@ -64,109 +64,101 @@ std::list<UserImpl>::iterator iter;
 time_t  delTime;
 };
 //-----------------------------------------------------------------------------
-class UsersImpl : public Users {
+class UsersImpl : public Users
+{
     friend class PROPERTY_NOTIFER_IP_BEFORE;
     friend class PROPERTY_NOTIFER_IP_AFTER;
 
-public:
-    using UserImplPtr = UserImpl*;
-
-    UsersImpl(SettingsImpl * s, Store * store,
-              Tariffs * tariffs, Services & svcs,
-              const Admin& sysAdmin);
-
-    int             FindByName(const std::string & login, UserPtr * user) override;
-    int             FindByName(const std::string & login, ConstUserPtr * user) const override;
-    bool            Exists(const std::string & login) const override;
-
-    bool            TariffInUse(const std::string & tariffName) const override;
+    public:
+        using UserImplPtr = UserImpl*;
 
-    void            AddNotifierUserAdd(NotifierBase<UserPtr> *) override;
-    void            DelNotifierUserAdd(NotifierBase<UserPtr> *) override;
+        UsersImpl(SettingsImpl * s, Store * store,
+                  Tariffs * tariffs, Services & svcs,
+                  const Admin& sysAdmin);
 
-    void            AddNotifierUserDel(NotifierBase<UserPtr> *) override;
-    void            DelNotifierUserDel(NotifierBase<UserPtr> *) override;
+        int             FindByName(const std::string & login, UserPtr * user) override;
+        int             FindByName(const std::string & login, ConstUserPtr * user) const override;
+        bool            Exists(const std::string & login) const override;
 
-    void            AddNotifierUserAdd(NotifierBase<UserImplPtr> *);
-    void            DelNotifierUserAdd(NotifierBase<UserImplPtr> *);
+        bool            TariffInUse(const std::string & tariffName) const override;
 
-    void            AddNotifierUserDel(NotifierBase<UserImplPtr> *);
-    void            DelNotifierUserDel(NotifierBase<UserImplPtr> *);
+        template <typename F>
+        auto            onUserImplAdd(F&& f) { return m_onAddImplCallbacks.add(std::forward<F>(f)); }
+        template <typename F>
+        auto            onUserImplDel(F&& f) { return m_onDelImplCallbacks.add(std::forward<F>(f)); }
 
-    int             Add(const std::string & login, const Admin * admin) override;
-    void            Del(const std::string & login, const Admin * admin) override;
+        int             Add(const std::string & login, const Admin * admin) override;
+        void            Del(const std::string & login, const Admin * admin) override;
 
-    bool            Authorize(const std::string & login, uint32_t ip,
-                              uint32_t enabledDirs, const Auth * auth) override;
-    bool            Unauthorize(const std::string & login,
-                                const Auth * auth,
-                                const std::string & reason) override;
+        bool            Authorize(const std::string & login, uint32_t ip,
+                                  uint32_t enabledDirs, const Auth * auth) override;
+        bool            Unauthorize(const std::string & login,
+                                    const Auth * auth,
+                                    const std::string & reason) override;
 
-    int             ReadUsers() override;
-    size_t          Count() const override { return users.size(); }
+        int             ReadUsers() override;
+        size_t          Count() const override { return users.size(); }
 
-    int             FindByIPIdx(uint32_t ip, UserPtr * user) const override;
-    int             FindByIPIdx(uint32_t ip, UserImpl ** user) const;
-    bool            IsIPInIndex(uint32_t ip) const override;
-    bool            IsIPInUse(uint32_t ip, const std::string & login, ConstUserPtr * user) const override;
+        int             FindByIPIdx(uint32_t ip, UserPtr * user) const override;
+        int             FindByIPIdx(uint32_t ip, UserImpl ** user) const;
+        bool            IsIPInIndex(uint32_t ip) const override;
+        bool            IsIPInUse(uint32_t ip, const std::string & login, ConstUserPtr * user) const override;
 
-    unsigned int    OpenSearch() override;
-    int             SearchNext(int handler, UserPtr * user) override;
-    int             SearchNext(int handler, UserImpl ** user);
-    int             CloseSearch(int handler) override;
+        unsigned int    OpenSearch() override;
+        int             SearchNext(int handler, UserPtr * user) override;
+        int             SearchNext(int handler, UserImpl ** user);
+        int             CloseSearch(int handler) override;
 
-    int             Start() override;
-    int             Stop() override;
+        int             Start() override;
+        int             Stop() override;
 
-private:
-    UsersImpl(const UsersImpl & rvalue);
-    UsersImpl & operator=(const UsersImpl & rvalue);
+    private:
+        UsersImpl(const UsersImpl & rvalue);
+        UsersImpl & operator=(const UsersImpl & rvalue);
 
-    void            AddToIPIdx(user_iter user);
-    void            DelFromIPIdx(uint32_t ip);
-    bool            FindByIPIdx(uint32_t ip, user_iter & iter) const;
+        void            AddToIPIdx(user_iter user);
+        void            DelFromIPIdx(uint32_t ip);
+        bool            FindByIPIdx(uint32_t ip, user_iter & iter) const;
 
-    bool            FindByNameNonLock(const std::string & login, user_iter * user);
-    bool            FindByNameNonLock(const std::string & login, const_user_iter * user) const;
+        bool            FindByNameNonLock(const std::string & login, user_iter * user);
+        bool            FindByNameNonLock(const std::string & login, const_user_iter * user) const;
 
-    void            RealDelUser();
-    void            ProcessActions();
+        void            RealDelUser();
+        void            ProcessActions();
 
-    void            AddUserIntoIndexes(user_iter user);
-    void            DelUserFromIndexes(user_iter user);
+        void            AddUserIntoIndexes(user_iter user);
+        void            DelUserFromIndexes(user_iter user);
 
-    void            Run(std::stop_token token);
-    void            NewMinute(const struct tm & t);
-    void            NewDay(const struct tm & t);
-    void            DayResetTraff(const struct tm & t);
+        void            Run(std::stop_token token);
+        void            NewMinute(const struct tm & t);
+        void            NewDay(const struct tm & t);
+        void            DayResetTraff(const struct tm & t);
 
-    bool            TimeToWriteDetailStat(const struct tm & t);
+        bool            TimeToWriteDetailStat(const struct tm & t);
 
-    std::list<UserImpl>                  users;
-    std::list<USER_TO_DEL>                usersToDelete;
+        std::list<UserImpl>                  users;
+        std::list<USER_TO_DEL>                usersToDelete;
 
-    std::map<uint32_t, user_iter>         ipIndex;
-    std::map<std::string, user_iter>      loginIndex;
+        std::map<uint32_t, user_iter>         ipIndex;
+        std::map<std::string, user_iter>      loginIndex;
 
-    SettingsImpl *     settings;
-    Tariffs *           m_tariffs;
-    Services &          m_services;
-    Store *             m_store;
-    const Admin&        m_sysAdmin;
-    Logger &        WriteServLog;
+        SettingsImpl *     settings;
+        Tariffs *           m_tariffs;
+        Services &          m_services;
+        Store *             m_store;
+        const Admin&        m_sysAdmin;
+        Logger &        WriteServLog;
 
-    bool                isRunning;
+        bool                isRunning;
 
-    mutable std::mutex      m_mutex;
-    std::jthread            m_thread;
-    mutable unsigned int    handle;
+        mutable std::mutex      m_mutex;
+        std::jthread            m_thread;
+        mutable unsigned int    handle;
 
-    mutable std::map<unsigned int, user_iter>  searchDescriptors;
+        mutable std::map<unsigned int, user_iter>  searchDescriptors;
 
-    std::set<NotifierBase<UserPtr>*> onAddNotifiers;
-    std::set<NotifierBase<UserPtr>*> onDelNotifiers;
-    std::set<NotifierBase<UserImplPtr>*> onAddNotifiersImpl;
-    std::set<NotifierBase<UserImplPtr>*> onDelNotifiersImpl;
+        Subscriptions<UserImplPtr> m_onAddImplCallbacks;
+        Subscriptions<UserImplPtr> m_onDelImplCallbacks;
 };
 
 }
diff --git a/tests/test_subscriptions.cpp b/tests/test_subscriptions.cpp
index ebc3c920..50780927 100644
--- a/tests/test_subscriptions.cpp
+++ b/tests/test_subscriptions.cpp
@@ -10,6 +10,9 @@
 #include <boost/test/unit_test.hpp>
 #pragma GCC diagnostic pop
 
+using STG::Subscriptions;
+using STG::ScopedConnection;
+
 namespace
 {
 
@@ -37,19 +40,19 @@ struct Receiver
 
 }
 
-BOOST_AUTO_TEST_SUITE(Subscriptions)
+BOOST_AUTO_TEST_SUITE(TestSubscriptions)
 
 BOOST_AUTO_TEST_CASE(Construction)
 {
-    STG::Subscriptions<> nullary;
+    Subscriptions<> nullary;
     BOOST_CHECK(nullary.empty());
     BOOST_CHECK_EQUAL(nullary.size(), 0);
 
-    STG::Subscriptions<uint8_t> unary;
+    Subscriptions<uint8_t> unary;
     BOOST_CHECK(unary.empty());
     BOOST_CHECK_EQUAL(unary.size(), 0);
 
-    STG::Subscriptions<uint8_t, std::string> binary;
+    Subscriptions<uint8_t, std::string> binary;
     BOOST_CHECK(binary.empty());
     BOOST_CHECK_EQUAL(binary.size(), 0);
 }
@@ -57,10 +60,10 @@ BOOST_AUTO_TEST_CASE(Construction)
 BOOST_AUTO_TEST_CASE(AddingAndRemoving)
 {
     Receiver r;
-    STG::Subscriptions<> nullary;
+    Subscriptions<> nullary;
     {
-        auto c1 = nullary.add(r, &Receiver::r0);
-        auto c2 = nullary.add([&r](){ r.r0(); });
+        ScopedConnection c1 = nullary.add(r, &Receiver::r0);
+        ScopedConnection c2 = nullary.add([&r](){ r.r0(); });
 
         BOOST_CHECK(!nullary.empty());
         BOOST_CHECK_EQUAL(nullary.size(), 2);
@@ -73,10 +76,10 @@ BOOST_AUTO_TEST_CASE(AddingAndRemoving)
     BOOST_CHECK(nullary.empty());
     BOOST_CHECK_EQUAL(nullary.size(), 0);
 
-    STG::Subscriptions<uint8_t> unary;
+    Subscriptions<uint8_t> unary;
     {
-        auto c1 = unary.add(r, &Receiver::r1);
-        auto c2 = unary.add([&r](const auto& v){ r.r1(v); });
+        ScopedConnection c1 = unary.add(r, &Receiver::r1);
+        ScopedConnection c2 = unary.add([&r](const auto& v){ r.r1(v); });
 
         BOOST_CHECK(!unary.empty());
         BOOST_CHECK_EQUAL(unary.size(), 2);
@@ -89,10 +92,10 @@ BOOST_AUTO_TEST_CASE(AddingAndRemoving)
     BOOST_CHECK(unary.empty());
     BOOST_CHECK_EQUAL(unary.size(), 0);
 
-    STG::Subscriptions<uint8_t, std::string> binary;
+    Subscriptions<uint8_t, std::string> binary;
     {
-        auto c1 = binary.add(r, &Receiver::r2);
-        auto c2 = binary.add([&r](const auto& v1, const auto& v2){ r.r2(v1, v2); });
+        ScopedConnection c1 = binary.add(r, &Receiver::r2);
+        ScopedConnection c2 = binary.add([&r](const auto& v1, const auto& v2){ r.r2(v1, v2); });
 
         BOOST_CHECK(!binary.empty());
         BOOST_CHECK_EQUAL(binary.size(), 2);
@@ -117,10 +120,10 @@ BOOST_AUTO_TEST_CASE(Notification)
     BOOST_CHECK_EQUAL(r.value1R2, 0);
     BOOST_CHECK_EQUAL(r.value2R2, "");
 
-    STG::Subscriptions<> nullary;
+    Subscriptions<> nullary;
     {
-        auto c1 = nullary.add(r, &Receiver::r0);
-        auto c2 = nullary.add([&r](){ r.r0(); });
+        ScopedConnection c1 = nullary.add(r, &Receiver::r0);
+        ScopedConnection c2 = nullary.add([&r](){ r.r0(); });
 
         nullary.notify();
 
@@ -151,10 +154,10 @@ BOOST_AUTO_TEST_CASE(Notification)
     BOOST_CHECK_EQUAL(r.value1R2, 0);
     BOOST_CHECK_EQUAL(r.value2R2, "");
 
-    STG::Subscriptions<uint8_t> unary;
+    Subscriptions<uint8_t> unary;
     {
-        auto c1 = unary.add(r, &Receiver::r1);
-        auto c2 = unary.add([&r](const auto& v){ r.r1(v); });
+        ScopedConnection c1 = unary.add(r, &Receiver::r1);
+        ScopedConnection c2 = unary.add([&r](const auto& v){ r.r1(v); });
 
         unary.notify(42);
 
@@ -185,10 +188,10 @@ BOOST_AUTO_TEST_CASE(Notification)
     BOOST_CHECK_EQUAL(r.value1R2, 0);
     BOOST_CHECK_EQUAL(r.value2R2, "");
 
-    STG::Subscriptions<uint8_t, std::string> binary;
+    Subscriptions<uint8_t, std::string> binary;
     {
-        auto c1 = binary.add(r, &Receiver::r2);
-        auto c2 = binary.add([&r](const auto& v1, const auto& v2){ r.r2(v1, v2); });
+        ScopedConnection c1 = binary.add(r, &Receiver::r2);
+        ScopedConnection c2 = binary.add([&r](const auto& v1, const auto& v2){ r.r2(v1, v2); });
 
         binary.notify(42, "Douglas");
 
diff --git a/tests/testusers.h b/tests/testusers.h
index adc0507e..c2f5da34 100644
--- a/tests/testusers.h
+++ b/tests/testusers.h
@@ -17,12 +17,6 @@ struct TestUsers : public STG::Users
     bool TariffInUse(const std::string& /*tariffName*/) const override
     { return -1; }
 
-    void AddNotifierUserAdd(STG::NotifierBase<UserPtr>* /*notifier*/) override {}
-    void DelNotifierUserAdd(STG::NotifierBase<UserPtr>* /*notifier*/) override {}
-
-    void AddNotifierUserDel(STG::NotifierBase<UserPtr>* /*notifier*/) override {}
-    void DelNotifierUserDel(STG::NotifierBase<UserPtr>* /*notifier*/) override {}
-
     int  Add(const std::string& /*login*/, const STG::Admin* /*admin*/) override
     { return 0; }
     void Del(const std::string& /*login*/, const STG::Admin* /*admin*/) override {}