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