# HG changeset patch # User David Demelier # Date 1475606584 -7200 # Node ID d10274d8393c9a8c9980d03a55c8bddc1ee46bbe # Parent 685a492c39bad5457663df0fa4d1a930dbdc83dd Irccd: use a Pollable concept instead of inheritance, closes #566 diff -r 685a492c39ba -r d10274d8393c lib/irccd/client.hpp --- a/lib/irccd/client.hpp Thu Sep 29 20:33:11 2016 +0200 +++ b/lib/irccd/client.hpp Tue Oct 04 20:43:04 2016 +0200 @@ -28,9 +28,10 @@ #include #include +#include + #include "net.hpp" #include "signals.hpp" -#include "pollable.hpp" namespace irccd { @@ -65,7 +66,7 @@ * | | * ------------------------------------+ */ -class Client : public Pollable { +class Client { public: /** * \brief The current connection state. @@ -258,7 +259,7 @@ * \param out the output set * \param max the maximum file descriptor */ - void prepare(fd_set &in, fd_set &out, net::Handle &max) override; + virtual void prepare(fd_set &in, fd_set &out, net::Handle &max); /** * Do some I/O using the protected recv and send functions. @@ -266,7 +267,7 @@ * \param in the input set * \param out the output set */ - void sync(fd_set &in, fd_set &out) override; + virtual void sync(fd_set &in, fd_set &out); }; /** diff -r 685a492c39ba -r d10274d8393c lib/irccd/irccd.cpp --- a/lib/irccd/irccd.cpp Thu Sep 29 20:33:11 2016 +0200 +++ b/lib/irccd/irccd.cpp Tue Oct 04 20:43:04 2016 +0200 @@ -26,6 +26,7 @@ #include "service-rule.hpp" #include "service-server.hpp" #include "service-transport.hpp" +#include "util.hpp" using namespace std; using namespace std::placeholders; @@ -42,9 +43,6 @@ , m_moduleService(std::make_shared()) , m_plugins(std::make_shared(*this)) { - m_services.push_back(m_interruptService); - m_services.push_back(m_servers); - m_services.push_back(m_transports); } void Irccd::post(std::function ev) noexcept @@ -58,21 +56,19 @@ void Irccd::run() { while (m_running) { - poll(250); + util::poller::poll(250, *m_interruptService, *m_servers, *m_transports); dispatch(); } } void Irccd::prepare(fd_set &in, fd_set &out, net::Handle &max) { - for (const auto &service : m_services) - service->prepare(in, out, max); + util::poller::prepare(in, out, max, *m_interruptService, *m_servers, *m_transports); } void Irccd::sync(fd_set &in, fd_set &out) { - for (const auto &service : m_services) - service->sync(in, out); + util::poller::sync(in, out, *m_interruptService, *m_servers, *m_transports); } void Irccd::dispatch() diff -r 685a492c39ba -r d10274d8393c lib/irccd/irccd.hpp --- a/lib/irccd/irccd.hpp Thu Sep 29 20:33:11 2016 +0200 +++ b/lib/irccd/irccd.hpp Tue Oct 04 20:43:04 2016 +0200 @@ -30,7 +30,7 @@ #include #include -#include "pollable.hpp" +#include "net.hpp" #include "sysconfig.hpp" /** @@ -42,7 +42,6 @@ class InterruptService; class ModuleService; class PluginService; -class Pollable; class RuleService; class ServerService; class TransportService; @@ -51,7 +50,7 @@ * \class Irccd * \brief Irccd main instance. */ -class Irccd : public Pollable { +class Irccd { private: // Main loop stuff. std::atomic m_running{true}; @@ -66,7 +65,6 @@ std::shared_ptr m_ruleService; std::shared_ptr m_moduleService; std::shared_ptr m_plugins; - std::vector> m_services; // Not copyable and not movable because services has references to irccd. Irccd(const Irccd &) = delete; @@ -82,16 +80,6 @@ IRCCD_EXPORT Irccd(); /** - * Add a generic service. - * - * \param service the service - */ - inline void addService(std::shared_ptr service) - { - m_services.push_back(std::move(service)); - } - - /** * Access the command service. * * \return the service @@ -158,7 +146,7 @@ * \param out the output set * \param max the maximum handle */ - IRCCD_EXPORT void prepare(fd_set &in, fd_set &out, net::Handle &max) override; + IRCCD_EXPORT void prepare(fd_set &in, fd_set &out, net::Handle &max); /** * Synchronize the services. @@ -166,7 +154,7 @@ * \param in the input set * \param out the output set */ - IRCCD_EXPORT void sync(fd_set &in, fd_set &out) override; + IRCCD_EXPORT void sync(fd_set &in, fd_set &out); /** * Add an event to the queue. This will immediately signals the event loop diff -r 685a492c39ba -r d10274d8393c lib/irccd/irccdctl.cpp --- a/lib/irccd/irccdctl.cpp Thu Sep 29 20:33:11 2016 +0200 +++ b/lib/irccd/irccdctl.cpp Tue Oct 04 20:43:04 2016 +0200 @@ -385,7 +385,7 @@ ElapsedTimer timer; while (m_messages.empty() && m_connection->isConnected() && timer.elapsed() < m_timeout) - m_connection->poll(); + util::poller::poll(250, *m_connection); if (m_messages.empty()) return nlohmann::json(); @@ -425,7 +425,7 @@ ElapsedTimer timer; while (m_events.empty() && m_connection->isConnected() && timer.elapsed() < m_timeout) - m_connection->poll(); + util::poller::poll(250, *m_connection); if (m_events.empty()) return nullptr; diff -r 685a492c39ba -r d10274d8393c lib/irccd/pollable.hpp --- a/lib/irccd/pollable.hpp Thu Sep 29 20:33:11 2016 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,117 +0,0 @@ -/* - * pollable.hpp -- pollable object - * - * Copyright (c) 2013-2016 David Demelier - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef IRCCD_POLLABLE_HPP -#define IRCCD_POLLABLE_HPP - -/** - * \file service.hpp - * \brief Pollable object. - */ - -#include "net.hpp" -#include "util.hpp" - -namespace irccd { - -/** - * \brief Pollable object. - * - * This class can be used to prepare an object into a select(2) system call. - * - * The primary use case of these objects is to be polled in the main loop while - * being generic. - * - * To use the pollable objects: - * - * 1. Create two fd_set, one for input and one for output. Don't forget to - * initialize them using FD_ZERO. - * - * 2. For all of your pollable objects, call the prepare function and pass the - * input and output sets. The max handle is usually the pollable socket. - * - * 3. Do your select(2) call using the input, output and socket handle and your - * desired timeout. - * - * 4. For all of your pollable objects, call the sync function and pass the - * input and output sets. - * - * Pollable objects are usually implemented using asynchronous signals defined - * in signals.hpp file. - */ -class Pollable { -public: - /** - * Default constructor. - */ - Pollable() noexcept = default; - - /** - * Virtual destructor defaulted. - */ - virtual ~Pollable() noexcept = default; - - /** - * Prepare the input and output set. - * - * \param in the input set - * \param out the output set - * \param max the handle to update - */ - virtual void prepare(fd_set &in, fd_set &out, net::Handle &max) - { - util::unused(in, out, max); - } - - /** - * Synchronize with result sets. - * - * \param in the input set - * \param out the output set - */ - virtual void sync(fd_set &in, fd_set &out) - { - util::unused(in, out); - } - - /** - * Convenient function for polling events with a timeout. - * - * \param timeout the timeout in milliseconds - */ - virtual void poll(int timeout = -1) - { - fd_set in, out; - timeval tv = {0, timeout * 1000}; - - FD_ZERO(&in); - FD_ZERO(&out); - - net::Handle max = 0; - - prepare(in, out, max); - - // Timeout or error are discarded. - if (::select(max + 1, &in, &out, nullptr, timeout < 0 ? nullptr : &tv) > 0) - sync(in, out); - } -}; - -} // !irccd - -#endif // !IRCCD_POLLABLE_HPP diff -r 685a492c39ba -r d10274d8393c lib/irccd/service-interrupt.hpp --- a/lib/irccd/service-interrupt.hpp Thu Sep 29 20:33:11 2016 +0200 +++ b/lib/irccd/service-interrupt.hpp Tue Oct 04 20:43:04 2016 +0200 @@ -24,7 +24,7 @@ * \brief Interrupt irccd event loop. */ -#include "pollable.hpp" +#include "net.hpp" namespace irccd { @@ -32,7 +32,7 @@ * \brief Interrupt irccd event loop. * \ingroup services */ -class InterruptService : public Pollable { +class InterruptService { private: net::TcpSocket m_in; net::TcpSocket m_out; @@ -48,12 +48,12 @@ /** * \copydoc Service::prepare */ - IRCCD_EXPORT void prepare(fd_set &in, fd_set &out, net::Handle &max) override; + IRCCD_EXPORT void prepare(fd_set &in, fd_set &out, net::Handle &max); /** * \copydoc Service::sync */ - IRCCD_EXPORT void sync(fd_set &in, fd_set &out) override; + IRCCD_EXPORT void sync(fd_set &in, fd_set &out); /** * Request interruption. diff -r 685a492c39ba -r d10274d8393c lib/irccd/service-server.hpp --- a/lib/irccd/service-server.hpp Thu Sep 29 20:33:11 2016 +0200 +++ b/lib/irccd/service-server.hpp Tue Oct 04 20:43:04 2016 +0200 @@ -29,7 +29,6 @@ #include #include "server.hpp" -#include "pollable.hpp" namespace irccd { @@ -39,7 +38,7 @@ * \brief Manage IRC servers. * \ingroup services */ -class ServerService : public Pollable { +class ServerService { private: Irccd &m_irccd; std::vector> m_servers; @@ -70,12 +69,12 @@ /** * \copydoc Service::prepare */ - IRCCD_EXPORT void prepare(fd_set &in, fd_set &out, net::Handle &max) override; + IRCCD_EXPORT void prepare(fd_set &in, fd_set &out, net::Handle &max); /** * \copydoc Service::sync */ - IRCCD_EXPORT void sync(fd_set &in, fd_set &out) override; + IRCCD_EXPORT void sync(fd_set &in, fd_set &out); /** * Get the list of servers diff -r 685a492c39ba -r d10274d8393c lib/irccd/service-transport.hpp --- a/lib/irccd/service-transport.hpp Thu Sep 29 20:33:11 2016 +0200 +++ b/lib/irccd/service-transport.hpp Tue Oct 04 20:43:04 2016 +0200 @@ -26,8 +26,6 @@ #include -#include "pollable.hpp" - namespace irccd { class TransportServer; @@ -37,7 +35,7 @@ * \brief manage transport servers and clients. * \ingroup services */ -class TransportService : public Pollable { +class TransportService { private: Irccd &m_irccd; @@ -58,12 +56,12 @@ /** * \copydoc Service::prepare */ - IRCCD_EXPORT void prepare(fd_set &in, fd_set &out, net::Handle &max) override; + IRCCD_EXPORT void prepare(fd_set &in, fd_set &out, net::Handle &max); /** * \copydoc Service::sync */ - IRCCD_EXPORT void sync(fd_set &in, fd_set &out) override; + IRCCD_EXPORT void sync(fd_set &in, fd_set &out); /** * Add a transport server. diff -r 685a492c39ba -r d10274d8393c lib/irccd/util.hpp --- a/lib/irccd/util.hpp Thu Sep 29 20:33:11 2016 +0200 +++ b/lib/irccd/util.hpp Tue Oct 04 20:43:04 2016 +0200 @@ -38,6 +38,7 @@ #include #include +#include "net.hpp" #include "sysconfig.hpp" namespace irccd { @@ -599,6 +600,93 @@ } // !json +/** + * \brief Miscellaneous utilities for Pollable objects + */ +namespace poller { + +/** + * \cond HIDDEN_SYMBOLS + */ + +inline void prepare(fd_set &, fd_set &, net::Handle &) noexcept +{ +} + +/** + * \endcond + */ + +/** + * Call prepare function for every Pollable objects. + * + * \param in the input set + * \param out the output set + * \param max the maximum handle + * \param first the first Pollable object + * \param rest the additional Pollable objects + */ +template +inline void prepare(fd_set &in, fd_set &out, net::Handle &max, Pollable &first, Rest&... rest) +{ + first.prepare(in, out, max); + prepare(in, out, max, rest...); +} + +/** + * \cond HIDDEN_SYMBOLS + */ + +inline void sync(fd_set &, fd_set &) noexcept +{ +} + +/** + * \endcond + */ + +/** + * Call sync function for every Pollable objects. + * + * \param in the input set + * \param out the output set + * \param first the first Pollable object + * \param rest the additional Pollable objects + */ +template +inline void sync(fd_set &in, fd_set &out, Pollable &first, Rest&... rest) +{ + first.sync(in, out); + sync(in, out, rest...); +} + +/** + * Prepare and sync Pollable objects. + * + * \param timeout the timeout in milliseconds (< 0 means forever) + * \param first the the first Pollable object + * \param rest the additional Pollable objects + */ +template +void poll(int timeout, Pollable &first, Rest&... rest) +{ + fd_set in, out; + timeval tv = {0, timeout * 1000}; + + FD_ZERO(&in); + FD_ZERO(&out); + + net::Handle max = 0; + + prepare(in, out, max, first, rest...); + + // Timeout or error are discarded. + if (::select(max + 1, &in, &out, nullptr, timeout < 0 ? nullptr : &tv) > 0) + sync(in, out, first, rest...); +} + +} // !poller + } // !util } // !irccd