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::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); }
/// Try to get a connection to \a l from the cache, or reserve space for /// a new connection to \a l. This function may evict entries from the /// cache. /// /// \returns If a connection was found in the cache, its value is /// assigned to \a conn and this function returns true. If a /// connection was not found but space was reserved, \a conn is /// set such that conn.get() == 0, and this function returns /// true. If a connection could not be found and space could /// not be returned, \a conn is unmodified and this function /// returns false. /// If force_nsert is true, a new connection entry will be /// created even if that means the cache limits will be /// exceeded. /// /// \note The connection must be returned to the cache by calling /// \a reclaim(). bool get_or_reserve(key_type const& l, connection_type& conn, bool force_insert = false) { std::lock_guard<mutex_type> lock(mtx_); typename cache_type::iterator const it = cache_.find(l); // Check if this key already exists in the cache. if (it != cache_.end()) { // Key exists in cache. // Update LRU meta data. key_tracker_.splice( key_tracker_.end() , key_tracker_ , lru_reference(it->second) ); // If connections to the locality are available in the cache, // remove the oldest one and return it. if (!cached_connections(it->second).empty()) { value_type& connections = cached_connections(it->second); conn = connections.front(); connections.pop_front(); #if defined(HPX_TRACK_STATE_OF_OUTGOING_TCP_CONNECTION) conn->set_state(Connection::state_reinitialized); #endif ++hits_; check_invariants(); return true; } // Otherwise, if we have less connections for this locality // than the maximum, try to reserve space in the cache for a new // connection. if (num_existing_connections(it->second) < max_num_connections(it->second) || force_insert) { // See if we have enough space or can make space available. // Note that if we don't have any space and there are no // outstanding connections for this locality, we grow the // cache size beyond its limit (hoping that it will be // reduced in size next time some connection is handed back // to the cache). if (!free_space() && num_existing_connections(it->second) != 0 && !force_insert) { // If we can't find or make space, give up. ++misses_; check_invariants(); return false; } // Make sure the input connection shared_ptr doesn't hold // anything. conn.reset(); // Increase the per-locality and overall connection counts. increment_connection_count(it->second); // Statistics ++insertions_; check_invariants(); return true; } // We've reached the maximum number of connections for this // locality, and none of them are checked into the cache, so // we have to give up. ++misses_; check_invariants(); return false; } // Key (locality) isn't in cache. // See if we have enough space or can make space available. // Note that we ignore the outcome of free_space() here as we have // to guarantee to have space for the new connection as there are // no connections outstanding for this locality. If free_space // fails we grow the cache size beyond its limit (hoping that it // will be reduced in size next time some connection is handed back // to the cache). free_space(); // Update LRU meta data. typename key_tracker_type::iterator kt = key_tracker_.insert(key_tracker_.end(), l); cache_.insert(std::make_pair( l, util::make_tuple( value_type(), 1, max_connections_per_locality_, kt )) ); // Make sure the input connection shared_ptr doesn't hold anything. conn.reset(); // Increase the overall connection counts. ++connections_; ++insertions_; check_invariants(); return true; }