improved unchoking, connecting and disconnecting logic in policy. seems to fix #29

This commit is contained in:
Arvid Norberg 2007-04-12 10:21:55 +00:00
parent a0434eba10
commit 52623405f0
5 changed files with 142 additions and 123 deletions

View File

@ -131,10 +131,7 @@ namespace libtorrent
virtual ~peer_connection(); virtual ~peer_connection();
void set_peer_info(policy::peer* pi) void set_peer_info(policy::peer* pi)
{ { m_peer_info = pi; }
assert(m_peer_info == 0);
m_peer_info = pi;
}
policy::peer* peer_info_struct() const policy::peer* peer_info_struct() const
{ return m_peer_info; } { return m_peer_info; }

View File

@ -155,11 +155,11 @@ namespace libtorrent
// the time when this peer was optimistically unchoked // the time when this peer was optimistically unchoked
// the last time. // the last time.
ptime last_optimistically_unchoked; libtorrent::ptime last_optimistically_unchoked;
// the time when the peer connected to us // the time when the peer connected to us
// or disconnected if it isn't connected right now // or disconnected if it isn't connected right now
ptime connected; libtorrent::ptime connected;
// this is the accumulated amount of // this is the accumulated amount of
// uploaded and downloaded data to this // uploaded and downloaded data to this
@ -204,21 +204,21 @@ namespace libtorrent
bool unchoke_one_peer(); bool unchoke_one_peer();
void choke_one_peer(); void choke_one_peer();
peer* find_choke_candidate(); iterator find_choke_candidate();
peer* find_unchoke_candidate(); iterator find_unchoke_candidate();
// the seed prefix means that the // the seed prefix means that the
// function is used while seeding. // function is used while seeding.
bool seed_unchoke_one_peer(); bool seed_unchoke_one_peer();
void seed_choke_one_peer(); void seed_choke_one_peer();
peer* find_seed_choke_candidate(); iterator find_seed_choke_candidate();
peer* find_seed_unchoke_candidate(); iterator find_seed_unchoke_candidate();
bool connect_peer(peer *); bool connect_peer(iterator p);
bool connect_one_peer(); bool connect_one_peer();
bool disconnect_one_peer(); bool disconnect_one_peer();
peer* find_disconnect_candidate(); iterator find_disconnect_candidate();
peer* find_connect_candidate(); iterator find_connect_candidate();
// a functor that identifies peers that have disconnected and that // a functor that identifies peers that have disconnected and that
// are too old for still being saved. // are too old for still being saved.

View File

