4
Copyright (c) 2007, Arvid Norberg
7
Redistribution and use in source and binary forms, with or without
8
modification, are permitted provided that the following conditions
11
* Redistributions of source code must retain the above copyright
12
notice, this list of conditions and the following disclaimer.
13
* Redistributions in binary form must reproduce the above copyright
14
notice, this list of conditions and the following disclaimer in
15
the documentation and/or other materials provided with the distribution.
16
* Neither the name of the author nor the names of its
17
contributors may be used to endorse or promote products derived
18
from this software without specific prior written permission.
20
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
24
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30
POSSIBILITY OF SUCH DAMAGE.
34
#include <boost/bind.hpp>
35
#include "libtorrent/invariant_check.hpp"
36
#include "libtorrent/connection_queue.hpp"
41
connection_queue::connection_queue(io_service& ios): m_next_ticket(0)
43
, m_half_open_limit(0)
46
, m_in_timeout_function(false)
50
bool connection_queue::free_slots() const
51
{ return m_num_connecting < m_half_open_limit || m_half_open_limit <= 0; }
53
void connection_queue::enqueue(boost::function<void(int)> const& on_connect
54
, boost::function<void()> const& on_timeout
55
, time_duration timeout)
57
mutex_t::scoped_lock l(m_mutex);
61
m_queue.push_back(entry());
62
entry& e = m_queue.back();
63
e.on_connect = on_connect;
64
e.on_timeout = on_timeout;
65
e.ticket = m_next_ticket;
71
void connection_queue::done(int ticket)
73
mutex_t::scoped_lock l(m_mutex);
77
std::list<entry>::iterator i = std::find_if(m_queue.begin()
78
, m_queue.end(), boost::bind(&entry::ticket, _1) == ticket);
79
if (i == m_queue.end())
81
// this might not be here in case on_timeout calls remove
84
if (i->connecting) --m_num_connecting;
89
void connection_queue::limit(int limit)
90
{ m_half_open_limit = limit; }
92
int connection_queue::limit() const
93
{ return m_half_open_limit; }
97
void connection_queue::check_invariant() const
99
int num_connecting = 0;
100
for (std::list<entry>::const_iterator i = m_queue.begin();
101
i != m_queue.end(); ++i)
103
if (i->connecting) ++num_connecting;
105
TORRENT_ASSERT(num_connecting == m_num_connecting);
110
void connection_queue::try_connect()
114
if (!free_slots() || m_queue.empty())
117
std::list<entry>::iterator i = std::find_if(m_queue.begin()
118
, m_queue.end(), boost::bind(&entry::connecting, _1) == false);
119
while (i != m_queue.end())
121
TORRENT_ASSERT(i->connecting == false);
122
ptime expire = time_now() + i->timeout;
123
if (m_num_connecting == 0)
125
m_timer.expires_at(expire);
126
m_timer.async_wait(boost::bind(&connection_queue::on_timeout, this, _1));
128
i->connecting = true;
136
try { ent.on_connect(ent.ticket); } catch (std::exception&) {}
138
if (!free_slots()) break;
139
i = std::find_if(i, m_queue.end(), boost::bind(&entry::connecting, _1) == false);
144
struct function_guard
146
function_guard(bool& v): val(v) { TORRENT_ASSERT(!val); val = true; }
147
~function_guard() { val = false; }
153
void connection_queue::on_timeout(asio::error_code const& e)
155
mutex_t::scoped_lock l(m_mutex);
159
function_guard guard_(m_in_timeout_function);
162
TORRENT_ASSERT(!e || e == asio::error::operation_aborted);
165
ptime next_expire = max_time();
166
ptime now = time_now();
167
std::list<entry> timed_out;
168
for (std::list<entry>::iterator i = m_queue.begin();
169
!m_queue.empty() && i != m_queue.end();)
171
if (i->connecting && i->expires < now)
173
std::list<entry>::iterator j = i;
175
timed_out.splice(timed_out.end(), m_queue, j, i);
179
if (i->expires < next_expire)
180
next_expire = i->expires;
184
// we don't want to call the timeout callback while we're locked
185
// since that is a recepie for dead-locks
188
for (std::list<entry>::iterator i = timed_out.begin()
189
, end(timed_out.end()); i != end; ++i)
191
try { i->on_timeout(); } catch (std::exception&) {}
196
if (next_expire < max_time())
198
m_timer.expires_at(next_expire);
199
m_timer.async_wait(boost::bind(&connection_queue::on_timeout, this, _1));