Cancelable timout: channels or interfaces?

If you have a simple question and just want an answer.
User avatar
aclassifier
XCore Expert
Posts: 512
Joined: Wed Apr 25, 2012 8:52 pm

Cancelable timout: channels or interfaces?

Post by aclassifier »

I have a situation that seems to be (too?) complicated with interfaces, but ok with channels:
  1. I have a test_task that repeatedly calls tester_function that in a switch with cases calls several test_A, test_B etc.
  2. delay_milliseconds is called in test_task with milliseconds returned from tester_function
  3. so tester_function does not call delay_milliseconds directly
  4. however, test_A, test_B etc. run through their own sequences, and also call delay_milliseconds
This is ok so far; delay_milliseconds does not have state, and doesn't need to. 1-4 is repeated with a test program of a display.

However, I have a t_button. When I press the button down it's told over channel c_button and sent to test_task.  The new semantics is to let a button press run one test sequence, like test_A, then test_B etc.  But I want no delay from the button click to the action. I need cancelable_delay_milliseconds task or server. What is most suited?
 
A. If cancelable_delay_milliseconds is a task and channels I can pass those channels around (c_delay, c_timeout) all the way to test_A, test_B that would use those channels with cancelable_delay_milliseconds task. I leave t_button to be connected to cancelable_delay_milliseconds at the highest caller. It is rather elegant, but has to be on different logical cores. When the button is pressed the ongoing delay will be cancelled, and the following "inner" delays will become zero until the code is returned to the top.
 
B. If cancelable_delay_milliseconds is a server like this:
 
interface delay_interface {
    [[clears_notification]] void delay_iff (const unsigned milliseconds_iff);
    [[notification]] slave void timeout (void);
    void delay_control (const int enable);
};
 
then I need to pass the interface to test_A, test_B etc. I find this much more difficult since the interface cannot be cut in two, one for the button (disable) and one for the others (delay, timeout). I assume I could make two interfaces to the cancelable_delay_milliseconds server and then send away what I need to? But then, to pick up the notification in a following select at all the places where I use the interface doesn't seem elegant. cancelable_delay_milliseconds seems complex at this level.
 
I have not coded any of the them completely, I would know A from my occam experience, so I thought B would be most interesting, I have done some of it, but it's only cancelable at the topmost level by now. 
 
Which direction would you advice me to persue? Make everything with channels, everything with interfaces or a mix?
--
Øyvind Teig
Trondheim (Norway)
https://www.teigfam.net/oyvind/home/
User avatar
davelacey
Experienced Member
Posts: 104
Joined: Fri Dec 11, 2009 8:29 pm

Post by davelacey »

If everything is in one task and you don't mind global state you could store a pointer to the button chanend as a global (this is all memory safe when using movable pointers). This would probably be the minimal changes to your current code. You'd have something like:

Code: Select all

chanend * movable p_c_button;
unsigned button_pressed = 0;
// Delay for the specified number of ms unless a has been
// pressed.
void delay_if_needed(unsigned period_ms){  
  if (button_pressed)    return;  
  timer tmr;  
  unsigned time;  
  tmr :> time;  
  select {  
    case tmr when timerafter(time+period_ms) :> void:    return;  
    case *p_c_button :> int:    button_pressed = 1;    break;  
  }
}
unsigned test_A();
unsigned test_B();
void do_button_handling_stuff();
void task_task(chanend c_button) {  
  chanend * movable p = &c_button; 
  p_c_button = move(p); 
  while (1) {
    unsigned delay_period;
    delay_period = test_A();
    delay_if_needed(delay_period);
    delay_period = test_B();
    delay_if_needed(delay_period);
    // Final check if the button has been pressed 
    select {
      case *p_c_button :> int:      button_pressed = 1;      break;    
      default:      break;    
    } 
    if (button_pressed) {  
      do_button_handling_stuff();
      button_pressed = 0;    
    }  
  }
  p = move(p_c_button);
}
You can also call delay_if_needed in test_A(), test_B() etc. If you don't like the global state then you could pass the chanend around.
Hagrid
Active Member
Posts: 44
Joined: Mon Jul 29, 2013 4:33 am

Post by Hagrid »

For the interface-based approach, you could perhaps use interface arrays.  One interface, two or more instances.

The code in the select would respond to event notifications from either instance.