@ -226,10 +226,20 @@ namespace libtorrent
break; break;
} }
} }
if (!interested) try
send_not_interested(); {
else if (!interested)
t->get_policy().peer_is_interesting(*this); send_not_interested();
else
t->get_policy().peer_is_interesting(*this);
}
catch (std::exception& e)
{
#ifndef NDEBUG
std::cerr << e.what() << std::endl;
assert(false);
#endif
}
assert(is_interesting() == interested); assert(is_interesting() == interested);
} }
@ -303,6 +313,9 @@ namespace libtorrent
} }
#endif #endif
#ifndef NDEBUG #ifndef NDEBUG
if (m_peer_info)
assert(m_peer_info->connection == 0);
boost::shared_ptr<torrent> t = m_torrent.lock(); boost::shared_ptr<torrent> t = m_torrent.lock();
if (t) assert(t->connection_for(remote()) != this); if (t) assert(t->connection_for(remote()) != this);
#endif #endif
@ -1151,9 +1164,12 @@ namespace libtorrent
// been downloaded. Release the files (they will open // been downloaded. Release the files (they will open
// in read only mode if needed) // in read only mode if needed)
try { t->finished(); } try { t->finished(); }
catch (std::exception&) catch (std::exception& e)
{ {
#ifndef NDEBUG
std::cerr << e.what() << std::endl;
assert(false); assert(false);
#endif
} }
} }
} }
@ -1173,7 +1189,7 @@ namespace libtorrent
} }
catch (std::exception const& e) catch (std::exception const& e)
{ {
std::string err = e.what(); std::cerr << e.what() << std::endl;
assert(false); assert(false);
} }
#endif #endif
@ -1193,7 +1209,7 @@ namespace libtorrent
} }
catch (std::exception const& e) catch (std::exception const& e)
{ {
std::string err = e.what(); std::cerr << e.what() << std::endl;
assert(false); assert(false);
} }
#endif #endif
@ -1204,7 +1220,7 @@ namespace libtorrent
} }
catch (std::exception const& e) catch (std::exception const& e)
{ {
std::string err = e.what(); std::cerr << e.what() << std::endl;
assert(false); assert(false);
} }
#endif #endif
@ -1520,10 +1536,10 @@ namespace libtorrent
if (t) if (t)
{ {
if (t->valid_metadata() && !t->is_seed()) if (t->has_picker())
{ {
piece_picker& picker = t->picker(); piece_picker& picker = t->picker();
while (!m_download_queue.empty()) while (!m_download_queue.empty())
{ {
picker.abort_download(m_download_queue.back()); picker.abort_download(m_download_queue.back());
@ -2236,6 +2252,10 @@ namespace libtorrent
#ifndef NDEBUG #ifndef NDEBUG
void peer_connection::check_invariant() const void peer_connection::check_invariant() const
{ {
if (m_peer_info)
assert(m_peer_info->connection == this
|| m_peer_info->connection == 0);
boost::shared_ptr<torrent> t = m_torrent.lock(); boost::shared_ptr<torrent> t = m_torrent.lock();
if (!t) if (!t)
{ {

View File

@ -381,11 +381,11 @@ namespace libtorrent
// finds the peer that has the worst download rate // finds the peer that has the worst download rate
// and returns it. May return 0 if all peers are // and returns it. May return 0 if all peers are
// choked. // choked.
policy::peer* policy::find_choke_candidate() policy::iterator policy::find_choke_candidate()
{ {
INVARIANT_CHECK; INVARIANT_CHECK;
peer* worst_peer = 0; iterator worst_peer = m_peers.end();
size_type min_weight = std::numeric_limits<int>::min(); size_type min_weight = std::numeric_limits<int>::min();
#ifndef NDEBUG #ifndef NDEBUG
@ -407,7 +407,7 @@ namespace libtorrent
if (c->is_disconnecting()) continue; if (c->is_disconnecting()) continue;
// if the peer isn't interested, just choke it // if the peer isn't interested, just choke it
if (!c->is_peer_interested()) if (!c->is_peer_interested())
return &(*i); return i;
size_type diff = i->total_download() size_type diff = i->total_download()
- i->total_upload(); - i->total_upload();
@ -416,26 +416,26 @@ namespace libtorrent
+ diff + diff
+ ((c->is_interesting() && c->has_peer_choked())?-10:10)*1024; + ((c->is_interesting() && c->has_peer_choked())?-10:10)*1024;
if (weight >= min_weight && worst_peer) continue; if (weight >= min_weight && worst_peer != m_peers.end()) continue;
min_weight = weight; min_weight = weight;
worst_peer = &(*i); worst_peer = i;
continue; continue;
} }
assert(unchoked_counter == 0); assert(unchoked_counter == 0);
return worst_peer; return worst_peer;
} }
policy::peer* policy::find_unchoke_candidate() policy::iterator policy::find_unchoke_candidate()
{ {
INVARIANT_CHECK; INVARIANT_CHECK;
// if all of our peers are unchoked, there's // if all of our peers are unchoked, there's
// no left to unchoke // no left to unchoke
if (m_num_unchoked == m_torrent->num_peers()) if (m_num_unchoked == m_torrent->num_peers())
return 0; return m_peers.end();
peer* unchoke_peer = 0; iterator unchoke_peer = m_peers.end();
ptime min_time = libtorrent::min_time(); ptime min_time = libtorrent::min_time();
float max_down_speed = 0.f; float max_down_speed = 0.f;
@ -455,53 +455,59 @@ namespace libtorrent
min_time = i->last_optimistically_unchoked; min_time = i->last_optimistically_unchoked;
max_down_speed = c->statistics().download_rate(); max_down_speed = c->statistics().download_rate();
unchoke_peer = &(*i); unchoke_peer = i;
} }
return unchoke_peer; return unchoke_peer;
} }
policy::peer* policy::find_disconnect_candidate() policy::iterator policy::find_disconnect_candidate()
{ {
peer *disconnect_peer = 0; iterator disconnect_peer = m_peers.end();
double slowest_transfer_rate = std::numeric_limits<double>::max(); double slowest_transfer_rate = std::numeric_limits<double>::max();
ptime local_time = time_now(); ptime now = time_now();
for (iterator i = m_peers.begin(); for (iterator i = m_peers.begin();
i != m_peers.end(); ++i) i != m_peers.end(); ++i)
{ {
peer_connection* c = i->connection; peer_connection* c = i->connection;
if(c == 0) if (c == 0) continue;
continue; if (c->is_disconnecting()) continue;
if(c->is_disconnecting())
// never disconnect an interesting peer if we have a candidate that
// isn't interesting
if (disconnect_peer != m_peers.end()
&& c->is_interesting()
&& !disconnect_peer->connection->is_interesting())
continue; continue;
double transferred_amount double transferred_amount
= (double)c->statistics().total_payload_download(); = (double)c->statistics().total_payload_download();
time_duration connected_time time_duration connected_time = now - i->connected;
= local_time - i->connected;
double connected_time_in_seconds double connected_time_in_seconds = total_seconds(connected_time);
= total_seconds(connected_time);
double transfer_rate double transfer_rate
= transferred_amount / (connected_time_in_seconds+1); = transferred_amount / (connected_time_in_seconds + 1);
if (transfer_rate <= slowest_transfer_rate) if (transfer_rate <= slowest_transfer_rate)
{ {
slowest_transfer_rate = transfer_rate; slowest_transfer_rate = transfer_rate;
disconnect_peer = &(*i); disconnect_peer = i;
} }
} }
return disconnect_peer; return disconnect_peer;
} }
policy::peer *policy::find_connect_candidate() policy::iterator policy::find_connect_candidate()
{ {
ptime now = time_now(); ptime now = time_now();
ptime ptime(now); ptime ptime(now);
policy::peer* candidate = 0; iterator candidate = m_peers.end();
// TODO: take failcount into account
// TODO: have a minimum time before retrying
for (iterator i = m_peers.begin(); for (iterator i = m_peers.begin();
i != m_peers.end(); ++i) i != m_peers.end(); ++i)
@ -518,7 +524,7 @@ namespace libtorrent
if (next_connect <= ptime) if (next_connect <= ptime)
{ {
ptime = next_connect; ptime = next_connect;
candidate = &(*i); candidate = i;
} }
} }
@ -527,7 +533,7 @@ namespace libtorrent
return candidate; return candidate;
} }
policy::peer* policy::find_seed_choke_candidate() policy::iterator policy::find_seed_choke_candidate()
{ {
INVARIANT_CHECK; INVARIANT_CHECK;
@ -535,7 +541,7 @@ namespace libtorrent
// first choice candidate. // first choice candidate.
// it is a candidate we owe nothing to and which has been unchoked // it is a candidate we owe nothing to and which has been unchoked
// the longest. // the longest.
peer* candidate = 0; iterator candidate = m_peers.end();
// not valid when candidate == 0 // not valid when candidate == 0
ptime last_unchoke = min_time(); ptime last_unchoke = min_time();
@ -543,15 +549,15 @@ namespace libtorrent
// second choice candidate. // second choice candidate.
// if there is no first choice candidate, this candidate will be chosen. // if there is no first choice candidate, this candidate will be chosen.
// it is the candidate that we owe the least to. // it is the candidate that we owe the least to.
peer* second_candidate = 0; iterator second_candidate = m_peers.end();
size_type lowest_share_diff = 0; // not valid when secondCandidate==0 size_type lowest_share_diff = 0; // not valid when secondCandidate==0
for (iterator i = m_peers.begin(); for (iterator i = m_peers.begin();
i != m_peers.end(); ++i) i != m_peers.end(); ++i)
{ {
peer_connection* c = i->connection; peer_connection* c = i->connection;
// ignore peers that are choked or // ignore peers that are choked or
// whose connection is closed // whose connection is closed
if (c == 0) continue; if (c == 0) continue;
if (c->is_choked()) continue; if (c->is_choked()) continue;
@ -561,32 +567,33 @@ namespace libtorrent
// select as second candidate the one that we owe the least // select as second candidate the one that we owe the least
// to // to
if (!second_candidate || share_diff <= lowest_share_diff) if (second_candidate == m_peers.end()
|| share_diff <= lowest_share_diff)
{ {
lowest_share_diff = share_diff; lowest_share_diff = share_diff;
second_candidate = &(*i); second_candidate = i;
} }
// select as first candidate the one that we don't owe anything to // select as first candidate the one that we don't owe anything to
// and has been waiting for an unchoke the longest // and has been waiting for an unchoke the longest
if (share_diff > 0) continue; if (share_diff > 0) continue;
if (!candidate || last_unchoke > i->last_optimistically_unchoked) if (candidate == m_peers.end()
|| last_unchoke > i->last_optimistically_unchoked)
{ {
last_unchoke = i->last_optimistically_unchoked; last_unchoke = i->last_optimistically_unchoked;
candidate = &(*i); candidate = i;
} }
} }
if (candidate) return candidate; if (candidate != m_peers.end()) return candidate;
if (second_candidate) return second_candidate; assert(second_candidate != m_peers.end());
assert(false); return second_candidate;
return 0;
} }
policy::peer* policy::find_seed_unchoke_candidate() policy::iterator policy::find_seed_unchoke_candidate()
{ {
INVARIANT_CHECK; INVARIANT_CHECK;
peer* candidate = 0; iterator candidate = m_peers.end();
ptime last_unchoke = time_now(); ptime last_unchoke = time_now();
for (iterator i = m_peers.begin(); for (iterator i = m_peers.begin();
@ -599,7 +606,7 @@ namespace libtorrent
if (c->is_disconnecting()) continue; if (c->is_disconnecting()) continue;
if (last_unchoke < i->last_optimistically_unchoked) continue; if (last_unchoke < i->last_optimistically_unchoked) continue;
last_unchoke = i->last_optimistically_unchoked; last_unchoke = i->last_optimistically_unchoked;
candidate = &(*i); candidate = i;
} }
return candidate; return candidate;
} }
@ -608,23 +615,23 @@ namespace libtorrent
{ {
INVARIANT_CHECK; INVARIANT_CHECK;
peer* p = find_seed_unchoke_candidate(); iterator p = find_seed_unchoke_candidate();
if (p != 0) if (p != m_peers.end())
{ {
assert(p->connection->is_choked()); assert(p->connection->is_choked());
p->connection->send_unchoke(); p->connection->send_unchoke();
p->last_optimistically_unchoked = time_now(); p->last_optimistically_unchoked = time_now();
++m_num_unchoked; ++m_num_unchoked;
} }
return p != 0; return p != m_peers.end();
} }
void policy::seed_choke_one_peer() void policy::seed_choke_one_peer()
{ {
INVARIANT_CHECK; INVARIANT_CHECK;
peer* p = find_seed_choke_candidate(); iterator p = find_seed_choke_candidate();
if (p != 0) if (p != m_peers.end())
{ {
assert(!p->connection->is_choked()); assert(!p->connection->is_choked());
p->connection->send_choke(); p->connection->send_choke();
@ -654,7 +661,7 @@ namespace libtorrent
int num_connected_peers = 0; int num_connected_peers = 0;
for (iterator i = m_peers.begin(); for (iterator i = m_peers.begin();
i != m_peers.end(); ++i) i != m_peers.end(); ++i)
{ {
if (i->connection && !i->connection->is_disconnecting()) if (i->connection && !i->connection->is_disconnecting())
++num_connected_peers; ++num_connected_peers;
@ -671,7 +678,7 @@ namespace libtorrent
ptime local_time = time_now(); ptime local_time = time_now();
if (m_last_optimistic_disconnect + seconds(120) <= local_time if (m_last_optimistic_disconnect + seconds(120) <= local_time
&& find_connect_candidate()) && find_connect_candidate() != m_peers.end())
{ {
m_last_optimistic_disconnect = local_time; m_last_optimistic_disconnect = local_time;
--max_connections; // this will have the effect of disconnecting the worst peer --max_connections; // this will have the effect of disconnecting the worst peer
@ -739,10 +746,10 @@ namespace libtorrent
{ {
do do
{ {
peer* p = find_seed_choke_candidate(); iterator p = find_seed_choke_candidate();
--m_num_unchoked; --m_num_unchoked;
assert(p != 0); assert(p != m_peers.end());
if (p == 0) break; if (p == m_peers.end()) break;
assert(!p->connection->is_choked()); assert(!p->connection->is_choked());
p->connection->send_choke(); p->connection->send_choke();
@ -755,8 +762,8 @@ namespace libtorrent
// TODO: This rotation should happen // TODO: This rotation should happen
// far less frequent than this! // far less frequent than this!
assert(m_num_unchoked <= m_torrent->num_peers()); assert(m_num_unchoked <= m_torrent->num_peers());
peer* p = find_seed_unchoke_candidate(); iterator p = find_seed_unchoke_candidate();
if (p) if (p != m_peers.end())
{ {
assert(p->connection->is_choked()); assert(p->connection->is_choked());
seed_choke_one_peer(); seed_choke_one_peer();
@ -813,9 +820,9 @@ namespace libtorrent
{ {
do do
{ {
peer* p = find_choke_candidate(); iterator p = find_choke_candidate();
if (!p) break; if (p == m_peers.end()) break;
assert(p); assert(p != m_peers.end());
assert(!p->connection->is_choked()); assert(!p->connection->is_choked());
p->connection->send_choke(); p->connection->send_choke();
--m_num_unchoked; --m_num_unchoked;
@ -828,8 +835,8 @@ namespace libtorrent
// TODO: This rotation should happen // TODO: This rotation should happen
// far less frequent than this! // far less frequent than this!
assert(m_num_unchoked <= m_torrent->num_peers()); assert(m_num_unchoked <= m_torrent->num_peers());
peer* p = find_unchoke_candidate(); iterator p = find_unchoke_candidate();
if (p) if (p != m_peers.end())
{ {
assert(p->connection->is_choked()); assert(p->connection->is_choked());
choke_one_peer(); choke_one_peer();
@ -949,7 +956,6 @@ namespace libtorrent
"connection in favour of this one"); "connection in favour of this one");
#endif #endif
i->connection->disconnect(); i->connection->disconnect();
i->connection = 0;
} }
} }
} }
@ -1001,8 +1007,6 @@ namespace libtorrent
, match_peer_ip(remote)); , match_peer_ip(remote));
} }
bool just_added = false;
if (i == m_peers.end()) if (i == m_peers.end())
{ {
// we don't have any info about this peer. // we don't have any info about this peer.
@ -1013,7 +1017,6 @@ namespace libtorrent
// because of the push_back() // because of the push_back()
i = boost::prior(m_peers.end()); i = boost::prior(m_peers.end());
if (flags & 0x02) p.seed = true; if (flags & 0x02) p.seed = true;
just_added = true;
} }
else else
{ {
@ -1043,23 +1046,6 @@ namespace libtorrent
return; return;
} }
} }
if (i->banned) return;
// TODO: move the connecting of peers somewhere else!
if (m_torrent->num_peers() < m_torrent->m_connections_quota.given
&& !m_torrent->is_paused())
{
if (!connect_peer(&*i) && just_added)
{
// if this peer was just added, and it
// failed to connect. Remove it from the list
// (to keep it in sync with the session's list)
assert(i == boost::prior(m_peers.end()));
m_peers.erase(i);
}
}
return;
} }
catch(std::exception& e) catch(std::exception& e)
{ {
@ -1184,8 +1170,8 @@ namespace libtorrent
bool policy::unchoke_one_peer() bool policy::unchoke_one_peer()
{ {
peer* p = find_unchoke_candidate(); iterator p = find_unchoke_candidate();
if (p == 0) return false; if (p == m_peers.end()) return false;
assert(p->connection); assert(p->connection);
assert(!p->connection->is_disconnecting()); assert(!p->connection->is_disconnecting());
@ -1198,8 +1184,8 @@ namespace libtorrent
void policy::choke_one_peer() void policy::choke_one_peer()
{ {
peer* p = find_choke_candidate(); iterator p = find_choke_candidate();
if (p == 0) return; if (p == m_peers.end()) return;
assert(p->connection); assert(p->connection);
assert(!p->connection->is_disconnecting()); assert(!p->connection->is_disconnecting());
assert(!p->connection->is_choked()); assert(!p->connection->is_choked());
@ -1211,22 +1197,27 @@ namespace libtorrent
{ {
if(m_torrent->num_peers() >= m_torrent->m_connections_quota.given) if(m_torrent->num_peers() >= m_torrent->m_connections_quota.given)
return false; return false;
peer* p = find_connect_candidate();
if (p == 0) return false; bool succeed = false;
assert(!p->banned); while (!succeed)
assert(!p->connection); {
assert(p->type == peer::connectable); iterator p = find_connect_candidate();
if (p == m_peers.end()) return false;
return connect_peer(p); assert(!p->banned);
assert(!p->connection);
assert(p->type == peer::connectable);
succeed = connect_peer(p);
}
return true;
} }
bool policy::connect_peer(peer *p) bool policy::connect_peer(iterator p)
{ {
INVARIANT_CHECK; INVARIANT_CHECK;
try try
{ {
assert(!p->connection); assert(!p->connection);
p->connection = &m_torrent->connect_to_peer(p); p->connection = &m_torrent->connect_to_peer(&*p);
assert(p->connection); assert(p->connection);
p->connection->add_stat(p->prev_amount_download, p->prev_amount_upload); p->connection->add_stat(p->prev_amount_download, p->prev_amount_upload);
p->prev_amount_download = 0; p->prev_amount_download = 0;
@ -1237,14 +1228,19 @@ namespace libtorrent
return true; return true;
} }
catch (std::exception& e) catch (std::exception& e)
{} {
++p->failcount;
// TODO: make this costumizable
if (p->failcount > 3)
m_peers.erase(p);
}
return false; return false;
} }
bool policy::disconnect_one_peer() bool policy::disconnect_one_peer()
{ {
peer *p = find_disconnect_candidate(); iterator p = find_disconnect_candidate();
if(!p) if (p == m_peers.end())
return false; return false;
#if defined(TORRENT_VERBOSE_LOGGING) #if defined(TORRENT_VERBOSE_LOGGING)
(*p->connection->m_logger) << "*** CLOSING CONNECTION 'too many connections'\n"; (*p->connection->m_logger) << "*** CLOSING CONNECTION 'too many connections'\n";
@ -1271,15 +1267,17 @@ namespace libtorrent
// if we couldn't find the connection in our list, just ignore it. // if we couldn't find the connection in our list, just ignore it.
if (i == m_peers.end()) return; if (i == m_peers.end()) return;
assert(i->connection == &c); assert(i->connection == &c);
i->connection = 0;
i->connected = time_now(); i->connected = time_now();
if (!i->connection->is_choked() && !m_torrent->is_aborted()) if (!c.is_choked() && !m_torrent->is_aborted())
{ {
unchoked = true; unchoked = true;
} }
if (c.failed()) if (c.failed())
{ {
// TODO: make 3 customizable
if (++i->failcount > 3) erase = true; if (++i->failcount > 3) erase = true;
i->connected = time_now(); i->connected = time_now();
} }
@ -1289,13 +1287,12 @@ namespace libtorrent
// because it isn't necessary. // because it isn't necessary.
if (m_torrent->ratio() != 0.f) if (m_torrent->ratio() != 0.f)
{ {
assert(i->connection->associated_torrent().lock().get() == m_torrent); assert(c.associated_torrent().lock().get() == m_torrent);
assert(i->connection->share_diff() < std::numeric_limits<size_type>::max()); assert(c.share_diff() < std::numeric_limits<size_type>::max());
m_available_free_upload += i->connection->share_diff(); m_available_free_upload += c.share_diff();
} }
i->prev_amount_download += c.statistics().total_payload_download(); i->prev_amount_download += c.statistics().total_payload_download();
i->prev_amount_upload += c.statistics().total_payload_upload(); i->prev_amount_upload += c.statistics().total_payload_upload();
i->connection = 0;
if (erase) if (erase)
m_peers.erase(i); m_peers.erase(i);

View File

@ -1266,7 +1266,11 @@ namespace libtorrent
assert(p != 0); assert(p != 0);
peer_iterator i = m_connections.find(p->remote()); peer_iterator i = m_connections.find(p->remote());
if (i == m_connections.end()) return; if (i == m_connections.end())
{
assert(false);
return;
}
if (ready_for_connections()) if (ready_for_connections())
{ {
@ -1282,6 +1286,7 @@ namespace libtorrent
} }
m_policy->connection_closed(*p); m_policy->connection_closed(*p);
p->set_peer_info(0);
m_connections.erase(i); m_connections.erase(i);
#ifndef NDEBUG #ifndef NDEBUG
m_policy->check_invariant(); m_policy->check_invariant();