Example #1
0
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);
  }
}
Example #2
0
        /// 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_);
//             }
        }
Example #3
0
        /// 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;
        }
Example #4
0
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);
}
Example #5
0
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);
  }
}
Example #6
0
        /// 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();
        }
Example #7
0
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);
}