text: /*
   *  Phusion Passenger - https://www.phusionpassenger.com/
   *  Copyright (c) 2011 Phusion
   *
   *  "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui.
   *
   *  Permission is hereby granted, free of charge, to any person obtaining a copy
   *  of this software and associated documentation files (the "Software"), to deal
   *  in the Software without restriction, including without limitation the rights
   *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   *  copies of the Software, and to permit persons to whom the Software is
   *  furnished to do so, subject to the following conditions:
   *
   *  The above copyright notice and this permission notice shall be included in
   *  all copies or substantial portions of the Software.
   *
   *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
   *  THE SOFTWARE.
   */
  #ifndef _PASSENGER_APPLICATION_POOL_SOCKET_H_
  #define _PASSENGER_APPLICATION_POOL_SOCKET_H_
  
  #include <string>
  #include <vector>
  #include <boost/thread.hpp>
  #include <boost/shared_ptr.hpp>
  #include <boost/weak_ptr.hpp>
  #include <climits>
  #include <cassert>
  #include <Logging.h>
  #include <Utils/PriorityQueue.h>
  #include <Utils/IOUtils.h>
  
  namespace Passenger {
  namespace ApplicationPool2 {
  
  using namespace std;
  using namespace boost;
  
  
  class Process;
  
  struct Connection {
  	int fd;
  	bool persistent: 1;
  	bool fail: 1;
  	
  	Connection() {
  		fd = -1;
  		persistent = false;
  		fail = false;
  	}
  	
  	void close() {
  		if (fd != -1) {
  			int fd2 = fd;
  			fd = -1;
  			persistent = false;
  			safelyClose(fd2);
  		}
  	}
  };
  
  /**
   * Not thread-safe except for the connection pooling methods, so only use
   * within the ApplicationPool lock.
   */
  class Socket {
  private:
  	boost::mutex connectionPoolLock;
  	int totalConnections;
  	vector<Connection> idleConnections;
  	
  	int connectionPoolLimit() const {
  		return concurrency;
  	}
  	
  	Connection connect() const {
  		Connection connection;
  		P_TRACE(3, "Connecting to " << address);
  		connection.fd = connectToServer(address);
  		return connection;
  	}
  	
  public:
  	// Read-only.
  	string name;
  	string address;
  	string protocol;
  	int concurrency;
  	
  	/** The handle inside the associated Process's 'sessionSockets' priority queue.
  	 * Guaranteed to be valid as long as the Process is alive.
  	 */
  	PriorityQueue<Socket>::Handle pqHandle;
  	
  	/** Invariant: sessions >= 0 */
  	int sessions;
  	
  	Socket()
  		: concurrency(0)
  		{ }
  	
  	Socket(const string &_name, const string &_address, const string &_protocol, int _concurrency)
  		: totalConnections(0),
  		  name(_name),
  		  address(_address),
  		  protocol(_protocol),
  		  concurrency(_concurrency),
  		  sessions(0)
  		{ }
  	
  	Socket(const Socket &other)
  		: totalConnections(other.totalConnections),
  		  idleConnections(other.idleConnections),
  		  name(other.name),
  		  address(other.address),
  		  protocol(other.protocol),
  		  concurrency(other.concurrency),
  		  pqHandle(other.pqHandle),
  		  sessions(other.sessions)
  		{ }
  	
  	Socket &operator=(const Socket &other) {
  		totalConnections = other.totalConnections;
  		idleConnections = other.idleConnections;
  		name = other.name;
  		address = other.address;
  		protocol = other.protocol;
  		concurrency = other.concurrency;
  		pqHandle = other.pqHandle;
  		sessions = other.sessions;
  		return *this;
  	}
  	
  	/**
  	 * Connect to this socket or reuse an existing connection.
  	 *
  	 * One MUST call checkinConnection() when one's done using the Connection.
  	 * Failure to do so will result in a resource leak.
  	 */
  	Connection checkoutConnection() {
  		lock_guard<boost::mutex> l(connectionPoolLock);
  		
  		if (!idleConnections.empty()) {
  			Connection connection = idleConnections.back();
  			idleConnections.pop_back();
  			return connection;
  		} else if (totalConnections < connectionPoolLimit()) {
  			Connection connection = connect();
  			connection.persistent = true;
  			totalConnections++;
  			return connection;
  		} else {
  			return connect();
  		}
  	}
  	
  	void checkinConnection(Connection connection) {
  		unique_lock<boost::mutex> l(connectionPoolLock);
  		
  		if (connection.persistent) {
  			if (connection.fail) {
  				totalConnections--;
  				l.unlock();
  				connection.close();
  			} else {
  				idleConnections.push_back(connection);
  			}
  		} else {
  			l.unlock();
  			connection.close();
  		}
  	}
  	
  	
  	bool idle() const {
  		return sessions == 0;
  	}
  	
  	int utilization() const {
  		/* Different sockets within a Process may have different
  		 * 'concurrency' values. We want:
  		 * - Process.sessionSockets to sort the sockets from least used to most used.
  		 * - to give sockets with concurrency == 0 more priority over sockets
  		 *   with concurrency > 0.
  		 * Therefore, we describe our utilization as a percentage of 'concurrency', with
  		 * the percentage value in [0..INT_MAX] instead of [0..1].
  		 */
  		if (concurrency == 0) {
  			// Allows Process.sessionSockets to give
  			// idle sockets more priority.
  			if (sessions == 0) {
  				return 0;
  			} else {
  				return 1;
  			}
  		} else {
  			return (int) (((long long) sessions * INT_MAX) / (double) concurrency);
  		}
  	}
  	
  	bool atFullCapacity() const {
  		return concurrency != 0 && sessions >= concurrency;
  	}
  };
  
  class SocketList: public vector<Socket> {
  public:
  	void add(const string &name, const string &address, const string &protocol, int concurrency) {
  		push_back(Socket(name, address, protocol, concurrency));
  	}
  
  	const Socket *findSocketWithName(const StaticString &name) const {
  		const_iterator it, end = this->end();
  		for (it = begin(); it != end; it++) {
  			if (it->name == name) {
  				return &(*it);
  			}
  		}
  		return NULL;
  	}
  
  	bool hasSessionSockets() const {
  		const_iterator it;
  		for (it = begin(); it != end(); it++) {
  			if (it->protocol == "session" || it->protocol == "http_session") {
  				return true;
  			}
  		}
  		return false;
  	}
  };
  
  typedef shared_ptr<SocketList> SocketListPtr;
  
  
  } // namespace ApplicationPool2
  } // namespace Pasenger
  
  #endif /* _PASSENGER_APPLICATION_POOL2_SOCKET_H_ */
  