~ubuntu-branches/ubuntu/intrepid/miro/intrepid

« back to all changes in this revision

Viewing changes to portable/libtorrent/src/connection_queue.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Christopher James Halse Rogers
  • Date: 2008-02-09 13:37:10 UTC
  • mfrom: (1.1.2 upstream)
  • Revision ID: james.westby@ubuntu.com-20080209133710-9rs90q6gckvp1b6i
Tags: 1.1.2-0ubuntu1
New upstream release

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
 
 
2
/*
 
3
 
 
4
Copyright (c) 2007, Arvid Norberg
 
5
All rights reserved.
 
6
 
 
7
Redistribution and use in source and binary forms, with or without
 
8
modification, are permitted provided that the following conditions
 
9
are met:
 
10
 
 
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.
 
19
 
 
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.
 
31
 
 
32
*/
 
33
 
 
34
#include <boost/bind.hpp>
 
35
#include "libtorrent/invariant_check.hpp"
 
36
#include "libtorrent/connection_queue.hpp"
 
37
 
 
38
namespace libtorrent
 
39
{
 
40
 
 
41
        connection_queue::connection_queue(io_service& ios): m_next_ticket(0)
 
42
                , m_num_connecting(0)
 
43
                , m_half_open_limit(0)
 
44
                , m_timer(ios)
 
45
#ifndef NDEBUG
 
46
                , m_in_timeout_function(false)
 
47
#endif
 
48
        {}
 
49
 
 
50
        bool connection_queue::free_slots() const
 
51
        { return m_num_connecting < m_half_open_limit || m_half_open_limit <= 0; }
 
52
 
 
53
        void connection_queue::enqueue(boost::function<void(int)> const& on_connect
 
54
                , boost::function<void()> const& on_timeout
 
55
                , time_duration timeout)
 
56
        {
 
57
                mutex_t::scoped_lock l(m_mutex);
 
58
 
 
59
                INVARIANT_CHECK;
 
60
 
 
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;
 
66
                e.timeout = timeout;
 
67
                ++m_next_ticket;
 
68
                try_connect();
 
69
        }
 
70
 
 
71
        void connection_queue::done(int ticket)
 
72
        {
 
73
                mutex_t::scoped_lock l(m_mutex);
 
74
 
 
75
                INVARIANT_CHECK;
 
76
 
 
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())
 
80
                {
 
81
                        // this might not be here in case on_timeout calls remove
 
82
                        return;
 
83
                }
 
84
                if (i->connecting) --m_num_connecting;
 
85
                m_queue.erase(i);
 
86
                try_connect();
 
87
        }
 
88
 
 
89
        void connection_queue::limit(int limit)
 
90
        { m_half_open_limit = limit; }
 
91
 
 
92
        int connection_queue::limit() const
 
93
        { return m_half_open_limit; }
 
94
 
 
95
#ifndef NDEBUG
 
96
 
 
97
        void connection_queue::check_invariant() const
 
98
        {
 
99
                int num_connecting = 0;
 
100
                for (std::list<entry>::const_iterator i = m_queue.begin();
 
101
                        i != m_queue.end(); ++i)
 
102
                {
 
103
                        if (i->connecting) ++num_connecting;
 
104
                }
 
105
                TORRENT_ASSERT(num_connecting == m_num_connecting);
 
106
        }
 
107
 
 
108
#endif
 
109
 
 
110
        void connection_queue::try_connect()
 
111
        {
 
112
                INVARIANT_CHECK;
 
113
 
 
114
                if (!free_slots() || m_queue.empty())
 
115
                        return;
 
116
 
 
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())
 
120
                {
 
121
                        TORRENT_ASSERT(i->connecting == false);
 
122
                        ptime expire = time_now() + i->timeout;
 
123
                        if (m_num_connecting == 0)
 
124
                        {
 
125
                                m_timer.expires_at(expire);
 
126
                                m_timer.async_wait(boost::bind(&connection_queue::on_timeout, this, _1));
 
127
                        }
 
128
                        i->connecting = true;
 
129
                        ++m_num_connecting;
 
130
                        i->expires = expire;
 
131
 
 
132
                        INVARIANT_CHECK;
 
133
 
 
134
                        entry& ent = *i;
 
135
                        ++i;
 
136
                        try { ent.on_connect(ent.ticket); } catch (std::exception&) {}
 
137
 
 
138
                        if (!free_slots()) break;
 
139
                        i = std::find_if(i, m_queue.end(), boost::bind(&entry::connecting, _1) == false);
 
140
                }
 
141
        }
 
142
 
 
143
#ifndef NDEBUG
 
144
        struct function_guard
 
145
        {
 
146
                function_guard(bool& v): val(v) { TORRENT_ASSERT(!val); val = true; }
 
147
                ~function_guard() { val = false; }
 
148
 
 
149
                bool& val;
 
150
        };
 
151
#endif
 
152
        
 
153
        void connection_queue::on_timeout(asio::error_code const& e)
 
154
        {
 
155
                mutex_t::scoped_lock l(m_mutex);
 
156
 
 
157
                INVARIANT_CHECK;
 
158
#ifndef NDEBUG
 
159
                function_guard guard_(m_in_timeout_function);
 
160
#endif
 
161
 
 
162
                TORRENT_ASSERT(!e || e == asio::error::operation_aborted);
 
163
                if (e) return;
 
164
 
 
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();)
 
170
                {
 
171
                        if (i->connecting && i->expires < now)
 
172
                        {
 
173
                                std::list<entry>::iterator j = i;
 
174
                                ++i;
 
175
                                timed_out.splice(timed_out.end(), m_queue, j, i);
 
176
                                --m_num_connecting;
 
177
                                continue;
 
178
                        }
 
179
                        if (i->expires < next_expire)
 
180
                                next_expire = i->expires;
 
181
                        ++i;
 
182
                }
 
183
 
 
184
                // we don't want to call the timeout callback while we're locked
 
185
                // since that is a recepie for dead-locks
 
186
                l.unlock();
 
187
 
 
188
                for (std::list<entry>::iterator i = timed_out.begin()
 
189
                        , end(timed_out.end()); i != end; ++i)
 
190
                {
 
191
                        try { i->on_timeout(); } catch (std::exception&) {}
 
192
                }
 
193
                
 
194
                l.lock();
 
195
                
 
196
                if (next_expire < max_time())
 
197
                {
 
198
                        m_timer.expires_at(next_expire);
 
199
                        m_timer.async_wait(boost::bind(&connection_queue::on_timeout, this, _1));
 
200
                }
 
201
                try_connect();
 
202
        }
 
203
 
 
204
}
 
205