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