copy wangle back into folly
[folly.git] / folly / wangle / acceptor / ConnectionManager.cpp
1 /*
2  * Copyright 2015 Facebook, Inc.
3  *
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
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 #include <folly/wangle/acceptor/ConnectionManager.h>
18
19 #include <glog/logging.h>
20 #include <folly/io/async/EventBase.h>
21
22 using folly::HHWheelTimer;
23 using std::chrono::milliseconds;
24
25 namespace folly { namespace wangle {
26
27 ConnectionManager::ConnectionManager(EventBase* eventBase,
28     milliseconds timeout, Callback* callback)
29   : connTimeouts_(new HHWheelTimer(eventBase)),
30     callback_(callback),
31     eventBase_(eventBase),
32     idleIterator_(conns_.end()),
33     idleLoopCallback_(this),
34     timeout_(timeout),
35     idleConnEarlyDropThreshold_(timeout_ / 2) {
36
37 }
38
39 void
40 ConnectionManager::addConnection(ManagedConnection* connection,
41     bool timeout) {
42   CHECK_NOTNULL(connection);
43   ConnectionManager* oldMgr = connection->getConnectionManager();
44   if (oldMgr != this) {
45     if (oldMgr) {
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);
49     }
50
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);
55
56     connection->setConnectionManager(this);
57     if (callback_) {
58       callback_->onConnectionAdded(*this);
59     }
60   }
61   if (timeout) {
62     scheduleTimeout(connection, timeout_);
63   }
64 }
65
66 void
67 ConnectionManager::scheduleTimeout(ManagedConnection* const connection,
68     std::chrono::milliseconds timeout) {
69   if (timeout > std::chrono::milliseconds(0)) {
70     connTimeouts_->scheduleTimeout(connection, timeout);
71   }
72 }
73
74 void ConnectionManager::scheduleTimeout(
75   folly::HHWheelTimer::Callback* callback,
76   std::chrono::milliseconds timeout) {
77   connTimeouts_->scheduleTimeout(callback, timeout);
78 }
79
80 void
81 ConnectionManager::removeConnection(ManagedConnection* connection) {
82   if (connection->getConnectionManager() == this) {
83     connection->cancelTimeout();
84     connection->setConnectionManager(nullptr);
85
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_) {
90       ++idleIterator_;
91     }
92     conns_.erase(it);
93
94     if (callback_) {
95       callback_->onConnectionRemoved(*this);
96       if (getNumConnections() == 0) {
97         callback_->onEmpty(*this);
98       }
99     }
100   }
101 }
102
103 void
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";
109   } else {
110     action_ = ShutdownAction::DRAIN2;
111     VLOG(3) << "proceeding directly to closing idle connections";
112   }
113   drainAllConnections();
114 }
115
116 void
117 ConnectionManager::drainAllConnections() {
118   DestructorGuard g(this);
119   size_t numCleared = 0;
120   size_t numKept = 0;
121
122   auto it = idleIterator_ == conns_.end() ?
123     conns_.begin() : idleIterator_;
124
125   while (it != conns_.end() && (numKept + numCleared) < 64) {
126     ManagedConnection& conn = *it++;
127     if (action_ == ShutdownAction::DRAIN1) {
128       conn.notifyPendingShutdown();
129     } else {
130       // Second time around: close idle sessions. If they aren't idle yet,
131       // have them close when they are idle
132       if (conn.isBusy()) {
133         numKept++;
134       } else {
135         numCleared++;
136       }
137       conn.closeWhenIdle();
138     }
139   }
140
141   if (action_ == ShutdownAction::DRAIN2) {
142     VLOG(2) << "Idle connections cleared: " << numCleared <<
143       ", busy conns kept: " << numKept;
144   }
145   if (it != conns_.end()) {
146     idleIterator_ = it;
147     eventBase_->runInLoop(&idleLoopCallback_);
148   } else {
149     action_ = ShutdownAction::DRAIN2;
150   }
151 }
152
153 void
154 ConnectionManager::dropAllConnections() {
155   DestructorGuard g(this);
156
157   // Iterate through our connection list, and drop each connection.
158   VLOG(3) << "connections to drop: " << conns_.size();
159   idleLoopCallback_.cancelTimeout();
160   unsigned i = 0;
161   while (!conns_.empty()) {
162     ManagedConnection& conn = conns_.front();
163     conns_.pop_front();
164     conn.cancelTimeout();
165     conn.setConnectionManager(nullptr);
166     // For debugging purposes, dump information about the first few
167     // connections.
168     static const unsigned MAX_CONNS_TO_DUMP = 2;
169     if (++i <= MAX_CONNS_TO_DUMP) {
170       conn.dumpConnectionState(3);
171     }
172     conn.dropConnection();
173   }
174   idleIterator_ = conns_.end();
175   idleLoopCallback_.cancelLoopCallback();
176
177   if (callback_) {
178     callback_->onEmpty(*this);
179   }
180 }
181
182 void
183 ConnectionManager::onActivated(ManagedConnection& conn) {
184   auto it = conns_.iterator_to(conn);
185   if (it == idleIterator_) {
186     idleIterator_++;
187   }
188   conns_.erase(it);
189   conns_.push_front(conn);
190 }
191
192 void
193 ConnectionManager::onDeactivated(ManagedConnection& conn) {
194   auto it = conns_.iterator_to(conn);
195   conns_.erase(it);
196   conns_.push_back(conn);
197   if (idleIterator_ == conns_.end()) {
198     idleIterator_--;
199   }
200 }
201
202 size_t
203 ConnectionManager::dropIdleConnections(size_t num) {
204   VLOG(4) << "attempt to drop " << num << " idle connections";
205   if (idleConnEarlyDropThreshold_ >= timeout_) {
206     return 0;
207   }
208
209   size_t count = 0;
210   while(count < num) {
211     auto it = idleIterator_;
212     if (it == conns_.end()) {
213       return count; // no more idle session
214     }
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
222     }
223     ManagedConnection& conn = *it;
224     idleIterator_++;
225     conn.timeoutExpired();
226     count++;
227   }
228
229   return count;
230 }
231
232
233 }} // folly::wangle