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