2 * Copyright 2015 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),
35 idleConnEarlyDropThreshold_(timeout_ / 2) {
40 ConnectionManager::addConnection(ManagedConnection* connection,
42 CHECK_NOTNULL(connection);
43 ConnectionManager* oldMgr = connection->getConnectionManager();
46 // 'connection' was being previously managed in a different thread.
47 // We must remove it from that manager before adding it to this one.
48 oldMgr->removeConnection(connection);
51 // put the connection into busy part first. This should not matter at all
52 // because the last callback for an idle connection must be onDeactivated(),
53 // so the connection must be moved to idle part then.
54 conns_.push_front(*connection);
56 connection->setConnectionManager(this);
58 callback_->onConnectionAdded(*this);
62 scheduleTimeout(connection, timeout_);
67 ConnectionManager::scheduleTimeout(ManagedConnection* const connection,
68 std::chrono::milliseconds timeout) {
69 if (timeout > std::chrono::milliseconds(0)) {
70 connTimeouts_->scheduleTimeout(connection, timeout);
74 void ConnectionManager::scheduleTimeout(
75 folly::HHWheelTimer::Callback* callback,
76 std::chrono::milliseconds timeout) {
77 connTimeouts_->scheduleTimeout(callback, timeout);
81 ConnectionManager::removeConnection(ManagedConnection* connection) {
82 if (connection->getConnectionManager() == this) {
83 connection->cancelTimeout();
84 connection->setConnectionManager(nullptr);
86 // Un-link the connection from our list, being careful to keep the iterator
87 // that we're using for idle shedding valid
88 auto it = conns_.iterator_to(*connection);
89 if (it == idleIterator_) {
95 callback_->onConnectionRemoved(*this);
96 if (getNumConnections() == 0) {
97 callback_->onEmpty(*this);
104 ConnectionManager::initiateGracefulShutdown(
105 std::chrono::milliseconds idleGrace) {
106 if (idleGrace.count() > 0) {
107 idleLoopCallback_.scheduleTimeout(idleGrace);
108 VLOG(3) << "Scheduling idle grace period of " << idleGrace.count() << "ms";
110 action_ = ShutdownAction::DRAIN2;
111 VLOG(3) << "proceeding directly to closing idle connections";
113 drainAllConnections();
117 ConnectionManager::drainAllConnections() {
118 DestructorGuard g(this);
119 size_t numCleared = 0;
122 auto it = idleIterator_ == conns_.end() ?
123 conns_.begin() : idleIterator_;
125 while (it != conns_.end() && (numKept + numCleared) < 64) {
126 ManagedConnection& conn = *it++;
127 if (action_ == ShutdownAction::DRAIN1) {
128 conn.notifyPendingShutdown();
130 // Second time around: close idle sessions. If they aren't idle yet,
131 // have them close when they are idle
137 conn.closeWhenIdle();
141 if (action_ == ShutdownAction::DRAIN2) {
142 VLOG(2) << "Idle connections cleared: " << numCleared <<
143 ", busy conns kept: " << numKept;
145 if (it != conns_.end()) {
147 eventBase_->runInLoop(&idleLoopCallback_);
149 action_ = ShutdownAction::DRAIN2;
154 ConnectionManager::dropAllConnections() {
155 DestructorGuard g(this);
157 // Iterate through our connection list, and drop each connection.
158 VLOG(3) << "connections to drop: " << conns_.size();
159 idleLoopCallback_.cancelTimeout();
161 while (!conns_.empty()) {
162 ManagedConnection& conn = conns_.front();
164 conn.cancelTimeout();
165 conn.setConnectionManager(nullptr);
166 // For debugging purposes, dump information about the first few
168 static const unsigned MAX_CONNS_TO_DUMP = 2;
169 if (++i <= MAX_CONNS_TO_DUMP) {
170 conn.dumpConnectionState(3);
172 conn.dropConnection();
174 idleIterator_ = conns_.end();
175 idleLoopCallback_.cancelLoopCallback();
178 callback_->onEmpty(*this);
183 ConnectionManager::onActivated(ManagedConnection& conn) {
184 auto it = conns_.iterator_to(conn);
185 if (it == idleIterator_) {
189 conns_.push_front(conn);
193 ConnectionManager::onDeactivated(ManagedConnection& conn) {
194 auto it = conns_.iterator_to(conn);
196 conns_.push_back(conn);
197 if (idleIterator_ == conns_.end()) {
203 ConnectionManager::dropIdleConnections(size_t num) {
204 VLOG(4) << "attempt to drop " << num << " idle connections";
205 if (idleConnEarlyDropThreshold_ >= timeout_) {
211 auto it = idleIterator_;
212 if (it == conns_.end()) {
213 return count; // no more idle session
215 auto idleTime = it->getIdleTime();
216 if (idleTime == std::chrono::milliseconds(0) ||
217 idleTime <= idleConnEarlyDropThreshold_) {
218 VLOG(4) << "conn's idletime: " << idleTime.count()
219 << ", earlyDropThreshold: " << idleConnEarlyDropThreshold_.count()
220 << ", attempt to drop " << count << "/" << num;
221 return count; // idleTime cannot be further reduced
223 ManagedConnection& conn = *it;
225 conn.timeoutExpired();