Using a chanend in both directions

Technical questions regarding the XTC tools and programming with XMOS.
User avatar
Respected Member
Posts: 450
Joined: Wed Apr 25, 2012 8:52 pm

Using a chanend in both directions

Post by aclassifier »

I have discovered hat I may use a chanend bidirectionally:

Code: Select all

#define debug_printf(fmt, ...) do { if(DEBUG_PRINT_XC_TEST_2) printf(fmt, __VA_ARGS__); } while (0)

void My_Server (chanend c_in, chanend c_out) {
    int value;
    while(1) {
        c_in :> value; // Input
        debug_printf ("    My_Server   _ %u\n", value);
        c_out <: value; // Output
        debug_printf ("    My_Server   X %u\n", value);

void My_PingPong (chanend c_in_out) {
    int value;
    while(1) {
        c_in_out :> value; // Input
        debug_printf ("    My_PingPong _ %u\n", value);
        c_in_out <: value; // Output
        debug_printf ("    My_PingPong Y %u\n", value);


void My_Client (chanend c_out, chanend c_in, chanend c_out_in) {
    int value = 0;
    while(1) {
        debug_printf ("My_Client       X %u\n", value);
        c_out <: value;    // Output
        c_in :> value;     // Input
        debug_printf ("  My_Client     Y %u\n", value);
        c_out_in <: value; // Output
        c_out_in :> value; // Input (Change dir: deadlock caught by run-time)
        debug_printf ("  My_Client     Z %u\n", value);

int main(void) {
    chan c_over;
    chan c_back;
    chan c_pingpong;
    par {
        My_Server   (c_over, c_back);
        My_PingPong (c_pingpong);
        My_Client   (c_over, c_back, c_pingpong);
    return 0;

/* Simulator
My_Client       X 0
    My_Server   _ 0
  My_Client     Y 1
    My_Server   X 1
    My_PingPong _ 1
  My_Client     Z 2
    My_PingPong Y 2
My_Client       X 2
    My_Server   _ 2
  My_Client     Y 3
    My_Server   X 3
    My_PingPong _ 3
  My_Client     Z 4
    My_PingPong Y 4
My_Client       X 4
    My_Server   _ 4
  My_Client     Y 5
    My_Server   X 5
    My_PingPong _ 5
  My_Client     Z 6
    My_PingPong Y 6
My_Client       X 6
    My_Server   _ 6
  My_Client     Y 7
    My_Server   X 7
    My_PingPong _ 7
  My_Client     Z 8
    My_PingPong Y 8
My_Client       X 8
    My_Server   _ 8
  My_Client     Y 9
    My_Server   X 9
    My_PingPong _ 9
  My_Client     Z 10
    My_PingPong Y 10
My_Client       X 10
    My_Server   _ 10
I have also discussed correct usage to avoid deadlock, and shown that the run-time will catch wrong usage causing a deadlock; here: ... directions
(Standard disclaimer: no money, ads, gift etc.)

Comments welcome!

But then, if I can save two chanends by reuse, why doesn't the tool do this automatically?

Øyvind Teig
Trondheim (Norway)
XCore Addict
Posts: 230
Joined: Wed Mar 10, 2010 12:46 pm

Post by peter »

Every channel is fully bi-directional. The tools make use of this to provide the synchronisation between two communicating tasks. When one task does:

Code: Select all

c <: data;
and the other side does:

Code: Select all

c :> data;
then neither side will proceed until both cores have reached that point in the code. There is a low-level sequence of acknowledgement inserted by the tools to synchronise the cores.

This is different to a streaming channel where the cores only send the data and control tokens that are explicitly sent by the core.
User avatar
Respected Member
Posts: 450
Joined: Wed Apr 25, 2012 8:52 pm

Post by aclassifier »

Thanks! Good to have it confirmed.

But also the streaming channel needs to do synchronisation when the buffer is full? Or: what is the exact semantics?

I haven't found anything on the bidirectionality in the xC documentation. (I may be blind?)

So an xC chanend is bidirectional, just like a chan in go/golang and a rendezvous in Ada. And, I should say, CSP, since it’s only a shared state where all parts wait standing still, and data may be moved in any direction at the synchronisation point. (It’s the implementation of the ALT in occam, and it being a multi-core distributed memory architecture that didn’t make occam’s chan bidirectional. And didn’t open for output guards.) A channel is a permission to do anything with some data that, at the synchronisation point, is shared.

Again, If I can save two chanends by "reuse", why doesn’t the tool do this automatically? When I do a clean ping-pong type sequence from a client to a server and back, like in the example, and I had declared a channel in each direction, the tool could have optimised and used only one channel for it? And saved two chanends.
Øyvind Teig
Trondheim (Norway)
User avatar
XCore Expert
Posts: 570
Joined: Thu Nov 26, 2015 11:47 pm

Post by akp »

I guess in a typical application I could see some use for channels that follow this paradigm (e.g. one control task configures a measurement task and then the channel changes from a configuration channel to a measurement reporting channel). So it would seem to be a good optimization if the compiler can determine the usage. At least in the meantime you have noted the conditions to use it explicitly by the programmer and it doesn't seem that hard. On the other hand, I tend to think that using it explicitly does have some danger for maintenance unless it's clear from the code and comments how to avoid deadlock if any changes need to be made to the way the channel is used.