typedef std::set<RegisteredRunner*> Runners;
/// all known runners
static Runners *TheRunners = NULL;
+/// used to avoid re-creating deleted TheRunners after shutdown finished.
+static bool RunnersGone = false;
-/// safely returns registered runners, initializing structures as needed
-static Runners &
-GetRunners()
+/// creates the registered runners container if needed
+/// \return either registered runners (if they should exist) or nil (otherwise)
+static Runners *
+FindRunners()
{
- if (!TheRunners)
+ if (!TheRunners && !RunnersGone)
TheRunners = new Runners;
- return *TheRunners;
+ return TheRunners;
}
-int
-RegisterRunner(RegisteredRunner *rr)
+static inline void
+GetRidOfRunner(RegisteredRunner *rr)
{
- Runners &runners = GetRunners();
- runners.insert(rr);
- return runners.size();
+ if (!dynamic_cast<IndependentRunner*>(rr))
+ delete rr;
+ // else ignore; IndependentRunner
}
-int
-DeregisterRunner(RegisteredRunner *rr)
+bool
+RegisterRunner(RegisteredRunner *rr)
{
- Runners &runners = GetRunners();
- runners.erase(rr);
- return runners.size();
+ if (Runners *runners = FindRunners()) {
+ runners->insert(rr);
+ return true;
+ }
+
+ // past finishShutdown
+ GetRidOfRunner(rr);
+ return false;
}
void
-RunRegistered(const RegisteredRunner::Method &m)
+RunRegistered(const RegisteredRunner::Method &event)
{
- Runners &runners = GetRunners();
- typedef Runners::iterator RRI;
- for (RRI i = runners.begin(); i != runners.end(); ++i)
- ((*i)->*m)();
-
- if (m == &RegisteredRunner::finishShutdown) {
- delete TheRunners;
- TheRunners = NULL;
+ if (Runners *runners = FindRunners()) {
+ // Many things may happen during the loop below. We copy to withstand
+ // runner removal/addition and avoid surprises due to registrations from
+ // parent constructors (with a half-baked "this"!). This copy also
+ // simplifies overall RR logic as it guarantees that registering a
+ // runner during event X loop does not execute runner::X().
+ Runners oldRunners(*runners);
+ for (auto runner: oldRunners) {
+ if (runners->find(runner) != runners->end()) // still registered
+ (runner->*event)();
+ }
}
+
+ if (event != &RegisteredRunner::finishShutdown)
+ return;
+
+ // this is the last event; delete registry-dependent runners (and only them)
+ if (Runners *runners = FindRunners()) {
+ RunnersGone = true;
+ TheRunners = nullptr;
+ // from now on, no runners can be registered or unregistered
+ for (auto runner: *runners)
+ GetRidOfRunner(runner); // leaves a dangling pointer in runners
+ delete runners;
+ }
+}
+
+/* IndependentRunner */
+
+void
+IndependentRunner::unregisterRunner()
+{
+ if (Runners *runners = FindRunners())
+ runners->erase(this);
+ // else it is too late, finishShutdown() has been called
}
bool
/// Called after stopping the main loop and before releasing memory.
/// Meant for quick/basic cleanup that does not require any other modules.
virtual ~RegisteredRunner() {}
- /// exists to simplify caller interface; override the destructor instead
- void finishShutdown() { delete this; }
+
+ /// Meant for cleanup of services needed by the already destroyed objects.
+ virtual void finishShutdown() {}
/// a pointer to one of the above notification methods
typedef void (RegisteredRunner::*Method)();
};
-/// registers a given runner with the given registry and returns registry count
-int RegisterRunner(RegisteredRunner *rr);
-
-/// de-registers a given runner with the given registry and returns registry count
-int DeregisterRunner(RegisteredRunner *rr);
+/// registers a given runner with the given registry and returns true on success
+bool RegisterRunner(RegisteredRunner *rr);
/// Calls a given method of all runners.
/// All runners are destroyed after the finishShutdown() call.
void RunRegistered(const RegisteredRunner::Method &m);
+/// A RegisteredRunner with lifetime determined by forces outside the Registry.
+class IndependentRunner: public RegisteredRunner
+{
+public:
+ virtual ~IndependentRunner() { unregisterRunner(); }
+
+protected:
+ void registerRunner() {RegisterRunner(this);}
+ void unregisterRunner(); ///< unregisters self; safe to call multiple times
+};
+
/// convenience macro to describe/debug the caller and the method being called
#define RunRegisteredHere(m) \
debugs(1, 2, "running " # m); \
checkLogging();
flags.readMore = false;
- DeregisterRunner(this);
clientdbEstablished(clientConnection->remote, -1); /* decrement */
pipeline.terminateAll(0);
// swanSong() in the close handler will cleanup.
if (Comm::IsConnOpen(clientConnection))
clientConnection->close();
-
- // deregister now to ensure finalShutdown() does not kill us prematurely.
- // fd_table purge will cleanup if close handler was not fast enough.
- DeregisterRunner(this);
}
char *
* managing, or for graceful half-close use the stopReceiving() or
* stopSending() methods.
*/
-class ConnStateData : public Server, public HttpControlMsgSink, public RegisteredRunner
+class ConnStateData : public Server, public HttpControlMsgSink, private IndependentRunner
{
public:
/* ========== IdleConnList ============================================ */
-IdleConnList::IdleConnList(const char *key, PconnPool *thePool) :
+IdleConnList::IdleConnList(const char *aKey, PconnPool *thePool) :
capacity_(PCONN_FDS_SZ),
size_(0),
parent_(thePool)
{
- hash.key = xstrdup(key);
- hash.next = NULL;
+ //Initialize hash_link members
+ key = xstrdup(aKey);
+ next = NULL;
+
theList_ = new Comm::ConnectionPointer[capacity_];
+
+ RegisterRunner(this);
+
// TODO: re-attach to MemPools. WAS: theList = (?? *)pconn_fds_pool->alloc();
}
delete[] theList_;
- xfree(hash.key);
+ xfree(key);
}
/** Search the list. Matches by FD socket number.
if (parent_) {
parent_->noteConnectionRemoved();
if (size_ == 0) {
- debugs(48, 3, HERE << "deleting " << hashKeyStr(&hash));
+ debugs(48, 3, "deleting " << hashKeyStr(this));
delete this;
}
}
}
if (parent_ && size_ == 0) {
- debugs(48, 3, HERE << "deleting " << hashKeyStr(&hash));
+ debugs(48, 3, "deleting " << hashKeyStr(this));
delete this;
}
}
* quite a bit of CPU. Just keep it in mind.
*/
Comm::ConnectionPointer
-IdleConnList::findUseable(const Comm::ConnectionPointer &key)
+IdleConnList::findUseable(const Comm::ConnectionPointer &aKey)
{
assert(size_);
// small optimization: do the constant bool tests only once.
- const bool keyCheckAddr = !key->local.isAnyAddr();
- const bool keyCheckPort = key->local.port() > 0;
+ const bool keyCheckAddr = !aKey->local.isAnyAddr();
+ const bool keyCheckPort = aKey->local.port() > 0;
for (int i=size_-1; i>=0; --i) {
continue;
// local end port is required, but dont match.
- if (keyCheckPort && key->local.port() != theList_[i]->local.port())
+ if (keyCheckPort && aKey->local.port() != theList_[i]->local.port())
continue;
// local address is required, but does not match.
- if (keyCheckAddr && key->local.matchIPAddr(theList_[i]->local) != 0)
+ if (keyCheckAddr && aKey->local.matchIPAddr(theList_[i]->local) != 0)
continue;
// our connection timeout handler is scheduled to run already. unsafe for now.
list->findAndClose(io.conn);
}
+void
+IdleConnList::endingShutdown()
+{
+ closeN(size_);
+}
+
/* ========== PconnPool PRIVATE FUNCTIONS ============================================ */
const char *
if (list == NULL) {
list = new IdleConnList(aKey, this);
- debugs(48, 3, HERE << "new IdleConnList for {" << hashKeyStr(&list->hash) << "}" );
- hash_join(table, &list->hash);
+ debugs(48, 3, "new IdleConnList for {" << hashKeyStr(list) << "}" );
+ hash_join(table, list);
} else {
- debugs(48, 3, HERE << "found IdleConnList for {" << hashKeyStr(&list->hash) << "}" );
+ debugs(48, 3, "found IdleConnList for {" << hashKeyStr(list) << "}" );
}
list->push(conn);
notifyManager("pop failure");
return Comm::ConnectionPointer();
} else {
- debugs(48, 3, HERE << "found " << hashKeyStr(&list->hash) <<
+ debugs(48, 3, "found " << hashKeyStr(list) <<
(keepOpen ? " to use" : " to kill"));
}
{
theCount -= list->count();
assert(theCount >= 0);
- hash_remove_link(table, &list->hash);
+ hash_remove_link(table, list);
}
void
#define SQUID_PCONN_H
#include "base/CbcPointer.h"
+#include "base/RunnersRegistry.h"
#include "mgr/forward.h"
#include <set>
/** \ingroup PConnAPI
* A list of connections currently open to a particular destination end-point.
*/
-class IdleConnList
+class IdleConnList: public hash_link, private IndependentRunner
{
CBDATA_CLASS(IdleConnList);
int count() const { return size_; }
void closeN(size_t count);
+ // IndependentRunner API
+ virtual void endingShutdown();
private:
bool isAvailable(int i) const;
bool removeAt(int index);
static IOCB Read;
static CTCB Timeout;
-public:
- hash_link hash; /** must be first */
-
private:
/** List of connections we are holding.
* Sorted as FIFO list for most efficient speeds on pop() and findUsable()