connection_table.cpp

Go to the documentation of this file.
00001 #ifndef CONNECTION_TABLE_IMPLEMENTATION_FILE
00002 #define CONNECTION_TABLE_IMPLEMENTATION_FILE
00003 
00004 /*****************************************************************************\
00005 *                                                                             *
00006 *  Name   : connection_table                                                  *
00007 *  Author : Chris Koeritz                                                     *
00008 *                                                                             *
00009 *******************************************************************************
00010 * Copyright (c) 1992-$now By Author.  This program is free software; you can  *
00011 * redistribute it and/or modify it under the terms of the GNU General Public  *
00012 * License as published by the Free Software Foundation; either version 2 of   *
00013 * the License or (at your option) any later version.  This is online at:      *
00014 *     http://www.fsf.org/copyleft/gpl.html                                    *
00015 * Please send any updates to: fred@gruntose.com                               *
00016 \*****************************************************************************/
00017 
00018 #include "connection.h"
00019 #include "connection_table.h"
00020 
00021 #include <basis/istring.h>
00022 #include <basis/log_base.h>
00023 #include <basis/mutex.h>
00024 #include <data_struct/byte_hasher.h>
00025 #include <data_struct/int_hash.cpp>
00026 #include <textual/string_manipulation.h>
00027 
00028 using namespace nodes;
00029 
00030 //#define DEBUG_CONNECTION_TABLE
00031   // uncomment for debugging version.
00032 
00033 const int CTB_MAX_BITS = 10;
00034   // number of bits in the table.  2^CTB_MAX_BITS entries will be created, and
00035   // there are probably at least 20 bytes per entry.
00036 
00037 #undef AUTO_LOCK
00038 #define AUTO_LOCK auto_synchronizer l(*_connlock)
00039 
00040 #undef LOG
00041 #define LOG(to_print) CLASS_EMERGENCY_LOG(program_wide_logger(), to_print)
00042 
00044 
00045 class conntab_hash : public int_hash<connection>
00046 {
00047 public:
00048   conntab_hash() : int_hash<connection>(CTB_MAX_BITS) {}
00049 };
00050 
00052 
00053 connection_table::connection_table(int heartbeats_until_expiration,
00054     int freshness_period, int ghost_period)
00055 : _connection_list(new conntab_hash),
00056   _connlock(new mutex),
00057   _heartbeats_until_expiration(heartbeats_until_expiration),
00058   _freshness_period(freshness_period),
00059   _ghost_period(ghost_period)
00060 {}
00061 
00062 connection_table::~connection_table()
00063 {
00064   WHACK(_connlock);
00065   WHACK(_connection_list);
00066 }
00067 
00068 int connection_table::connections_on_primary(const transport_id &real_id,
00069     const transport_id &ignored_link_id, int primary_id)
00070 { return count_users(real_id, ignored_link_id, primary_id, PRIMARY_ID); }
00071 
00072 void connection_table::complain_missing_connection
00073     (const connection_id &conn_id, const istring &where) const
00074 {
00075   istring to_print = timestamp(true, true) + where
00076       + istring(istring::SPRINTF, "connection %d cannot be found.",
00077             conn_id.raw_id());
00078   EMERGENCY_LOG(program_wide_logger(), to_print);
00079 }
00080 
00081 connection_id connection_table::find_state(const transport_id &real_id,
00082     connection::states to_find, int low_level)
00083 {
00084   FUNCDEF("find_state");
00085   if (!real_id) return 0;
00086   AUTO_LOCK;
00087   for (int i = 0; i < _connection_list->ids().elements(); i++) {
00088     int curr_id = _connection_list->ids()[i];
00089     connection *conn = _connection_list->find(curr_id);
00090     if (conn && conn->alive && (conn->real_transport_id == real_id)
00091           && (conn->state() == to_find) ) {
00092       // if they specified a "don't care" id, then this connection will work.
00093       if (negative(low_level)) return conn->conn_id();
00094       // the low-level id must match this candidate's.
00095       if (conn->low_level[PRIMARY_ID] == low_level) return conn->conn_id();
00096     }
00097   }
00098   return 0;
00099 }
00100 
00101 bool connection_table::connected(const connection_id &conn_id)
00102 {
00103   FUNCDEF("connected");
00104   if (!conn_id) return false;
00105   AUTO_LOCK;
00106   connection *conn = _connection_list->find(conn_id.raw_id());
00107   if (!conn) return false;
00108   return conn->connected();
00109 }
00110 
00111 bool connection_table::transport_connected(const transport_id &tran_id)
00112 {
00113   FUNCDEF("transport_connected");
00114   if (!tran_id) return false;
00115   bool connected = false;
00116   AUTO_LOCK;
00117   for (int i = 0; i < _connection_list->ids().elements(); i++) {
00118     int curr_id = _connection_list->ids()[i];
00119     connection *conn = _connection_list->find(curr_id);
00120     if (!conn) continue;
00121 //if not conn, that's an error.  complain about it.
00122     if (conn->alive && (conn->real_transport_id == tran_id)
00123           && conn->connected()) {
00124       connected = true;
00125       break;
00126     } else if (conn->alive && (conn->local_transport_id == tran_id) 
00127           && conn->connected()) {
00128       connected = true;
00129       break;
00130     }
00131   }
00132   return connected;
00133 }
00134 
00135 bool connection_table::live(const connection_id &conn_id)
00136 {
00137   FUNCDEF("live");
00138   if (!conn_id) return false;
00139   AUTO_LOCK;
00140   connection *conn = _connection_list->find(conn_id.raw_id());
00141   if (!conn) return false;
00142   return conn->alive;
00143 }
00144 
00145 bool connection_table::reset_time(const connection_id &conn_id)
00146 {
00147   FUNCDEF("reset_time");
00148   if (!conn_id) return false;
00149   AUTO_LOCK;
00150   connection *conn = _connection_list->find(conn_id.raw_id());
00151   if (!conn) return false;
00152   conn->liveness.kabump();
00153   return true;
00154 }
00155 
00156 heartbeat connection_table::liveness(const connection_id &conn_id)
00157 {
00158   if (!conn_id) return heartbeat(0, 0);
00159   AUTO_LOCK;
00160   connection *conn = _connection_list->find(conn_id.raw_id());
00161   if (!conn) return heartbeat(0, 0);
00162   return conn->liveness;
00163 }
00164 
00165 bool connection_table::set_liveness(const connection_id &conn_id,
00166     const heartbeat &new_liveness)
00167 {
00168   if (!conn_id) return false;
00169   AUTO_LOCK;
00170   connection *conn = _connection_list->find(conn_id.raw_id());
00171   if (!conn) return false;
00172   conn->liveness = new_liveness;
00173   return true;
00174 }
00175 
00176 bool connection_table::record_request(const connection_id &conn_id)
00177 {
00178   FUNCDEF("record_request");
00179   if (!conn_id) return false;
00180   AUTO_LOCK;
00181   connection *conn = _connection_list->find(conn_id.raw_id());
00182   if (!conn) return false;
00183   conn->liveness.made_request();
00184   return true;
00185 }
00186 
00187 connection_id connection_table::find_linked(sides side,
00188     const transport_id &tran_id)
00189 {
00190   if (!tran_id) return 0;
00191   AUTO_LOCK;
00192   for (int i = 0; i < _connection_list->ids().elements(); i++) {
00193     int curr_id = _connection_list->ids()[i];
00194     connection *conn = _connection_list->find(curr_id);
00195     if (conn && conn->alive
00196         && ( ((side == REMOTE_LINK) && (conn->remote_transport_id == tran_id))
00197           || ((side == LOCAL_LINK) && (conn->local_transport_id == tran_id))
00198           || ((side == LOCAL_REAL) && (conn->real_transport_id == tran_id)))) {
00199       connection_id found = conn->conn_id();
00200       return found;
00201     }
00202   }
00203   // never found an appropriate one.
00204   return 0;
00205 }
00206 
00207 connection_id connection_table::find_low_level
00208     (network_address::address_type type, int low_level_id, int index)
00209 {
00210   FUNCDEF("find_low_level");
00211   AUTO_LOCK;
00212   if (!low_level_id) return 0;
00213   // simple-minded checks.
00214   if (index < 0) index = 0;
00215   if (index > 1) index = 1;
00216   for (int i = 0; i < _connection_list->ids().elements(); i++) {
00217     int curr_id = _connection_list->ids()[i];
00218     connection *conn = _connection_list->find(curr_id);
00219     if (conn && conn->alive && (conn->remote_host.type() == type)
00220         && (conn->low_level[index] == low_level_id) )
00221       return conn->conn_id();
00222   }
00223   return 0;
00224 }
00225 
00226 connection_id connection_table::find_destination(const transport_id &real_id,
00227     const network_address &destination)
00228 {
00229   FUNCDEF("find [id/destination]");
00230   if (!real_id) return 0;
00231   AUTO_LOCK;
00232   for (int i = 0; i < _connection_list->ids().elements(); i++) {
00233     int curr_id = _connection_list->ids()[i];
00234     connection *conn = _connection_list->find(curr_id);
00235     if (conn && (conn->remote_host == destination)
00236         && (conn->real_transport_id == real_id)
00237         && conn->alive) {
00238       connection_id found = conn->conn_id();
00239       return found;
00240     }
00241   }
00242   return 0;
00243 }
00244 
00245 int connection_table::get_low_level_id(const transport_id &real_id, int index)
00246 {
00247   FUNCDEF("get_low_level_id");
00248   AUTO_LOCK;
00249   if (!real_id) return 0;
00250   // simple-minded checks.
00251   if (index < 0) index = 0;
00252   if (index > 1) index = 1;
00253   for (int i = 0; i < _connection_list->ids().elements(); i++) {
00254     int curr_id = _connection_list->ids()[i];
00255     connection *conn = _connection_list->find(curr_id);
00256     if (conn && conn->connected()
00257         && (conn->real_transport_id == real_id)
00258         && conn->alive) {
00259       int found = conn->low_level[index];
00260       return found;
00261     }
00262   }
00263   return 0;
00264 }
00265 
00266 bool connection_table::owns_connection(const connection_id &conn_id,
00267     const transport_id &link_id)
00268 {
00269   FUNCDEF("owns_connection");
00270   AUTO_LOCK;
00271   connection to_fill;
00272   if (!find(conn_id.raw_id(), to_fill)) return false;
00273   return to_fill.local_transport_id == link_id;
00274 }
00275 
00276 int connection_table::count_users(const transport_id &real_id,
00277     const transport_id &ignored_link_id, int low_level_id, int index)
00278 {
00279   FUNCDEF("count_users");
00280   AUTO_LOCK;
00281   // simple-minded checks.
00282   if (!low_level_id) return 0;  // zero is an invalid id.
00283   if (!real_id) return 0;
00284   if (index < 0) index = 0;  // keep index in range.
00285   if (index > 1) index = 1;
00286   int users_counted = 0;
00287   for (int i = 0; i < _connection_list->ids().elements(); i++) {
00288     int curr_id = _connection_list->ids()[i];
00289     connection *conn = _connection_list->find(curr_id);
00290     if (conn && conn->alive && (conn->real_transport_id == real_id)
00291         && (conn->low_level[index] == low_level_id)
00292         && (conn->state() != connection::DISCONNECTED) 
00293         && (conn->local_transport_id != ignored_link_id) )
00294       users_counted++;
00295   }
00296   return users_counted;
00297 }
00298 
00299 connection_id connection_table::update(const connection &to_update)
00300 {
00301   FUNCDEF("update");
00302   if (!to_update.conn_id()) return 0;
00303   AUTO_LOCK;
00304   connection *conn = _connection_list->find(to_update.conn_id().raw_id());
00305   if (!conn) {
00306     FUNCTION(func);
00307     complain_missing_connection(to_update.conn_id(), function_name);
00308     return 0;
00309   }
00310   *conn = to_update;
00311   return to_update.conn_id();
00312 }
00313 
00314 //hmmm: get rid of whole dead connections trammeling along behind stuff.
00315 //      just be honest about when connections get whacked.  they already
00316 //      have a built in timeout mechanism and such...
00317 
00318 bool connection_table::find(const connection_id &conn_id, connection &to_fill)
00319 {
00320   FUNCDEF("find [id/conn]");
00321   to_fill = connection();  // reset the contents.
00322   if (!conn_id) return false;
00323   AUTO_LOCK;
00324   connection *conn = _connection_list->find(conn_id.raw_id());
00325   if (!conn) return false;
00326   if (conn->alive) {
00327     to_fill = *conn;
00328     return true;
00329   }
00330   return false;
00331 }
00332 
00333 connection_id connection_table::add(const connection &to_add_in)
00334 {
00335   FUNCDEF("add");
00336   AUTO_LOCK;
00337   connection to_add = to_add_in;  // copy so we can blast the contents.
00338   if (!to_add.conn_id()) {
00339     LOG("no id was assigned for connection; failing it.");
00340     return 0;
00341   }
00342   to_add.alive = true;
00343   to_add.liveness = heartbeat(_heartbeats_until_expiration, _freshness_period);
00344   connection_id found = find_linked(LOCAL_LINK, to_add.local_transport_id);
00345   if (!found) {
00346     // that transport doesn't have a connection yet.
00347     to_add.state(connection::DISCONNECTED);
00348     // create a new connection object and fill it out.
00349     connection *the_conn = new connection(to_add);
00350     _connection_list->add(the_conn->conn_id().raw_id(), the_conn);
00351       // append new entry to the connection list.
00352   } else {
00353 #ifdef DEBUG_CONNECTION_TABLE
00354     LOG(istring(istring::SPRINTF, "found existing connection %d!", found));
00355 #endif
00356     return 0;
00357   }
00358   return to_add.conn_id();
00359 }
00360 
00361 bool connection_table::zap(const connection_id &conn_id)
00362 {
00363   FUNCDEF("zap");
00364   if (!conn_id) return false;
00365   AUTO_LOCK;
00366   connection *conn = _connection_list->find(conn_id.raw_id());
00367   if (!conn) return false;
00368   conn->alive = false;
00369   conn->liveness = heartbeat(_heartbeats_until_expiration,
00370       _ghost_period / _heartbeats_until_expiration);
00371   return true;
00372 }
00373 
00374 void connection_table::clear_dead()
00375 {
00376   FUNCDEF("clear_dead");
00377   AUTO_LOCK;
00378   for (int i = 0; i < _connection_list->ids().elements(); i++) {
00379     int curr_id = _connection_list->ids()[i];
00380     connection *conn = _connection_list->find(curr_id);
00381 
00382     if (!conn) continue;
00383 //THAT IS A BAD ERROR ABOVE...
00384 //hmmm: at least remove the id!!!
00385 //hmmm: complain about mistakes like this.
00386 
00387     if (!conn->alive) {
00388       if (conn->liveness.dead()) {
00389         // this one has had it.
00390 #ifdef DEBUG_CONNECTION_TABLE
00391         LOG(istring(istring::SPRINTF, "clearing out stale, dead "
00392             "connection %d.", conn->conn_id()));
00393 #endif
00394         _connection_list->zap(curr_id);
00395         i--;  // skip back to where loop expects.
00396       } else if (conn->liveness.due()) {
00397 #ifdef DEBUG_CONNECTION_TABLE
00398         LOG(istring(istring::SPRINTF, "making new heartbeat request on "
00399              "connection %d.", curr_id));
00400 #endif
00401         conn->liveness.made_request();  // next cycle.
00402       }
00403     }
00404   }
00405 }
00406 
00407 void connection_table::show_connections(istring &output, int indentation,
00408     bool alive_only)
00409 {
00410   AUTO_LOCK;
00411   istring indent = string_manipulation::indentation(indentation);
00412   bool any_printed = false;
00413   for (int i = 0; i < _connection_list->ids().elements(); i++) {
00414     int curr_id = _connection_list->ids()[i];
00415     connection *conn = _connection_list->find(curr_id);
00416     if (conn && (!alive_only || conn->alive) ) {
00417       output += indent + conn->text_form() + log_base::platform_ending();
00418       any_printed = true;
00419     }
00420   }
00421   if (!any_printed)
00422     output += indent + istring("No connections exist.")
00423         + log_base::platform_ending();
00424 }
00425 
00426 bool connection_table::set_conn_state(const transport_id &tran_id,
00427     connection::states state)
00428 {
00429   FUNCDEF("set_conn_state");
00430   if (!tran_id) return false;
00431   AUTO_LOCK;
00432   connection_id found = find_linked(LOCAL_LINK, tran_id);
00433   if (!found) return false;
00434   connection *conn = _connection_list->find(found.raw_id());
00435   if (!conn) return false;
00436   bool to_return = false;
00437   if (conn->alive) {
00438     conn->state(state);
00439     to_return = true; 
00440     if (state == connection::DISCONNECTED)
00441       conn->low_level[PRIMARY_ID] = 0;  // not used now.
00442   }
00443   return to_return;
00444 }
00445 
00446 int connection_table::set_conn_states(const transport_id &real_id,
00447     connection::states state)
00448 {
00449   FUNCDEF("set_conn_states");
00450   AUTO_LOCK;
00451   int conns_processed = 0;
00452   for (int i = 0; i < _connection_list->ids().elements(); i++) {
00453     int curr_id = _connection_list->ids()[i];
00454     connection *conn = _connection_list->find(curr_id);
00455     if (conn && (conn->real_transport_id == real_id)) {
00456       conn->state(state);
00457       conns_processed++;
00458     }
00459   }
00460   return conns_processed;
00461 }
00462 
00463 connection::states connection_table::get_conn_state(const transport_id &tran_id)
00464 {
00465   FUNCDEF("get_conn_state");
00466   connection::states to_return = connection::DISCONNECTED;
00467   if (!tran_id) return to_return;
00468   AUTO_LOCK;
00469   connection_id found = find_linked(LOCAL_LINK, tran_id);
00470   if (!found) return to_return;
00471   connection *conn = _connection_list->find(found.raw_id());
00472   if (!conn) return to_return;
00473   if (conn->alive)
00474     to_return = conn->state();
00475   return to_return;
00476 }
00477 
00478 transport_id connection_table::socket_to_real_transport
00479     (network_address::address_type type, int socket, transport_id &linked_id)
00480 {
00481   FUNCDEF("socket_to_real_transport");
00482   AUTO_LOCK;
00483   linked_id = 0;  // reset.
00484   bool is_linked;  // is the transport a linked one?
00485   connection_id conn_id_found = find_low_level(type, socket, PRIMARY_ID);
00486   if (conn_id_found.raw_id()) is_linked = true;
00487   else {
00488     // we know it's not a linked transport now.
00489     is_linked = false;
00490     conn_id_found = find_low_level(type, socket, SECONDARY_ID);
00491     if (!conn_id_found) {
00492 #ifdef DEBUG_CONNECTION_TABLE
00493       LOG(istring(istring::SPRINTF, "can't find connection for "
00494           "socket %d in primary or secondary low-level ids; "
00495           "failing out.", socket));
00496 #endif
00497       return 0;
00498     }
00499   }
00500   // lookup the connection we got.
00501   GET_CONN_ENTRY((*this), conn_id_found);
00502   if (!conn_found) return 0;
00503 
00504   // if it's a linked transport we also give back that id.
00505   if (is_linked) linked_id = conn_entry.local_transport_id;
00506   // return the real id.
00507   return conn_entry.real_transport_id;
00508 }
00509 
00510 int_set connection_table::find_related_conns(int low_level_primary,
00511     int real_transport)
00512 {
00513   FUNCDEF("find_related_conns");
00514   AUTO_LOCK;
00515   if (!low_level_primary) return int_set();
00516   if (!real_transport) return int_set();
00517   int_set to_return;
00518   for (int i = 0; i < _connection_list->ids().elements(); i++) {
00519     int curr_id = _connection_list->ids()[i];
00520     connection *conn = _connection_list->find(curr_id);
00521     if (conn && conn->alive && (conn->real_transport_id == real_transport)
00522         && (conn->low_level[PRIMARY_ID] == low_level_primary) )
00523       to_return += curr_id;
00524   }
00525   return to_return;
00526 }
00527 
00528 int_set connection_table::find_live_reals()
00529 {
00530   FUNCDEF("find_live_reals");
00531   AUTO_LOCK;
00532   int_set to_return;
00533   for (int i = 0; i < _connection_list->ids().elements(); i++) {
00534     int curr_id = _connection_list->ids()[i];
00535     connection *conn = _connection_list->find(curr_id);
00536     if (conn && conn->alive) to_return += conn->real_transport_id.raw_id();
00537   }
00538   return to_return;
00539 }
00540 
00541 connection_id connection_table::find_dupe(int real_transport,
00542     int low_level_primary, const network_address &sender,
00543     int other_side_transport)
00544 {
00545   FUNCDEF("find_dupe");
00546   AUTO_LOCK;
00547   for (int i = 0; i < _connection_list->ids().elements(); i++) {
00548     int curr_id = _connection_list->ids()[i];
00549     connection *conn = _connection_list->find(curr_id);
00550     if (conn && conn->alive) {
00551       if ( (conn->low_level[PRIMARY_ID] == low_level_primary)
00552 && (conn->real_transport_id == real_transport)
00553           && (conn->remote_host == sender)
00554           && (conn->remote_transport_id == other_side_transport) )
00555        return conn->conn_id();
00556     }
00557   }
00558   return 0;
00559 }
00560 
00561 
00562 #endif //CONNECTION_TABLE_IMPLEMENTATION_FILE
00563 

Generated on Fri Sep 5 04:28:25 2008 for HOOPLE Libraries by  doxygen 1.5.1