client.cpp

00001 
00002 /***************************************************************************
00003  *  client.cpp - Fawkes network client
00004  *
00005  *  Created: Tue Nov 21 18:44:58 2006
00006  *  Copyright  2006-2009  Tim Niemueller [www.niemueller.de]
00007  *
00008  ****************************************************************************/
00009 
00010 /*  This program is free software; you can redistribute it and/or modify
00011  *  it under the terms of the GNU General Public License as published by
00012  *  the Free Software Foundation; either version 2 of the License, or
00013  *  (at your option) any later version. A runtime exception applies to
00014  *  this software (see LICENSE.GPL_WRE file mentioned below for details).
00015  *
00016  *  This program is distributed in the hope that it will be useful,
00017  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00018  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00019  *  GNU Library General Public License for more details.
00020  *
00021  *  Read the full text in the LICENSE.GPL_WRE file in the doc directory.
00022  */
00023 
00024 #include <netcomm/fawkes/client.h>
00025 #include <netcomm/fawkes/client_handler.h>
00026 #include <netcomm/fawkes/message_queue.h>
00027 #include <netcomm/fawkes/transceiver.h>
00028 #include <netcomm/socket/stream.h>
00029 #include <netcomm/utils/exceptions.h>
00030 
00031 #include <core/threading/thread.h>
00032 #include <core/threading/mutex.h>
00033 #include <core/threading/mutex_locker.h>
00034 #include <core/threading/wait_condition.h>
00035 #include <core/exceptions/system.h>
00036 
00037 #include <list>
00038 #include <cstring>
00039 #include <cstdlib>
00040 #include <unistd.h>
00041 
00042 namespace fawkes {
00043 
00044 /** @class HandlerAlreadyRegisteredException netcomm/fawkes/client.h
00045  * Client handler has already been registered.
00046  * Only a single client handler can be registered per component. If you try
00047  * to register a handler where there is already a handler this exception
00048  * is thrown.
00049  * @ingroup NetComm
00050  */
00051 
00052 /** Costructor. */
00053 HandlerAlreadyRegisteredException::HandlerAlreadyRegisteredException()
00054   : Exception("A handler for this component has already been registered")
00055 {
00056 }
00057 
00058 
00059 /** Fawkes network client send thread.
00060  * Spawned by the FawkesNetworkClient to handle outgoing traffic.
00061  *
00062  * @ingroup NetComm
00063  * @author Tim Niemueller
00064  */
00065 class FawkesNetworkClientSendThread : public Thread
00066 {
00067  public:
00068 
00069   /** Constructor.
00070    * @param s client stream socket
00071    * @param parent parent FawkesNetworkClient instance
00072    */
00073   FawkesNetworkClientSendThread(StreamSocket *s, FawkesNetworkClient *parent)
00074     : Thread("FawkesNetworkClientSendThread", Thread::OPMODE_WAITFORWAKEUP)
00075   {
00076     __s = s;
00077     __parent = parent;
00078     __outbound_mutex    = new Mutex();
00079     __outbound_msgqs[0] = new FawkesNetworkMessageQueue();
00080     __outbound_msgqs[1] = new FawkesNetworkMessageQueue();
00081     __outbound_active   = 0;
00082     __outbound_msgq     = __outbound_msgqs[0];
00083   }
00084 
00085   /** Destructor. */
00086   ~FawkesNetworkClientSendThread()
00087   {
00088     for (unsigned int i = 0; i < 2; ++i) {
00089       while ( ! __outbound_msgqs[i]->empty() ) {
00090         FawkesNetworkMessage *m = __outbound_msgqs[i]->front();
00091         m->unref();
00092         __outbound_msgqs[i]->pop();
00093       }
00094     }
00095     delete __outbound_msgqs[0];
00096     delete __outbound_msgqs[1];
00097     delete __outbound_mutex;
00098   }
00099 
00100   virtual void once()
00101   {
00102     __parent->set_send_slave_alive();
00103   }
00104 
00105   virtual void loop()
00106   {
00107     if ( ! __parent->connected() )  return;
00108 
00109     while ( __outbound_havemore ) {
00110       __outbound_mutex->lock();
00111       __outbound_havemore = false;
00112       FawkesNetworkMessageQueue *q = __outbound_msgq;
00113       __outbound_active = 1 - __outbound_active;
00114       __outbound_msgq = __outbound_msgqs[__outbound_active];
00115       __outbound_mutex->unlock();
00116 
00117       if ( ! q->empty() ) {
00118         try {
00119           FawkesNetworkTransceiver::send(__s, q);
00120         } catch (ConnectionDiedException &e) {
00121           __parent->connection_died();
00122           exit();
00123         }
00124       }
00125     }
00126   }
00127 
00128   /** Force sending of messages.
00129    * All messages are sent out immediately, if loop is not running already anyway.
00130    */
00131   void force_send()
00132   {
00133     if ( loop_mutex->try_lock() ) {
00134       loop();
00135       loop_mutex->unlock();
00136     }
00137   }
00138 
00139   /** Enqueue message to send and take ownership.
00140    * This method takes ownership of the message. If you want to use the message
00141    * after enqueing you must reference:
00142    * @code
00143    * message->ref();
00144    * send_slave->enqueue(message);
00145    * // message can now still be used
00146    * @endcode
00147    * Without extra referencing the message may not be used after enqueuing.
00148    * @param message message to send
00149    */
00150   void enqueue(FawkesNetworkMessage *message)
00151   {
00152     __outbound_mutex->lock();
00153     __outbound_msgq->push(message);
00154     __outbound_havemore = true;
00155     __outbound_mutex->unlock();
00156     wakeup();
00157   }
00158 
00159  /** Stub to see name in backtrace for easier debugging. @see Thread::run() */
00160  protected: virtual void run() { Thread::run(); }
00161 
00162  private:
00163   StreamSocket *__s;
00164   FawkesNetworkClient *__parent;
00165   Mutex                     *__outbound_mutex;
00166   unsigned int               __outbound_active;
00167   bool                       __outbound_havemore;
00168   FawkesNetworkMessageQueue *__outbound_msgq;
00169   FawkesNetworkMessageQueue *__outbound_msgqs[2];
00170   
00171 };
00172 
00173 
00174 /**  Fawkes network client receive thread.
00175  * Spawned by the FawkesNetworkClient to handle incoming traffic.
00176  *
00177  * @ingroup NetComm
00178  * @author Tim Niemueller
00179  */
00180 class FawkesNetworkClientRecvThread : public Thread
00181 {
00182  public:
00183   /** Constructor.
00184    * @param s client stream socket
00185    * @param parent parent FawkesNetworkClient instance
00186    * @param recv_mutex receive mutex, locked while messages are received
00187    */
00188   FawkesNetworkClientRecvThread(StreamSocket *s, FawkesNetworkClient *parent,
00189                                 Mutex *recv_mutex)
00190     : Thread("FawkesNetworkClientRecvThread")
00191   {
00192     __s = s;
00193     __parent = parent;
00194     __inbound_msgq = new FawkesNetworkMessageQueue();
00195     __recv_mutex = recv_mutex;
00196   }
00197 
00198   /** Destructor. */
00199   ~FawkesNetworkClientRecvThread()
00200   {
00201     while ( ! __inbound_msgq->empty() ) {
00202       FawkesNetworkMessage *m = __inbound_msgq->front();
00203       m->unref();
00204       __inbound_msgq->pop();
00205     }
00206     delete __inbound_msgq;
00207   }
00208 
00209   /** Receive and process messages. */
00210   void recv()
00211   {
00212     std::list<unsigned int> wakeup_list;
00213 
00214     try {
00215       FawkesNetworkTransceiver::recv(__s, __inbound_msgq);
00216 
00217       MutexLocker lock(__recv_mutex);
00218 
00219       __inbound_msgq->lock();
00220       while ( ! __inbound_msgq->empty() ) {
00221         FawkesNetworkMessage *m = __inbound_msgq->front();
00222         wakeup_list.push_back(m->cid());
00223         __parent->dispatch_message(m);
00224         m->unref();
00225         __inbound_msgq->pop();
00226       }
00227       __inbound_msgq->unlock();
00228 
00229       lock.unlock();
00230     
00231       wakeup_list.sort();
00232       wakeup_list.unique();
00233       for (std::list<unsigned int>::iterator i = wakeup_list.begin(); i != wakeup_list.end(); ++i) {
00234         __parent->wake_handlers(*i);
00235       }
00236     } catch (ConnectionDiedException &e) {
00237       throw;
00238     }
00239   }
00240 
00241   virtual void once()
00242   {
00243     __parent->set_recv_slave_alive();
00244   }
00245 
00246   virtual void loop()
00247   {
00248     // just return if not connected
00249     if (! __s ) return;
00250 
00251     short p = 0;
00252     try {
00253       p = __s->poll();
00254     } catch (InterruptedException &e) {
00255       return;
00256     }
00257 
00258     if ( (p & Socket::POLL_ERR) ||
00259          (p & Socket::POLL_HUP) ||
00260          (p & Socket::POLL_RDHUP)) {
00261       __parent->connection_died();
00262       exit();
00263     } else if ( p & Socket::POLL_IN ) {
00264       // Data can be read
00265       try {
00266         recv();
00267       } catch (ConnectionDiedException &e) {
00268         __parent->connection_died();
00269         exit();
00270       }
00271     }
00272   }
00273 
00274  /** Stub to see name in backtrace for easier debugging. @see Thread::run() */
00275  protected: virtual void run() { Thread::run(); }
00276 
00277  private:
00278   StreamSocket *__s;
00279   FawkesNetworkClient *__parent;
00280   FawkesNetworkMessageQueue *  __inbound_msgq;
00281   Mutex *__recv_mutex;
00282 };
00283 
00284 
00285 /** @class FawkesNetworkClient netcomm/fawkes/client.h
00286  * Simple Fawkes network client. Allows access to a remote instance via the
00287  * network. Encapsulates all needed interaction with the network.
00288  *
00289  * @ingroup NetComm
00290  * @author Tim Niemueller
00291  */
00292 
00293 /** Constructor.
00294  * @param hostname remote host to connect to.
00295  * @param port port to connect to.
00296  * @param ip optional: use ip to connect, and hostname for cosmetic purposes
00297  */
00298 FawkesNetworkClient::FawkesNetworkClient(const char *hostname, unsigned short int port, const char *ip)
00299 {
00300   __hostname = strdup(hostname);
00301   __ip = ip ? strdup(ip) : NULL;
00302   __port     = port;
00303 
00304   s = NULL;
00305   __send_slave = NULL;
00306   __recv_slave = NULL;
00307 
00308   connection_died_recently = false;
00309   __send_slave_alive = false;
00310   __recv_slave_alive = false;
00311 
00312   slave_status_mutex = new Mutex();
00313 
00314   _id     = 0;
00315   _has_id = false;
00316 
00317   __recv_mutex          = new Mutex();
00318   __recv_waitcond       = new WaitCondition(__recv_mutex);
00319   __connest_mutex       = new Mutex();
00320   __connest_waitcond    = new WaitCondition(__connest_mutex);
00321   __connest             = false;
00322   __connest_interrupted = false;
00323 }
00324 
00325 
00326 /** Constructor.
00327  * Note, you cannot call the connect() without parameters the first time you
00328  * establish an connection when using this ctor!
00329  */
00330 FawkesNetworkClient::FawkesNetworkClient()
00331 {
00332   __hostname = NULL;
00333   __ip = NULL;
00334   __port     = 0;
00335 
00336   s = NULL;
00337   __send_slave = NULL;
00338   __recv_slave = NULL;
00339 
00340   connection_died_recently = false;
00341   __send_slave_alive = false;
00342   __recv_slave_alive = false;
00343 
00344   slave_status_mutex = new Mutex();
00345 
00346   _id     = 0;
00347   _has_id = false;
00348 
00349   __recv_mutex          = new Mutex();
00350   __recv_waitcond       = new WaitCondition(__recv_mutex);
00351   __connest_mutex       = new Mutex();
00352   __connest_waitcond    = new WaitCondition(__connest_mutex);
00353   __connest             = false;
00354   __connest_interrupted = false;
00355 }
00356 
00357 
00358 /** Constructor.
00359  * @param id id of the client.
00360  * @param hostname remote host to connect to.
00361  * @param port port to connect to.
00362  * @param ip optional: use ip to connect, and hostname for cosmetic purposes
00363  */
00364 FawkesNetworkClient::FawkesNetworkClient(unsigned int id, const char *hostname,
00365                                          unsigned short int port, const char *ip)
00366 {
00367   __hostname = strdup(hostname);
00368   __ip = ip ? strdup(ip) : NULL;
00369   __port     = port;
00370 
00371   s = NULL;
00372   __send_slave = NULL;
00373   __recv_slave = NULL;
00374 
00375   connection_died_recently = false;
00376   __send_slave_alive = false;
00377   __recv_slave_alive = false;
00378 
00379   slave_status_mutex = new Mutex();
00380 
00381   _id     = id;
00382   _has_id = true;
00383 
00384   __recv_mutex          = new Mutex();
00385   __recv_waitcond       = new WaitCondition(__recv_mutex);
00386   __connest_mutex       = new Mutex();
00387   __connest_waitcond    = new WaitCondition(__connest_mutex);
00388   __connest             = false;
00389   __connest_interrupted = false;
00390 }
00391 
00392 
00393 /** Destructor. */
00394 FawkesNetworkClient::~FawkesNetworkClient()
00395 {
00396   disconnect();
00397 
00398   delete s;
00399   if (__hostname) free(__hostname);
00400   if (__ip) free(__ip);
00401   delete slave_status_mutex;
00402 
00403   delete __connest_waitcond;
00404   delete __connest_mutex;
00405   delete __recv_waitcond;
00406   delete __recv_mutex;
00407 }
00408 
00409 
00410 /** Connect to remote.
00411  * @exception SocketException thrown by Socket::connect()
00412  * @exception NullPointerException thrown if hostname has not been set
00413  */
00414 void
00415 FawkesNetworkClient::connect()
00416 {
00417   if ( __hostname == NULL && __ip == NULL) {
00418     throw NullPointerException("Hostname not set. Cannot connect.");
00419   }
00420 
00421   if ( s != NULL ) {
00422     disconnect();
00423   }
00424 
00425 
00426   connection_died_recently = false;
00427 
00428   try {
00429     s = new StreamSocket();
00430     s->connect(__ip ? __ip : __hostname, __port);
00431     __send_slave = new FawkesNetworkClientSendThread(s, this);
00432     __send_slave->start();
00433     __recv_slave = new FawkesNetworkClientRecvThread(s, this, __recv_mutex);
00434     __recv_slave->start();
00435   } catch (SocketException &e) {
00436     connection_died_recently = true;
00437     if ( __send_slave ) {
00438       __send_slave->cancel();
00439       __send_slave->join();
00440       delete __send_slave;
00441       __send_slave = NULL;
00442     }
00443     if ( __recv_slave ) {
00444       __recv_slave->cancel();
00445       __recv_slave->join();
00446       delete __recv_slave;
00447       __recv_slave = NULL;
00448     }
00449     __send_slave_alive = false;
00450     __recv_slave_alive = false;
00451     delete s;
00452     s = NULL;
00453     throw;
00454   }
00455 
00456   __connest_mutex->lock();
00457   while ( ! __connest && ! __connest_interrupted ) {
00458     __connest_waitcond->wait();
00459   }
00460   bool interrupted = __connest_interrupted;
00461   __connest_interrupted = false;
00462   __connest_mutex->unlock();
00463   if ( interrupted ) {
00464     throw InterruptedException("FawkesNetworkClient::connect()");
00465   }
00466 
00467   notify_of_connection_established();
00468 }
00469 
00470 
00471 /** Connect to new host and port.
00472  * @param hostname new hostname to connect to
00473  * @param port new port to connect to
00474  * @see connect() Look there for more documentation and notes about possible
00475  * exceptions.
00476  */
00477 void
00478 FawkesNetworkClient::connect(const char *hostname, unsigned short int port)
00479 {
00480   connect(hostname, NULL, port);
00481 }
00482 
00483 /** Connect to new ip and port, and set hostname.
00484  * @param hostname remote host name
00485  * @param ip new ip to connect to
00486  * @param port new port to connect to
00487  * @see connect() Look there for more documentation and notes about possible
00488  * exceptions.
00489  */
00490 void
00491 FawkesNetworkClient::connect(const char *hostname, const char *ip, unsigned short int port)
00492 {
00493   if (__hostname)  free(__hostname);
00494   if (__ip) free(__ip);
00495   __hostname = strdup(hostname);
00496   __ip = ip ? strdup(ip) : NULL;
00497   __port = port;
00498   connect();
00499 }
00500 
00501 /** Disconnect socket. */
00502 void
00503 FawkesNetworkClient::disconnect()
00504 {
00505   if ( s == NULL ) return;
00506 
00507   if ( __send_slave_alive ) {
00508     if ( ! connection_died_recently ) {
00509       __send_slave->force_send();
00510       // Give other side some time to read the messages just sent
00511       usleep(100000);
00512     }
00513     __send_slave->cancel();
00514     __send_slave->join();
00515     delete __send_slave;
00516     __send_slave = NULL;
00517   }
00518   if ( __recv_slave_alive ) {
00519     __recv_slave->cancel();
00520     __recv_slave->join();
00521     delete __recv_slave;
00522     __recv_slave = NULL;
00523   }
00524   __send_slave_alive = false;
00525   __recv_slave_alive = false;
00526   delete s;
00527   s = NULL;
00528 
00529   if (! connection_died_recently) {
00530     connection_died();
00531   }
00532 }
00533 
00534 
00535 /** Interrupt connect().
00536  * This is for example handy to interrupt in connection_died() before a
00537  * connection_established() event has been received.
00538  */
00539 void
00540 FawkesNetworkClient::interrupt_connect()
00541 {
00542   __connest_mutex->lock();
00543   __connest_interrupted = true;
00544   __connest_waitcond->wake_all();
00545   __connest_mutex->unlock();
00546 }
00547 
00548 
00549 /** Enqueue message to send.
00550  * This method takes ownership of the message. If you want to use the message
00551  * after enqueing you must reference:
00552  * @code
00553  * message->ref();
00554  * fawkes_network_client->enqueue(message);
00555  * // message can now still be used
00556  * @endcode
00557  * Without extra referencing the message may not be used after enqueuing.
00558  * @param message message to send
00559  */
00560 void
00561 FawkesNetworkClient::enqueue(FawkesNetworkMessage *message)
00562 {
00563   if (__send_slave)  __send_slave->enqueue(message);
00564 }
00565 
00566 
00567 /** Enqueue message to send and wait for answer. It is guaranteed that an
00568  * answer cannot be missed. However, if the component sends another message
00569  * (which is not the answer to the query) this will also trigger the wait
00570  * condition to be woken up. The component ID to wait for is taken from the
00571  * message.
00572  * This message also calls unref() on the message. If you want to use it
00573  * after enqueuing make sure you ref() before calling this method.
00574  * @param message message to send
00575  * @param timeout_sec timeout for the waiting operation in seconds, 0 to wait
00576  * forever (warning, this may result in a deadlock!)
00577  */
00578 void
00579 FawkesNetworkClient::enqueue_and_wait(FawkesNetworkMessage *message,
00580                                       unsigned int timeout_sec)
00581 {
00582   if (__send_slave && __recv_slave) {
00583     __recv_mutex->lock();
00584     if ( __recv_received.find(message->cid()) != __recv_received.end()) {
00585       __recv_mutex->unlock();
00586       unsigned int cid = message->cid();
00587       throw Exception("There is already a thread waiting for messages of "
00588                       "component id %u", cid);
00589     }
00590     __send_slave->enqueue(message);
00591     unsigned int cid = message->cid();
00592     __recv_received[cid] = false;
00593     while (!__recv_received[cid] && ! connection_died_recently) {
00594       if (!__recv_waitcond->reltimed_wait(timeout_sec, 0)) {
00595         __recv_received.erase(cid);
00596         __recv_mutex->unlock();
00597         throw TimeoutException("Timeout reached while waiting for incoming message "
00598                                "(outgoing was %u:%u)", message->cid(), message->msgid());
00599       }
00600     }
00601     __recv_received.erase(cid);
00602     __recv_mutex->unlock();
00603   } else {
00604     unsigned int cid = message->cid();
00605     unsigned int msgid = message->msgid();
00606     throw Exception("Cannot enqueue given message %u:%u, sender or "
00607                     "receiver missing", cid, msgid);
00608   }
00609 }
00610 
00611 
00612 /** Register handler.
00613  * Handlers are used to handle incoming packets. There may only be one handler per
00614  * component!
00615  * Cannot be called while processing a message.
00616  * @param handler handler to register
00617  * @param component_id component ID to register the handler for.
00618  */
00619 void
00620 FawkesNetworkClient::register_handler(FawkesNetworkClientHandler *handler,
00621                                       unsigned int component_id)
00622 {
00623   handlers.lock();
00624   if ( handlers.find(component_id) != handlers.end() ) {
00625     handlers.unlock();
00626     throw HandlerAlreadyRegisteredException();
00627   } else {
00628     handlers[component_id] = handler;
00629   }
00630   handlers.unlock();
00631 }
00632 
00633 
00634 /** Deregister handler.
00635  * Cannot be called while processing a message.
00636  * @param component_id component ID
00637  */
00638 void
00639 FawkesNetworkClient::deregister_handler(unsigned int component_id)
00640 {
00641   handlers.lock();
00642   if ( handlers.find(component_id) != handlers.end() ) {
00643     handlers[component_id]->deregistered(_id);
00644     handlers.erase(component_id);
00645   }
00646   handlers.unlock();
00647   __recv_mutex->lock();
00648   if (__recv_received.find(component_id) != __recv_received.end()) {
00649     __recv_received[component_id] = true;
00650     __recv_waitcond->wake_all();
00651   }
00652   __recv_mutex->unlock();
00653 }
00654 
00655 
00656 void
00657 FawkesNetworkClient::dispatch_message(FawkesNetworkMessage *m)
00658 {
00659   unsigned int cid = m->cid();
00660   handlers.lock();
00661   if (handlers.find(cid) != handlers.end()) {
00662     handlers[cid]->inbound_received(m, _id);
00663   }
00664   handlers.unlock();
00665 }
00666 
00667 
00668 void
00669 FawkesNetworkClient::wake_handlers(unsigned int cid)
00670 {
00671   __recv_mutex->lock();
00672   if (__recv_received.find(cid) != __recv_received.end()) {
00673     __recv_received[cid] = true;
00674   }
00675   __recv_waitcond->wake_all();
00676   __recv_mutex->unlock();
00677 }
00678 
00679 void
00680 FawkesNetworkClient::notify_of_connection_dead()
00681 {
00682   __connest_mutex->lock();
00683   __connest = false;
00684   __connest_mutex->unlock();
00685 
00686   handlers.lock();
00687   for ( HandlerMap::iterator i = handlers.begin(); i != handlers.end(); ++i ) {
00688     i->second->connection_died(_id);
00689   }
00690   handlers.unlock();
00691 
00692   __recv_mutex->lock();
00693   __recv_waitcond->wake_all();
00694   __recv_mutex->unlock();
00695 }
00696 
00697 void
00698 FawkesNetworkClient::notify_of_connection_established()
00699 {
00700   handlers.lock();
00701   for ( HandlerMap::iterator i = handlers.begin(); i != handlers.end(); ++i ) {
00702     i->second->connection_established(_id);
00703   }
00704   handlers.unlock();
00705 }
00706 
00707 
00708 void
00709 FawkesNetworkClient::connection_died()
00710 {
00711   connection_died_recently = true;
00712   notify_of_connection_dead();
00713 }
00714 
00715 
00716 void
00717 FawkesNetworkClient::set_send_slave_alive()
00718 {
00719   slave_status_mutex->lock();
00720   __send_slave_alive = true;
00721   if ( __send_slave_alive && __recv_slave_alive ) {
00722     __connest_mutex->lock();
00723     __connest = true;
00724     __connest_waitcond->wake_all();
00725     __connest_mutex->unlock();
00726   }
00727   slave_status_mutex->unlock();
00728 }
00729 
00730 
00731 void
00732 FawkesNetworkClient::set_recv_slave_alive()
00733 {
00734   slave_status_mutex->lock();
00735   __recv_slave_alive = true;
00736   if ( __send_slave_alive && __recv_slave_alive ) {
00737     __connest_mutex->lock();
00738     __connest = true;
00739     __connest_waitcond->wake_all();
00740     __connest_mutex->unlock();
00741   }
00742   slave_status_mutex->unlock();
00743 }
00744 
00745 
00746 /** Wait for messages for component ID.
00747  * This will wait for messages of the given component ID to arrive. The calling
00748  * thread is blocked until messages are available.
00749  * @param component_id component ID to monitor
00750  * @param timeout_sec timeout for the waiting operation in seconds, 0 to wait
00751  * forever (warning, this may result in a deadlock!)
00752  */
00753 void
00754 FawkesNetworkClient::wait(unsigned int component_id, unsigned int timeout_sec)
00755 {
00756   __recv_mutex->lock();
00757   if ( __recv_received.find(component_id) != __recv_received.end()) {
00758     __recv_mutex->unlock();
00759     throw Exception("There is already a thread waiting for messages of "
00760                     "component id %u", component_id);
00761   }
00762   __recv_received[component_id] = false;
00763   while (! __recv_received[component_id] && ! connection_died_recently) {
00764     if (!__recv_waitcond->reltimed_wait(timeout_sec, 0)) {
00765       __recv_received.erase(component_id);
00766       __recv_mutex->unlock();
00767       throw TimeoutException("Timeout reached while waiting for incoming message "
00768                              "(component %u)", component_id);
00769     }
00770   }
00771   __recv_received.erase(component_id);
00772   __recv_mutex->unlock();
00773 }
00774 
00775 
00776 /** Wake a waiting thread.
00777  * This will wakeup all threads currently waiting for the specified component ID.
00778  * This can be helpful to wake a sleeping thread if you received a signal.
00779  * @param component_id component ID for threads to wake up
00780  */
00781 void
00782 FawkesNetworkClient::wake(unsigned int component_id)
00783 {
00784   __recv_mutex->lock();
00785   if ( __recv_received.find(component_id) != __recv_received.end()) {
00786     __recv_received[component_id] = true;
00787   }
00788   __recv_waitcond->wake_all();
00789   __recv_mutex->unlock();
00790 }
00791 
00792 
00793 /** Check if connection is alive.
00794  * @return true if connection is alive at the moment, false otherwise
00795  */
00796 bool
00797 FawkesNetworkClient::connected() const throw()
00798 {
00799   return (! connection_died_recently && (s != NULL));
00800 }
00801 
00802 
00803 /** Check whether the client has an id.
00804  * @return true if client has an ID
00805  */
00806 bool
00807 FawkesNetworkClient::has_id() const
00808 {
00809   return _has_id;
00810 }
00811 
00812 
00813 /** Get the client's ID.
00814  * @return the ID
00815  */
00816 unsigned int
00817 FawkesNetworkClient::id() const
00818 {
00819   if ( !_has_id ) {
00820     throw Exception("Trying to get the ID of a client that has no ID");
00821   }
00822 
00823   return _id;
00824 }
00825 
00826 /** Get the client's hostname
00827  * @return hostname or NULL
00828  */
00829 const char *
00830 FawkesNetworkClient::get_hostname() const
00831 {
00832   return __hostname;
00833 }
00834 
00835 /** Get the client's ip
00836  * @return ip or NULL
00837  */
00838 const char *
00839 FawkesNetworkClient::get_ip() const
00840 {
00841   return __ip;
00842 }
00843 
00844 } // end namespace fawkes

Generated on Tue Feb 22 13:32:28 2011 for Fawkes API by  doxygen 1.4.7