void ConnectionPool::quiesce_connection(int hash_slot) { pthread_mutex_lock(&_tp_hash_lock); pjsip_transport* tp = _tp_hash[hash_slot].tp; if (tp != NULL) { if (_tp_hash[hash_slot].state == PJSIP_TP_STATE_CONNECTED) { // Connection was established, so update statistics. --_active_connections; decrement_connection_count(tp); } // Remove the transport from the hash and the map. _tp_hash[hash_slot].tp = NULL; _tp_hash[hash_slot].state = PJSIP_TP_STATE_DISCONNECTED; _tp_map.erase(tp); // Release the lock now so we don't have a deadlock if pjsip_transport_shutdown // calls the transport state listener. pthread_mutex_unlock(&_tp_hash_lock); // Quiesce the transport. PJSIP will destroy the transport when there // are no further references to it. pjsip_transport_shutdown(tp); // Remove our reference to the transport. pjsip_transport_dec_ref(tp); } else { pthread_mutex_unlock(&_tp_hash_lock); } }
/// Returns a connection for \a l to the cache. /// /// \note The cache must already be aware of the connection, through /// a prior call to \a get() or \a get_or_reserve(). void reclaim(key_type const& l, connection_type const& conn) { std::lock_guard<mutex_type> lock(mtx_); // Search for an entry for this key. typename cache_type::iterator const ct = cache_.find(l); if (ct != cache_.end()) { // Update LRU meta data. key_tracker_.splice( key_tracker_.end() , key_tracker_ , lru_reference(ct->second) ); // Return the connection back to the cache only if the number // of connections does not need to be shrunk. if (num_existing_connections(ct->second) <= max_num_connections(ct->second)) { // Add the connection to the entry. cached_connections(ct->second).push_back(conn); ++reclaims_; #if defined(HPX_TRACK_STATE_OF_OUTGOING_TCP_CONNECTION) conn->set_state(Connection::state_reclaimed); #endif } else { // Adjust the number of existing connections for this key. decrement_connection_count(ct->second); // do the accounting ++evictions_; // the connection itself will go out of scope on return #if defined(HPX_TRACK_STATE_OF_OUTGOING_TCP_CONNECTION) conn->set_state(Connection::state_deleting); #endif } // FIXME: Again, this should probably throw instead of asserting, // as invariants could be invalidated here due to caller error. check_invariants(); } // else { // // Key should already exist in the cache. FIXME: This should // // probably throw as could easily be triggered by caller error. // HPX_ASSERT(shutting_down_); // } }
/// Evict the least recently used removable entry from the cache if the /// cache is full. /// /// \returns Returns true if an entry was evicted or if the cache is not /// full, and false if nothing could be evicted. bool free_space() { // If the cache isn't full, just return true. if (connections_ < max_connections_) return true; // Find the least recently used key. typename key_tracker_type::iterator kt = key_tracker_.begin(); while (connections_ >= max_connections_) { // Find the least recently used keys data. typename cache_type::iterator ct = cache_.find(*kt); HPX_ASSERT(ct != cache_.end()); // If the entry is empty, ignore it and try the next least // recently used entry. if (cached_connections(ct->second).empty()) { // Remove the key if its connection count is zero. if (0 == num_existing_connections(ct->second)) { cache_.erase(ct); key_tracker_.erase(kt); kt = key_tracker_.begin(); } else { // REVIEW: Should we reorder key_tracker_ to speed up // the eviction? ++kt; } // If we've gone through key_tracker_ and haven't found // anything evict-able, then all the entries must be // currently checked out. if (key_tracker_.end() == kt) return false; continue; } // Remove the oldest connection. cached_connections(ct->second).pop_front(); // Adjust the overall and per-locality connection count. decrement_connection_count(ct->second); // Statistics ++evictions_; } return true; }
void ConnectionPool::transport_state_update(pjsip_transport* tp, pjsip_transport_state state) { // Transport state has changed. pthread_mutex_lock(&_tp_hash_lock); std::map<pjsip_transport*, int>::const_iterator i = _tp_map.find(tp); if (i != _tp_map.end()) { int hash_slot = i->second; if ((state == PJSIP_TP_STATE_CONNECTED) && (_tp_hash[hash_slot].state == PJSIP_TP_STATE_DISCONNECTED)) { // New connection has connected successfully, so update the statistics. LOG_DEBUG("Transport %s in slot %d has connected", tp->obj_name, hash_slot); _tp_hash[hash_slot].state = state; ++_active_connections; increment_connection_count(tp); } else if (state == PJSIP_TP_STATE_DISCONNECTED) { // Either a connection has failed, or a new connection failed to // connect. LOG_DEBUG("Transport %s in slot %d has failed", tp->obj_name, hash_slot); if (_tp_hash[hash_slot].state == PJSIP_TP_STATE_CONNECTED) { // A connection has failed, so update the statistics. --_active_connections; decrement_connection_count(tp); } // Remove the transport from the hash and the map. _tp_hash[hash_slot].tp = NULL; _tp_hash[hash_slot].state = PJSIP_TP_STATE_DISCONNECTED; _tp_map.erase(tp); // Remove our reference to the transport. pjsip_transport_dec_ref(tp); } } pthread_mutex_unlock(&_tp_hash_lock); }
void ConnectionPool::quiesce_connection(int hash_slot) { pthread_mutex_lock(&_tp_hash_lock); pjsip_transport* tp = _tp_hash[hash_slot].tp; if (tp != NULL) { if (_tp_hash[hash_slot].connected) { // Connection was established, so update statistics. --_active_connections; decrement_connection_count(tp); } // Don't listen for any more state changes on this connection. pjsip_transport_remove_state_listener(tp, _tp_hash[hash_slot].listener_key, (void *)this); // Remove the transport from the hash and the map. _tp_hash[hash_slot].tp = NULL; _tp_hash[hash_slot].listener_key = NULL; _tp_hash[hash_slot].connected = PJ_FALSE; _tp_map.erase(tp); // Release the lock now so we don't have a deadlock if pjsip_transport_shutdown // calls the transport state listener. pthread_mutex_unlock(&_tp_hash_lock); // Quiesce the transport. PJSIP will destroy the transport when there // are no further references to it. pjsip_transport_shutdown(tp); // Remove our reference to the transport. pjsip_transport_dec_ref(tp); } else { pthread_mutex_unlock(&_tp_hash_lock); } }
/// Destroys all connections for the given locality in the cache, reset /// all associated counts. void clear(key_type const& l, connection_type const& conn) { std::lock_guard<mutex_type> lock(mtx_); // Check if this key already exists in the cache. typename cache_type::iterator const it = cache_.find(l); if (it != cache_.end()) { // Adjust the number of existing connections for this key. decrement_connection_count(it->second); // do the accounting ++evictions_; // the connection itself will go out of scope on return #if defined(HPX_TRACK_STATE_OF_OUTGOING_TCP_CONNECTION) conn->set_state(Connection::state_deleting); #endif } check_invariants(); }
void ConnectionPool::transport_state_update(pjsip_transport* tp, pjsip_transport_state state) { // Transport state has changed. pthread_mutex_lock(&_tp_hash_lock); std::map<pjsip_transport*, int>::const_iterator i = _tp_map.find(tp); if (i != _tp_map.end()) { int hash_slot = i->second; if ((state == PJSIP_TP_STATE_CONNECTED) && (!_tp_hash[hash_slot].connected)) { // New connection has connected successfully, so update the statistics. TRC_DEBUG("Transport %s in slot %d has connected", tp->obj_name, hash_slot); _tp_hash[hash_slot].connected = PJ_TRUE; ++_active_connections; increment_connection_count(tp); if (_recycle_period > 0) { // Compute a TTL for the connection. To avoid all the recycling being // synchronized we set the TTL to the specified average recycle time // perturbed by a random factor. int ttl = _recycle_period + (rand() % (2 * _recycle_margin)) - _recycle_margin; _tp_hash[hash_slot].recycle_time = time(NULL) + ttl; } else { // Connection recycling is disabled. _tp_hash[hash_slot].recycle_time = 0; } } else if ((state == PJSIP_TP_STATE_DISCONNECTED) || (state == PJSIP_TP_STATE_DESTROYED)) { // Either a connection has failed or been shutdown, or a new connection // failed to connect. TRC_DEBUG("Transport %s in slot %d has failed", tp->obj_name, hash_slot); if (_tp_hash[hash_slot].connected) { // A connection has failed, so update the statistics. --_active_connections; decrement_connection_count(tp); } else { // Failed to establish a connection to this server, so blacklist // it so we steer clear of it for a while. We don't blacklist // if an existing connection fails as this may be a transient error // or even a disconnect triggered by an inactivity timeout . AddrInfo server; server.transport = IPPROTO_TCP; server.port = pj_sockaddr_get_port(&tp->key.rem_addr); server.address.af = tp->key.rem_addr.addr.sa_family; if (server.address.af == AF_INET) { server.address.addr.ipv4.s_addr = tp->key.rem_addr.ipv4.sin_addr.s_addr; } else { memcpy((char*)&server.address.addr.ipv6, (char*)&tp->key.rem_addr.ipv6.sin6_addr, sizeof(struct in6_addr)); } PJUtils::blacklist_server(server); } // Don't listen for any more state changes on this connection (but note // it's illegal to call any methods on the transport once it's entered the // `destroyed` state). if (state != PJSIP_TP_STATE_DESTROYED) { pjsip_transport_remove_state_listener(tp, _tp_hash[hash_slot].listener_key, (void *)this); } // Remove the transport from the hash and the map. _tp_hash[hash_slot].tp = NULL; _tp_hash[hash_slot].listener_key = NULL; _tp_hash[hash_slot].connected = PJ_FALSE; _tp_map.erase(tp); // Remove our reference to the transport. pjsip_transport_dec_ref(tp); } } pthread_mutex_unlock(&_tp_hash_lock); }