Jump to content
sheridp@umich.edu

Passthrough Module -- Fifo Implementation

Recommended Posts

I'm trying to understand how to model a simple pass through module correctly.  Let's say I've got 3 threads/modules A, B, and C such that A sends messages to B which forwards them to C (A --> B  --> C).

A naive approach for thread B:

sc_fifo<Message> incoming_fifo("incoming", 3);
sc_fifo<Message> outgoing_fifo("outgoing", 3);

thread_A(){
  while(true){
    Message msg = new Message();
    incoming_fifo.write(msg);
  }
}
  
thread_B(){
  Message msg;
  while(true){
     incoming_fifo.read(msg);
     outgoing_fifo.write(msg);
  }
}
  
thread_C(){}

Further, let's say that module C is negligent and not reading from outgoing_fifo (sorry, the names are B-thread centric).  One would naively assume that thread_A can send 6 messages (3 in each fifo) before the fifos become full and it's write becomes blocking.

The problem is, thread_A can actually send 7 messages, because you essentially have an extra slot, conceptually, in thread_B between the success of its read and the block on its write. 

One solution might be to use tlm_fifos, first waiting on outgoing_fifo.ok_to_put() then blocking on incoming_fifo.get().  But this becomes problematic if for example there was another module also putting into outgoing_fifo --then ok_to_put() might be true at one point, but when the read/get from incoming_fifo awakens thread_B, it there might no longer be room in the outgoing_fifo.  (A similar situation arises if you try to wait on ok_to_get(), but there are multiple threads pulling from incoming_fifo).  

Is there a way to solve this problem so that you can be sure that you don't pull from incoming_fifo if you don't have room in outgoing_fifo, ie. simultaneous condition checking, without resorting to something like polling? 

Share this post


Link to post
Share on other sites

The model you describe is not "just" a passthrough model though, as you assume that tokens can be both intercepted (other processes reading from incoming_fifo) and injected (other processes than B writing to outgoing_fifo). From my experience, it is very difficult to design robust models with multiple readers and writers for FIFOs, at least without higher-level synchronization orchestrating the accesses somehow.  That said, let's assume these arbitrary FIFO accesses are needed and OK for your use case.

I would suggest to indeed use the non-blocking APIs to do the forwarding:

while( true )
{
  if( incoming_fifo.num_available() > 0 && outgoing_fifo.num_free() > 0 ) {
    // forwarding possible, process token - will never block
    outgoing_fifo.write( incoming_fifo.read() );
  }
  // at least one side is blocking, wait for activity on either side
  wait( incoming_fifo.data_written_event() | outgoing_fifo.data_read_event() );
}

This will not lead to an additional implicit slot in the passthrough block, but the forwarding is "non-greedy" now, i.e. happens only if a slot is available on both sides.  Together with arbitrary other readers/writers on the FIFOs, your passthrough model might never succeed to forward a single token.  See above.

Minor comment: Your thread_A pseudo code above looks like Java/SystemVerilog and would have a memory leak in C++.  You should not put dynamically allocated objects into a FIFO.

Hope that helps,
  Philipp

Share this post


Link to post
Share on other sites

Hi Philipp,

Thanks, this seems to solve the problem correctly.

Regarding your comment about the memory leak, I completely agree, but was wondering what is the standard technique for doing dynamic object passing?  Should I have my fifos declared to take shared_ptr<Message> objects?  Because the fifo.write(&T) takes its parameter by reference, it won't take ownership of the underlying object, but this is essentially what I intend when I pass something on to another module.  Further, how can I pass an objects that manages heap memory, eg. a std::vector?

Regards,

Patrick

Share this post


Link to post
Share on other sites

Hi Patrick,

You can use every Copyable/CopyAssignable type with sc_fifo or tlm_fifo, as entries are always copied in and out of the fifo slots. There is no special support for e.g. move-only types, yet.  It might be a nice to explore options to add such support in order to reduce the cost for passing complex types through fifos.

So yes, you can pass std::shared_ptr and std::vector, but e.g. not std::unique_ptr.

Greetings from Duisburg,
  Philipp

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×