Line data Source code
1 :
2 : /* Copyright (c) 2005-2016, Stefan Eilemann <eile@equalizergraphics.com>
3 : * Daniel Nachbaur <danielnachbaur@gmail.com>
4 : * Cedric Stalder <cedric.stalder@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 "node.h"
21 :
22 : #include "channel.h"
23 : #include "config.h"
24 : #include "global.h"
25 : #include "log.h"
26 : #include "nodeFactory.h"
27 : #include "pipe.h"
28 : #include "server.h"
29 : #include "window.h"
30 :
31 : #include <eq/fabric/commands.h>
32 : #include <eq/fabric/elementVisitor.h>
33 : #include <eq/fabric/event.h>
34 : #include <eq/fabric/paths.h>
35 :
36 : #include <co/barrier.h>
37 : #include <co/global.h>
38 : #include <co/objectICommand.h>
39 :
40 : #include <lunchbox/clock.h>
41 : #include <lunchbox/os.h>
42 : #include <lunchbox/sleep.h>
43 :
44 : #include <boost/filesystem/operations.hpp>
45 : #include <boost/filesystem/path.hpp>
46 :
47 : namespace eq
48 : {
49 : namespace server
50 : {
51 : typedef fabric::Node< Config, Node, Pipe, NodeVisitor > Super;
52 : typedef co::CommandFunc<Node> NodeFunc;
53 : namespace
54 : {
55 : #define S_MAKE_ATTR_STRING( attr ) ( std::string("EQ_NODE_") + #attr )
56 56 : std::string _sAttributeStrings[] = {
57 : S_MAKE_ATTR_STRING( SATTR_LAUNCH_COMMAND )
58 28 : };
59 56 : std::string _cAttributeStrings[] = {
60 : S_MAKE_ATTR_STRING( CATTR_LAUNCH_COMMAND_QUOTE )
61 28 : };
62 :
63 0 : void _addEnv( std::ostringstream& stream, const char* key )
64 : {
65 0 : char* env = getenv( key );
66 0 : if( env )
67 0 : stream << key << "=" << env << " ";
68 0 : }
69 : }
70 :
71 862 : Node::Node( Config* parent )
72 : : Super( parent )
73 : , _active( 0 )
74 : , _finishedFrame( 0 )
75 : , _flushedFrame( 0 )
76 : , _state( STATE_STOPPED )
77 862 : , _bufferedTasks( new co::BufferConnection )
78 1724 : , _lastDrawPipe( 0 )
79 : {
80 862 : const Global* global = Global::instance();
81 1724 : for( int i=0; i < Node::SATTR_LAST; ++i )
82 : {
83 862 : const SAttribute attr = static_cast< SAttribute >( i );
84 862 : setSAttribute( attr, global->getNodeSAttribute( attr ));
85 : }
86 1724 : for( int i=0; i < Node::CATTR_LAST; ++i )
87 : {
88 862 : const CAttribute attr = static_cast< CAttribute >( i );
89 862 : setCAttribute( attr, global->getNodeCAttribute( attr ));
90 : }
91 3448 : for( int i = 0; i < IATTR_LAST; ++i )
92 : {
93 2586 : const IAttribute attr = static_cast< IAttribute >( i );
94 2586 : setIAttribute( attr, global->getNodeIAttribute( attr ));
95 : }
96 862 : }
97 :
98 1720 : Node::~Node()
99 : {
100 1720 : }
101 :
102 4 : void Node::attach( const uint128_t& id, const uint32_t instanceID )
103 : {
104 4 : Super::attach( id, instanceID );
105 :
106 4 : co::CommandQueue* cmdQ = getCommandThreadQueue();
107 : registerCommand( fabric::CMD_OBJECT_SYNC,
108 4 : NodeFunc( this, &Node::_cmdSync ), cmdQ );
109 : registerCommand( fabric::CMD_NODE_CONFIG_INIT_REPLY,
110 4 : NodeFunc( this, &Node::_cmdConfigInitReply ), cmdQ );
111 : registerCommand( fabric::CMD_NODE_CONFIG_EXIT_REPLY,
112 4 : NodeFunc( this, &Node::_cmdConfigExitReply ), cmdQ );
113 : registerCommand( fabric::CMD_NODE_FRAME_FINISH_REPLY,
114 4 : NodeFunc( this, &Node::_cmdFrameFinishReply ), cmdQ );
115 4 : }
116 :
117 20 : ServerPtr Node::getServer()
118 : {
119 20 : return getConfig() ? getConfig()->getServer() : 0;
120 : }
121 :
122 0 : ConstServerPtr Node::getServer() const
123 : {
124 0 : return getConfig() ? getConfig()->getServer() : 0;
125 : }
126 :
127 12 : co::CommandQueue* Node::getMainThreadQueue()
128 : {
129 12 : return getConfig()->getMainThreadQueue();
130 : }
131 :
132 20 : co::CommandQueue* Node::getCommandThreadQueue()
133 : {
134 20 : return getConfig()->getCommandThreadQueue();
135 : }
136 :
137 0 : Channel* Node::getChannel( const ChannelPath& path )
138 : {
139 0 : const Pipes& pipes = getPipes();
140 0 : LBASSERT( pipes.size() > path.pipeIndex );
141 :
142 0 : if( pipes.size() <= path.pipeIndex )
143 0 : return 0;
144 :
145 0 : return pipes[ path.pipeIndex ]->getChannel( path );
146 : }
147 :
148 4 : void Node::addTasks( const uint32_t tasks )
149 : {
150 4 : setTasks( getTasks() | tasks );
151 4 : }
152 :
153 2 : void Node::activate()
154 : {
155 2 : ++_active;
156 2 : LBLOG( LOG_VIEW ) << "activate: " << _active << std::endl;
157 2 : }
158 :
159 2 : void Node::deactivate()
160 : {
161 2 : LBASSERT( _active != 0 );
162 2 : --_active;
163 2 : LBLOG( LOG_VIEW ) << "deactivate: " << _active << std::endl;
164 2 : };
165 :
166 862 : void Node::setSAttribute( const SAttribute attr, const std::string& value )
167 : {
168 862 : _sAttributes[attr] = value;
169 862 : }
170 :
171 432 : const std::string& Node::getSAttribute( const SAttribute attr ) const
172 : {
173 432 : return _sAttributes[attr];
174 : }
175 :
176 682 : const std::string& Node::getSAttributeString( const SAttribute attr )
177 : {
178 682 : return _sAttributeStrings[attr];
179 : }
180 :
181 862 : void Node::setCAttribute( const CAttribute attr, const char value )
182 : {
183 862 : _cAttributes[attr] = value;
184 862 : }
185 :
186 432 : char Node::getCAttribute( const CAttribute attr ) const
187 : {
188 432 : return _cAttributes[attr];
189 : }
190 :
191 682 : const std::string& Node::getCAttributeString( const CAttribute attr )
192 : {
193 682 : return _cAttributeStrings[attr];
194 : }
195 :
196 : //===========================================================================
197 : // Operations
198 : //===========================================================================
199 :
200 : //---------------------------------------------------------------------------
201 : // launch and connect
202 : //---------------------------------------------------------------------------
203 :
204 : namespace
205 : {
206 0 : class NetNode : public co::Node
207 : {
208 : public:
209 0 : NetNode( server::Node& node ) : co::Node(), _node( node ) {}
210 :
211 : private:
212 : server::Node& _node;
213 :
214 0 : std::string getWorkDir() const override
215 0 : { return _node.getConfig()->getWorkDir(); }
216 :
217 0 : std::string getLaunchQuote() const override
218 : {
219 : return std::string( 1, _node.getCAttribute(
220 0 : server::Node::CATTR_LAUNCH_COMMAND_QUOTE ));
221 : }
222 : };
223 :
224 0 : static co::NodePtr _createNetNode( Node* node )
225 : {
226 0 : co::NodePtr netNode = new NetNode( *node );
227 : const co::ConnectionDescriptions& descriptions =
228 0 : node->getConnectionDescriptions();
229 0 : for( co::ConnectionDescriptionPtr desc : descriptions )
230 : {
231 : netNode->addConnectionDescription(
232 0 : new co::ConnectionDescription( *desc ));
233 :
234 0 : if( node->getHost().empty( ))
235 : {
236 0 : node->setHost( desc->getHostname( ));
237 0 : LBWARN << "No host specified, guessing " << node->getHost()
238 0 : << " from " << desc << std::endl;
239 : }
240 0 : }
241 :
242 0 : netNode->setHostname( node->getHost( ));
243 0 : return netNode;
244 : }
245 : }
246 :
247 2 : bool Node::connect()
248 : {
249 2 : LBASSERT( isActive( ));
250 :
251 2 : if( _node )
252 2 : return _node->isConnected();
253 :
254 0 : if( !isStopped( ))
255 : {
256 0 : LBASSERT( _state == STATE_FAILED );
257 0 : return true;
258 : }
259 :
260 0 : co::LocalNodePtr localNode = getLocalNode();
261 0 : LBASSERT( localNode );
262 :
263 0 : _node = _createNetNode( this );
264 :
265 0 : LBLOG( LOG_INIT ) << "Connecting node" << std::endl;
266 0 : if( localNode->connect( _node ) ||
267 0 : localNode->launch( _node, _createLaunchCommand( )))
268 : {
269 0 : return true;
270 : }
271 :
272 0 : LBWARN << "Connection to " << _node->getNodeID() << " failed" << std::endl;
273 0 : _state = STATE_FAILED;
274 0 : _node = nullptr;
275 0 : return false;
276 : }
277 :
278 2 : bool Node::syncLaunch( const lunchbox::Clock& clock )
279 : {
280 2 : LBASSERT( isActive( ));
281 :
282 2 : if( !_node )
283 0 : return false;
284 :
285 2 : if( _node->isConnected( ))
286 2 : return true;
287 :
288 0 : LBASSERT( !isApplicationNode( ));
289 :
290 0 : co::LocalNodePtr localNode = getLocalNode();
291 0 : const int64_t timeOut = getIAttribute( IATTR_LAUNCH_TIMEOUT );
292 0 : _node = localNode->syncLaunch( _node->getNodeID(),
293 : std::max( int64_t( 0 ),
294 0 : timeOut - clock.getTime64( )));
295 0 : if( _node )
296 0 : return true;
297 :
298 0 : sendError( fabric::ERROR_NODE_CONNECT ) << _host;
299 0 : _state = STATE_FAILED;
300 0 : return false;
301 : }
302 :
303 0 : std::string Node::_createLaunchCommand() const
304 : {
305 0 : const std::string& command = getSAttribute( SATTR_LAUNCH_COMMAND );
306 0 : const size_t commandPos = command.find( "%c" );
307 0 : if( commandPos == std::string::npos )
308 0 : return command + " " + _createRemoteCommand();
309 :
310 0 : return command.substr( 0, commandPos ) + _createRemoteCommand() +
311 0 : command.substr( commandPos + 1 );
312 : }
313 :
314 0 : std::string Node::_createRemoteCommand() const
315 : {
316 0 : const Config* config = getConfig();
317 0 : std::string program = config->getRenderClient();
318 0 : if( program.empty( ))
319 : {
320 0 : LBWARN << "No render client name, auto-launch will fail" << std::endl;
321 0 : return program;
322 : }
323 :
324 : //----- environment
325 0 : std::ostringstream os;
326 0 : const std::string& quote = _node->getLaunchQuote();
327 :
328 : //----- program + args
329 : #ifndef WIN32
330 : # ifdef Darwin
331 : const char libPath[] = "DYLD_LIBRARY_PATH";
332 : # else
333 0 : const char libPath[] = "LD_LIBRARY_PATH";
334 : # endif
335 :
336 0 : os << "env ";
337 0 : _addEnv( os, libPath );
338 0 : _addEnv( os, "PATH" );
339 0 : _addEnv( os, "PYTHONPATH" );
340 :
341 0 : for( int i=0; environ[i] != 0; ++i )
342 : {
343 0 : if( strlen( environ[i] ) > 2 &&
344 0 : ( strncmp( environ[i], "LB_", 3 ) == 0 ||
345 0 : strncmp( environ[i], "CO_", 3 ) == 0 ||
346 0 : strncmp( environ[i], "EQ_", 3 ) == 0 ))
347 : {
348 0 : os << quote << environ[i] << quote << " ";
349 : }
350 : }
351 :
352 0 : os << "LB_LOG_LEVEL=" <<lunchbox::Log::getLogLevelString() << " ";
353 0 : if( lunchbox::Log::topics != 0 )
354 0 : os << "LB_LOG_TOPICS=" <<lunchbox::Log::topics << " ";
355 : #endif
356 :
357 : const boost::filesystem::path absolute =
358 0 : boost::filesystem::system_complete( boost::filesystem::path( program ));
359 0 : program = absolute.string();
360 :
361 0 : return os.str() + quote + program + quote + " -- --eq-client %o ";
362 : }
363 :
364 : //---------------------------------------------------------------------------
365 : // init
366 : //---------------------------------------------------------------------------
367 2 : void Node::configInit( const uint128_t& initID, const uint32_t frameNumber )
368 : {
369 2 : LBASSERT( _state == STATE_STOPPED );
370 2 : _state = STATE_INITIALIZING;
371 :
372 2 : const Config* config = getConfig();
373 2 : _flushedFrame = config->getFinishedFrame();
374 2 : _finishedFrame = config->getFinishedFrame();
375 2 : _frameIDs.clear();
376 :
377 2 : LBLOG( LOG_INIT ) << "Create node" << std::endl;
378 2 : getConfig()->send( _node, fabric::CMD_CONFIG_CREATE_NODE ) << getID();
379 :
380 2 : LBLOG( LOG_INIT ) << "Init node" << std::endl;
381 2 : send( fabric::CMD_NODE_CONFIG_INIT ) << initID << frameNumber;
382 2 : }
383 :
384 2 : bool Node::syncConfigInit()
385 : {
386 2 : LBASSERT( _state == STATE_INITIALIZING || _state == STATE_INIT_SUCCESS ||
387 : _state == STATE_INIT_FAILED );
388 :
389 2 : _state.waitNE( STATE_INITIALIZING );
390 :
391 2 : if( _state == STATE_INIT_SUCCESS )
392 : {
393 2 : _state = STATE_RUNNING;
394 2 : return true;
395 : }
396 :
397 0 : LBWARN << "Node initialization failed" << std::endl;
398 0 : configExit();
399 0 : return false;
400 : }
401 :
402 : //---------------------------------------------------------------------------
403 : // exit
404 : //---------------------------------------------------------------------------
405 2 : void Node::configExit()
406 : {
407 2 : if( _state == STATE_EXITING )
408 2 : return;
409 :
410 2 : LBASSERT( _state == STATE_RUNNING || _state == STATE_INIT_FAILED );
411 2 : _state = STATE_EXITING;
412 :
413 2 : LBLOG( LOG_INIT ) << "Exit node" << std::endl;
414 2 : send( fabric::CMD_NODE_CONFIG_EXIT );
415 2 : flushSendBuffer();
416 : }
417 :
418 2 : bool Node::syncConfigExit()
419 : {
420 2 : LBASSERT( _state == STATE_EXITING || _state == STATE_EXIT_SUCCESS ||
421 : _state == STATE_EXIT_FAILED );
422 :
423 2 : _state.waitNE( STATE_EXITING );
424 2 : const bool success = ( _state == STATE_EXIT_SUCCESS );
425 2 : LBASSERT( success || _state == STATE_EXIT_FAILED );
426 :
427 2 : _state = isActive() ? STATE_FAILED : STATE_STOPPED;
428 2 : setTasks( fabric::TASK_NONE );
429 2 : _frameIDs.clear();
430 2 : _flushBarriers();
431 2 : return success;
432 : }
433 :
434 : //---------------------------------------------------------------------------
435 : // update
436 : //---------------------------------------------------------------------------
437 0 : void Node::update( const uint128_t& frameID, const uint32_t frameNumber )
438 : {
439 0 : if( !isRunning( ))
440 0 : return;
441 :
442 0 : LBVERB << "Start frame " << frameNumber << std::endl;
443 0 : LBASSERT( isActive( ));
444 :
445 0 : _frameIDs[ frameNumber ] = frameID;
446 :
447 0 : uint128_t configVersion = co::VERSION_INVALID;
448 0 : if( !isApplicationNode( )) // synced in Config::_cmdFrameStart
449 0 : configVersion = getConfig()->getVersion();
450 :
451 : send( fabric::CMD_NODE_FRAME_START )
452 0 : << getVersion() << configVersion << frameID << frameNumber;
453 0 : LBLOG( LOG_TASKS ) << "TASK node start frame " << std::endl;
454 :
455 0 : const Pipes& pipes = getPipes();
456 0 : for( Pipes::const_iterator i = pipes.begin(); i != pipes.end(); ++i )
457 0 : (*i)->update( frameID, frameNumber );
458 :
459 0 : if( !_lastDrawPipe ) // no FrameDrawFinish sent
460 : {
461 0 : send( fabric::CMD_NODE_FRAME_DRAW_FINISH ) << frameID << frameNumber;
462 0 : LBLOG( LOG_TASKS ) << "TASK node draw finish " << getName() << " "
463 0 : << std::endl;
464 : }
465 0 : _lastDrawPipe = 0;
466 :
467 0 : send( fabric::CMD_NODE_FRAME_TASKS_FINISH ) << frameID << frameNumber;
468 0 : LBLOG( LOG_TASKS ) << "TASK node tasks finish " << std::endl;
469 :
470 0 : _finish( frameNumber );
471 0 : flushSendBuffer();
472 : }
473 :
474 0 : uint32_t Node::_getFinishLatency() const
475 : {
476 0 : switch( getIAttribute( Node::IATTR_THREAD_MODEL ))
477 : {
478 : case fabric::UNDEFINED:
479 : case fabric::DRAW_SYNC:
480 0 : if( getTasks() & fabric::TASK_DRAW )
481 : {
482 : // More than one frame latency doesn't make sense, since the
483 : // draw sync for frame+1 does not allow for more
484 0 : const Config* config = getConfig();
485 0 : const uint32_t latency = config->getLatency();
486 :
487 0 : return LB_MIN( latency, 1 );
488 : }
489 0 : break;
490 :
491 : case fabric::LOCAL_SYNC:
492 0 : if( getTasks() != fabric::TASK_NONE )
493 : // local sync enforces no latency
494 0 : return 0;
495 0 : break;
496 :
497 : case fabric::ASYNC:
498 0 : break;
499 : default:
500 0 : LBUNIMPLEMENTED;
501 : }
502 :
503 0 : const Config* config = getConfig();
504 0 : return config->getLatency();
505 : }
506 :
507 0 : void Node::_finish( const uint32_t currentFrame )
508 : {
509 0 : const Pipes& pipes = getPipes();
510 0 : for( Pipes::const_iterator i = pipes.begin(); i != pipes.end(); ++i )
511 : {
512 0 : const Pipe* pipe = *i;
513 0 : if( pipe->getIAttribute( Pipe::IATTR_HINT_THREAD ) && pipe->isRunning())
514 : {
515 0 : const uint32_t latency = _getFinishLatency();
516 0 : if( currentFrame > latency )
517 0 : flushFrames( currentFrame - latency );
518 0 : return;
519 : }
520 : }
521 :
522 : // else only non-threaded pipes, all local tasks are done, send finish now.
523 0 : flushFrames( currentFrame );
524 : }
525 :
526 0 : void Node::flushFrames( const uint32_t frameNumber )
527 : {
528 0 : LBLOG( LOG_TASKS ) << "Flush frames including " << frameNumber << std::endl;
529 :
530 0 : while( _flushedFrame < frameNumber )
531 : {
532 0 : ++_flushedFrame;
533 0 : _sendFrameFinish( _flushedFrame );
534 : }
535 :
536 0 : flushSendBuffer();
537 0 : }
538 :
539 0 : void Node::_sendFrameFinish( const uint32_t frameNumber )
540 : {
541 0 : FrameIDHash::iterator i = _frameIDs.find( frameNumber );
542 0 : if( i == _frameIDs.end( ))
543 0 : return; // finish already send
544 :
545 0 : send( fabric::CMD_NODE_FRAME_FINISH ) << i->second << frameNumber;
546 0 : _frameIDs.erase( i );
547 0 : LBLOG( LOG_TASKS ) << "TASK node finish frame " << frameNumber << std::endl;
548 : }
549 :
550 : //---------------------------------------------------------------------------
551 : // Barrier cache
552 : //---------------------------------------------------------------------------
553 0 : co::Barrier* Node::getBarrier()
554 : {
555 0 : if( _barriers.empty( ))
556 : {
557 : co::Barrier* barrier = new co::Barrier( getServer(),
558 0 : _node->getNodeID( ));
559 0 : barrier->setAutoObsolete( getConfig()->getLatency() + 1 );
560 0 : return barrier;
561 : }
562 : // else
563 :
564 0 : co::Barrier* barrier = _barriers.back();
565 0 : _barriers.pop_back();
566 0 : barrier->setHeight( 0 );
567 0 : return barrier;
568 : }
569 :
570 0 : void Node::changeLatency( const uint32_t latency )
571 : {
572 0 : for( co::Barriers::const_iterator i = _barriers.begin();
573 0 : i != _barriers.end(); ++ i )
574 : {
575 0 : co::Barrier* barrier = *i;
576 0 : barrier->setAutoObsolete( latency + 1 );
577 : }
578 0 : }
579 :
580 0 : void Node::releaseBarrier( co::Barrier* barrier )
581 : {
582 0 : _barriers.push_back( barrier );
583 0 : }
584 :
585 2 : void Node::_flushBarriers()
586 : {
587 2 : for( co::BarriersCIter i =_barriers.begin(); i != _barriers.end(); ++i )
588 0 : delete *i;
589 2 : _barriers.clear();
590 2 : }
591 :
592 0 : bool Node::removeConnectionDescription( co::ConnectionDescriptionPtr cd )
593 : {
594 : // Don't use std::find, RefPtr::operator== compares pointers, not values.
595 0 : for(co::ConnectionDescriptions::iterator i=_connectionDescriptions.begin();
596 0 : i != _connectionDescriptions.end(); ++i )
597 : {
598 0 : if( *cd != **i )
599 0 : continue;
600 :
601 0 : _connectionDescriptions.erase( i );
602 0 : return true;
603 : }
604 0 : return false;
605 : }
606 :
607 4 : co::ObjectOCommand Node::send( const uint32_t cmd )
608 : {
609 4 : return send( cmd, getID( ));
610 : }
611 :
612 22 : co::ObjectOCommand Node::send( const uint32_t cmd, const uint128_t& id )
613 : {
614 : return co::ObjectOCommand( co::Connections( 1, _bufferedTasks ), cmd,
615 22 : co::COMMANDTYPE_OBJECT, id, CO_INSTANCE_ALL );
616 : }
617 :
618 0 : EventOCommand Node::sendError( const uint32_t error )
619 : {
620 0 : return getConfig()->sendError( Event::NODE_ERROR, Error( error, getID( )));
621 : }
622 :
623 12 : void Node::flushSendBuffer()
624 : {
625 12 : _bufferedTasks->sendBuffer( _node->getConnection( ));
626 12 : }
627 :
628 : //===========================================================================
629 : // command handling
630 : //===========================================================================
631 2 : bool Node::_cmdConfigInitReply( co::ICommand& cmd )
632 : {
633 2 : co::ObjectICommand command( cmd );
634 2 : LBVERB << "handle configInit reply " << command << std::endl;
635 2 : LBASSERT( _state == STATE_INITIALIZING );
636 2 : _state = command.read< uint64_t >() ? STATE_INIT_SUCCESS : STATE_INIT_FAILED;
637 :
638 2 : return true;
639 : }
640 :
641 2 : bool Node::_cmdConfigExitReply( co::ICommand& cmd )
642 : {
643 2 : co::ObjectICommand command( cmd );
644 2 : LBVERB << "handle configExit reply " << command << std::endl;
645 2 : LBASSERT( _state == STATE_EXITING );
646 :
647 2 : _state = command.read< bool >() ? STATE_EXIT_SUCCESS : STATE_EXIT_FAILED;
648 2 : return true;
649 : }
650 :
651 0 : bool Node::_cmdFrameFinishReply( co::ICommand& cmd )
652 : {
653 0 : co::ObjectICommand command( cmd );
654 0 : LBVERB << "handle frame finish reply " << command << std::endl;
655 :
656 0 : const uint32_t frameNumber = command.read< uint32_t >();
657 :
658 0 : _finishedFrame = frameNumber;
659 0 : getConfig()->notifyNodeFrameFinished( frameNumber );
660 :
661 0 : return true;
662 : }
663 :
664 432 : void Node::output( std::ostream& os ) const
665 : {
666 432 : if( !_host.empty( ))
667 258 : os << "host \"" << _host << '"' << std::endl;
668 :
669 432 : const co::ConnectionDescriptions& descriptions = _connectionDescriptions;
670 2202 : for( co::ConnectionDescriptions::const_iterator i = descriptions.begin();
671 1468 : i != descriptions.end(); ++i )
672 : {
673 302 : co::ConnectionDescriptionPtr desc = *i;
674 302 : os << *desc;
675 302 : }
676 :
677 432 : bool attrPrinted = false;
678 :
679 1728 : for( Node::SAttribute i = static_cast<Node::SAttribute>( 0 );
680 864 : i < Node::SATTR_LAST;
681 : i = static_cast<Node::SAttribute>( static_cast<uint32_t>( i )+1))
682 : {
683 432 : const std::string& value = getSAttribute( i );
684 432 : if( value == Global::instance()->getNodeSAttribute( i ))
685 432 : continue;
686 :
687 0 : if( !attrPrinted )
688 : {
689 0 : os << std::endl << "attributes" << std::endl;
690 0 : os << "{" << std::endl << lunchbox::indent;
691 0 : attrPrinted = true;
692 : }
693 :
694 : os << ( i==Node::SATTR_LAUNCH_COMMAND ? "launch_command " :
695 0 : "ERROR" )
696 0 : << "\"" << value << "\"" << std::endl;
697 : }
698 :
699 1728 : for( Node::CAttribute i = static_cast<Node::CAttribute>( 0 );
700 864 : i < Node::CATTR_LAST;
701 : i = static_cast<Node::CAttribute>( static_cast<uint32_t>( i )+1))
702 : {
703 432 : const char value = getCAttribute( i );
704 432 : if( value == Global::instance()->getNodeCAttribute( i ))
705 432 : continue;
706 :
707 0 : if( !attrPrinted )
708 : {
709 0 : os << std::endl << "attributes" << std::endl;
710 0 : os << "{" << std::endl << lunchbox::indent;
711 0 : attrPrinted = true;
712 : }
713 :
714 : os << ( i==Node::CATTR_LAUNCH_COMMAND_QUOTE ? "launch_command_quote " :
715 0 : "ERROR" )
716 0 : << "'" << value << "'" << std::endl;
717 : }
718 :
719 3456 : for( Node::IAttribute i = static_cast< Node::IAttribute>( 0 );
720 1728 : i < Node::IATTR_LAST;
721 : i = static_cast< Node::IAttribute >( static_cast<uint32_t>( i )+1))
722 : {
723 1296 : const int value = getIAttribute( i );
724 1296 : if( value == Global::instance()->getNodeIAttribute( i ))
725 1296 : continue;
726 :
727 0 : if( !attrPrinted )
728 : {
729 0 : os << std::endl << "attributes" << std::endl;
730 0 : os << "{" << std::endl << lunchbox::indent;
731 0 : attrPrinted = true;
732 : }
733 :
734 : os << ( i== Node::IATTR_LAUNCH_TIMEOUT ? "launch_timeout " :
735 : i== Node::IATTR_THREAD_MODEL ? "thread_model " :
736 : i== Node::IATTR_HINT_AFFINITY ? "hint_affinity " :
737 0 : "ERROR" )
738 0 : << static_cast< fabric::IAttribute >( value ) << std::endl;
739 : }
740 :
741 432 : if( attrPrinted )
742 0 : os << lunchbox::exdent << "}" << std::endl << std::endl;
743 432 : }
744 :
745 : }
746 : }
747 :
748 : #include "../fabric/node.ipp"
749 : template class eq::fabric::Node< eq::server::Config, eq::server::Node,
750 : eq::server::Pipe, eq::server::NodeVisitor >;
751 : /** @cond IGNORE */
752 : template std::ostream& eq::fabric::operator << ( std::ostream&,
753 84 : const eq::server::Super& );
754 : /** @endcond */
|