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 : }
|