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 "config.h"
21 :
22 : #include "canvas.h"
23 : #include "changeLatencyVisitor.h"
24 : #include "compound.h"
25 : #include "compoundVisitor.h"
26 : #include "configUpdateDataVisitor.h"
27 : #include "equalizers/equalizer.h"
28 : #include "global.h"
29 : #include "layout.h"
30 : #include "log.h"
31 : #include "node.h"
32 : #include "observer.h"
33 : #include "segment.h"
34 : #include "server.h"
35 : #include "view.h"
36 : #include "window.h"
37 :
38 : #include <eq/fabric/commands.h>
39 : #include <eq/fabric/event.h>
40 : #include <eq/fabric/iAttribute.h>
41 : #include <eq/fabric/paths.h>
42 :
43 : #include <co/objectICommand.h>
44 :
45 : #include <boost/foreach.hpp>
46 : #include <lunchbox/sleep.h>
47 :
48 : #include "channelStopFrameVisitor.h"
49 : #include "configDeregistrator.h"
50 : #include "configRegistrator.h"
51 : #include "configUpdateSyncVisitor.h"
52 : #include "configUpdateVisitor.h"
53 : #include "nodeFailedVisitor.h"
54 :
55 : namespace eq
56 : {
57 : namespace server
58 : {
59 : typedef co::CommandFunc<Config> ConfigFunc;
60 : using fabric::ON;
61 : using fabric::OFF;
62 :
63 404 : Config::Config(ServerPtr parent)
64 : : Super(parent)
65 : , _currentFrame(0)
66 : , _incarnation(1)
67 : , _finishedFrame(0)
68 : , _state(STATE_UNUSED)
69 : , _needsFinish(false)
70 : , _lastCheck(0)
71 404 : , _private(0)
72 : {
73 404 : const Global* global = Global::instance();
74 3232 : for (int i = 0; i < FATTR_ALL; ++i)
75 : {
76 2828 : const FAttribute attr = static_cast<FAttribute>(i);
77 2828 : setFAttribute(attr, global->getConfigFAttribute(attr));
78 : }
79 2828 : for (int i = 0; i < IATTR_ALL; ++i)
80 : {
81 2424 : const IAttribute attr = static_cast<IAttribute>(i);
82 2424 : setIAttribute(attr, global->getConfigIAttribute(attr));
83 : }
84 404 : }
85 :
86 1206 : Config::~Config()
87 : {
88 1742 : while (!_compounds.empty())
89 : {
90 670 : Compound* compound = _compounds.back();
91 670 : removeCompound(compound);
92 670 : delete compound;
93 : }
94 804 : }
95 :
96 2 : void Config::attach(const uint128_t& id, const uint32_t instanceID)
97 : {
98 2 : Super::attach(id, instanceID);
99 :
100 2 : co::CommandQueue* mainQ = getMainThreadQueue();
101 2 : co::CommandQueue* cmdQ = getCommandThreadQueue();
102 :
103 2 : registerCommand(fabric::CMD_CONFIG_INIT,
104 4 : ConfigFunc(this, &Config::_cmdInit), mainQ);
105 2 : registerCommand(fabric::CMD_CONFIG_EXIT,
106 4 : ConfigFunc(this, &Config::_cmdExit), mainQ);
107 2 : registerCommand(fabric::CMD_CONFIG_UPDATE,
108 4 : ConfigFunc(this, &Config::_cmdUpdate), mainQ);
109 2 : registerCommand(fabric::CMD_CONFIG_CREATE_REPLY,
110 4 : ConfigFunc(this, &Config::_cmdCreateReply), cmdQ);
111 2 : registerCommand(fabric::CMD_CONFIG_START_FRAME,
112 4 : ConfigFunc(this, &Config::_cmdStartFrame), mainQ);
113 2 : registerCommand(fabric::CMD_CONFIG_STOP_FRAMES,
114 4 : ConfigFunc(this, &Config::_cmdStopFrames), mainQ);
115 2 : registerCommand(fabric::CMD_CONFIG_FINISH_ALL_FRAMES,
116 4 : ConfigFunc(this, &Config::_cmdFinishAllFrames), mainQ);
117 2 : registerCommand(fabric::CMD_CONFIG_CHECK_FRAME,
118 4 : ConfigFunc(this, &Config::_cmdCheckFrame), mainQ);
119 2 : }
120 :
121 : namespace
122 : {
123 : class ChannelViewFinder : public ConfigVisitor
124 : {
125 : public:
126 2264 : ChannelViewFinder(const Segment* const segment, const View* const view)
127 2264 : : _segment(segment)
128 : , _view(view)
129 2264 : , _result(0)
130 : {
131 2264 : }
132 :
133 2264 : virtual ~ChannelViewFinder() {}
134 21920 : virtual VisitorResult visit(Channel* channel)
135 : {
136 21920 : if (channel->getView() != _view)
137 18248 : return TRAVERSE_CONTINUE;
138 :
139 3672 : if (channel->getSegment() != _segment)
140 2632 : return TRAVERSE_CONTINUE;
141 :
142 1040 : _result = channel;
143 1040 : return TRAVERSE_TERMINATE;
144 : }
145 :
146 2264 : Channel* getResult() { return _result; }
147 : private:
148 : const Segment* const _segment;
149 : const View* const _view;
150 : Channel* _result;
151 : };
152 :
153 2 : class UpdateEqualizersVisitor : public ConfigVisitor
154 : {
155 : public:
156 2 : UpdateEqualizersVisitor() {}
157 : // No need to go down on nodes.
158 2 : VisitorResult visitPre(Node*) override { return TRAVERSE_PRUNE; }
159 132 : VisitorResult visit(Compound* compound) override
160 : {
161 132 : Channel* dest = compound->getInheritChannel();
162 132 : if (!dest)
163 14 : return TRAVERSE_CONTINUE;
164 :
165 118 : View* view = dest->getView();
166 118 : if (!view)
167 0 : return TRAVERSE_CONTINUE;
168 :
169 118 : const Equalizers& equalizers = compound->getEqualizers();
170 136 : for (EqualizersCIter i = equalizers.begin(); i != equalizers.end(); ++i)
171 18 : view->getEqualizer() = *(*i);
172 :
173 118 : return TRAVERSE_CONTINUE;
174 : }
175 : };
176 : }
177 :
178 398 : const Channel* Config::findChannel(const std::string& name) const
179 : {
180 398 : return Super::find<Channel>(name);
181 : }
182 :
183 2264 : Channel* Config::findChannel(const Segment* segment, const View* view)
184 : {
185 4528 : ChannelViewFinder finder(segment, view);
186 2264 : accept(finder);
187 4528 : return finder.getResult();
188 : }
189 :
190 404 : Node* Config::findApplicationNode()
191 : {
192 404 : const Nodes& nodes = getNodes();
193 404 : for (Nodes::const_iterator i = nodes.begin(); i != nodes.end(); ++i)
194 : {
195 404 : Node* node = *i;
196 404 : if (node->isApplicationNode())
197 404 : return node;
198 : }
199 0 : return 0;
200 : }
201 :
202 6 : co::NodePtr Config::findApplicationNetNode()
203 : {
204 6 : Node* node = findApplicationNode();
205 6 : LBASSERT(node);
206 6 : return node ? node->getNode() : 0;
207 : }
208 :
209 420 : void Config::activateCanvas(Canvas* canvas)
210 : {
211 420 : LBASSERT(canvas->isStopped());
212 420 : LBASSERT(lunchbox::find(getCanvases(), canvas) != getCanvases().end());
213 :
214 420 : const Layouts& layouts = canvas->getLayouts();
215 420 : const Segments& segments = canvas->getSegments();
216 :
217 1076 : for (Layouts::const_iterator i = layouts.begin(); i != layouts.end(); ++i)
218 : {
219 656 : const Layout* layout = *i;
220 656 : if (!layout)
221 4 : continue;
222 :
223 652 : const Views& views = layout->getViews();
224 1372 : for (Views::const_iterator j = views.begin(); j != views.end(); ++j)
225 : {
226 720 : View* view = *j;
227 :
228 7164 : for (Segments::const_iterator k = segments.begin();
229 4776 : k != segments.end(); ++k)
230 : {
231 1668 : Segment* segment = *k;
232 1668 : Viewport viewport = segment->getViewport();
233 1668 : viewport.intersect(view->getViewport());
234 :
235 1668 : if (!viewport.hasArea())
236 : {
237 444 : LBLOG(LOG_VIEW)
238 0 : << "View " << view->getName() << view->getViewport()
239 0 : << " doesn't intersect " << segment->getName()
240 444 : << segment->getViewport() << std::endl;
241 :
242 888 : continue;
243 : }
244 :
245 1224 : Channel* segmentChannel = segment->getChannel();
246 1224 : if (!segmentChannel)
247 : {
248 0 : LBWARN << "Segment " << segment->getName()
249 0 : << " has no output channel" << std::endl;
250 0 : continue;
251 : }
252 :
253 1224 : if (findChannel(segment, view))
254 0 : continue;
255 :
256 : // create and add new channel
257 1224 : Channel* channel = new Channel(*segmentChannel);
258 1224 : channel->init(); // not in ctor, virtual method
259 1224 : channel->setOutput(view, segment);
260 :
261 : //----- compute channel viewport:
262 : // segment/view intersection in canvas space...
263 1224 : Viewport contribution = viewport;
264 : // ... in segment space...
265 1224 : contribution.transform(segment->getViewport());
266 :
267 : // segment output area
268 1224 : if (segmentChannel->hasFixedViewport())
269 : {
270 1212 : Viewport subViewport = segmentChannel->getViewport();
271 1212 : LBASSERT(subViewport.isValid());
272 1212 : if (!subViewport.isValid())
273 0 : subViewport = eq::fabric::Viewport::FULL;
274 :
275 : // ...our part of it
276 1212 : subViewport.apply(contribution);
277 1212 : channel->setViewport(subViewport);
278 1212 : LBLOG(LOG_VIEW)
279 0 : << "View @" << (void*)view << ' ' << view->getViewport()
280 0 : << " intersects " << segment->getName()
281 0 : << segment->getViewport() << " at " << subViewport
282 1212 : << " using channel @" << (void*)channel << std::endl;
283 : }
284 : else
285 : {
286 12 : PixelViewport pvp = segmentChannel->getPixelViewport();
287 12 : LBASSERT(pvp.isValid());
288 12 : pvp.apply(contribution);
289 12 : channel->setPixelViewport(pvp);
290 12 : LBLOG(LOG_VIEW)
291 0 : << "View @" << (void*)view << ' ' << view->getViewport()
292 0 : << " intersects " << segment->getName()
293 0 : << segment->getViewport() << " at " << pvp
294 12 : << " using channel @" << (void*)channel << std::endl;
295 : }
296 :
297 1224 : if (channel->getWindow()->isAttached())
298 : // parent is already registered - register channel as well
299 0 : getServer()->registerObject(channel);
300 : }
301 : }
302 : }
303 420 : }
304 :
305 0 : void Config::updateCanvas(Canvas* canvas)
306 : {
307 0 : postNeedsFinish();
308 0 : activateCanvas(canvas);
309 :
310 : // Create one compound group for all new output channels of each layout
311 0 : const Layouts& layouts = canvas->getLayouts();
312 0 : for (LayoutsCIter i = layouts.begin(); i != layouts.end(); ++i)
313 : {
314 0 : Compound* group = new Compound(this);
315 :
316 0 : const Layout* layout = *i;
317 0 : const Views& views = layout->getViews();
318 0 : for (ViewsCIter j = views.begin(); j != views.end(); ++j)
319 : {
320 0 : const View* view = *j;
321 0 : const Channels& channels = view->getChannels();
322 :
323 0 : if (channels.empty())
324 0 : LBWARN << "View without destination channels will be ignored"
325 0 : << std::endl;
326 :
327 0 : for (ChannelsCIter k = channels.begin(); k != channels.end(); ++k)
328 : {
329 0 : Channel* channel = *k;
330 0 : LBASSERT(!channel->isActive());
331 :
332 0 : Compound* compound = new Compound(group);
333 : compound->setIAttribute(Compound::IATTR_STEREO_MODE,
334 0 : fabric::AUTO);
335 0 : compound->setChannel(channel);
336 : }
337 : }
338 0 : group->init();
339 : }
340 :
341 0 : canvas->init();
342 0 : LBDEBUG << *this << std::endl;
343 0 : }
344 :
345 0 : Observer* Config::createObserver()
346 : {
347 0 : return new Observer(this);
348 : }
349 :
350 0 : void Config::releaseObserver(Observer* observer)
351 : {
352 0 : delete observer;
353 0 : }
354 :
355 0 : Layout* Config::createLayout()
356 : {
357 0 : return new Layout(this);
358 : }
359 :
360 0 : void Config::releaseLayout(Layout* layout)
361 : {
362 0 : delete layout;
363 0 : }
364 :
365 0 : Canvas* Config::createCanvas()
366 : {
367 0 : return new Canvas(this);
368 : }
369 :
370 0 : void Config::releaseCanvas(Canvas* canvas)
371 : {
372 0 : delete canvas;
373 0 : }
374 :
375 : template <class T>
376 0 : bool Config::_postDelete(const uint128_t& id)
377 : {
378 0 : T* child = find<T>(id);
379 0 : if (!child)
380 0 : return false;
381 :
382 0 : child->postDelete();
383 0 : return true;
384 : }
385 :
386 0 : void Config::removeChild(const uint128_t& id)
387 : {
388 0 : LBASSERT(isRunning());
389 :
390 0 : if (_postDelete<Observer>(id) || _postDelete<Layout>(id) ||
391 0 : _postDelete<Canvas>(id))
392 : {
393 0 : return;
394 : }
395 0 : LBUNIMPLEMENTED;
396 : }
397 :
398 670 : void Config::addCompound(Compound* compound)
399 : {
400 670 : LBASSERT(compound->_config == this);
401 670 : _compounds.push_back(compound);
402 670 : }
403 :
404 1340 : bool Config::removeCompound(Compound* compound)
405 : {
406 1340 : LBASSERT(compound->_config == this);
407 1340 : Compounds::iterator i = lunchbox::find(_compounds, compound);
408 1340 : if (i == _compounds.end())
409 670 : return false;
410 :
411 670 : _compounds.erase(i);
412 670 : return true;
413 : }
414 :
415 2 : void Config::setApplicationNetNode(co::NodePtr netNode)
416 : {
417 2 : if (netNode.isValid())
418 : {
419 2 : LBASSERT(_state == STATE_UNUSED);
420 2 : _state = STATE_STOPPED;
421 2 : setAppNodeID(netNode->getNodeID());
422 : }
423 : else
424 : {
425 0 : LBASSERT(_state == STATE_STOPPED);
426 0 : _state = STATE_UNUSED;
427 0 : setAppNodeID(uint128_t());
428 : }
429 :
430 2 : Node* node = findApplicationNode();
431 2 : LBASSERT(node);
432 2 : if (node)
433 2 : node->setNode(netNode);
434 2 : }
435 :
436 0 : Channel* Config::getChannel(const ChannelPath& path)
437 : {
438 0 : Nodes nodes = getNodes();
439 0 : LBASSERTINFO(nodes.size() > path.nodeIndex, nodes.size() << " <= "
440 : << path.nodeIndex);
441 :
442 0 : if (nodes.size() <= path.nodeIndex)
443 0 : return 0;
444 :
445 0 : return nodes[path.nodeIndex]->getChannel(path);
446 : }
447 :
448 1442 : Segment* Config::getSegment(const SegmentPath& path)
449 : {
450 1442 : Canvas* canvas = getCanvas(path);
451 1442 : LBASSERT(canvas);
452 :
453 1442 : if (canvas)
454 1442 : return canvas->getSegment(path);
455 :
456 0 : return 0;
457 : }
458 :
459 1246 : View* Config::getView(const ViewPath& path)
460 : {
461 1246 : Layout* layout = getLayout(path);
462 1246 : LBASSERT(layout);
463 :
464 1246 : if (layout)
465 1246 : return layout->getView(path);
466 :
467 0 : return 0;
468 : }
469 :
470 : namespace
471 : {
472 : template <class C>
473 2374 : static VisitorResult _accept(C* config, ConfigVisitor& visitor)
474 : {
475 2374 : VisitorResult result = TRAVERSE_CONTINUE;
476 2374 : const Compounds& compounds = config->getCompounds();
477 4984 : for (Compounds::const_iterator i = compounds.begin(); i != compounds.end();
478 : ++i)
479 : {
480 3032 : switch ((*i)->accept(visitor))
481 : {
482 : case TRAVERSE_TERMINATE:
483 422 : return TRAVERSE_TERMINATE;
484 :
485 : case TRAVERSE_PRUNE:
486 1308 : result = TRAVERSE_PRUNE;
487 1308 : break;
488 :
489 : case TRAVERSE_CONTINUE:
490 : default:
491 1302 : break;
492 : }
493 : }
494 1952 : return result;
495 : }
496 : }
497 :
498 2374 : VisitorResult Config::_acceptCompounds(ConfigVisitor& visitor)
499 : {
500 2374 : return _accept(this, visitor);
501 : }
502 :
503 0 : VisitorResult Config::_acceptCompounds(ConfigVisitor& visitor) const
504 : {
505 0 : return _accept(this, visitor);
506 : }
507 :
508 : //===========================================================================
509 : // operations
510 : //===========================================================================
511 :
512 2 : void Config::register_()
513 : {
514 4 : ConfigRegistrator registrator;
515 2 : accept(registrator);
516 2 : }
517 :
518 2 : void Config::deregister()
519 : {
520 2 : sync();
521 4 : ConfigDeregistrator deregistrator;
522 2 : accept(deregistrator);
523 2 : }
524 :
525 12 : uint128_t Config::commit()
526 : {
527 12 : return Super::commit(_incarnation);
528 : }
529 :
530 0 : void Config::restore()
531 : {
532 0 : _currentFrame = 0;
533 0 : _finishedFrame = 0;
534 0 : setApplicationNetNode(0);
535 0 : _workDir.clear();
536 0 : _renderClient.clear();
537 0 : Super::restore();
538 0 : }
539 :
540 0 : EventOCommand Config::sendError(const uint32_t type, const Error& error)
541 : {
542 0 : return Super::sendError(findApplicationNetNode(), type, error);
543 : }
544 :
545 : //---------------------------------------------------------------------------
546 : // update running entities (init/exit/runtime change)
547 : //---------------------------------------------------------------------------
548 4 : bool Config::_updateRunning(const bool canFail)
549 : {
550 4 : if (_state == STATE_STOPPED)
551 0 : return true;
552 :
553 4 : LBASSERT(_state == STATE_RUNNING || _state == STATE_INITIALIZING ||
554 : _state == STATE_EXITING);
555 :
556 4 : if (!_connectNodes() && !canFail)
557 0 : return false;
558 :
559 4 : _startNodes();
560 4 : _updateCanvases();
561 4 : const bool result = _updateNodes(canFail);
562 4 : _stopNodes();
563 :
564 : // Don't use visitor, it would get confused with modified child vectors
565 4 : _deleteEntities(getCanvases());
566 4 : _deleteEntities(getLayouts());
567 4 : _deleteEntities(getObservers());
568 4 : const Nodes& nodes = getNodes();
569 8 : for (Nodes::const_iterator i = nodes.begin(); i != nodes.end(); ++i)
570 : {
571 4 : const Pipes& pipes = (*i)->getPipes();
572 12 : for (Pipes::const_iterator j = pipes.begin(); j != pipes.end(); ++j)
573 : {
574 8 : const Windows& windows = (*j)->getWindows();
575 8 : _deleteEntities(windows);
576 : }
577 : }
578 :
579 4 : return result;
580 : }
581 :
582 4 : bool Config::_connectNodes()
583 : {
584 4 : bool success = true;
585 8 : lunchbox::Clock clock;
586 4 : const Nodes& nodes = getNodes();
587 8 : for (Nodes::const_iterator i = nodes.begin(); i != nodes.end(); ++i)
588 : {
589 4 : Node* node = *i;
590 4 : if (!node->isActive())
591 2 : continue;
592 :
593 2 : if (!node->connect())
594 0 : success = false;
595 : }
596 :
597 8 : for (Nodes::const_iterator i = nodes.begin(); i != nodes.end(); ++i)
598 : {
599 4 : Node* node = *i;
600 4 : if (node->isActive() && !node->syncLaunch(clock))
601 0 : success = false;
602 : }
603 :
604 8 : return success;
605 : }
606 :
607 4 : void Config::_startNodes()
608 : {
609 : // start up newly running nodes
610 8 : std::vector<lunchbox::Request<void>> requests;
611 4 : const Nodes& nodes = getNodes();
612 4 : requests.reserve(nodes.size());
613 :
614 8 : BOOST_FOREACH (Node* node, nodes)
615 : {
616 4 : if (node->isActive() && node->isStopped())
617 : {
618 2 : if (!node->isApplicationNode())
619 0 : requests.push_back(_createConfig(node));
620 : }
621 : else
622 : {
623 2 : LBASSERT(!node->isActive() || node->getState() == STATE_FAILED ||
624 : node->getState() == STATE_RUNNING);
625 : }
626 : }
627 : // Request dtor waits for finish
628 4 : }
629 :
630 4 : void Config::_updateCanvases()
631 : {
632 4 : const Canvases& canvases = getCanvases();
633 8 : for (Canvases::const_iterator i = canvases.begin(); i != canvases.end();
634 : ++i)
635 : {
636 4 : Canvas* canvas = *i;
637 4 : if (canvas->needsDelete())
638 0 : canvas->exit();
639 : }
640 4 : }
641 :
642 4 : void Config::_stopNodes()
643 : {
644 : // wait for the nodes to stop, destroy entities, disconnect
645 8 : Nodes stoppingNodes;
646 4 : const Nodes& nodes = getNodes();
647 8 : for (Nodes::const_iterator i = nodes.begin(); i != nodes.end(); ++i)
648 : {
649 4 : Node* node = *i;
650 4 : const State state = node->getState();
651 4 : if (state != STATE_STOPPED && state != STATE_FAILED)
652 6 : continue;
653 :
654 2 : LBASSERT(!node->isActive() || state == STATE_FAILED);
655 2 : if (node->isApplicationNode())
656 2 : continue;
657 :
658 0 : co::NodePtr netNode = node->getNode();
659 0 : if (!netNode) // already disconnected
660 0 : continue;
661 :
662 0 : LBLOG(LOG_INIT) << "Exiting node" << std::endl;
663 :
664 0 : if (state == STATE_FAILED)
665 0 : node->setState(STATE_STOPPED);
666 :
667 0 : stoppingNodes.push_back(node);
668 0 : LBASSERT(netNode.isValid());
669 :
670 0 : netNode->send(fabric::CMD_SERVER_DESTROY_CONFIG) << getID()
671 0 : << LB_UNDEFINED_UINT32;
672 0 : netNode->send(fabric::CMD_CLIENT_EXIT);
673 : }
674 :
675 : // now wait that the render clients disconnect
676 4 : uint32_t nSleeps = 50; // max 5 seconds for all clients
677 12 : for (Nodes::const_iterator i = stoppingNodes.begin();
678 8 : i != stoppingNodes.end(); ++i)
679 : {
680 0 : Node* node = *i;
681 0 : co::NodePtr netNode = node->getNode();
682 0 : node->setNode(0);
683 :
684 0 : if (nSleeps)
685 0 : while (netNode->isConnected() && --nSleeps)
686 0 : lunchbox::sleep(100); // ms
687 :
688 0 : if (netNode->isConnected())
689 : {
690 0 : co::LocalNodePtr localNode = getLocalNode();
691 0 : LBASSERT(localNode.isValid());
692 :
693 0 : LBWARN << "Forcefully disconnecting exited render client node"
694 0 : << std::endl;
695 0 : localNode->disconnect(netNode);
696 : }
697 :
698 0 : LBLOG(LOG_INIT) << "Disconnected node" << std::endl;
699 : }
700 4 : }
701 :
702 4 : bool Config::_updateNodes(const bool canFail)
703 : {
704 8 : ConfigUpdateVisitor update(_initID, _currentFrame);
705 4 : accept(update);
706 :
707 8 : ConfigUpdateSyncVisitor syncUpdate;
708 4 : accept(syncUpdate);
709 :
710 4 : const bool failure = syncUpdate.hadFailure();
711 :
712 4 : if (syncUpdate.needsSync()) // init failure, call again (exit pending)
713 : {
714 0 : LBASSERT(failure);
715 0 : accept(syncUpdate);
716 0 : LBASSERT(!syncUpdate.needsSync());
717 : }
718 :
719 4 : if (syncUpdate.getNumRunningChannels() == 0 && !canFail)
720 : {
721 0 : LBWARN << "Config has no running channels, will exit" << std::endl;
722 0 : return false;
723 : }
724 :
725 4 : return !failure;
726 : }
727 :
728 : template <class T>
729 20 : void Config::_deleteEntities(const std::vector<T*>& entities)
730 : {
731 64 : for (size_t i = 0; i < entities.size();) // don't use iterator! (delete)
732 : {
733 44 : T* entity = entities[i];
734 44 : if (entity->needsDelete())
735 : {
736 0 : LBASSERT(entity->isAttached());
737 0 : getServer()->deregisterObject(entity);
738 0 : delete entity;
739 : }
740 : else
741 44 : ++i;
742 : }
743 20 : }
744 :
745 0 : lunchbox::Request<void> Config::_createConfig(Node* node)
746 : {
747 0 : LBASSERT(!node->isApplicationNode());
748 0 : LBASSERT(node->isActive());
749 :
750 : // create config on each non-app node
751 : // app-node already has config from chooseConfig
752 0 : lunchbox::Request<void> request = getLocalNode()->registerRequest<void>();
753 :
754 0 : node->getNode()->send(fabric::CMD_SERVER_CREATE_CONFIG)
755 0 : << co::ObjectVersion(this) << request;
756 0 : return request;
757 : }
758 :
759 2 : void Config::_syncClock()
760 : {
761 2 : const Nodes& nodes = getNodes();
762 4 : for (Nodes::const_iterator i = nodes.begin(); i != nodes.end(); ++i)
763 : {
764 2 : Node* node = *i;
765 2 : if (node->isRunning() || node->isApplicationNode())
766 : {
767 2 : LBASSERT(node->isApplicationNode() || node->isActive());
768 4 : co::NodePtr netNode = node->getNode();
769 2 : LBASSERT(netNode->isConnected());
770 :
771 4 : send(netNode, fabric::CMD_CONFIG_SYNC_CLOCK)
772 6 : << getServer()->getTime();
773 : }
774 : }
775 2 : }
776 :
777 : //---------------------------------------------------------------------------
778 : // init
779 : //---------------------------------------------------------------------------
780 2 : bool Config::_init(const uint128_t& initID)
781 : {
782 2 : LBASSERT(_state == STATE_STOPPED);
783 2 : _state = STATE_INITIALIZING;
784 2 : _currentFrame = 0;
785 2 : _finishedFrame = 0;
786 2 : _initID = initID;
787 :
788 16 : for (auto compound : _compounds)
789 14 : compound->init();
790 :
791 4 : for (auto observer : getObservers())
792 2 : observer->init();
793 :
794 4 : for (auto canvas : getCanvases())
795 2 : canvas->init();
796 :
797 2 : const auto& layouts = getLayouts();
798 16 : for (auto layout : layouts)
799 28 : for (auto view : layout->getViews())
800 14 : view->init();
801 :
802 : // any of the above entities might have been updated
803 2 : commit();
804 :
805 2 : if (!_updateRunning(false))
806 0 : return false;
807 :
808 : // Needed to set up active state for first LB update
809 16 : for (CompoundsCIter i = _compounds.begin(); i != _compounds.end(); ++i)
810 14 : (*i)->update(0);
811 :
812 : // Update equalizer properties in views
813 4 : UpdateEqualizersVisitor updater;
814 2 : accept(updater);
815 :
816 2 : _needsFinish = false;
817 2 : _state = STATE_RUNNING;
818 2 : return true;
819 : }
820 :
821 : //---------------------------------------------------------------------------
822 : // exit
823 : //---------------------------------------------------------------------------
824 2 : bool Config::exit()
825 : {
826 2 : if (_state != STATE_RUNNING)
827 0 : LBWARN << "Exiting non-initialized config" << std::endl;
828 :
829 2 : LBASSERT(_state == STATE_RUNNING || _state == STATE_INITIALIZING);
830 2 : _state = STATE_EXITING;
831 :
832 2 : const Canvases& canvases = getCanvases();
833 4 : for (Canvases::const_iterator i = canvases.begin(); i != canvases.end();
834 : ++i)
835 : {
836 2 : Canvas* canvas = *i;
837 2 : canvas->exit();
838 : }
839 :
840 48 : for (Compounds::const_iterator i = _compounds.begin();
841 32 : i != _compounds.end(); ++i)
842 : {
843 14 : Compound* compound = *i;
844 14 : compound->exit();
845 : }
846 :
847 2 : const bool success = _updateRunning(true);
848 :
849 : // send exit event to app, needed if this is called from init()
850 4 : EventOCommand cmd(send(findApplicationNetNode(), fabric::CMD_CONFIG_EVENT));
851 2 : Event event;
852 2 : event.serial = getSerial();
853 2 : event.time = getServer()->getTime();
854 2 : event.originator = getID();
855 2 : cmd << EVENT_EXIT << event;
856 :
857 2 : _needsFinish = false;
858 2 : _state = STATE_STOPPED;
859 4 : return success;
860 : }
861 :
862 : //---------------------------------------------------------------------------
863 : // frame
864 : //---------------------------------------------------------------------------
865 2 : void Config::_startFrame(const uint128_t& frameID)
866 : {
867 2 : LBASSERT(_state == STATE_RUNNING);
868 2 : _verifyFrameFinished(_currentFrame);
869 2 : _syncClock();
870 :
871 2 : ++_currentFrame;
872 2 : ++_incarnation;
873 2 : LBLOG(LOG_TASKS) << "----- Start Frame ----- " << _currentFrame
874 2 : << std::endl;
875 :
876 48 : for (Compounds::const_iterator i = _compounds.begin();
877 32 : i != _compounds.end(); ++i)
878 : {
879 14 : Compound* compound = *i;
880 14 : compound->update(_currentFrame);
881 : }
882 :
883 4 : ConfigUpdateDataVisitor configDataVisitor;
884 2 : accept(configDataVisitor);
885 :
886 2 : const Nodes& nodes = getNodes();
887 4 : co::NodePtr appNode = findApplicationNetNode();
888 4 : for (Nodes::const_iterator i = nodes.begin(); i != nodes.end(); ++i)
889 : {
890 2 : Node* node = *i;
891 2 : node->update(frameID, _currentFrame);
892 2 : if (node->isRunning() && node->isApplicationNode())
893 2 : appNode = 0; // release sent (see below)
894 : }
895 :
896 2 : if (appNode) // release appNode local sync
897 0 : send(appNode, fabric::CMD_CONFIG_RELEASE_FRAME_LOCAL) << _currentFrame;
898 :
899 : // Fix 2976899: Config::finishFrame deadlocks when no nodes are active
900 2 : notifyNodeFrameFinished(_currentFrame);
901 2 : }
902 :
903 2 : void Config::_verifyFrameFinished(const uint32_t frameNumber)
904 : {
905 2 : const Nodes& nodes = getNodes();
906 4 : for (Nodes::const_iterator i = nodes.begin(); i != nodes.end(); ++i)
907 : {
908 2 : Node* node = *i;
909 4 : if (node->isRunning() &&
910 2 : node->getFinishedFrame() + getLatency() < frameNumber)
911 : {
912 0 : NodeFailedVisitor nodeFailedVisitor;
913 0 : node->accept(nodeFailedVisitor);
914 : }
915 : }
916 2 : }
917 :
918 4 : void Config::notifyNodeFrameFinished(const uint32_t frameNumber)
919 : {
920 4 : if (_finishedFrame >= frameNumber) // node finish already done
921 0 : return;
922 :
923 4 : const Nodes& nodes = getNodes();
924 6 : for (Nodes::const_iterator i = nodes.begin(); i != nodes.end(); ++i)
925 : {
926 4 : const Node* node = *i;
927 4 : if (node->isRunning() && node->getFinishedFrame() < frameNumber)
928 : {
929 2 : LBASSERT(_needsFinish || node->isActive());
930 4 : return;
931 : }
932 : }
933 :
934 2 : _finishedFrame = frameNumber;
935 :
936 : // All nodes have finished the frame. Notify the application's config that
937 : // the frame is finished
938 :
939 : // do not use send/_bufferedTasks, not thread-safe!
940 4 : send(findApplicationNetNode(), fabric::CMD_CONFIG_FRAME_FINISH)
941 2 : << frameNumber;
942 2 : LBLOG(LOG_TASKS) << "TASK config frame finished "
943 2 : << " frame " << frameNumber << std::endl;
944 : }
945 :
946 2 : void Config::_flushAllFrames()
947 : {
948 2 : if (_currentFrame == 0)
949 0 : return;
950 :
951 2 : const Nodes& nodes = getNodes();
952 4 : for (Nodes::const_iterator i = nodes.begin(); i != nodes.end(); ++i)
953 : {
954 2 : Node* node = *i;
955 2 : if (node->isRunning())
956 2 : node->flushFrames(_currentFrame);
957 : }
958 :
959 2 : LBLOG(LOG_TASKS) << "--- Flush All Frames -- " << std::endl;
960 : }
961 :
962 0 : void Config::changeLatency(const uint32_t latency)
963 : {
964 0 : if (getLatency() == latency)
965 0 : return;
966 :
967 0 : setLatency(latency);
968 :
969 : // update latency on all frames and barriers
970 0 : ChangeLatencyVisitor visitor(latency);
971 0 : accept(visitor);
972 : }
973 :
974 : //---------------------------------------------------------------------------
975 : // command handlers
976 : //---------------------------------------------------------------------------
977 2 : bool Config::_cmdInit(co::ICommand& cmd)
978 : {
979 4 : co::ObjectICommand command(cmd);
980 :
981 2 : LB_TS_THREAD(_mainThread);
982 2 : LBVERB << "handle config start init " << command << std::endl;
983 :
984 2 : sync();
985 2 : commit();
986 :
987 2 : const uint128_t& initID = command.read<uint128_t>();
988 2 : const uint32_t requestID = command.read<uint32_t>();
989 2 : const bool result = _init(initID);
990 :
991 2 : if (!result)
992 0 : exit();
993 :
994 2 : sync();
995 6 : LBDEBUG << "Config init " << (result ? "successful" : "failed")
996 6 : << std::endl;
997 :
998 2 : const uint128_t version = commit();
999 4 : send(command.getRemoteNode(), fabric::CMD_CONFIG_INIT_REPLY)
1000 2 : << version << requestID << result;
1001 4 : return true;
1002 : }
1003 :
1004 2 : bool Config::_cmdExit(co::ICommand& cmd)
1005 : {
1006 4 : co::ObjectICommand command(cmd);
1007 2 : LBVERB << "handle config exit " << command << std::endl;
1008 :
1009 : bool result;
1010 2 : if (_state == STATE_RUNNING)
1011 2 : result = exit();
1012 : else
1013 : {
1014 0 : LBINFO << "Exit of non-running config?" << std::endl;
1015 0 : result = false;
1016 : }
1017 :
1018 6 : LBDEBUG << "Config exit " << (result ? "successful" : "failed")
1019 6 : << std::endl;
1020 4 : send(command.getRemoteNode(), fabric::CMD_CONFIG_EXIT_REPLY)
1021 6 : << command.read<uint32_t>() << result;
1022 4 : return true;
1023 : }
1024 :
1025 4 : bool Config::_cmdUpdate(co::ICommand& cmd)
1026 : {
1027 8 : co::ObjectICommand command(cmd);
1028 :
1029 4 : LBVERB << "handle config update " << command << std::endl;
1030 :
1031 4 : const uint32_t versionID = command.read<uint32_t>();
1032 4 : const uint32_t finishID = command.read<uint32_t>();
1033 :
1034 4 : sync();
1035 4 : commit();
1036 :
1037 8 : co::NodePtr node = command.getRemoteNode();
1038 4 : if (!_needsFinish)
1039 : {
1040 8 : send(node, fabric::CMD_CONFIG_UPDATE_VERSION)
1041 12 : << getVersion() << versionID << finishID << LB_UNDEFINED_UINT32;
1042 4 : return true;
1043 : }
1044 :
1045 0 : co::LocalNodePtr localNode = getLocalNode();
1046 0 : lunchbox::Request<void> request = localNode->registerRequest<void>();
1047 :
1048 0 : send(node, fabric::CMD_CONFIG_UPDATE_VERSION) << getVersion() << versionID
1049 0 : << finishID << request;
1050 :
1051 0 : _flushAllFrames();
1052 0 : _finishedFrame.waitEQ(_currentFrame); // wait for render clients idle
1053 0 : request.wait(); // wait for app sync
1054 0 : _needsFinish = false;
1055 :
1056 0 : const bool canFail = (getIAttribute(IATTR_ROBUSTNESS) != OFF);
1057 0 : const bool result = _updateRunning(canFail);
1058 0 : if (!result && !canFail)
1059 : {
1060 0 : LBWARN << "Config update failed, exiting config" << std::endl;
1061 0 : exit();
1062 : }
1063 :
1064 0 : const uint128_t version = commit();
1065 0 : send(command.getRemoteNode(), fabric::CMD_CONFIG_UPDATE_REPLY)
1066 0 : << version << command.read<uint32_t>() << result;
1067 0 : return true;
1068 : }
1069 :
1070 2 : bool Config::_cmdStartFrame(co::ICommand& cmd)
1071 : {
1072 4 : co::ObjectICommand command(cmd);
1073 :
1074 2 : LBVERB << "handle config frame start " << command << std::endl;
1075 :
1076 2 : _startFrame(command.read<uint128_t>());
1077 :
1078 2 : if (_state == STATE_STOPPED)
1079 : {
1080 : // unlock app
1081 0 : send(command.getRemoteNode(), fabric::CMD_CONFIG_FRAME_FINISH)
1082 0 : << _currentFrame;
1083 : }
1084 4 : return true;
1085 : }
1086 :
1087 2 : bool Config::_cmdFinishAllFrames(co::ICommand& cmd)
1088 : {
1089 4 : co::ObjectICommand command(cmd);
1090 :
1091 2 : LBVERB << "handle config all frames finish " << command << std::endl;
1092 :
1093 2 : _flushAllFrames();
1094 4 : return true;
1095 : }
1096 :
1097 0 : bool Config::_cmdStopFrames(co::ICommand& cmd)
1098 : {
1099 0 : co::ObjectICommand command(cmd);
1100 :
1101 0 : LBVERB << "handle config stop frames " << command << std::endl;
1102 :
1103 0 : ChannelStopFrameVisitor visitor(_currentFrame);
1104 0 : accept(visitor);
1105 :
1106 0 : return true;
1107 : }
1108 :
1109 0 : bool Config::_cmdCreateReply(co::ICommand& cmd)
1110 : {
1111 0 : co::ObjectICommand command(cmd);
1112 :
1113 0 : LB_TS_THREAD(_cmdThread);
1114 0 : LB_TS_NOT_THREAD(_mainThread);
1115 :
1116 0 : getLocalNode()->serveRequest(command.read<uint32_t>());
1117 0 : return true;
1118 : }
1119 :
1120 0 : bool Config::_cmdCheckFrame(co::ICommand& cmd)
1121 : {
1122 0 : const int64_t lastInterval = getServer()->getTime() - _lastCheck;
1123 0 : _lastCheck = getServer()->getTime();
1124 :
1125 0 : co::ObjectICommand command(cmd);
1126 0 : LBVERB << "Check nodes for frame finish " << command << std::endl;
1127 :
1128 0 : const uint32_t frameNumber = command.read<uint32_t>();
1129 0 : const uint32_t timeout = getTimeout();
1130 :
1131 0 : bool retry = false;
1132 0 : const Nodes& nodes = getNodes();
1133 0 : for (Nodes::const_iterator i = nodes.begin(); i != nodes.end(); ++i)
1134 : {
1135 0 : Node* node = *i;
1136 0 : if (node->isRunning() && node->isActive())
1137 : {
1138 0 : co::NodePtr netNode = node->getNode();
1139 0 : if (netNode->isClosed())
1140 0 : continue;
1141 :
1142 0 : if (node->getFinishedFrame() >= frameNumber)
1143 0 : continue;
1144 :
1145 : const int64_t interval =
1146 0 : getServer()->getTime() - netNode->getLastReceiveTime();
1147 0 : getLocalNode()->ping(netNode);
1148 :
1149 : // TODO?: handle timed out nodes.
1150 : // currently we get a false positive due to lack of communication
1151 : // from client to server. we do not get ping responses in time.
1152 : // running clients should inform the server about their status with
1153 : // a timeout/2 period.
1154 :
1155 0 : if (interval > timeout && lastInterval <= timeout)
1156 0 : continue;
1157 :
1158 : // retry
1159 0 : LBINFO << "Retry waiting for node " << node->getName()
1160 0 : << " to finish frame " << frameNumber << " last seen "
1161 0 : << interval << " ms ago"
1162 0 : << " last run " << lastInterval << std::endl;
1163 0 : retry = true;
1164 : // else node timeout
1165 : }
1166 : }
1167 :
1168 0 : if (retry)
1169 0 : return true;
1170 :
1171 0 : send(command.getRemoteNode(), fabric::CMD_CONFIG_FRAME_FINISH)
1172 0 : << _currentFrame;
1173 0 : return true;
1174 : }
1175 :
1176 204 : void Config::output(std::ostream& os) const
1177 : {
1178 204 : os << std::endl << lunchbox::disableFlush << lunchbox::disableHeader;
1179 :
1180 1680 : for (Compounds::const_iterator i = _compounds.begin();
1181 1120 : i != _compounds.end(); ++i)
1182 : {
1183 356 : os << **i;
1184 : }
1185 :
1186 204 : os << lunchbox::enableHeader << lunchbox::enableFlush;
1187 204 : }
1188 : }
1189 : }
1190 :
1191 : #include "../fabric/config.ipp"
1192 : #include "nodeFactory.h"
1193 : template class eq::fabric::Config<eq::server::Server, eq::server::Config,
1194 : eq::server::Observer, eq::server::Layout,
1195 : eq::server::Canvas, eq::server::Node,
1196 : eq::server::ConfigVisitor>;
1197 :
1198 : /** @cond IGNORE */
1199 : template std::ostream& eq::fabric::operator<<(std::ostream&,
1200 : const eq::server::Config::Super&);
1201 : /** @endcond */
1202 :
1203 : #define FIND_ID_TEMPLATE1(type) \
1204 : template void eq::server::Config::Super::find<type>(const uint128_t&, \
1205 : type**);
1206 :
1207 : FIND_ID_TEMPLATE1(eq::server::Canvas);
1208 : FIND_ID_TEMPLATE1(eq::server::Channel);
1209 : FIND_ID_TEMPLATE1(eq::server::Layout);
1210 : FIND_ID_TEMPLATE1(eq::server::Node);
1211 : FIND_ID_TEMPLATE1(eq::server::Observer);
1212 : FIND_ID_TEMPLATE1(eq::server::Pipe);
1213 : FIND_ID_TEMPLATE1(eq::server::Segment);
1214 : FIND_ID_TEMPLATE1(eq::server::View);
1215 : FIND_ID_TEMPLATE1(eq::server::Window);
1216 :
1217 : #define FIND_ID_TEMPLATE2(type) \
1218 : template type* eq::server::Config::Super::find<type>(const uint128_t&);
1219 :
1220 : FIND_ID_TEMPLATE2(eq::server::Canvas);
1221 : FIND_ID_TEMPLATE2(eq::server::Channel);
1222 : FIND_ID_TEMPLATE2(eq::server::Layout);
1223 : FIND_ID_TEMPLATE2(eq::server::Node);
1224 : FIND_ID_TEMPLATE2(eq::server::Observer);
1225 : FIND_ID_TEMPLATE2(eq::server::Pipe);
1226 : FIND_ID_TEMPLATE2(eq::server::Segment);
1227 : FIND_ID_TEMPLATE2(eq::server::View);
1228 : FIND_ID_TEMPLATE2(eq::server::Window);
1229 :
1230 : #define FIND_NAME_TEMPLATE1(type) \
1231 : template void eq::server::Config::Super::find<type>(const std::string&, \
1232 : const type**) const;
1233 : FIND_NAME_TEMPLATE1(eq::server::Canvas);
1234 : FIND_NAME_TEMPLATE1(eq::server::Channel);
1235 : FIND_NAME_TEMPLATE1(eq::server::Layout);
1236 : FIND_NAME_TEMPLATE1(eq::server::Node);
1237 : FIND_NAME_TEMPLATE1(eq::server::Observer);
1238 : FIND_NAME_TEMPLATE1(eq::server::Pipe);
1239 : FIND_NAME_TEMPLATE1(eq::server::Segment);
1240 : FIND_NAME_TEMPLATE1(eq::server::View);
1241 : FIND_NAME_TEMPLATE1(eq::server::Window);
1242 :
1243 : #define FIND_NAME_TEMPLATE2(type) \
1244 : template type* eq::server::Config::Super::find<type>(const std::string&);
1245 :
1246 : FIND_NAME_TEMPLATE2(eq::server::Canvas);
1247 : FIND_NAME_TEMPLATE2(eq::server::Channel);
1248 : FIND_NAME_TEMPLATE2(eq::server::Layout);
1249 : FIND_NAME_TEMPLATE2(eq::server::Node);
1250 : FIND_NAME_TEMPLATE2(eq::server::Observer);
1251 : FIND_NAME_TEMPLATE2(eq::server::Pipe);
1252 : FIND_NAME_TEMPLATE2(eq::server::Segment);
1253 : FIND_NAME_TEMPLATE2(eq::server::View);
1254 : FIND_NAME_TEMPLATE2(eq::server::Window);
1255 :
1256 : #define CONST_FIND_NAME_TEMPLATE2(type) \
1257 : template const type* eq::server::Config::Super::find<type>( \
1258 : const std::string&) const;
1259 :
1260 : CONST_FIND_NAME_TEMPLATE2(eq::server::Canvas);
1261 : CONST_FIND_NAME_TEMPLATE2(eq::server::Channel);
1262 : CONST_FIND_NAME_TEMPLATE2(eq::server::Layout);
1263 : CONST_FIND_NAME_TEMPLATE2(eq::server::Node);
1264 : CONST_FIND_NAME_TEMPLATE2(eq::server::Observer);
1265 : CONST_FIND_NAME_TEMPLATE2(eq::server::Pipe);
1266 : CONST_FIND_NAME_TEMPLATE2(eq::server::Segment);
1267 : CONST_FIND_NAME_TEMPLATE2(eq::server::View);
1268 60 : CONST_FIND_NAME_TEMPLATE2(eq::server::Window);
|