1 // libTorrent - BitTorrent library
2 // Copyright (C) 2005-2011, Jari Sundell
3 //
4 // This program is free software; you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation; either version 2 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program; if not, write to the Free Software
16 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17 //
18 // In addition, as a special exception, the copyright holders give
19 // permission to link the code of portions of this program with the
20 // OpenSSL library under certain conditions as described in each
21 // individual source file, and distribute linked combinations
22 // including the two.
23 //
24 // You must obey the GNU General Public License in all respects for
25 // all of the code used other than OpenSSL.  If you modify file(s)
26 // with this exception, you may extend this exception to your version
27 // of the file(s), but you are not obligated to do so.  If you do not
28 // wish to do so, delete this exception statement from your version.
29 // If you delete this exception statement from all source files in the
30 // program, then also delete it here.
31 //
32 // Contact:  Jari Sundell <jaris@ifi.uio.no>
33 //
34 //           Skomakerveien 33
35 //           3185 Skoppum, NORWAY
36 
37 #ifndef LIBTORRENT_DOWNLOAD_GROUP_ENTRY_H
38 #define LIBTORRENT_DOWNLOAD_GROUP_ENTRY_H
39 
40 #include <algorithm>
41 #include <vector>
42 #include lt_tr1_functional
43 #include <torrent/common.h>
44 #include <torrent/exceptions.h>
45 
46 namespace torrent {
47 
48 class choke_queue;
49 class PeerConnectionBase;
50 
51 struct weighted_connection {
weighted_connectionweighted_connection52   weighted_connection(PeerConnectionBase* pcb, uint32_t w) : connection(pcb), weight(w) {}
53 
54   bool operator == (const PeerConnectionBase* pcb) { return pcb == connection; }
55   bool operator != (const PeerConnectionBase* pcb) { return pcb != connection; }
56 
57   PeerConnectionBase* connection;
58   uint32_t            weight;
59 };
60 
61 // TODO: Rename to choke_entry, create an new class called group entry?
62 class group_entry {
63 public:
64   typedef std::vector<weighted_connection> container_type;
65 
66   static const uint32_t unlimited = ~uint32_t();
67 
group_entry()68   group_entry() : m_max_slots(unlimited), m_min_slots(0) {}
69 
size_connections()70   uint32_t            size_connections() const  { return m_queued.size() + m_unchoked.size(); }
71 
max_slots()72   uint32_t            max_slots() const         { return m_max_slots; }
min_slots()73   uint32_t            min_slots() const         { return m_min_slots; }
74 
set_max_slots(uint32_t s)75   void                set_max_slots(uint32_t s) { m_max_slots = s; }
set_min_slots(uint32_t s)76   void                set_min_slots(uint32_t s) { m_min_slots = s; }
77 
queued()78   const container_type* queued()   { return &m_queued; }
unchoked()79   const container_type* unchoked() { return &m_unchoked; }
80 
81 protected:
82   friend class choke_queue;
83   friend class PeerConnectionBase;
84 
mutable_queued()85   container_type*     mutable_queued()   { return &m_queued; }
mutable_unchoked()86   container_type*     mutable_unchoked() { return &m_unchoked; }
87 
88   void                connection_unchoked(PeerConnectionBase* pcb);
89   void                connection_choked(PeerConnectionBase* pcb);
90 
91   void                connection_queued(PeerConnectionBase* pcb);
92   void                connection_unqueued(PeerConnectionBase* pcb);
93 
94 private:
95   uint32_t            m_max_slots;
96   uint32_t            m_min_slots;
97 
98   // After a cycle the end of the vector should have the
99   // highest-priority connections, and any new connections get put at
100   // the back so they should always good candidates for unchoking.
101   container_type      m_queued;
102   container_type      m_unchoked;
103 };
104 
connection_unchoked(PeerConnectionBase * pcb)105 inline void group_entry::connection_unchoked(PeerConnectionBase* pcb) {
106   container_type::iterator itr = std::find_if(m_unchoked.begin(), m_unchoked.end(),
107                                               std::bind(&weighted_connection::operator==, std::placeholders::_1, pcb));
108 
109   if (itr != m_unchoked.end()) throw internal_error("group_entry::connection_unchoked(pcb) failed.");
110 
111   m_unchoked.push_back(weighted_connection(pcb, uint32_t()));
112 }
113 
connection_queued(PeerConnectionBase * pcb)114 inline void group_entry::connection_queued(PeerConnectionBase* pcb) {
115   container_type::iterator itr = std::find_if(m_queued.begin(), m_queued.end(),
116                                               std::bind(&weighted_connection::operator==, std::placeholders::_1, pcb));
117 
118   if (itr != m_queued.end()) throw internal_error("group_entry::connection_queued(pcb) failed.");
119 
120   m_queued.push_back(weighted_connection(pcb, uint32_t()));
121 }
122 
123 inline void
connection_choked(PeerConnectionBase * pcb)124 group_entry::connection_choked(PeerConnectionBase* pcb) {
125   container_type::iterator itr = std::find_if(m_unchoked.begin(), m_unchoked.end(),
126                                               std::bind(&weighted_connection::operator==, std::placeholders::_1, pcb));
127 
128   if (itr == m_unchoked.end()) throw internal_error("group_entry::connection_choked(pcb) failed.");
129 
130   std::swap(*itr, m_unchoked.back());
131   m_unchoked.pop_back();
132 }
133 
134 inline void
connection_unqueued(PeerConnectionBase * pcb)135 group_entry::connection_unqueued(PeerConnectionBase* pcb) {
136   container_type::iterator itr = std::find_if(m_queued.begin(), m_queued.end(),
137                                               std::bind(&weighted_connection::operator==, std::placeholders::_1, pcb));
138 
139   if (itr == m_queued.end()) throw internal_error("group_entry::connection_unqueued(pcb) failed.");
140 
141   std::swap(*itr, m_queued.back());
142   m_queued.pop_back();
143 }
144 
145 }
146 
147 #endif
148