WriteDataPort implementation details

Hi.

I have two component : A is a producer, B is a consumer. I have one structure (Bar), that A give to B.

So in A i have ;

    RTT::WriteDataPort<Bar>    m_bar_port;
and in B i have :
    RTT::ReadDataPort<Bar>    m_bar_port;

Bar is a simple structure with a lot of debug traces :

  struct Bar
  {
    bool m_flag;
 
    Bar()
    {
      RTT::Logger::In in(__PRETTY_FUNCTION__);
      RTT::Logger::log( RTT::Logger::Debug ) << "enter" << RTT::Logger::endl;
      m_flag = false;
      RTT::Logger::log( RTT::Logger::Debug ) << "exit" << RTT::Logger::endl;
    }
 
    Bar(const Bar &bar)
    {
      RTT::Logger::In in(__PRETTY_FUNCTION__);
      RTT::Logger::log( RTT::Logger::Debug ) << "enter" << RTT::Logger::endl;
      m_flag = bar.m_flag;
      RTT::Logger::log( RTT::Logger::Debug ) << "exit" << RTT::Logger::endl;
    }
 
    ~Bar()
    {
      RTT::Logger::In in(__PRETTY_FUNCTION__);
      RTT::Logger::log( RTT::Logger::Debug ) << "enter" << RTT::Logger::endl;
      RTT::Logger::log( RTT::Logger::Debug ) << "exit" << RTT::Logger::endl;
    }
 
    const Bar & operator=(const Bar &bar)
    {
      RTT::Logger::In in(__PRETTY_FUNCTION__);
      RTT::Logger::log( RTT::Logger::Debug ) << "enter" << RTT::Logger::endl;
      if(this != &bar)
        {
          if(m_flag != bar.m_flag)
            {
              RTT::Logger::log( RTT::Logger::Debug ) << "m_flag != bar.m_flag" << RTT::Logger::endl;
              m_flag = bar.m_flag;
            }
        }
      RTT::Logger::log( RTT::Logger::Debug ) << "exit" << RTT::Logger::endl;
      return *this;
    }
  };

I use the deployer for autoconfiguring and autoconnecting A and B.

Then I only start A (not B !).

My questions are :

 why the connection has instantiated 10 Bar ?
 why A needs to write 10 times to its m_bar_port before all those instance are initialised ?
I give you my test case.

I'am very surprised that a data port is implemented with a kind of buffer.

Thanks for your reading.

Regards.

Paul.

AttachmentSize
02_test.tar_.bz22.47 KB

WriteDataPort implementation details

Hi Paul,

On Tue, Dec 8, 2009 at 12:52, <paul [dot] chavent [..] ...> wrote:
> Hi.
>
> I have two component : A is a producer, B is a consumer. I have one
> structure (Bar), that A give to B.

...

>
>
> I use the deployer for autoconfiguring and autoconnecting A and B.
>
> Then I only start A (not B !).
>
> My questions are : why the connection has instantiated 10 Bar ?

I've ran your testcase.

What you're seeing is the lock-free implementation doing it's work. In
order to exchange data without using mutexes and without using
malloc/new, several predefined objects must live into memory which get
their final value when using operator=(). This is the typical memory
vs performance trade-off you're seeing. We're caching stuff in memory
and get a tremendous real-time/deterministic performance in return.
This is however hidden for the users, your component will see the
correct values, that is when A writes x, B will read x.

> why A needs to write 10 times to its m_bar_port before all those instance
> are initialised ?
>
> I give you my test case.
>
> I'am very surprised that a data port is implemented with a kind of buffer.

Every lock-free primitive is implemented with a 'pool' of ready-to-use
instances of your data. The data exchange itself behaves unbuffered.

A note on the number '10' : In RTT 1.x, we needed to guess the number
of threads that could concurrently access data ports, and a reasonable
default was choosen. We fixed this in the RTT 2.0 branch by design
such that such magic numers are no longer necessary, and we know the
exact number of threads.

