Communication between two tasks

If you have a simple question and just want an answer.
peter
XCore Addict
Posts: 230
Joined: Wed Mar 10, 2010 12:46 pm

Post by peter »

There is a possibility that the pointer you are passing is going out of scope in the case where it is going wrong and being changed on the stack. You really should ensure that the sender stays active until the print has finished by sending the pointer back once it has finished printing and waiting for that.


cschopges
Member++
Posts: 28
Joined: Mon Jul 25, 2016 2:03 pm

Post by cschopges »

Thanks, that was the problem.

But why should I send the pointer back since I'm not interested in the content? How will task2 know if it can send the next pointer or it should receive the previously send one?
peter
XCore Addict
Posts: 230
Joined: Wed Mar 10, 2010 12:46 pm

Post by peter »

There is no reason to pass the pointer back necessarily, just a suggestion of a way to ensure that the printing had completed before the other task exited.
zhy44th
Member
Posts: 11
Joined: Mon Dec 18, 2017 9:58 am

Post by zhy44th »

cschopges wrote:A quick example:

Code: Select all

#include <print.h>

typedef struct my_struct {
  unsigned id1;
  unsigned id2;
  unsigned id3;
  unsigned id4;
  char data[8];
} my_struct;

void print_msg(my_struct f, char prepend[]){
  printstr(prepend);
  printhex(f.id3);
  printstr("\t");
  if(f.id2)
    printstr(" : Ext\t");
  else
    printstr(" : Std\t");
  if(f.id1)
    printstr(" : R \t");
  else
    printstr(" : D \t");
  printint(f.id4);
  printstr("\t");
  if(!f.id1)
    for(unsigned i=0;i<f.id4;i++){
      printhex(f.data[i]);
      printstr("\t");
    }
  printstrln("");
}

void task1(streaming chanend c_to)
    my_struct * unsafe test;
    unsafe{
        c_to :> test;
        print_msg(*test, "received: ");
    }

}

void task2(streaming chanend c_to){
    my_struct rx_buff;
    unsafe{
        my_struct * unsafe test = &rx_buff;

        test->id1 = 0;
        test->id2 = 1;
        test->id3 = 0x18da29F1;
        test->id4 = 3;
        test->data[0] = 0x02  ;
        test->data[1] = 0x21;
        test->data[2] = 0x01;
        test->data[3] = 0x0;
        test->data[4] = 0x0;
        test->data[5] = 0x0;
        test->data[6] = 0x0;
        test->data[7] = 0x0;

        //print_msg(*test, "send: ");
        c_to <: test;
    }
}

int main(){
    streaming chan c_to;
    par{
        task1(c_to);
        task2(c_to);
    }
    return 0;
}
If I uncomment //print_msg(*test, "send: "); it works, otherwise not. Why? I can't figure it out.
Moreover, does everything needs to be in an unsafe region?

Thanks for your code.
Why the structure pointer transfer fast than array's pointer?
robertxmos
XCore Addict
Posts: 169
Joined: Fri Oct 23, 2015 10:23 am

Post by robertxmos »

Hi zhy44th,

Regarding structure pointer and array transfer, it depends :-)
Arrays and references will implicitly use memcpy type functions (remote_memcpy for when code is on another tile) and optimise them.
Also, if it can't be done at compile time, array access will be range checked too.
Pointers (smart and unsafe) will only work for shared memory and you control every action.
The value and reference semantics will be slightly different too - you get what you ask for.

Regarding the code, there are a couple of problems.
First, the function task2 will complete and exit, unwinding its stack and hence leave the sent pointer 'test' dangling - not nice!
To fix this you need to handshake with task1 the ownership of 'rx_buffer' - see earlier answer.

The second is more unfortunate.
The compiler will look at your code and decide that rx_buffer's address is used to initialise a pointer, but the pointer is never dereferenced.
Hence, there is no need to do the initialisation of the structure - and your set up code is promptly optimised away.
This is a bug in the compiler - passing a pointer over a channel suggests it will be dereferenced, hence the optimisation should be prevented.

The work around is to prevent the compiler doing the optimisation by:
1. Build with -O0 (bad idea)
2. pass the structure's pointer to a function (as you do with print_msg) AND make sure it can't see how the pointer is used (compilers can be very sneaky).

#2 may be achieved by placing the creation and initiation of the structure in one file (compilation unit) and the use/sending in another file viz:
// file1.xc

Code: Select all

void task1(streaming chanend c_to) {
...
    c_to :> test;
    print_msg(test, "received: ");
    c_to <: test;
}

// Can only see that the structure 'might' be dereferenced in sendBuffer() - hence no optimisation of fields allowed.
unsafe void sendBuffer(streaming chanend c_to,  my_struct * unsafe test);

void task2(streaming chanend c_to){
    my_struct rx_buff;
    unsafe{
        my_struct * unsafe test = &rx_buff;
        test->id1 = 0;
...
       sendBuffer(c_to, test);
    }
}
//file2.xc

Code: Select all

unsafe void sendBuffer(treaming chanend c_to,  my_struct * unsafe test) {
  c_to <: test;
  c_to :> test;
}
The downside is the extra file and the overhead of a function call.
You can't inline the function as this would allow the compiler to see and hence optimise!
I'm sure there are other imaginative ways you could solve your original problem if you don't like the above.

robert
robertxmos
XCore Addict
Posts: 169
Joined: Fri Oct 23, 2015 10:23 am

Post by robertxmos »

FYI
This is a bug in the compiler - passing a pointer over a channel suggests it will be dereferenced, hence the optimisation should be prevented.
The bug has been fixed for the future release 14.3.3 and 14.4.0.
Post Reply