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