2 * Copyright 2014 Facebook, Inc.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 #include <folly/wangle/acceptor/ConnectionManager.h>
19 #include <glog/logging.h>
20 #include <folly/io/async/EventBase.h>
22 using folly::HHWheelTimer;
23 using std::chrono::milliseconds;
25 namespace folly { namespace wangle {
27 ConnectionManager::ConnectionManager(EventBase* eventBase,
28 milliseconds timeout, Callback* callback)
29 : connTimeouts_(new HHWheelTimer(eventBase)),
31 eventBase_(eventBase),
32 idleIterator_(conns_.end()),
33 idleLoopCallback_(this),
39 ConnectionManager::addConnection(ManagedConnection* connection,
41 CHECK_NOTNULL(connection);
42 ConnectionManager* oldMgr = connection->getConnectionManager();
45 // 'connection' was being previously managed in a different thread.
46 // We must remove it from that manager before adding it to this one.
47 oldMgr->removeConnection(connection);
49 conns_.push_back(*connection);
50 connection->setConnectionManager(this);
52 callback_->onConnectionAdded(*this);
56 scheduleTimeout(connection, timeout_);
61 ConnectionManager::scheduleTimeout(ManagedConnection* const connection,
62 std::chrono::milliseconds timeout) {
63 if (timeout > std::chrono::milliseconds(0)) {
64 connTimeouts_->scheduleTimeout(connection, timeout);
68 void ConnectionManager::scheduleTimeout(
69 folly::HHWheelTimer::Callback* callback,
70 std::chrono::milliseconds timeout) {
71 connTimeouts_->scheduleTimeout(callback, timeout);
75 ConnectionManager::removeConnection(ManagedConnection* connection) {
76 if (connection->getConnectionManager() == this) {
77 connection->cancelTimeout();
78 connection->setConnectionManager(nullptr);
80 // Un-link the connection from our list, being careful to keep the iterator
81 // that we're using for idle shedding valid
82 auto it = conns_.iterator_to(*connection);
83 if (it == idleIterator_) {
89 callback_->onConnectionRemoved(*this);
90 if (getNumConnections() == 0) {
91 callback_->onEmpty(*this);
98 ConnectionManager::initiateGracefulShutdown(
99 std::chrono::milliseconds idleGrace) {
100 if (idleGrace.count() > 0) {
101 idleLoopCallback_.scheduleTimeout(idleGrace);
102 VLOG(3) << "Scheduling idle grace period of " << idleGrace.count() << "ms";
104 action_ = ShutdownAction::DRAIN2;
105 VLOG(3) << "proceeding directly to closing idle connections";
107 drainAllConnections();
111 ConnectionManager::drainAllConnections() {
112 DestructorGuard g(this);
113 size_t numCleared = 0;
116 auto it = idleIterator_ == conns_.end() ?
117 conns_.begin() : idleIterator_;
119 while (it != conns_.end() && (numKept + numCleared) < 64) {
120 ManagedConnection& conn = *it++;
121 if (action_ == ShutdownAction::DRAIN1) {
122 conn.notifyPendingShutdown();
124 // Second time around: close idle sessions. If they aren't idle yet,
125 // have them close when they are idle
131 conn.closeWhenIdle();
135 if (action_ == ShutdownAction::DRAIN2) {
136 VLOG(2) << "Idle connections cleared: " << numCleared <<
137 ", busy conns kept: " << numKept;
139 if (it != conns_.end()) {
141 eventBase_->runInLoop(&idleLoopCallback_);
143 action_ = ShutdownAction::DRAIN2;
148 ConnectionManager::dropAllConnections() {
149 DestructorGuard g(this);
151 // Iterate through our connection list, and drop each connection.
152 VLOG(3) << "connections to drop: " << conns_.size();
153 idleLoopCallback_.cancelTimeout();
155 while (!conns_.empty()) {
156 ManagedConnection& conn = conns_.front();
158 conn.cancelTimeout();
159 conn.setConnectionManager(nullptr);
160 // For debugging purposes, dump information about the first few
162 static const unsigned MAX_CONNS_TO_DUMP = 2;
163 if (++i <= MAX_CONNS_TO_DUMP) {
164 conn.dumpConnectionState(3);
166 conn.dropConnection();
168 idleIterator_ = conns_.end();
169 idleLoopCallback_.cancelLoopCallback();
172 callback_->onEmpty(*this);