Peter

Hi.Thank you for this

Hi.

Thank you for this explanation.

I ran in this case because i needed to init the instance of the class shared on this port doing some allocation.

But, it seems that i needn't to worry about allocation...

Should I expect to be in "realtime" after init of the pool ?

Regards.

Paul.

P.S : sorry for multipost, i have some problems with my browser.

Hi.Thank you for this

Hi.

Thank you for this explanation.

I ran in this case because i needed to init the instance of the class shared on this port doing some allocation.

But, it seems that i needn't to worry about allocation...

Should I expect to be in "realtime" after init of the pool ?

Regards.

Paul.

P.S : sorry for multipost, i have some problems with my browser.

Hi.Thank you for this

On Mon, Dec 14, 2009 at 17:48, <paul [dot] chavent [..] ...> wrote:
> Hi.
>
> Thank you for this explanation.
>
> I ran in this case because i needed to init the instance of the class shared on this port doing some allocation.

The rule is:
- copy constructor may allocate
- operator= must be real-time if possible

>
> But, it seems that i needn't to worry about allocation...
>
> Should I expect to be in "realtime" after init of the pool ?

That's at least the idea. The RTT 1.x data ports take their initial
port value in the constructor of the writing port. So if this value is
setup correctly, your whole connection is setup fine. In 2.x, you will
be able to (re-)init a connection with a port->setDataSample(
sample_value ) such that this is made explicit and can be changed
later on.

Peter

WriteDataPort implementation details

Hi.

I have two component : A is a producer, B is a consumer. I have one structure (Bar), that A give to B.

So in A i have ;

    RTT::WriteDataPort<Bar>    m_bar_port;

and in B i have :
    RTT::ReadDataPort<Bar>    m_bar_port;

Bar is a simple structure with a lot of debug traces :

  struct Bar
  {
    bool m_flag;
 
    Bar()
    {
      RTT::Logger::In in(__PRETTY_FUNCTION__);
      RTT::Logger::log( RTT::Logger::Debug ) << "enter" << RTT::Logger::endl;
      m_flag = false;
      RTT::Logger::log( RTT::Logger::Debug ) << "exit" << RTT::Logger::endl;
    }
 
    Bar(const Bar &bar)
    {
      RTT::Logger::In in(__PRETTY_FUNCTION__);
      RTT::Logger::log( RTT::Logger::Debug ) << "enter" << RTT::Logger::endl;
      m_flag = bar.m_flag;
      RTT::Logger::log( RTT::Logger::Debug ) << "exit" << RTT::Logger::endl;
    }
 
    ~Bar()
    {
      RTT::Logger::In in(__PRETTY_FUNCTION__);
      RTT::Logger::log( RTT::Logger::Debug ) << "enter" << RTT::Logger::endl;
      RTT::Logger::log( RTT::Logger::Debug ) << "exit" << RTT::Logger::endl;
    }
 
    const Bar & operator=(const Bar &bar)
    {
      RTT::Logger::In in(__PRETTY_FUNCTION__);
      RTT::Logger::log( RTT::Logger::Debug ) << "enter" << RTT::Logger::endl;
      if(this != &bar)
        {
          if(m_flag != bar.m_flag)
            {
              RTT::Logger::log( RTT::Logger::Debug ) << "m_flag != bar.m_flag" << RTT::Logger::endl;
              m_flag = bar.m_flag;
            }
        }
      RTT::Logger::log( RTT::Logger::Debug ) << "exit" << RTT::Logger::endl;
      return *this;
    }
  };

I use the deployer for autoconfiguring and autoconnecting A and B.

Then I only start A (not B !).

My questions are :
why the connection has instantiated 10 Bar ?
why A needs to write 10 times to its m_bar_port before all those instance are initialised ?

I give you my test case.

I'am very surprised that a data port is implemented with a kind of buffer.

Thanks for your reading.

Regards.

Paul.