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