LCOV - code coverage report
Current view: top level - eq/client - client.cpp (source / functions) Hit Total Coverage
Test: lcov2.info Lines: 95 190 50.0 %
Date: 2014-06-18 Functions: 22 29 75.9 %

          Line data    Source code
       1             : 
       2             : /* Copyright (c) 2005-2014, Stefan Eilemann <eile@equalizergraphics.com>
       3             :  *                    2012, Daniel Nachbaur <danielnachbaur@gmail.com>
       4             :  *
       5             :  * This library is free software; you can redistribute it and/or modify it under
       6             :  * the terms of the GNU Lesser General Public License version 2.1 as published
       7             :  * by the Free Software Foundation.
       8             :  *
       9             :  * This library is distributed in the hope that it will be useful, but WITHOUT
      10             :  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
      11             :  * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
      12             :  * details.
      13             :  *
      14             :  * You should have received a copy of the GNU Lesser General Public License
      15             :  * along with this library; if not, write to the Free Software Foundation, Inc.,
      16             :  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
      17             :  */
      18             : 
      19             : #include "client.h"
      20             : 
      21             : #include "commandQueue.h"
      22             : #include "config.h"
      23             : #include "node.h"
      24             : #include "global.h"
      25             : #include "init.h"
      26             : #include "nodeFactory.h"
      27             : #include "server.h"
      28             : 
      29             : #include <eq/fabric/commands.h>
      30             : #include <eq/fabric/configVisitor.h>
      31             : #include <eq/fabric/leafVisitor.h>
      32             : #include <eq/fabric/elementVisitor.h>
      33             : #include <eq/fabric/nodeType.h>
      34             : #include <eq/fabric/view.h>
      35             : 
      36             : #include <co/iCommand.h>
      37             : #include <co/connection.h>
      38             : #include <co/connectionDescription.h>
      39             : #include <co/global.h>
      40             : #include <lunchbox/dso.h>
      41             : 
      42             : #ifdef WIN32_API
      43             : #  include <direct.h>  // for chdir
      44             : #  define chdir _chdir
      45             : #endif
      46             : 
      47             : namespace eq
      48             : {
      49             : namespace
      50             : {
      51             : typedef stde::hash_set< Server* > ServerSet;
      52             : }
      53             : 
      54             : /** @cond IGNORE */
      55             : namespace detail
      56             : {
      57          15 : class Client
      58             : {
      59             : public:
      60          15 :     Client()
      61             :         : queue( co::Global::getCommandQueueLimit( ))
      62             :         , modelUnit( EQ_UNDEFINED_UNIT )
      63          15 :         , running( false )
      64          15 :     {}
      65             : 
      66             :     CommandQueue queue; //!< The command->node command queue.
      67             :     Strings activeLayouts;
      68             :     ServerSet localServers;
      69             :     std::string gpuFilter;
      70             :     float modelUnit;
      71             :     bool running;
      72             : };
      73             : }
      74             : 
      75             : typedef co::CommandFunc<Client> ClientFunc;
      76             : 
      77             : static co::ConnectionPtr _startLocalServer();
      78             : static void _joinLocalServer();
      79             : 
      80             : typedef co::Connection* (*eqsStartLocalServer_t)( const std::string& file );
      81             : typedef void (*eqsJoinLocalServer_t)();
      82             : 
      83             : typedef fabric::Client Super;
      84             : /** @endcond */
      85             : 
      86          15 : Client::Client()
      87             :         : Super()
      88          15 :         , _impl( new detail::Client )
      89             : {
      90             :     registerCommand( fabric::CMD_CLIENT_EXIT,
      91          15 :                      ClientFunc( this, &Client::_cmdExit ), &_impl->queue );
      92             : 
      93          15 :     LBVERB << "New client at " << (void*)this << std::endl;
      94          15 : }
      95             : 
      96          44 : Client::~Client()
      97             : {
      98          15 :     LBVERB << "Delete client at " << (void*)this << std::endl;
      99          15 :     LBASSERT( isClosed( ));
     100          15 :     close();
     101          15 :     delete _impl;
     102          29 : }
     103             : 
     104          16 : bool Client::connectServer( ServerPtr server )
     105             : {
     106          16 :     if( Super::connectServer( server.get( )))
     107             :     {
     108           0 :         server->setClient( this );
     109           0 :         return true;
     110             :     }
     111             : 
     112          64 :     if( !server->getConnectionDescriptions().empty() ||
     113          64 :         !Global::getServer().empty() || getenv( "EQ_SERVER" ))
     114             :     {
     115           0 :         return false;
     116             :     }
     117             : 
     118             :     // Use app-local server if no explicit server was set
     119          16 :     co::ConnectionPtr connection = _startLocalServer();
     120          16 :     if( !connection )
     121           0 :         return false;
     122             : 
     123          16 :     if( !connect( server.get(), connection ))
     124             :         // giving up
     125           0 :         return false;
     126             : 
     127          16 :     server->setClient( this );
     128          16 :     _impl->localServers.insert( server.get( ));
     129          16 :     return true;
     130             : }
     131             : 
     132             : /** @cond IGNORE */
     133             : namespace
     134             : {
     135          12 :     lunchbox::DSO _libeqserver;
     136             : }
     137             : 
     138          16 : co::ConnectionPtr _startLocalServer()
     139             : {
     140          16 :     Strings dirNames;
     141          16 :     dirNames.push_back( "" );
     142             : 
     143             : #ifdef EQ_BUILD_DIR
     144             : #  ifdef NDEBUG
     145             :     dirNames.push_back( std::string( EQ_BUILD_DIR ) + "lib/Release/" );
     146             : #  else
     147          16 :     dirNames.push_back( std::string( EQ_BUILD_DIR ) + "lib/Debug/" );
     148             : #  endif
     149          16 :     dirNames.push_back( std::string( EQ_BUILD_DIR ) + "lib/" );
     150             : #endif
     151             : 
     152             : #ifdef _MSC_VER
     153             :     const std::string libName = "EqualizerServer.dll";
     154             : #elif defined (_WIN32)
     155             :     const std::string libName = "libEqualizerServer.dll";
     156             : #elif defined (Darwin)
     157             :     dirNames.push_back( "/opt/local/lib/" ); // MacPorts
     158             :     const std::string libName = "libEqualizerServer.dylib";
     159             : #else
     160          32 :     const std::string libName = "libEqualizerServer.so";
     161             : #endif
     162             : 
     163          48 :     while( !_libeqserver.isOpen() && !dirNames.empty( ))
     164             :     {
     165          16 :         _libeqserver.open( dirNames.back() + libName );
     166          16 :         dirNames.pop_back();
     167             :     }
     168             : 
     169          16 :     if( !_libeqserver.isOpen( ))
     170             :     {
     171           0 :         LBWARN << "Can't open Equalizer server library" << std::endl;
     172           0 :         return 0;
     173             :     }
     174             : 
     175             :     eqsStartLocalServer_t eqsStartLocalServer = (eqsStartLocalServer_t)
     176          16 :         _libeqserver.getFunctionPointer( "eqsStartLocalServer" );
     177             : 
     178          16 :     if( !eqsStartLocalServer )
     179             :     {
     180           0 :         LBWARN << "Can't find server entry function eqsStartLocalServer"
     181           0 :                << std::endl;
     182           0 :         return 0;
     183             :     }
     184             : 
     185          32 :     co::ConnectionPtr conn = eqsStartLocalServer( Global::getConfigFile( ));
     186          16 :     if( conn )
     187          16 :         conn->unref(); // WAR "C" linkage
     188          32 :     return conn;
     189             : }
     190             : 
     191          16 : static void _joinLocalServer()
     192             : {
     193             :     eqsJoinLocalServer_t eqsJoinLocalServer = (eqsJoinLocalServer_t)
     194          16 :         _libeqserver.getFunctionPointer( "eqsJoinLocalServer" );
     195             : 
     196          16 :     if( !eqsJoinLocalServer )
     197             :     {
     198           0 :         LBWARN << "Can't find server entry function eqsJoinLocalServer"
     199           0 :                << std::endl;
     200          16 :         return;
     201             :     }
     202             : 
     203          16 :     eqsJoinLocalServer();
     204          16 :     _libeqserver.close();
     205             : }
     206             : /** @endcond */
     207             : 
     208          16 : bool Client::disconnectServer( ServerPtr server )
     209             : {
     210          16 :     ServerSet::iterator i = _impl->localServers.find( server.get( ));
     211          16 :     if( i == _impl->localServers.end( ))
     212             :     {
     213           0 :         server->setClient( 0 );
     214           0 :         const bool success = Super::disconnectServer( server );
     215           0 :         _impl->queue.flush();
     216           0 :         return success;
     217             :     }
     218             : 
     219             :     // shut down process-local server (see _startLocalServer)
     220          16 :     LBASSERT( server->isConnected( ));
     221          16 :     const bool success = server->shutdown();
     222          16 :     _joinLocalServer();
     223          16 :     server->setClient( 0 );
     224             : 
     225          16 :     LBASSERT( !server->isConnected( ));
     226          16 :     _impl->localServers.erase( i );
     227          16 :     _impl->queue.flush();
     228          16 :     return success;
     229             : }
     230             : 
     231             : namespace
     232             : {
     233           0 : bool _isParameterOption( const std::string& name, const int argc, char** argv,
     234             :                          const int index )
     235             : {
     236           0 :     return ( index < argc-1 &&          // has enough total arguments
     237           0 :              name == argv[index] &&     // name matches
     238           0 :              argv[index+1][0] != '-' ); // next arg not an option
     239             : }
     240             : }
     241             : 
     242          15 : bool Client::initLocal( const int argc, char** argv )
     243             : {
     244          15 :     bool isClient = false;
     245          15 :     std::string clientOpts;
     246             : 
     247          15 :     for( int i=1; i<argc; ++i )
     248             :     {
     249           0 :         if( std::string( "--eq-client" ) == argv[i] )
     250             :         {
     251           0 :             isClient = true;
     252           0 :             if( i < argc-1 && argv[i+1][0] != '-' ) // server-started client
     253             :             {
     254           0 :                 clientOpts = argv[++i];
     255             : 
     256           0 :                 if( !deserialize( clientOpts ))
     257           0 :                     LBWARN << "Failed to parse client listen port parameters"
     258           0 :                            << std::endl;
     259           0 :                 LBASSERT( !clientOpts.empty( ));
     260             :             }
     261             :         }
     262           0 :         else if( _isParameterOption( "--eq-layout", argc, argv, i ))
     263           0 :             _impl->activeLayouts.push_back( argv[++i] );
     264           0 :         else if( _isParameterOption( "--eq-gpufilter" , argc, argv, i ))
     265           0 :             _impl->gpuFilter = argv[ ++i ];
     266           0 :         else if( _isParameterOption( "--eq-modelunit", argc, argv, i ))
     267             :         {
     268           0 :             std::istringstream unitString( argv[++i] );
     269           0 :             unitString >> _impl->modelUnit;
     270             :         }
     271             :     }
     272          15 :     LBVERB << "Launching " << getNodeID() << std::endl;
     273             : 
     274          15 :     if( !Super::initLocal( argc, argv ))
     275           0 :         return false;
     276             : 
     277          15 :     if( isClient )
     278             :     {
     279           0 :         LBVERB << "Client node started from command line with option "
     280           0 :                << clientOpts << std::endl;
     281             : 
     282           0 :         if( !_setupClient( clientOpts ))
     283             :         {
     284           0 :             exitLocal();
     285           0 :             return false;
     286             :         }
     287             : 
     288           0 :         clientLoop();
     289           0 :         exitClient();
     290             :     }
     291             : 
     292          15 :     return true;
     293             : }
     294             : 
     295           0 : bool Client::_setupClient( const std::string& clientArgs )
     296             : {
     297           0 :     LBASSERT( isListening( ));
     298           0 :     if( clientArgs.empty( ))
     299           0 :         return true;
     300             : 
     301           0 :     size_t nextPos = clientArgs.find( CO_SEPARATOR );
     302           0 :     if( nextPos == std::string::npos )
     303             :     {
     304           0 :         LBERROR << "Could not parse working directory: " << clientArgs
     305           0 :                 << std::endl;
     306           0 :         return false;
     307             :     }
     308             : 
     309           0 :     const std::string workDir = clientArgs.substr( 0, nextPos );
     310           0 :     std::string description = clientArgs.substr( nextPos + 1 );
     311             : 
     312           0 :     Global::setWorkDir( workDir );
     313           0 :     if( !workDir.empty() && chdir( workDir.c_str( )) == -1 )
     314           0 :         LBWARN << "Can't change working directory to " << workDir << ": "
     315           0 :                << lunchbox::sysError << std::endl;
     316             : 
     317           0 :     nextPos = description.find( CO_SEPARATOR );
     318           0 :     if( nextPos == std::string::npos )
     319             :     {
     320           0 :         LBERROR << "Could not parse server node type: " << description
     321           0 :                 << " is left from " << clientArgs << std::endl;
     322           0 :         return false;
     323             :     }
     324             : 
     325           0 :     co::NodePtr server = createNode( fabric::NODETYPE_SERVER );
     326           0 :     if( !server->deserialize( description ))
     327           0 :         LBWARN << "Can't parse server data" << std::endl;
     328             : 
     329           0 :     LBASSERTINFO( description.empty(), description );
     330           0 :     if( !connect( server ))
     331             :     {
     332           0 :         LBERROR << "Can't connect server node using " << *server << std::endl;
     333           0 :         return false;
     334             :     }
     335             : 
     336           0 :     return true;
     337             : }
     338             : 
     339           0 : void Client::clientLoop()
     340             : {
     341           0 :     LBINFO << "Entered client loop" << std::endl;
     342             : 
     343           0 :     _impl->running = true;
     344           0 :     while( _impl->running )
     345           0 :         processCommand();
     346             : 
     347             :     // cleanup
     348           0 :     _impl->queue.flush();
     349           0 : }
     350             : 
     351          15 : bool Client::exitLocal()
     352             : {
     353          15 :     _impl->activeLayouts.clear();
     354          15 :     _impl->modelUnit = EQ_UNDEFINED_UNIT;
     355          15 :     return fabric::Client::exitLocal();
     356             : }
     357             : 
     358           0 : void Client::exitClient()
     359             : {
     360           0 :     bool ret = exitLocal();
     361           0 :     LBINFO << "Exit " << lunchbox::className( this ) << " process used "
     362           0 :            << getRefCount() << std::endl;
     363             : 
     364           0 :     if( !eq::exit( ))
     365           0 :         ret = false;
     366           0 :     ::exit( ret ? EXIT_SUCCESS : EXIT_FAILURE );
     367             : }
     368             : 
     369           1 : bool Client::hasCommands()
     370             : {
     371           1 :     return !_impl->queue.isEmpty();
     372             : }
     373             : 
     374         391 : co::CommandQueue* Client::getMainThreadQueue()
     375             : {
     376         391 :     return &_impl->queue;
     377             : }
     378             : 
     379           8 : const Strings& Client::getActiveLayouts()
     380             : {
     381           8 :     return _impl->activeLayouts;
     382             : }
     383             : 
     384          10 : const std::string& Client::getGPUFilter() const
     385             : {
     386          10 :     return _impl->gpuFilter;
     387             : }
     388             : 
     389           8 : float Client::getModelUnit() const
     390             : {
     391           8 :     return _impl->modelUnit;
     392             : }
     393             : 
     394           0 : co::NodePtr Client::createNode( const uint32_t type )
     395             : {
     396           0 :     switch( type )
     397             :     {
     398             :         case fabric::NODETYPE_SERVER:
     399             :         {
     400           0 :             Server* server = new Server;
     401           0 :             server->setClient( this );
     402           0 :             return server;
     403             :         }
     404             : 
     405             :         default:
     406           0 :             return Super::createNode( type );
     407             :     }
     408             : }
     409             : 
     410           0 : bool Client::_cmdExit( co::ICommand& command )
     411             : {
     412           0 :     _impl->running = false;
     413             :     // Close connection here, this is the last command we'll get on it
     414           0 :     command.getLocalNode()->disconnect( command.getRemoteNode( ));
     415           0 :     return true;
     416             : }
     417             : 
     418             : namespace
     419             : {
     420          16 : class StopNodesVisitor : public ServerVisitor
     421             : {
     422             : public:
     423          16 :     virtual ~StopNodesVisitor() {}
     424          30 :     virtual VisitorResult visitPre( Node* node )
     425             :     {
     426          30 :         node->dirtyClientExit();
     427          30 :         return TRAVERSE_CONTINUE;
     428             :     }
     429             : };
     430             : }
     431             : 
     432          17 : void Client::notifyDisconnect( co::NodePtr node )
     433             : {
     434          17 :     if( node->getType() == fabric::NODETYPE_SERVER )
     435             :     {
     436             :         // local command dispatching
     437             :         co::OCommand( this, this, fabric::CMD_CLIENT_EXIT,
     438          16 :                       co::COMMANDTYPE_NODE );
     439             : 
     440          16 :         ServerPtr server = static_cast< Server* >( node.get( ));
     441          32 :         StopNodesVisitor stopNodes;
     442          32 :         server->accept( stopNodes );
     443             :     }
     444          17 :     fabric::Client::notifyDisconnect( node );
     445          17 : }
     446             : 
     447          36 : }

Generated by: LCOV version 1.10