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 : #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->commit();
222 :
223 : node->send( fabric::CMD_SERVER_CREATE_CONFIG )
224 4 : << co::ObjectVersion( config ) << LB_UNDEFINED_UINT32;
225 :
226 4 : server::Node* appNode = config->findApplicationNode();
227 : const co::ConnectionDescriptions& descs =
228 4 : appNode->getConnectionDescriptions();
229 :
230 4 : if( config->getNodes().size() > 1 )
231 : {
232 0 : if( descs.empty() && node->getConnectionDescriptions().empty( ))
233 : {
234 0 : LBWARN << "Likely misconfiguration: Neither the application nor the"
235 0 : << " config file has a connection for this multi-node "
236 0 : << "config. Render clients will be unable to communicate "
237 0 : << "with the application process." << std::endl;
238 : }
239 0 : if( getConnectionDescriptions().empty( ))
240 : {
241 0 : LBWARN << "Likely misconfiguration: The server has no listening "
242 0 : << "connection for this multi-node config. Render clients "
243 0 : << "will be unable to communicate with the server."
244 0 : << std::endl;
245 : }
246 : }
247 :
248 : node->send( fabric::CMD_SERVER_CHOOSE_CONFIG_REPLY )
249 4 : << config->getID() << requestID << co::serialize( descs );
250 8 : return true;
251 : }
252 :
253 24 : bool Server::_cmdReleaseConfig( co::ICommand& command )
254 : {
255 24 : const uint128_t& configID = command.read< uint128_t >();
256 24 : const uint32_t requestID = command.read< uint32_t >();
257 :
258 24 : LBVERB << "Handle release config " << command << " config " << configID
259 24 : << std::endl;
260 :
261 24 : co::NodePtr node = command.getRemoteNode();
262 :
263 24 : Config* config = 0;
264 24 : const Configs& configs = getConfigs();
265 84 : for( Configs::const_iterator i = configs.begin();
266 84 : i != configs.end() && !config; ++i )
267 : {
268 4 : Config* candidate = *i;
269 4 : if( candidate->getID() == configID )
270 4 : config = candidate;
271 : }
272 :
273 24 : if( !config )
274 : {
275 20 : LBWARN << "Release request for unknown config" << std::endl;
276 20 : node->send( fabric::CMD_SERVER_RELEASE_CONFIG_REPLY ) << requestID;
277 20 : return true;
278 : }
279 :
280 4 : if( config->isRunning( ))
281 : {
282 0 : LBWARN << "Release of running configuration" << std::endl;
283 0 : config->exit(); // Make sure config is exited
284 : }
285 :
286 8 : lunchbox::Request< void > request = registerRequest< void >();
287 : node->send( fabric::CMD_SERVER_DESTROY_CONFIG )
288 4 : << config->getID() << request;
289 4 : request.wait();
290 :
291 : #ifdef EQUALIZER_USE_HWSD
292 4 : if( config->isAutoConfig( ))
293 : {
294 0 : LBASSERT( _admins.empty( ));
295 0 : config->deregister();
296 0 : config::Server::release( config );
297 : }
298 : else
299 : #endif
300 : {
301 4 : ConfigRestoreVisitor restore;
302 4 : config->accept( restore );
303 4 : config->commit();
304 : }
305 :
306 4 : node->send( fabric::CMD_SERVER_RELEASE_CONFIG_REPLY ) << requestID;
307 4 : LBVERB << "Released config " << configID << std::endl;
308 28 : return true;
309 : }
310 :
311 4 : bool Server::_cmdDestroyConfigReply( co::ICommand& command )
312 : {
313 4 : serveRequest( command.read< uint32_t >( ));
314 4 : return true;
315 : }
316 :
317 24 : bool Server::_cmdShutdown( co::ICommand& command )
318 : {
319 24 : const uint32_t requestID = command.read< uint32_t >();
320 :
321 24 : co::NodePtr node = command.getRemoteNode();
322 :
323 24 : if( !_admins.empty( ))
324 : {
325 0 : LBWARN << "Ignoring shutdown request, " << _admins.size()
326 0 : << " admin clients connected" << std::endl;
327 :
328 0 : node->send( fabric::CMD_SERVER_SHUTDOWN_REPLY ) << requestID << false;
329 0 : return true;
330 : }
331 :
332 24 : const Configs& configs = getConfigs();
333 28 : for( Configs::const_iterator i = configs.begin(); i != configs.end(); ++i )
334 : {
335 4 : Config* candidate = *i;
336 4 : if( candidate->isUsed( ))
337 : {
338 0 : LBWARN << "Ignoring shutdown request due to used config"
339 0 : << std::endl;
340 :
341 : node->send( fabric::CMD_SERVER_SHUTDOWN_REPLY )
342 0 : << requestID << false;
343 0 : return true;
344 : }
345 : }
346 :
347 24 : LBDEBUG << "Shutting down server" << std::endl;
348 :
349 24 : _running = false;
350 24 : node->send( fabric::CMD_SERVER_SHUTDOWN_REPLY ) << requestID << true;
351 :
352 : #ifndef WIN32
353 : // WAR for 2874188: Lockup at shutdown
354 24 : lunchbox::sleep( 100 );
355 : #endif
356 :
357 24 : return true;
358 : }
359 :
360 0 : bool Server::_cmdMap( co::ICommand& command )
361 : {
362 0 : co::NodePtr node = command.getRemoteNode();
363 0 : _admins.push_back( node );
364 :
365 0 : const Configs& configs = getConfigs();
366 0 : for( Configs::const_iterator i = configs.begin(); i != configs.end(); ++i )
367 : {
368 0 : Config* config = *i;
369 : node->send( fabric::CMD_SERVER_CREATE_CONFIG )
370 0 : << co::ObjectVersion( config ) << LB_UNDEFINED_UINT32;
371 : }
372 :
373 0 : node->send( fabric::CMD_SERVER_MAP_REPLY ) << command.read< uint32_t >();
374 0 : return true;
375 : }
376 :
377 0 : bool Server::_cmdUnmap( co::ICommand& command )
378 : {
379 0 : co::NodePtr node = command.getRemoteNode();
380 0 : co::Nodes::iterator i = lunchbox::find( _admins, node );
381 :
382 0 : LBASSERT( i != _admins.end( ));
383 0 : if( i != _admins.end( ))
384 : {
385 0 : _admins.erase( i );
386 0 : const Configs& configs = getConfigs();
387 0 : for( Configs::const_iterator j = configs.begin();
388 0 : j != configs.end(); ++j )
389 : {
390 0 : Config* config = *j;
391 : node->send( fabric::CMD_SERVER_DESTROY_CONFIG )
392 0 : << config->getID() << LB_UNDEFINED_UINT32;
393 : }
394 : }
395 :
396 0 : node->send( fabric::CMD_SERVER_UNMAP_REPLY ) << command.read< uint32_t >();
397 0 : return true;
398 : }
399 :
400 : }
401 : }
402 : #include "../fabric/server.ipp"
403 : template class eq::fabric::Server< co::Node, eq::server::Server,
404 : eq::server::Config,
405 : eq::server::NodeFactory,
406 : co::LocalNode, eq::server::ServerVisitor >;
407 :
408 : /** @cond IGNORE */
409 : template std::ostream&
410 84 : eq::fabric::operator << ( std::ostream&, const eq::server::Super& );
411 : /** @endcond */
|