0

I am using the Protothread Library from Adam Dunkels. It is supposed to wait 1 second and then continue. But instead it gets stuck there and everything after that line is not executed.

lastTimeBlink = millis();                                       
PT_WAIT_UNTIL(pt, millis() - lastTimeBlink > 1000);

Why does it get stuck there? The entire code is uploaded here: https://pastebin.com/jRsuvn54

(The comments are on german)

3
  • 1
    It's not really all that helpful to include just a short snippet of code for an issue like this.
    – jwh20
    Commented Dec 18, 2020 at 11:54
  • I've added the entire code
    – hypertext
    Commented Dec 18, 2020 at 13:10
  • The Protothread library examples seems to use a timer for this.
    – Gerben
    Commented Dec 18, 2020 at 13:58

1 Answer 1

1

By best attempt to provide something of an answer.

You have said:

PT_WAIT_UNTIL(pt, millis() - lastTimeBlink > 1000);

That is not literally represented in your code, so I take your question to refer to:

int dist2 = dist1.toInt();
...
PT_WAIT_UNTIL(pt, millis() - lastTimeBlink > (dist2 * 1710));

or a similar section.

When you execute this PT_WAIT_UNTIL, dist2 contains whatever value you initialized it with in int dist2 = dist1.toInt(); Assuming the condition isn't met, you are exiting runCommand() by way of PT_WAIT_UNTIL's return PT_WAITING;

#define PT_WAIT_UNTIL(pt, condition) \
  do {                               \
    LC_SET((pt)->lc);                \
    if(!(condition)) {               \
      return PT_WAITING;             \
    }                                \
  } while(0)

The LC_SET here in PT_WAIT_UNTIL is being used to generate a label that can be jumped-to later:

#define LC_SET(s)                                \
   do {                                          \
     LC_CONCAT(LC_LABEL, __LINE__):              \
     (s) = &&LC_CONCAT(LC_LABEL, __LINE__);      \
   } while(0)

Later when you later call runCommand to resume the thread, you execute LC_RESUME by way of PT_BEGIN(pt);

#define PT_BEGIN(pt) { char PT_YIELD_FLAG = 1; LC_RESUME((pt)->lc)
...
#define LC_RESUME(s)        \
  do {                      \
    if(s != NULL) {         \
      goto *s;              \
    }                       \
  } while(0)

goto *s here is ultimately jumping to the label created by your use of PT_WAIT_UNTIL.

You are now back in PT_WAIT_UNTIL checking the condition with the line if(!(condition)). That is, you are checking millis() - lastTimeBlink > (dist2 * 1710) However, what you are probably (more on that below) doing here is reading junk off the stack. dist2 was initialized on a previous execution of runCommand and its effective lifetime ended when you returned in PT_WAIT_UNTIL on that prior execution. On this new execution dist2 is uninitialized because you have jumped over its initialization. If you turn the warning level up in File/Preferences you should see something for this.

A number of things can happen at this point, most of them not good. I can't say what the compiler's optimizer will do to this code for certain, but not much would surprised me. It seems likely, just on familiarity with optimizing compilers and what your code looks like, that it is generating code to read dist2. That may sound like a given, but it isn't when you're doing something undefined. It's possible dist2 has the value from a previous run where you're executing this function in a loop. But it could also have value that makes dist2 * 1710 evaluate to a duration of up to nearly 50 days, causes PT_WAIT_UNTIL to return from runCommand for so long as you're testing.

Generally, if you're going to use these proto-threads you're going to have to be very careful in thinking about what's actually initialized and what just looks like it is. If you have a PT_whatever between a non-static local variable initialization or assignment and code where it's used, that's something to examine.

4
  • The reason I've put 1000 instead of (dist2*1710) in the question is simply because it doesn't work either way. The calculation of (dist2*1710) works perfectly fine. There is no problem with that.
    – hypertext
    Commented Dec 18, 2020 at 19:13
  • But I think I understood what the reason is. Basically the function is only being run once. It isn't run when the condition is met. If the function would be in a loop, then everything would work, as the program is able to jump to the label and check again, if the condition is met. Then it could also continue. The thread isn't really paused, it's stopped, and there is a label where to jump back to, when run again. As the function is not run again, you can't jump back to the label.
    – hypertext
    Commented Dec 18, 2020 at 19:14
  • Thank you anyways, as your answer helped me to understand it.
    – hypertext
    Commented Dec 18, 2020 at 19:15
  • I appreciate that it helped, but if you find the problem is actually something different you could write your own answer to your problem and mark that as accepted. I think in two days you can do mark your own answers as accepted. You can vote this up for being useful without declaring it the answer.
    – timemage
    Commented Dec 18, 2020 at 19:20

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.