Line data Source code
1 :
2 : /* Copyright (c) 2005-2016, Stefan Eilemann <eile@equalizergraphics.com>
3 : * Cedric Stalder <cedric.stalder@gmail.com>
4 : * Daniel Nachbaur <danielnachbaur@gmail.com>
5 : *
6 : * This library is free software; you can redistribute it and/or modify it under
7 : * the terms of the GNU Lesser General Public License version 2.1 as published
8 : * by the Free Software Foundation.
9 : *
10 : * This library is distributed in the hope that it will be useful, but WITHOUT
11 : * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12 : * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
13 : * details.
14 : *
15 : * You should have received a copy of the GNU Lesser General Public License
16 : * along with this library; if not, write to the Free Software Foundation, Inc.,
17 : * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 : */
19 :
20 : #include "server.h"
21 :
22 : #include "channel.h"
23 : #include "compound.h"
24 : #include "config.h"
25 : #include "global.h"
26 : #include "loader.h"
27 : #include "node.h"
28 : #include "nodeFactory.h"
29 : #include "pipe.h"
30 : #include "window.h"
31 : #ifdef EQUALIZER_USE_HWSD
32 : # include "config/server.h"
33 : #endif
34 :
35 : #include <eq/fabric/commands.h>
36 : #include <eq/fabric/configParams.h>
37 :
38 : #include <co/iCommand.h>
39 : #include <co/connectionDescription.h>
40 : #include <co/global.h>
41 : #include <co/init.h>
42 : #include <co/localNode.h>
43 : #include <lunchbox/refPtr.h>
44 : #include <lunchbox/sleep.h>
45 :
46 : #include <sstream>
47 :
48 : #include "configBackupVisitor.h"
49 : #include "configRestoreVisitor.h"
50 :
51 : namespace eq
52 : {
53 : namespace server
54 : {
55 : namespace
56 : {
57 28 : static NodeFactory _nf;
58 : }
59 :
60 : typedef co::CommandFunc<Server> ServerFunc;
61 : typedef fabric::Server< co::Node, Server, Config, NodeFactory, co::LocalNode,
62 : ServerVisitor > Super;
63 :
64 446 : Server::Server()
65 : : Super( &_nf )
66 : , _mainThreadQueue( co::Global::getCommandQueueLimit( ))
67 446 : , _running( false )
68 : {
69 446 : lunchbox::Log::setClock( &_clock );
70 446 : disableInstanceCache();
71 :
72 : registerCommand( fabric::CMD_SERVER_CHOOSE_CONFIG,
73 : ServerFunc( this, &Server::_cmdChooseConfig ),
74 446 : &_mainThreadQueue );
75 : registerCommand( fabric::CMD_SERVER_RELEASE_CONFIG,
76 : ServerFunc( this, &Server::_cmdReleaseConfig ),
77 446 : &_mainThreadQueue );
78 : registerCommand( fabric::CMD_SERVER_DESTROY_CONFIG_REPLY,
79 446 : ServerFunc( this, &Server::_cmdDestroyConfigReply ), 0 );
80 : registerCommand( fabric::CMD_SERVER_SHUTDOWN,
81 : ServerFunc( this, &Server::_cmdShutdown ),
82 446 : &_mainThreadQueue );
83 : registerCommand( fabric::CMD_SERVER_MAP,
84 446 : ServerFunc( this, &Server::_cmdMap ), &_mainThreadQueue );
85 : registerCommand( fabric::CMD_SERVER_UNMAP,
86 : ServerFunc( this, &Server::_cmdUnmap ),
87 446 : &_mainThreadQueue );
88 446 : }
89 :
90 1332 : Server::~Server()
91 : {
92 444 : LBASSERT( getConfigs().empty( )); // not possible - config RefPtr's myself
93 444 : deleteConfigs();
94 444 : lunchbox::Log::setClock( 0 );
95 888 : }
96 :
97 24 : void Server::init()
98 : {
99 24 : lunchbox::Thread::setName( "Server" );
100 24 : LBASSERT( isListening( ));
101 :
102 24 : const Configs& configs = getConfigs();
103 : #ifndef EQUALIZER_USE_HWSD
104 : if( configs.empty( ))
105 : LBWARN << "No configurations loaded" << std::endl;
106 : #endif
107 :
108 72 : LBDEBUG << lunchbox::disableFlush << lunchbox::disableHeader
109 24 : << "Running server: " << std::endl
110 48 : << lunchbox::indent << Global::instance() << *this
111 24 : << lunchbox::exdent << lunchbox::enableHeader
112 72 : << lunchbox::enableFlush << std::endl;
113 :
114 28 : for( Configs::const_iterator i = configs.begin(); i != configs.end(); ++i )
115 4 : (*i)->register_();
116 24 : }
117 :
118 24 : void Server::exit()
119 : {
120 24 : const Configs& configs = getConfigs();
121 28 : for( Configs::const_iterator i = configs.begin(); i != configs.end(); ++i )
122 4 : (*i)->deregister();
123 24 : }
124 :
125 24 : void Server::run()
126 : {
127 24 : init();
128 24 : handleCommands();
129 24 : exit();
130 24 : }
131 :
132 888 : void Server::deleteConfigs()
133 : {
134 888 : const Configs& configs = getConfigs();
135 2200 : while( !configs.empty( ))
136 : {
137 424 : Config* config = configs.back();
138 424 : _removeConfig( config );
139 424 : delete config;
140 : }
141 888 : }
142 :
143 : //===========================================================================
144 : // ICommand handling methods
145 : //===========================================================================
146 24 : void Server::handleCommands()
147 : {
148 24 : _running = true;
149 102 : while( _running ) // set to false in _cmdShutdown()
150 : {
151 54 : const co::ICommands& commands = _mainThreadQueue.popAll();
152 54 : LBASSERT( !commands.empty( ));
153 :
154 84 : for( co::ICommandsCIter i = commands.begin(); i != commands.end(); ++i )
155 : {
156 : // We want to avoid a non-const copy of commands, hence the cast...
157 54 : co::ICommand& command = const_cast< co::ICommand& >( *i );
158 :
159 54 : if( !command( ))
160 : {
161 0 : LBABORT( "Error handling " << command );
162 : }
163 54 : if( !_running )
164 24 : break;
165 : }
166 54 : }
167 24 : _mainThreadQueue.flush();
168 24 : }
169 :
170 4 : bool Server::_cmdChooseConfig( co::ICommand& command )
171 : {
172 4 : const uint32_t requestID = command.read< uint32_t >();
173 4 : const fabric::ConfigParams& params = command.read< fabric::ConfigParams >();
174 :
175 4 : LBVERB << "Handle choose config " << command << " req " << requestID
176 0 : << " renderer " << params.getWorkDir() << '/'
177 4 : << params.getRenderClient() << std::endl;
178 :
179 4 : Config* config = 0;
180 4 : const Configs& configs = getConfigs();
181 8 : for( ConfigsCIter i = configs.begin(); i != configs.end() && !config; ++i )
182 : {
183 4 : Config* candidate = *i;
184 4 : const float version = candidate->getFAttribute( Config::FATTR_VERSION );
185 4 : LBASSERT( version == 1.2f );
186 4 : if( !candidate->isUsed() && version == 1.2f )
187 4 : config = candidate;
188 : }
189 :
190 : #ifdef EQUALIZER_USE_HWSD
191 4 : if( !config )
192 : {
193 0 : const std::string& configFile = command.read< std::string >();
194 0 : config = config::Server::configure( this, configFile, params );
195 0 : if( config )
196 : {
197 0 : config->register_();
198 0 : LBDEBUG << lunchbox::disableFlush << lunchbox::disableHeader
199 0 : << "Configured:" << std::endl
200 0 : << lunchbox::indent << Global::instance() << *this
201 0 : << lunchbox::exdent << lunchbox::enableHeader
202 0 : << lunchbox::enableFlush << std::endl;
203 0 : }
204 : }
205 : #endif
206 :
207 8 : co::NodePtr node = command.getRemoteNode();
208 :
209 4 : if( !config )
210 : {
211 : node->send( fabric::CMD_SERVER_CHOOSE_CONFIG_REPLY )
212 0 : << uint128_t() << requestID;
213 0 : return true;
214 : }
215 :
216 8 : ConfigBackupVisitor backup;
217 4 : config->accept( backup );
218 4 : config->setApplicationNetNode( node );
219 4 : config->setWorkDir( params.getWorkDir( ));
220 4 : config->setRenderClient( params.getRenderClient( ));
221 4 : config->setRenderClientArgs( params.getRenderClientArgs( ));
222 4 : config->setRenderClientEnvPrefixes( params.getRenderClientEnvPrefixes( ));
223 4 : config->commit();
224 :
225 : node->send( fabric::CMD_SERVER_CREATE_CONFIG )
226 4 : << co::ObjectVersion( config ) << LB_UNDEFINED_UINT32;
227 :
228 4 : server::Node* appNode = config->findApplicationNode();
229 : const co::ConnectionDescriptions& descs =
230 4 : appNode->getConnectionDescriptions();
231 :
232 4 : if( config->getNodes().size() > 1 )
233 : {
234 0 : if( descs.empty() && node->getConnectionDescriptions().empty( ))
235 : {
236 0 : LBWARN << "Likely misconfiguration: Neither the application nor the"
237 0 : << " config file has a connection for this multi-node "
238 0 : << "config. Render clients will be unable to communicate "
239 0 : << "with the application process." << std::endl;
240 : }
241 0 : if( getConnectionDescriptions().empty( ))
242 : {
243 0 : LBWARN << "Likely misconfiguration: The server has no listening "
244 0 : << "connection for this multi-node config. Render clients "
245 0 : << "will be unable to communicate with the server."
246 0 : << std::endl;
247 : }
248 : }
249 :
250 : node->send( fabric::CMD_SERVER_CHOOSE_CONFIG_REPLY )
251 4 : << config->getID() << requestID << co::serialize( descs );
252 8 : return true;
253 : }
254 :
255 24 : bool Server::_cmdReleaseConfig( co::ICommand& command )
256 : {
257 24 : const uint128_t& configID = command.read< uint128_t >();
258 24 : const uint32_t requestID = command.read< uint32_t >();
259 :
260 24 : LBVERB << "Handle release config " << command << " config " << configID
261 24 : << std::endl;
262 :
263 24 : co::NodePtr node = command.getRemoteNode();
264 :
265 24 : Config* config = 0;
266 24 : const Configs& configs = getConfigs();
267 84 : for( Configs::const_iterator i = configs.begin();
268 84 : i != configs.end() && !config; ++i )
269 : {
270 4 : Config* candidate = *i;
271 4 : if( candidate->getID() == configID )
272 4 : config = candidate;
273 : }
274 :
275 24 : if( !config )
276 : {
277 20 : LBWARN << "Release request for unknown config" << std::endl;
278 20 : node->send( fabric::CMD_SERVER_RELEASE_CONFIG_REPLY ) << requestID;
279 20 : return true;
280 : }
281 :
282 4 : if( config->isRunning( ))
283 : {
284 0 : LBWARN << "Release of running configuration" << std::endl;
285 0 : config->exit(); // Make sure config is exited
286 : }
287 :
288 8 : lunchbox::Request< void > request = registerRequest< void >();
289 : node->send( fabric::CMD_SERVER_DESTROY_CONFIG )
290 4 : << config->getID() << request;
291 4 : request.wait();
292 :
293 : #ifdef EQUALIZER_USE_HWSD
294 4 : if( config->isAutoConfig( ))
295 : {
296 0 : LBASSERT( _admins.empty( ));
297 0 : config->deregister();
298 0 : config::Server::release( config );
299 : }
300 : else
301 : #endif
302 : {
303 4 : ConfigRestoreVisitor restore;
304 4 : config->accept( restore );
305 4 : config->commit();
306 : }
307 :
308 4 : node->send( fabric::CMD_SERVER_RELEASE_CONFIG_REPLY ) << requestID;
309 4 : LBVERB << "Released config " << configID << std::endl;
310 28 : return true;
311 : }
312 :
313 4 : bool Server::_cmdDestroyConfigReply( co::ICommand& command )
314 : {
315 4 : serveRequest( command.read< uint32_t >( ));
316 4 : return true;
317 : }
318 :
319 24 : bool Server::_cmdShutdown( co::ICommand& command )
320 : {
321 24 : const uint32_t requestID = command.read< uint32_t >();
322 :
323 24 : co::NodePtr node = command.getRemoteNode();
324 :
325 24 : if( !_admins.empty( ))
326 : {
327 0 : LBWARN << "Ignoring shutdown request, " << _admins.size()
328 0 : << " admin clients connected" << std::endl;
329 :
330 0 : node->send( fabric::CMD_SERVER_SHUTDOWN_REPLY ) << requestID << false;
331 0 : return true;
332 : }
333 :
334 24 : const Configs& configs = getConfigs();
335 28 : for( Configs::const_iterator i = configs.begin(); i != configs.end(); ++i )
336 : {
337 4 : Config* candidate = *i;
338 4 : if( candidate->isUsed( ))
339 : {
340 0 : LBWARN << "Ignoring shutdown request due to used config"
341 0 : << std::endl;
342 :
343 : node->send( fabric::CMD_SERVER_SHUTDOWN_REPLY )
344 0 : << requestID << false;
345 0 : return true;
346 : }
347 : }
348 :
349 24 : LBDEBUG << "Shutting down server" << std::endl;
350 :
351 24 : _running = false;
352 24 : node->send( fabric::CMD_SERVER_SHUTDOWN_REPLY ) << requestID << true;
353 :
354 : #ifndef WIN32
355 : // WAR for 2874188: Lockup at shutdown
356 24 : lunchbox::sleep( 100 );
357 : #endif
358 :
359 24 : return true;
360 : }
361 :
362 0 : bool Server::_cmdMap( co::ICommand& command )
363 : {
364 0 : co::NodePtr node = command.getRemoteNode();
365 0 : _admins.push_back( node );
366 :
367 0 : const Configs& configs = getConfigs();
368 0 : for( Configs::const_iterator i = configs.begin(); i != configs.end(); ++i )
369 : {
370 0 : Config* config = *i;
371 : node->send( fabric::CMD_SERVER_CREATE_CONFIG )
372 0 : << co::ObjectVersion( config ) << LB_UNDEFINED_UINT32;
373 : }
374 :
375 0 : node->send( fabric::CMD_SERVER_MAP_REPLY ) << command.read< uint32_t >();
376 0 : return true;
377 : }
378 :
379 0 : bool Server::_cmdUnmap( co::ICommand& command )
380 : {
381 0 : co::NodePtr node = command.getRemoteNode();
382 0 : co::Nodes::iterator i = lunchbox::find( _admins, node );
383 :
384 0 : LBASSERT( i != _admins.end( ));
385 0 : if( i != _admins.end( ))
386 : {
387 0 : _admins.erase( i );
388 0 : const Configs& configs = getConfigs();
389 0 : for( Configs::const_iterator j = configs.begin();
390 0 : j != configs.end(); ++j )
391 : {
392 0 : Config* config = *j;
393 : node->send( fabric::CMD_SERVER_DESTROY_CONFIG )
394 0 : << config->getID() << LB_UNDEFINED_UINT32;
395 : }
396 : }
397 :
398 0 : node->send( fabric::CMD_SERVER_UNMAP_REPLY ) << command.read< uint32_t >();
399 0 : return true;
400 : }
401 :
402 : }
403 : }
404 : #include "../fabric/server.ipp"
405 : template class eq::fabric::Server< co::Node, eq::server::Server,
406 : eq::server::Config,
407 : eq::server::NodeFactory,
408 : co::LocalNode, eq::server::ServerVisitor >;
409 :
410 : /** @cond IGNORE */
411 : template std::ostream&
412 84 : eq::fabric::operator << ( std::ostream&, const eq::server::Super& );
413 : /** @endcond */
|