Go Down

Topic: Yun, Bridge library and runAsynchronously (Read 14176 times) previous topic - next topic

bode

Dec 20, 2014, 01:31 am Last Edit: Dec 20, 2014, 02:42 am by bode
Hello everyone,

I am having a situation where run() executes properly but runAsynchronously() does nothing. The openwrt build is built=Fri Apr 11 04:46:31 CEST 2014

All the relevant code is within loop(), including declaration Process p.
runAsynchronously() invokes a python script and it does nothing unless followed by while (p.running());
run() works as expected.

Code: [Select]
#include <Bridge.h>
#include <Process.h>

const int int1Pin = 2;    // interrupt line goes here

// A variable changing within an interrupt must be declared as volatile.
volatile int cnt;
int cnt_old;

void setup() {
  Serial.begin(9600);
  pinMode(int1Pin, INPUT);
  attachInterrupt(1, incC, RISING);

  Bridge.begin();
}

void loop() {
  Process p;
 
  if (cnt_old != cnt) {
    cnt_old = cnt;
    p.begin("python");
    p.addParameter("/root/udp_cli.py");   
    p.addParameter(String(cnt));
    p.run();
    /*
      p.runAsynchronously();
      while (p.running()); // required otherwise it does not work
    */
  }
}

void incC()
{
  cnt++;
  Serial.println(cnt);
}


Basically, on an interrupt the code increments the interrupt counter and sends out a UDP message.

I searched the forum but couldn't find an answer (found "out of scope" issue). I apologize if the issue has been answered.

Thanks a lot,

Bode

PaulS

Code: [Select]
  attachInterrupt(1, incC, RISING);  // attach int0
Is the code or the useless comment correct?

When you run the command asynchronously, you need to periodically check to see if the process is done. How do you propose to do that when the Process variable is local to loop(), and goes out of scope when loop ends? Even if you make it global, how will you know when the process is done?
The art of getting good answers lies in asking good questions.

bode

#2
Dec 20, 2014, 02:28 am Last Edit: Dec 20, 2014, 02:46 am by bode
Code: [Select]
  attachInterrupt(1, incC, RISING);  // attach int0
Is the code or the useless comment correct?

When you run the command asynchronously, you need to periodically check to see if the process is done. How do you propose to do that when the Process variable is local to loop(), and goes out of scope when loop ends? Even if you make it global, how will you know when the process is done?
Thank you for replying.

- The commented out code is (obviously) the one that is not working.
- The process variable is local to loop() but loop runs indefinitely so how does the process variable go out of scope? The loop() should continue where it left before the interrupt.
- I am not interested in checking the results of runAsynchronously() - it is a "fire & forget" call (sends out a UDP message)! What is the point of running asynchronously if I have to check the result? I might as well use just run().

ShapeShifter

#3
Dec 20, 2014, 03:41 am Last Edit: Dec 20, 2014, 03:43 am by ShapeShifter
- The process variable is local to loop() but loop runs indefinitely so how does the process variable go out of scope? The loop() should continue where it left before the interrupt.
The code WILL continue when the interrupt returns. However, loop() does not RUN indefinitely, it is CALLED indefinitely. There is a difference that doesn't always matter, but in this case it does.

What's really happening behind the scenes is that there is a hidden main() function that essentially does this:
Code: [Select]
void main()
{
   setup(); // call setup once

   while (true)
   {
       loop(); // call loop over and over, forever
   }
}


Each time loop is called, a new Process instance is created. Sometimes (when the counter didn't change) loop quickly exits and the Process instance is destroyed. Other times, the counter has changed, so the process is started, and then almost immediately it is destroyed when loop() returns. It's getting called over and over, but the Process variable is indeed going out of scope on each iteration.

You could move the Process declaration outside to make it global, or you could keep it in loop() but make it static so that it isn't allocated and released on each iteration. Or you could add your own infinite loop inside of loop() so that loop() never returns.

bode

#4
Dec 20, 2014, 04:18 pm Last Edit: Dec 20, 2014, 04:25 pm by bode
The code WILL continue when the interrupt returns. However, loop() does not RUN indefinitely, it is CALLED indefinitely. There is a difference that doesn't always matter, but in this case it does.

What's really happening behind the scenes is that there is a hidden main() function that essentially does this:
Code: [Select]
void main()
{
   setup(); // call setup once

   while (true)
   {
       loop(); // call loop over and over, forever
   }
}


Each time loop is called, a new Process instance is created. Sometimes (when the counter didn't change) loop quickly exits and the Process instance is destroyed. Other times, the counter has changed, so the process is started, and then almost immediately it is destroyed when loop() returns. It's getting called over and over, but the Process variable is indeed going out of scope on each iteration.

You could move the Process declaration outside to make it global, or you could keep it in loop() but make it static so that it isn't allocated and released on each iteration. Or you could add your own infinite loop inside of loop() so that loop() never returns.
Wow, thank you very much for the reply and thorough explanation!
My fault, I didn't pay enough attention to the fact that this is not a loop construct but a call to loop() function in an infinite loop.

BTW, there are functions like YunClient.readStringUntil(), YunClient.print, etc. that are not documented on the YunClient library pages. Where are they documented? Are we taking about some inherited functionality?

ShapeShifter

Yep - a subtle difference that usually doesn't matter, but it sure does in a case like this!

I believe that YunClient (and most of the Bridge library, in fact) inherits from the Stream class. So all of those functions should be applicable: http://arduino.cc/en/Reference/Stream

avantassel


DarkSabre

You could move the Process declaration outside to make it global, or you could keep it in loop() but make it static so that it isn't allocated and released on each iteration. Or you could add your own infinite loop inside of loop() so that loop() never returns.
ShapeShifter's first suggestion is what I do, which is to declare the process before void setup() so that the process isn't destroyed each time that void loop() finishes a cycle.

TrashCompactor

ShapeShifter's first suggestion is what I do, which is to declare the process before void setup() so that the process isn't destroyed each time that void loop() finishes a cycle.
So does the problem only present itself when loop() finishes before the runAsynchronously() process is done running? 

ShapeShifter

So does the problem only present itself when loop() finishes before the runAsynchronously() process is done running?  
Correct. The process started by runAsynchronously() will run until the Linux process completes, or is terminated early by:
  • The Process object being destroyed, like when the object goes out of scope.
  • Another call is made to begin() on the same Process object

The first case is what you mention: if the Process object goes out of scope before the Linux process has a chance to finish, that Linux process will be terminated.

The second case could happen when a global Process object is reused. As an example, assume bode's code in the first post in this thread is re-written to make the "Process p;" declaration global by moving it from inside loop() and placing it at the top of the file. That way, when cnt has changed, the process will be started, and will run asynchronously. the loop() function exits and can re-enter many times will the Linux process runs. But, if another interrupt comes in, and cnt changes again before the Linux process completes, the call to p.begin() will re-initialize the Process object, and the first thing it will do is terminate the previous Linux process if it is still running. So, if another interrupt comes in quickly enough, another instance of the Linux process will be started, but the previous instance might also be terminated early. If this would be a problem, then a while loop waiting for p.running() to return false could be added just before the p.begin() call - this way, if the process is still running when another interrupt comes in, it will wait for that process to finish before starting the next one.

Note that this could tie up the loop() function until the process finishes, and if that is a problem, the code to check whether cnt has changed and start the function could be inside a IF statement that checks for the process to be not running. In this way, if a second interrupt comes in before the process is finished, loop will not look to see if cnt changed and will not start another process until the previous process has finished.

TrashCompactor

To ditto others, thank you for the thorough explanation!

Go Up