From d346e05e12c0a8e62417c5d9603edf797b92fce7 Mon Sep 17 00:00:00 2001 From: Ronald Landheer-Cieslak Date: Sat, 1 Dec 2007 13:41:16 -0500 Subject: [PATCH] Laying the ground works for garbage-collecting dead connections Dead connections can now be identified by the fact that their (new) status flag is no longer good__, and are disconnected from the ConnectionHandler when an exception is thrown by a data handler. Now, all that's left to do is to garbage-collect the instance in the connection handler, which is the owner of the connection object itself. --- lib/Spin/Connection.cpp | 32 ++++++++++++++++++++++++++++---- lib/Spin/Connection.h | 12 ++++++++++++ lib/Spin/Private/ConnectionHandler.cpp | 30 ++++++++++++++++++++++-------- lib/Spin/Private/ConnectionHandler.h | 3 ++- 4 files changed, 64 insertions(+), 13 deletions(-) diff --git a/lib/Spin/Connection.cpp b/lib/Spin/Connection.cpp index a25274c..bce867f 100755 --- a/lib/Spin/Connection.cpp +++ b/lib/Spin/Connection.cpp @@ -19,9 +19,11 @@ namespace Spin Connection::Connection(const Connection & connection) : bio_(connection.bio_), data_handler_(connection.data_handler_), - attributes_(connection.attributes_) + attributes_(connection.attributes_), + status_(connection.status_) { connection.bio_ = 0; + connection.status_ |= done__; } Connection::~Connection() @@ -36,6 +38,13 @@ namespace Spin std::pair< std::size_t, int > Connection::write(const std::vector< char > & data) { assert(data.size() < INT_MAX); + if (status_ != good__) + { + status_ |= error__; + throw std::runtime_error("Connection no longer usable"); + } + else + { /* all is well */ } int written(0); int reason(no_error__); @@ -47,7 +56,10 @@ namespace Spin else if (written < static_cast< int >(data.size())) { if (!BIO_should_retry(bio_)) + { + status_ = done__; throw std::runtime_error("Permanent error - sorry it didn't work out"); + } else { /* non-permanent error */ } reason |= should_retry__; @@ -71,6 +83,14 @@ namespace Spin std::pair< std::size_t, int > Connection::read(std::vector< char > & buffer) { + if (status_ != good__) + { + status_ |= error__; + throw std::runtime_error("Connection no longer usable"); + } + else + { /* all is well */ } + std::size_t bytes_read_into_buffer(0); int reason(no_error__); bool continue_until_retry(false); @@ -90,7 +110,10 @@ read_entry_point: { bytes_read_into_buffer += bytes_read; if (!BIO_should_retry(bio_) && bytes_read <= 0) + { + status_ = done__; throw std::runtime_error("Permanent error - sorry it didn't work out"); + } else { reason |= should_retry__; @@ -161,7 +184,7 @@ read_entry_point: Details::Address Connection::getPeerAddress() const { - if (!bio_) + if (!bio_ || status_ != good__) throw std::logic_error("No connection"); else { /* carry on */ } @@ -175,12 +198,13 @@ read_entry_point: Connection::Connection(::BIO * bio) : bio_(bio), data_handler_(0), - attributes_(max_attribute_count__) + attributes_(max_attribute_count__), + status_(good__) { /* no-op */ } void Connection::onDataReady_() { - if (data_handler_) + if (data_handler_ && status_ == good__) (*data_handler_)(*this); else { /* no-op */ } diff --git a/lib/Spin/Connection.h b/lib/Spin/Connection.h index f52a71e..11aad0e 100755 --- a/lib/Spin/Connection.h +++ b/lib/Spin/Connection.h @@ -20,6 +20,12 @@ namespace Spin class SPIN_API Connection { public : + enum Status + { + good__ = 0, + error__ = 1, + done__ = 2 + }; enum Reason { no_error__ = 0, // no errors, all is well @@ -49,6 +55,11 @@ namespace Spin Details::Address getPeerAddress() const; + int getStatus() const + { + return status_; + } + private : enum { default_read_block_size__ = 4096, max_attribute_count__ = 8 }; // Not Assignable @@ -60,6 +71,7 @@ namespace Spin mutable ::BIO * bio_; Handlers::NewDataHandler * data_handler_; std::vector< boost::any > attributes_; + mutable int status_; static unsigned long next_attribute_index__; diff --git a/lib/Spin/Private/ConnectionHandler.cpp b/lib/Spin/Private/ConnectionHandler.cpp index 3c69f8a..e7fa03c 100755 --- a/lib/Spin/Private/ConnectionHandler.cpp +++ b/lib/Spin/Private/ConnectionHandler.cpp @@ -78,7 +78,7 @@ namespace { return apply_to_2nd_impl_< Pair, OpType >(op); } -#if 0 + template < typename Tuple, typename OpType, int n > struct apply_to_nth_impl_ { @@ -90,7 +90,7 @@ namespace result_type operator()(Tuple tuple) const { - return op_(get< n >(tuple)); + return op_(boost::tuples::get< n >(tuple)); } OpType op_; @@ -101,7 +101,6 @@ namespace { return apply_to_nth_impl_< Tuple, OpType, n >(op); } -#endif } namespace Spin { @@ -232,27 +231,42 @@ namespace Spin } else { - typedef std::list< NotificationCallback > CallbacksToCall; + typedef std::list< boost::tuples::tuple< int, NotificationCallback > > CallbacksToCall; CallbacksToCall callbacks_to_call; { CallbacksLock_::scoped_lock lock(callbacks_lock_); - for (Callbacks_::const_iterator curr(callbacks_.begin()); curr != callbacks_.end(); ++curr) + Callbacks_::const_iterator begin(callbacks_.begin()); + for (Callbacks_::const_iterator curr(begin); curr != callbacks_.end(); ++curr) { if (FD_ISSET(boost::tuples::get<0>(*curr), &read_fds)) - callbacks_to_call.push_back(boost::tuples::get<1>(*curr)); + callbacks_to_call.push_back(boost::make_tuple(boost::tuples::get<0>(*curr), boost::tuples::get<1>(*curr))); else { /* fd not set */ } } } // lock ends here + typedef std::list< int > CallbacksToRemove; + CallbacksToRemove callbacks_to_remove; for (CallbacksToCall::const_iterator curr(callbacks_to_call.begin()); curr != callbacks_to_call.end(); ++curr) { try { - (*curr)(); + (boost::tuples::get<1>(*curr))(); } catch (...) { - // FIXME: do something: log an error message, tag the connection as useless - anything! + callbacks_to_remove.push_back(boost::tuples::get<0>(*curr)); + } + } + if (!callbacks_to_remove.empty()) + { + CallbacksLock_::scoped_lock lock(callbacks_lock_); + for (CallbacksToRemove::const_iterator curr(callbacks_to_remove.begin()); curr != callbacks_to_remove.end(); ++curr) + { + Callbacks_::iterator where(std::find_if(callbacks_.begin(), callbacks_.end(), apply_to_nth< Callbacks_::value_type, 0 >(std::bind2nd(std::equal_to< int >(), *curr)))); + if (where != callbacks_.end()) + boost::tuples::get<2>(*where) = pending_detachment__; + else + { /* The callback is no longer there anyway */ } } } } diff --git a/lib/Spin/Private/ConnectionHandler.h b/lib/Spin/Private/ConnectionHandler.h index 7f6f3a8..3a28261 100755 --- a/lib/Spin/Private/ConnectionHandler.h +++ b/lib/Spin/Private/ConnectionHandler.h @@ -32,7 +32,8 @@ namespace Spin ConnectionHandler(const ConnectionHandler&); ConnectionHandler & operator=(const ConnectionHandler&); - typedef std::list< boost::tuple< int /* file_descriptor */, NotificationCallback /* callback */, AttachmentState_ /* state */ > > Callbacks_; + typedef boost::tuple< int /* file_descriptor */, NotificationCallback /* callback */, AttachmentState_ /* state */ > Callback_; + typedef std::list< Callback_ > Callbacks_; typedef boost::recursive_mutex CallbacksLock_; ConnectionHandler(); -- 2.11.4.GIT