2015-11-17 16:33:28 +08:00
|
|
|
/**
|
|
|
|
|
* LibEV.h
|
2016-01-15 21:19:09 +08:00
|
|
|
*
|
2015-11-17 16:33:28 +08:00
|
|
|
* Implementation for the AMQP::TcpHandler that is optimized for libev. You can
|
|
|
|
|
* use this class instead of a AMQP::TcpHandler class, just pass the event loop
|
|
|
|
|
* to the constructor and you're all set
|
2016-01-15 21:19:09 +08:00
|
|
|
*
|
2015-11-17 16:33:28 +08:00
|
|
|
* Compile with: "g++ -std=c++11 libev.cpp -lamqpcpp -lev -lpthread"
|
2016-01-15 21:19:09 +08:00
|
|
|
*
|
2015-11-17 16:33:28 +08:00
|
|
|
* @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com>
|
2018-01-30 17:45:16 +08:00
|
|
|
* @copyright 2015 - 2018 Copernica BV
|
2015-11-17 16:33:28 +08:00
|
|
|
*/
|
|
|
|
|
|
2015-11-17 18:17:21 +08:00
|
|
|
/**
|
|
|
|
|
* Include guard
|
|
|
|
|
*/
|
|
|
|
|
#pragma once
|
|
|
|
|
|
2015-12-30 23:04:34 +08:00
|
|
|
/**
|
|
|
|
|
* Dependencies
|
|
|
|
|
*/
|
|
|
|
|
#include <ev.h>
|
2018-01-30 18:07:27 +08:00
|
|
|
#include "amqpcpp/linux_tcp.h"
|
|
|
|
|
|
2015-11-17 16:33:28 +08:00
|
|
|
/**
|
|
|
|
|
* Set up namespace
|
|
|
|
|
*/
|
|
|
|
|
namespace AMQP {
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Class definition
|
|
|
|
|
*/
|
|
|
|
|
class LibEvHandler : public TcpHandler
|
|
|
|
|
{
|
|
|
|
|
private:
|
2018-11-12 05:54:42 +08:00
|
|
|
/**
|
|
|
|
|
* Internal interface for the object that is being watched
|
|
|
|
|
*/
|
|
|
|
|
class Watchable
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
/**
|
|
|
|
|
* The method that is called when a filedescriptor becomes active
|
|
|
|
|
* @param fd
|
|
|
|
|
* @param events
|
|
|
|
|
*/
|
|
|
|
|
virtual void onActive(int fd, int events) = 0;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Method that is called when the timer expires
|
|
|
|
|
*/
|
|
|
|
|
virtual void onExpired() = 0;
|
|
|
|
|
};
|
|
|
|
|
|
2015-11-17 16:33:28 +08:00
|
|
|
/**
|
|
|
|
|
* Helper class that wraps a libev I/O watcher
|
|
|
|
|
*/
|
|
|
|
|
class Watcher
|
|
|
|
|
{
|
|
|
|
|
private:
|
|
|
|
|
/**
|
|
|
|
|
* The event loop to which it is attached
|
|
|
|
|
* @var struct ev_loop
|
|
|
|
|
*/
|
|
|
|
|
struct ev_loop *_loop;
|
2016-01-15 21:19:09 +08:00
|
|
|
|
2015-11-17 16:33:28 +08:00
|
|
|
/**
|
|
|
|
|
* The actual watcher structure
|
|
|
|
|
* @var struct ev_io
|
|
|
|
|
*/
|
|
|
|
|
struct ev_io _io;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Callback method that is called by libev when a filedescriptor becomes active
|
|
|
|
|
* @param loop The loop in which the event was triggered
|
|
|
|
|
* @param w Internal watcher object
|
|
|
|
|
* @param revents Events triggered
|
|
|
|
|
*/
|
|
|
|
|
static void callback(struct ev_loop *loop, struct ev_io *watcher, int revents)
|
|
|
|
|
{
|
2018-11-12 05:54:42 +08:00
|
|
|
// retrieve the watched object
|
|
|
|
|
Watchable *object = static_cast<Watchable*>(watcher->data);
|
2016-01-15 21:19:09 +08:00
|
|
|
|
2018-11-12 05:54:42 +08:00
|
|
|
// tell the object that its filedescriptor is active
|
|
|
|
|
object->onActive(watcher->fd, revents);
|
2015-11-17 16:33:28 +08:00
|
|
|
}
|
2016-01-15 21:19:09 +08:00
|
|
|
|
2015-11-17 16:33:28 +08:00
|
|
|
public:
|
|
|
|
|
/**
|
|
|
|
|
* Constructor
|
|
|
|
|
* @param loop The current event loop
|
2018-11-12 05:54:42 +08:00
|
|
|
* @param object The object being watched
|
2015-11-17 16:33:28 +08:00
|
|
|
* @param fd The filedescriptor being watched
|
|
|
|
|
* @param events The events that should be monitored
|
|
|
|
|
*/
|
2018-11-12 05:54:42 +08:00
|
|
|
Watcher(struct ev_loop *loop, Watchable *object, int fd, int events) : _loop(loop)
|
2015-11-17 16:33:28 +08:00
|
|
|
{
|
|
|
|
|
// initialize the libev structure
|
|
|
|
|
ev_io_init(&_io, callback, fd, events);
|
2016-01-15 21:19:09 +08:00
|
|
|
|
2018-11-12 05:54:42 +08:00
|
|
|
// store the object in the data "void*"
|
|
|
|
|
_io.data = object;
|
2016-01-15 21:19:09 +08:00
|
|
|
|
2015-11-17 16:33:28 +08:00
|
|
|
// start the watcher
|
|
|
|
|
ev_io_start(_loop, &_io);
|
|
|
|
|
}
|
2016-01-15 21:19:09 +08:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Watchers cannot be copied or moved
|
|
|
|
|
*
|
|
|
|
|
* @param that The object to not move or copy
|
|
|
|
|
*/
|
|
|
|
|
Watcher(Watcher &&that) = delete;
|
|
|
|
|
Watcher(const Watcher &that) = delete;
|
|
|
|
|
|
2015-11-17 16:33:28 +08:00
|
|
|
/**
|
|
|
|
|
* Destructor
|
|
|
|
|
*/
|
|
|
|
|
virtual ~Watcher()
|
|
|
|
|
{
|
|
|
|
|
// stop the watcher
|
|
|
|
|
ev_io_stop(_loop, &_io);
|
|
|
|
|
}
|
2018-11-12 05:54:42 +08:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Check if a filedescriptor is covered by the watcher
|
|
|
|
|
* @param fd
|
|
|
|
|
* @return bool
|
|
|
|
|
*/
|
|
|
|
|
bool contains(int fd) const { return _io.fd == fd; }
|
2016-01-15 21:19:09 +08:00
|
|
|
|
2015-11-17 16:33:28 +08:00
|
|
|
/**
|
|
|
|
|
* Change the events for which the filedescriptor is monitored
|
|
|
|
|
* @param events
|
|
|
|
|
*/
|
|
|
|
|
void events(int events)
|
|
|
|
|
{
|
2016-01-04 23:53:22 +08:00
|
|
|
// stop the watcher if it was active
|
|
|
|
|
ev_io_stop(_loop, &_io);
|
2016-01-15 21:19:09 +08:00
|
|
|
|
2016-01-04 23:53:22 +08:00
|
|
|
// set the events
|
2015-11-17 16:33:28 +08:00
|
|
|
ev_io_set(&_io, _io.fd, events);
|
2016-01-15 21:19:09 +08:00
|
|
|
|
2016-01-04 23:53:22 +08:00
|
|
|
// and restart it
|
|
|
|
|
ev_io_start(_loop, &_io);
|
2015-11-17 16:33:28 +08:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2017-06-16 17:36:34 +08:00
|
|
|
/**
|
2018-11-12 05:54:42 +08:00
|
|
|
* Wrapper around a connection, this will monitor the filedescriptors
|
|
|
|
|
* and run a timer to send out heartbeats
|
2017-06-16 17:36:34 +08:00
|
|
|
*/
|
2018-11-12 05:54:42 +08:00
|
|
|
class Wrapper : private Watchable
|
2017-06-16 17:36:34 +08:00
|
|
|
{
|
|
|
|
|
private:
|
2018-11-12 05:54:42 +08:00
|
|
|
/**
|
|
|
|
|
* The connection that is monitored
|
|
|
|
|
* @var TcpConnection
|
|
|
|
|
*/
|
|
|
|
|
TcpConnection *_connection;
|
|
|
|
|
|
2017-06-16 17:36:34 +08:00
|
|
|
/**
|
|
|
|
|
* The event loop to which it is attached
|
|
|
|
|
* @var struct ev_loop
|
|
|
|
|
*/
|
|
|
|
|
struct ev_loop *_loop;
|
|
|
|
|
|
|
|
|
|
/**
|
2018-11-12 05:54:42 +08:00
|
|
|
* The watcher for the timer
|
2017-06-16 17:36:34 +08:00
|
|
|
* @var struct ev_io
|
|
|
|
|
*/
|
|
|
|
|
struct ev_timer _timer;
|
2018-11-12 05:54:42 +08:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* IO-watchers to monitor filedescriptors
|
|
|
|
|
* @var std::vector
|
|
|
|
|
*/
|
|
|
|
|
std::vector<std::unique_ptr<Watcher>> _watchers;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* When should we send the next heartbeat?
|
|
|
|
|
* @var ev_tstamp
|
|
|
|
|
*/
|
|
|
|
|
ev_tstamp _next;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* When does the connection expire / was the server for a too longer period of time idle?
|
|
|
|
|
* @var ev_tstamp
|
|
|
|
|
*/
|
|
|
|
|
ev_tstamp _expire;
|
|
|
|
|
|
2018-11-13 05:07:48 +08:00
|
|
|
/**
|
|
|
|
|
* Are heartbeats enabled?
|
|
|
|
|
* @var bool
|
|
|
|
|
*/
|
|
|
|
|
bool _enabled = false;
|
|
|
|
|
|
2018-11-12 05:54:42 +08:00
|
|
|
/**
|
|
|
|
|
* Interval between heartbeats
|
|
|
|
|
* @var uint16_t
|
|
|
|
|
*/
|
|
|
|
|
uint16_t _interval;
|
|
|
|
|
|
2017-06-16 17:36:34 +08:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Callback method that is called by libev when the timer expires
|
|
|
|
|
* @param loop The loop in which the event was triggered
|
|
|
|
|
* @param timer Internal timer object
|
|
|
|
|
* @param revents The events that triggered this call
|
|
|
|
|
*/
|
|
|
|
|
static void callback(struct ev_loop *loop, struct ev_timer *timer, int revents)
|
|
|
|
|
{
|
2018-11-12 05:54:42 +08:00
|
|
|
// retrieve the object
|
|
|
|
|
Watchable *object = static_cast<Watchable*>(timer->data);
|
2017-06-16 17:36:34 +08:00
|
|
|
|
2018-11-12 05:54:42 +08:00
|
|
|
// tell the object that it expired
|
|
|
|
|
object->onExpired();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Method that is called when the timer expired
|
|
|
|
|
*/
|
|
|
|
|
virtual void onExpired() override
|
|
|
|
|
{
|
2018-11-13 05:07:48 +08:00
|
|
|
// do nothing if heartbeats are not enabled
|
|
|
|
|
if (!_enabled) return;
|
|
|
|
|
|
2018-11-12 05:54:42 +08:00
|
|
|
// get the current time
|
|
|
|
|
ev_tstamp now = ev_now(_loop);
|
|
|
|
|
|
|
|
|
|
// should we send out a new heartbeat?
|
|
|
|
|
if (now >= _next)
|
|
|
|
|
{
|
|
|
|
|
// send a heartbeat frame
|
|
|
|
|
_connection->heartbeat();
|
|
|
|
|
|
|
|
|
|
// remember when we should send out the next one
|
|
|
|
|
_next += _interval;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// was the server idle for a too longer period of time?
|
|
|
|
|
if (now >= _expire)
|
|
|
|
|
{
|
|
|
|
|
// close the connection with immediate effect (this will destruct the connection)
|
|
|
|
|
_connection->close(true);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// find the earliest thing that expires
|
|
|
|
|
_timer.repeat = std::min(_next, _expire) - now;
|
|
|
|
|
|
|
|
|
|
// restart the timer
|
|
|
|
|
ev_timer_again(_loop, &_timer);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Method that is called when a filedescriptor becomes active
|
|
|
|
|
* @param fd the filedescriptor that is active
|
|
|
|
|
* @param events the events that are active (readable/writable)
|
|
|
|
|
*/
|
|
|
|
|
virtual void onActive(int fd, int events) override
|
|
|
|
|
{
|
|
|
|
|
// if the server is readable, we have some extra time before it expires
|
|
|
|
|
if (events & EV_READ) _expire = ev_now(_loop) + _interval * 2;
|
|
|
|
|
|
|
|
|
|
// pass on to the connection
|
|
|
|
|
_connection->process(fd, events);
|
2017-06-16 17:36:34 +08:00
|
|
|
}
|
2017-06-21 15:35:48 +08:00
|
|
|
|
2017-06-16 17:36:34 +08:00
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
/**
|
|
|
|
|
* Constructor
|
|
|
|
|
* @param loop The current event loop
|
2018-04-12 18:05:22 +08:00
|
|
|
* @param connection The TCP connection
|
|
|
|
|
* @param interval Timer interval
|
2017-06-16 17:36:34 +08:00
|
|
|
*/
|
2018-11-12 05:54:42 +08:00
|
|
|
Wrapper(struct ev_loop *loop, AMQP::TcpConnection *connection, uint16_t timeout) :
|
|
|
|
|
_connection(connection),
|
|
|
|
|
_loop(loop),
|
|
|
|
|
_next(ev_now(loop) + timeout),
|
|
|
|
|
_expire(ev_now(loop) + timeout * 2),
|
|
|
|
|
_interval(timeout)
|
2017-06-16 17:36:34 +08:00
|
|
|
{
|
2018-11-12 05:54:42 +08:00
|
|
|
// store the object in the data "void*"
|
|
|
|
|
_timer.data = this;
|
2017-06-21 15:35:48 +08:00
|
|
|
|
2017-06-16 17:36:34 +08:00
|
|
|
// initialize the libev structure
|
2018-11-12 05:54:42 +08:00
|
|
|
ev_timer_init(&_timer, callback, timeout, timeout);
|
2018-04-12 18:05:22 +08:00
|
|
|
|
2018-11-12 19:12:04 +08:00
|
|
|
// start the timer (this is the time that we reserve for setting up the connection)
|
|
|
|
|
ev_timer_start(_loop, &_timer);
|
|
|
|
|
|
2018-04-12 18:05:22 +08:00
|
|
|
// the timer should not keep the event loop active
|
|
|
|
|
ev_unref(_loop);
|
2017-06-16 17:36:34 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Watchers cannot be copied or moved
|
|
|
|
|
*
|
|
|
|
|
* @param that The object to not move or copy
|
|
|
|
|
*/
|
2018-11-12 05:54:42 +08:00
|
|
|
Wrapper(Wrapper &&that) = delete;
|
|
|
|
|
Wrapper(const Wrapper &that) = delete;
|
2017-06-16 17:36:34 +08:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Destructor
|
|
|
|
|
*/
|
2018-11-12 05:54:42 +08:00
|
|
|
virtual ~Wrapper()
|
2017-06-16 17:36:34 +08:00
|
|
|
{
|
2018-04-12 18:05:22 +08:00
|
|
|
// restore loop refcount
|
|
|
|
|
ev_ref(_loop);
|
|
|
|
|
|
2017-06-16 17:36:34 +08:00
|
|
|
// stop the timer
|
2018-04-12 18:05:22 +08:00
|
|
|
ev_timer_stop(_loop, &_timer);
|
2017-06-16 17:36:34 +08:00
|
|
|
}
|
2018-04-12 18:05:22 +08:00
|
|
|
|
2018-11-12 05:54:42 +08:00
|
|
|
/**
|
2018-11-12 05:58:13 +08:00
|
|
|
* Start the timer (and expose the interval)
|
2018-11-12 05:54:42 +08:00
|
|
|
* @return uint16_t
|
|
|
|
|
*/
|
2018-11-12 05:58:13 +08:00
|
|
|
uint16_t start()
|
2018-11-12 05:54:42 +08:00
|
|
|
{
|
2018-11-13 05:07:48 +08:00
|
|
|
// remember that heartbeats are enabled
|
|
|
|
|
_enabled = true;
|
|
|
|
|
|
2018-11-12 19:12:04 +08:00
|
|
|
// expose the interval (the timer is already running, so we do not have to explicitly start it)
|
2018-11-12 05:54:42 +08:00
|
|
|
return _interval;
|
|
|
|
|
}
|
|
|
|
|
|
2017-06-16 17:36:34 +08:00
|
|
|
/**
|
2018-04-12 18:05:22 +08:00
|
|
|
* Check if the timer is associated with a certain connection
|
2017-06-16 17:36:34 +08:00
|
|
|
* @param connection
|
2018-04-12 18:05:22 +08:00
|
|
|
* @return bool
|
2017-06-16 17:36:34 +08:00
|
|
|
*/
|
2018-04-12 18:05:22 +08:00
|
|
|
bool contains(const AMQP::TcpConnection *connection) const
|
2017-06-16 17:36:34 +08:00
|
|
|
{
|
2018-11-12 05:54:42 +08:00
|
|
|
// compare the connections
|
|
|
|
|
return _connection == connection;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Monitor a filedescriptor
|
|
|
|
|
* @param fd
|
|
|
|
|
* @param events
|
|
|
|
|
*/
|
|
|
|
|
void monitor(int fd, int events)
|
|
|
|
|
{
|
|
|
|
|
// should we remove?
|
|
|
|
|
if (events == 0)
|
|
|
|
|
{
|
|
|
|
|
// remove the io-watcher
|
|
|
|
|
_watchers.erase(std::remove_if(_watchers.begin(), _watchers.end(), [fd](const std::unique_ptr<Watcher> &watcher) -> bool {
|
|
|
|
|
|
|
|
|
|
// compare filedescriptors
|
|
|
|
|
return watcher->contains(fd);
|
|
|
|
|
}), _watchers.end());
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// look in the array for this filedescriptor
|
|
|
|
|
for (auto &watcher : _watchers)
|
|
|
|
|
{
|
|
|
|
|
// do we have a match?
|
2018-11-12 05:58:13 +08:00
|
|
|
if (watcher->contains(fd)) return watcher->events(events);
|
2018-11-12 05:54:42 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// we should monitor a new filedescriptor
|
|
|
|
|
_watchers.emplace_back(new Watcher(_loop, this, fd, events));
|
|
|
|
|
}
|
2017-06-16 17:36:34 +08:00
|
|
|
}
|
|
|
|
|
};
|
2015-11-17 17:07:33 +08:00
|
|
|
|
2015-11-17 16:33:28 +08:00
|
|
|
/**
|
|
|
|
|
* The event loop
|
|
|
|
|
* @var struct ev_loop*
|
|
|
|
|
*/
|
|
|
|
|
struct ev_loop *_loop;
|
2016-01-15 21:19:09 +08:00
|
|
|
|
2015-11-17 16:33:28 +08:00
|
|
|
/**
|
2018-11-12 05:54:42 +08:00
|
|
|
* Each connection is wrapped
|
|
|
|
|
* @var std::vector
|
2015-11-17 16:33:28 +08:00
|
|
|
*/
|
2018-11-12 05:54:42 +08:00
|
|
|
std::vector<std::unique_ptr<Wrapper>> _wrappers;
|
2016-01-15 21:19:09 +08:00
|
|
|
|
|
|
|
|
|
2018-04-12 18:05:22 +08:00
|
|
|
/**
|
2018-11-12 05:54:42 +08:00
|
|
|
* Lookup a connection-wrapper
|
2018-04-12 18:05:22 +08:00
|
|
|
* @param connection
|
2018-11-12 05:54:42 +08:00
|
|
|
* @return Wrapper
|
2018-04-12 18:05:22 +08:00
|
|
|
*/
|
2018-11-12 05:54:42 +08:00
|
|
|
Wrapper *lookup(TcpConnection *connection)
|
2018-04-12 18:05:22 +08:00
|
|
|
{
|
2018-11-12 05:54:42 +08:00
|
|
|
// look for the appropriate connection
|
|
|
|
|
for (auto &wrapper : _wrappers)
|
|
|
|
|
{
|
|
|
|
|
// do we have a match?
|
|
|
|
|
if (wrapper->contains(connection)) return wrapper.get();
|
|
|
|
|
}
|
2018-04-12 18:05:22 +08:00
|
|
|
|
2018-11-12 05:54:42 +08:00
|
|
|
// we need a new wrapper
|
|
|
|
|
auto *wrapper = new Wrapper(_loop, connection, 60);
|
|
|
|
|
|
|
|
|
|
// add to the wrappers
|
|
|
|
|
_wrappers.emplace_back(wrapper);
|
|
|
|
|
|
|
|
|
|
// done
|
|
|
|
|
return wrapper;
|
2018-04-12 18:05:22 +08:00
|
|
|
}
|
|
|
|
|
|
2015-11-17 16:33:28 +08:00
|
|
|
/**
|
|
|
|
|
* Method that is called by AMQP-CPP to register a filedescriptor for readability or writability
|
|
|
|
|
* @param connection The TCP connection object that is reporting
|
|
|
|
|
* @param fd The filedescriptor to be monitored
|
|
|
|
|
* @param flags Should the object be monitored for readability or writability?
|
|
|
|
|
*/
|
|
|
|
|
virtual void monitor(TcpConnection *connection, int fd, int flags) override
|
|
|
|
|
{
|
2018-11-12 05:54:42 +08:00
|
|
|
// lookup the appropriate wrapper and start monitoring
|
|
|
|
|
lookup(connection)->monitor(fd, flags);
|
2015-11-17 16:33:28 +08:00
|
|
|
}
|
|
|
|
|
|
2017-06-16 17:36:34 +08:00
|
|
|
protected:
|
|
|
|
|
/**
|
|
|
|
|
* Method that is called when the heartbeat frequency is negotiated between the server and the client.
|
|
|
|
|
* @param connection The connection that suggested a heartbeat interval
|
|
|
|
|
* @param interval The suggested interval from the server
|
|
|
|
|
* @return uint16_t The interval to use
|
|
|
|
|
*/
|
|
|
|
|
virtual uint16_t onNegotiate(TcpConnection *connection, uint16_t interval) override
|
|
|
|
|
{
|
2018-11-12 05:54:42 +08:00
|
|
|
// lookup the wrapper
|
2018-11-12 05:58:13 +08:00
|
|
|
return lookup(connection)->start();
|
2017-06-16 17:36:34 +08:00
|
|
|
}
|
|
|
|
|
|
2018-04-12 18:05:22 +08:00
|
|
|
/**
|
2018-11-12 05:54:42 +08:00
|
|
|
* Method that is called when the TCP connection is destructed
|
2018-04-12 18:05:22 +08:00
|
|
|
* @param connection The TCP connection
|
|
|
|
|
*/
|
2018-11-12 05:54:42 +08:00
|
|
|
virtual void onDetached(TcpConnection *connection) override
|
2018-04-12 18:05:22 +08:00
|
|
|
{
|
2018-11-12 05:54:42 +08:00
|
|
|
// remove from the array
|
|
|
|
|
_wrappers.erase(std::remove_if(_wrappers.begin(), _wrappers.end(), [connection](const std::unique_ptr<Wrapper> &wrapper) -> bool {
|
|
|
|
|
return wrapper->contains(connection);
|
|
|
|
|
}), _wrappers.end());
|
2018-04-12 18:05:22 +08:00
|
|
|
}
|
|
|
|
|
|
2015-11-17 16:33:28 +08:00
|
|
|
public:
|
|
|
|
|
/**
|
|
|
|
|
* Constructor
|
|
|
|
|
* @param loop The event loop to wrap
|
|
|
|
|
*/
|
2018-04-12 18:05:22 +08:00
|
|
|
LibEvHandler(struct ev_loop *loop) : _loop(loop) {}
|
2016-01-15 21:19:09 +08:00
|
|
|
|
2015-11-17 16:33:28 +08:00
|
|
|
/**
|
|
|
|
|
* Destructor
|
|
|
|
|
*/
|
|
|
|
|
virtual ~LibEvHandler() = default;
|
|
|
|
|
};
|
2016-01-15 21:19:09 +08:00
|
|
|
|
2015-11-17 16:33:28 +08:00
|
|
|
/**
|
|
|
|
|
* End of namespace
|
|
|
|
|
*/
|
|
|
|
|
}
|