LCOV - code coverage report
Current view: top level - eq - client.cpp (source / functions) Hit Total Coverage
Test: Equalizer Lines: 109 201 54.2 %
Date: 2017-12-16 05:07:20 Functions: 22 35 62.9 %

          Line data    Source code
       1             : 
       2             : /* Copyright (c) 2005-2017, Stefan Eilemann <eile@equalizergraphics.com>
       3             :  *                          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 "global.h"
      24             : #include "init.h"
      25             : #include "node.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/elementVisitor.h>
      32             : #include <eq/fabric/leafVisitor.h>
      33             : #include <eq/fabric/nodeType.h>
      34             : #include <eq/fabric/view.h>
      35             : 
      36             : #include <eq/server/localServer.h>
      37             : 
      38             : #include <co/connection.h>
      39             : #include <co/connectionDescription.h>
      40             : #include <co/global.h>
      41             : #include <co/iCommand.h>
      42             : #include <lunchbox/dso.h>
      43             : #include <lunchbox/file.h>
      44             : #include <lunchbox/term.h>
      45             : 
      46             : #include <boost/filesystem/path.hpp>
      47             : #include <boost/program_options/options_description.hpp>
      48             : #include <boost/program_options/parsers.hpp>
      49             : #include <boost/program_options/variables_map.hpp>
      50             : 
      51             : #ifdef _MSC_VER
      52             : #include <direct.h>
      53             : #define chdir _chdir
      54             : #endif
      55             : 
      56             : #ifdef EQ_QT_USED
      57             : #include <QApplication> // must be included before any header defining Bool
      58             : 
      59             : #include "os.h"
      60             : #else
      61             : class QApplication;
      62             : #endif
      63             : 
      64             : namespace eq
      65             : {
      66             : namespace
      67             : {
      68             : typedef std::unordered_set<Server*> ServerSet;
      69             : }
      70             : 
      71             : /** @cond IGNORE */
      72             : namespace detail
      73             : {
      74           1 : class Client
      75             : {
      76             : public:
      77           1 :     Client()
      78           1 :         : queue(co::Global::getCommandQueueLimit())
      79             :         , modelUnit(EQ_UNDEFINED_UNIT)
      80             :         , qtApp(0)
      81           1 :         , running(false)
      82             :     {
      83           1 :     }
      84             : 
      85             :     CommandQueue queue; //!< The command->node command queue.
      86             :     std::string name;
      87             :     Strings activeLayouts;
      88             :     ServerSet localServers;
      89             :     std::string gpuFilter;
      90             :     float modelUnit;
      91             :     QApplication* qtApp;
      92             :     bool running;
      93             : 
      94           1 :     void initQt(int argc LB_UNUSED, char** argv LB_UNUSED)
      95             :     {
      96             : #if EQ_GLX_USED || EQ_WGL_USED || EQ_AGL_USED
      97           1 :         return;
      98             : #endif
      99             : #ifdef EQ_QT_USED
     100             :         if (!QApplication::instance())
     101             :         {
     102             : #ifdef __linux__
     103             :             ::XInitThreads();
     104             : #endif
     105             :             qtApp = new QApplication(argc, argv);
     106             :         }
     107             : #endif
     108             :     }
     109             : };
     110             : }
     111             : 
     112             : typedef co::CommandFunc<Client> ClientFunc;
     113             : 
     114             : typedef fabric::Client Super;
     115             : /** @endcond */
     116             : 
     117           1 : Client::Client()
     118             :     : Super()
     119           1 :     , _impl(new detail::Client)
     120             : {
     121           2 :     registerCommand(fabric::CMD_CLIENT_EXIT,
     122           3 :                     ClientFunc(this, &Client::_cmdExit), &_impl->queue);
     123           2 :     registerCommand(fabric::CMD_CLIENT_INTERRUPT,
     124           3 :                     ClientFunc(this, &Client::_cmdInterrupt), &_impl->queue);
     125             : 
     126           1 :     LBVERB << "New client at " << (void*)this << std::endl;
     127           1 : }
     128             : 
     129           2 : Client::~Client()
     130             : {
     131           1 :     LBVERB << "Delete client at " << (void*)this << std::endl;
     132           1 :     LBASSERT(isClosed());
     133           1 :     close();
     134           1 :     delete _impl;
     135           1 : }
     136             : 
     137           1 : bool Client::connectServer(ServerPtr server)
     138             : {
     139           1 :     if (Super::connectServer(server))
     140             :     {
     141           0 :         server->setClient(this);
     142           0 :         return true;
     143             :     }
     144             : 
     145           4 :     if (!server->getConnectionDescriptions().empty() ||
     146           4 :         !Global::getServer().empty() || getenv("EQ_SERVER"))
     147             :     {
     148           0 :         return false;
     149             :     }
     150             : 
     151             :     // Use app-local server if no explicit server was set
     152           1 :     if (!server::startLocalServer(Global::getConfig()))
     153           0 :         return false;
     154             : 
     155           2 :     co::ConnectionPtr connection = server::connectLocalServer();
     156           1 :     if (!connection || !connect(server, connection))
     157           0 :         return false;
     158             : 
     159           1 :     server->setClient(this);
     160           1 :     _impl->localServers.insert(server.get());
     161           1 :     return true;
     162             : }
     163             : 
     164           1 : bool Client::disconnectServer(ServerPtr server)
     165             : {
     166           1 :     ServerSet::iterator i = _impl->localServers.find(server.get());
     167           1 :     if (i == _impl->localServers.end())
     168             :     {
     169           0 :         server->setClient(0);
     170           0 :         const bool success = Super::disconnectServer(server);
     171           0 :         _impl->queue.flush();
     172           0 :         return success;
     173             :     }
     174             : 
     175             :     // shut down process-local server (see _startLocalServer)
     176           1 :     LBASSERT(server->isConnected());
     177           1 :     const bool success = server->shutdown();
     178           1 :     server::joinLocalServer();
     179           1 :     server->setClient(0);
     180             : 
     181           1 :     LBASSERT(!server->isConnected());
     182           1 :     _impl->localServers.erase(i);
     183           1 :     _impl->queue.flush();
     184           1 :     return success;
     185             : }
     186             : 
     187             : namespace
     188             : {
     189             : namespace arg = boost::program_options;
     190             : 
     191           1 : arg::options_description _getProgramOptions()
     192             : {
     193             :     arg::options_description options("eq::Client options",
     194           1 :                                      lunchbox::term::getSize().first);
     195           2 :     options.add_options()("eq-client", "Internal, used for render clients")(
     196           1 :         "eq-layout", arg::value<std::string>(),
     197             :         "Name of the layout to "
     198             :         "activate on all canvases during Config::init(). The option can be "
     199           2 :         "used multiple times.")("eq-gpufilter", arg::value<std::string>(),
     200             :                                 "regex selecting the "
     201           1 :                                 "hwsd GPUs by name")(
     202           1 :         "eq-modelunit", arg::value<float>(),
     203             :         "Scale the rendered models "
     204             :         "in all views. The model unit defines the size of the model wrt the "
     205             :         "virtual room unit which is always in meter. The default unit is 1 "
     206           1 :         "(1 meter or EQ_M).");
     207             : 
     208           1 :     return options;
     209             : }
     210             : }
     211             : 
     212           0 : std::string Client::getHelp()
     213             : {
     214           0 :     std::ostringstream os;
     215           0 :     os << _getProgramOptions();
     216           0 :     return os.str();
     217             : }
     218             : 
     219           1 : bool Client::initLocal(const int argc, char** argv)
     220             : {
     221           1 :     if (_impl->name.empty() && argc > 0 && argv)
     222             :     {
     223           2 :         const boost::filesystem::path prog = argv[0];
     224           1 :         setName(prog.stem().string());
     225             :     }
     226             : 
     227           2 :     const auto options = _getProgramOptions();
     228           2 :     arg::variables_map vm;
     229             :     try
     230             :     {
     231           2 :         Strings args;
     232           4 :         for (int i = 0; i < argc; ++i)
     233           3 :             if (strcmp(argv[i], "--") != 0)
     234           3 :                 args.push_back(argv[i]);
     235             : 
     236           2 :         arg::store(arg::command_line_parser(args)
     237           1 :                        .options(options)
     238           1 :                        .allow_unregistered()
     239           2 :                        .run(),
     240           1 :                    vm);
     241           1 :         arg::notify(vm);
     242             :     }
     243           0 :     catch (const std::exception& e)
     244             :     {
     245           0 :         LBERROR << "Error in argument parsing: " << e.what() << std::endl;
     246           0 :         return false;
     247             :     }
     248             : 
     249           1 :     const bool isClient = vm.count("eq-client");
     250           2 :     std::string clientOpts;
     251             : 
     252           1 :     if (vm.count("eq-layout"))
     253           0 :         _impl->activeLayouts.push_back(vm["eq-layout"].as<std::string>());
     254           1 :     if (vm.count("eq-gpufilter"))
     255           0 :         _impl->gpuFilter = vm["eq-gpufilter"].as<std::string>();
     256           1 :     if (vm.count("eq-modelunit"))
     257           0 :         _impl->modelUnit = vm["eq-modelunit"].as<float>();
     258             : 
     259           1 :     LBVERB << "Launching " << getNodeID() << std::endl;
     260           1 :     if (!Super::initLocal(argc, argv))
     261           0 :         return false;
     262             : 
     263           1 :     if (isClient)
     264             :     {
     265           0 :         LBVERB << "Client node started from command line with option "
     266           0 :                << clientOpts << std::endl;
     267             : 
     268           0 :         if (!_setupClient(clientOpts))
     269             :         {
     270           0 :             exitLocal();
     271           0 :             return false;
     272             :         }
     273             : 
     274           0 :         _impl->running = true;
     275           0 :         clientLoop();
     276           0 :         exitClient();
     277             :     }
     278             : 
     279           1 :     _impl->initQt(argc, argv);
     280           1 :     return true;
     281             : }
     282             : 
     283           0 : bool Client::_setupClient(const std::string& clientArgs)
     284             : {
     285           0 :     LBASSERT(isListening());
     286           0 :     if (clientArgs.empty())
     287           0 :         return true;
     288             : 
     289           0 :     size_t nextPos = clientArgs.find(CO_SEPARATOR);
     290           0 :     if (nextPos == std::string::npos)
     291             :     {
     292           0 :         LBERROR << "Could not parse working directory: " << clientArgs
     293           0 :                 << std::endl;
     294           0 :         return false;
     295             :     }
     296             : 
     297           0 :     const std::string workDir = clientArgs.substr(0, nextPos);
     298           0 :     std::string description = clientArgs.substr(nextPos + 1);
     299             : 
     300           0 :     Global::setWorkDir(workDir);
     301           0 :     if (!workDir.empty() && chdir(workDir.c_str()) == -1)
     302           0 :         LBWARN << "Can't change working directory to " << workDir << ": "
     303           0 :                << lunchbox::sysError << std::endl;
     304             : 
     305           0 :     nextPos = description.find(CO_SEPARATOR);
     306           0 :     if (nextPos == std::string::npos)
     307             :     {
     308           0 :         LBERROR << "Could not parse server node type: " << description
     309           0 :                 << " is left from " << clientArgs << std::endl;
     310           0 :         return false;
     311             :     }
     312             : 
     313           0 :     co::NodePtr server = createNode(fabric::NODETYPE_SERVER);
     314           0 :     if (!server->deserialize(description))
     315           0 :         LBWARN << "Can't parse server data" << std::endl;
     316             : 
     317           0 :     LBASSERTINFO(description.empty(), description);
     318           0 :     if (!connect(server))
     319             :     {
     320           0 :         LBERROR << "Can't connect server node using " << *server << std::endl;
     321           0 :         return false;
     322             :     }
     323             : 
     324           0 :     return true;
     325             : }
     326             : 
     327           0 : void Client::clientLoop()
     328             : {
     329           0 :     LBINFO << "Entered client loop" << std::endl;
     330           0 :     while (isRunning())
     331           0 :         processCommand();
     332           0 : }
     333             : 
     334           1 : bool Client::exitLocal()
     335             : {
     336             : #ifdef EQ_QT_USED
     337           1 :     delete _impl->qtApp;
     338           1 :     _impl->qtApp = 0;
     339             : #endif
     340           1 :     _impl->activeLayouts.clear();
     341           1 :     _impl->modelUnit = EQ_UNDEFINED_UNIT;
     342           1 :     return fabric::Client::exitLocal();
     343             : }
     344             : 
     345           0 : void Client::exitClient()
     346             : {
     347           0 :     _impl->queue.flush();
     348           0 :     bool ret = exitLocal();
     349           0 :     LBINFO << "Exit " << lunchbox::className(this) << " process used "
     350           0 :            << getRefCount() << std::endl;
     351             : 
     352           0 :     if (!eq::exit())
     353           0 :         ret = false;
     354           0 :     ::exit(ret ? EXIT_SUCCESS : EXIT_FAILURE);
     355             : }
     356             : 
     357           2 : bool Client::hasCommands()
     358             : {
     359           2 :     return !_impl->queue.isEmpty();
     360             : }
     361             : 
     362           0 : bool Client::isRunning() const
     363             : {
     364           0 :     return _impl->running;
     365             : }
     366             : 
     367          50 : co::CommandQueue* Client::getMainThreadQueue()
     368             : {
     369          50 :     return &_impl->queue;
     370             : }
     371             : 
     372           0 : void Client::addActiveLayout(const std::string& activeLayout)
     373             : {
     374           0 :     _impl->activeLayouts.push_back(activeLayout);
     375           0 : }
     376             : 
     377           1 : void Client::setName(const std::string& name)
     378             : {
     379           1 :     _impl->name = name;
     380           1 : }
     381             : 
     382           1 : const std::string& Client::getName() const
     383             : {
     384           1 :     return _impl->name;
     385             : }
     386             : 
     387           1 : const Strings& Client::getActiveLayouts() const
     388             : {
     389           1 :     return _impl->activeLayouts;
     390             : }
     391             : 
     392           1 : const std::string& Client::getGPUFilter() const
     393             : {
     394           1 :     return _impl->gpuFilter;
     395             : }
     396             : 
     397           1 : float Client::getModelUnit() const
     398             : {
     399           1 :     return _impl->modelUnit;
     400             : }
     401             : 
     402           0 : void Client::interruptMainThread()
     403             : {
     404           0 :     send(fabric::CMD_CLIENT_INTERRUPT);
     405           0 : }
     406             : 
     407           0 : co::NodePtr Client::createNode(const uint32_t type)
     408             : {
     409           0 :     switch (type)
     410             :     {
     411             :     case fabric::NODETYPE_SERVER:
     412             :     {
     413           0 :         Server* server = new Server;
     414           0 :         server->setClient(this);
     415           0 :         return server;
     416             :     }
     417             : 
     418             :     default:
     419           0 :         return Super::createNode(type);
     420             :     }
     421             : }
     422             : 
     423           0 : bool Client::_cmdExit(co::ICommand& command)
     424             : {
     425           0 :     _impl->running = false;
     426             :     // Close connection here, this is the last command we'll get on it
     427           0 :     command.getLocalNode()->disconnect(command.getRemoteNode());
     428           0 :     return true;
     429             : }
     430             : 
     431           0 : bool Client::_cmdInterrupt(co::ICommand&)
     432             : {
     433           0 :     return true;
     434             : }
     435             : 
     436             : namespace
     437             : {
     438           1 : class StopNodesVisitor : public ServerVisitor
     439             : {
     440             : public:
     441           1 :     virtual ~StopNodesVisitor() {}
     442           0 :     virtual VisitorResult visitPre(Node* node)
     443             :     {
     444           0 :         node->dirtyClientExit();
     445           0 :         return TRAVERSE_CONTINUE;
     446             :     }
     447             : };
     448             : }
     449             : 
     450           1 : void Client::notifyDisconnect(co::NodePtr node)
     451             : {
     452           1 :     if (node->getType() == fabric::NODETYPE_SERVER)
     453             :     {
     454             :         // local command dispatching
     455           1 :         co::OCommand(this, this, fabric::CMD_CLIENT_EXIT, co::COMMANDTYPE_NODE);
     456             : 
     457           2 :         ServerPtr server = static_cast<Server*>(node.get());
     458           2 :         StopNodesVisitor stopNodes;
     459           1 :         server->accept(stopNodes);
     460             :     }
     461           1 :     fabric::Client::notifyDisconnect(node);
     462           1 : }
     463          30 : }

Generated by: LCOV version 1.11