1 /*
2  * Copyright (c) Facebook, Inc. and its affiliates.
3  * All rights reserved.
4  *
5  * This source code is licensed under the BSD-style license found in the
6  * LICENSE file in the root directory of this source tree.
7  */
8 
9 #include "proxygen/lib/http/connpool/ServerIdleSessionController.h"
10 
11 namespace proxygen {
12 
getIdleSession()13 folly::Future<HTTPSessionBase*> ServerIdleSessionController::getIdleSession() {
14   folly::Promise<HTTPSessionBase*> promise;
15   folly::Future<HTTPSessionBase*> future = promise.getFuture();
16   SessionPool* maxPool = nullptr;
17 
18   {
19     std::lock_guard<std::mutex> lock(lock_);
20     maxPool = popBestIdlePool();
21     if (markedForDeath_ || !maxPool || !maxPool->getEventBase()) {
22       return folly::makeFuture<HTTPSessionBase*>(nullptr);
23     }
24   }
25 
26   if (maxPool->getEventBase()->isInEventBaseThread()) {
27     LOG(ERROR) << "Idle session already belongs to current thread!";
28     return folly::makeFuture<HTTPSessionBase*>(nullptr);
29   }
30 
31   maxPool->getEventBase()->runInEventBaseThread(
32       [this, maxPool, promise = std::move(promise)]() mutable {
33         // Caller (in this case Server::getTransaction()) needs to guarantee
34         // that 'this' still exists.
35         HTTPSessionBase* session =
36             isMarkedForDeath() ? nullptr : maxPool->removeOldestIdleSession();
37         if (session) {
38           session->detachThreadLocals(true);
39         }
40         promise.setValue(session);
41       });
42   return future;
43 }
44 
addIdleSession(const HTTPSessionBase * session,SessionPool * sessionPool)45 void ServerIdleSessionController::addIdleSession(const HTTPSessionBase* session,
46                                                  SessionPool* sessionPool) {
47   std::lock_guard<std::mutex> lock(lock_);
48   if (sessionMap_.find(session) != sessionMap_.end()) {
49     // removeIdleSession should've been called before re-adding
50     LOG(ERROR) << "Session " << session << " already exists!";
51     return;
52   }
53   if (sessionsByIdleAge_.size() < maxIdleCount_) {
54     auto newIt = sessionsByIdleAge_.insert(sessionsByIdleAge_.end(),
55                                            {session, sessionPool});
56     sessionMap_[session] = newIt;
57   }
58 }
59 
removeIdleSession(const HTTPSessionBase * session)60 void ServerIdleSessionController::removeIdleSession(
61     const HTTPSessionBase* session) {
62   std::lock_guard<std::mutex> lock(lock_);
63   auto it = sessionMap_.find(session);
64   if (it != sessionMap_.end()) {
65     sessionsByIdleAge_.erase(it->second);
66     sessionMap_.erase(it);
67   }
68 }
69 
markForDeath()70 void ServerIdleSessionController::markForDeath() {
71   std::lock_guard<std::mutex> lock(lock_);
72   markedForDeath_ = true;
73   sessionMap_.clear();
74   sessionsByIdleAge_.clear();
75 }
76 
77 // must be called under lock_
popBestIdlePool()78 SessionPool* FOLLY_NULLABLE ServerIdleSessionController::popBestIdlePool() {
79   if (!sessionsByIdleAge_.empty()) {
80     auto ret = *sessionsByIdleAge_.begin();
81     sessionsByIdleAge_.erase(sessionsByIdleAge_.begin());
82     sessionMap_.erase(ret.session);
83     return ret.sessionPool;
84   }
85   return nullptr;
86 }
87 
88 } // namespace proxygen
89