ESP32 FreeRTOS Task Problem

I was writing a demo program - it is very simple just two tasks running on an ESP32 S3 one sets a GPIO line high the other low:

void task1(void* arg) {
  for (;;) {
    digitalWrite(2,1);
  }
}
void task2(void* arg) {
  for (;;) {
    digitalWrite(2, 0);
  }
}

void setup() {
  pinMode(2, OUTPUT);
  TaskHandle_t th1;
  xTaskCreatePinnedToCore(task1, "task1", 2048, NULL, 2, &th1, 1);
  TaskHandle_t th2;
  xTaskCreatePinnedToCore(task2, "task2", 2048, NULL, 2, &th2, 1);
}

void loop() {
}

It works if I set the priority to 1 i.e. the same as loopTask. It also works if I set the core to 0. I have tried all sorts of things to find out what is going on and I don't think that there are any system locks involved and I have tried higher priorities. It seems that task1 is activated but it is never prempted.
I have tried the equivalent program using the ESP32 IDF and it just works.
What am I missing?
mikej

Does setup() actually run to completion?

My guess is that task1 (when set to a high priority) consumes all CPU time thus preventing setup() from progressing to the line where it creates task2.

Hi @drmikejames ,

Welcome to the forum..

either a yield() or delay(OfSomeValue) is needed in both loops..
i'd throw a delay(1000) in the main empty loop too..

good luck.. ~q

Brilliant - why didn't I think to check that?!?!
Ok now I've checked it, by toggling another GPIO line at the end of setup and yes it doesn't get there!
Changing it to:

vTaskSuspendAll();
  TaskHandle_t th1;
  xTaskCreatePinnedToCore(task1, "task1", 2048, NULL, 2, &th1, 1);
  TaskHandle_t th2;
  xTaskCreatePinnedToCore(task2, "task2", 2048, NULL, 2, &th2, 1);
xTaskResumeAll();

and it works as per the IDF version.
So I suppose the Arduino version of xTaskCreatePinnedToCore has a yield in there somewhere - this really isn't what you want.
Many thanks
mikej

It works if I put a yield into the first task -- but then the pulses are not even and not the same as what you get with the IDF - I've ticked the solution

possible, straight IDF doesn't have a loopTask..
what are you trying to accomplish??
~q

No it doesn't but it has similar facilities and I was comparing two versions of the program allowing for differences.
I'm not trying to accomplish anything other than making the demo program as listed in the first question work.. which it now does.
thanks

There are a lot of things wrong with your code.

A very serious issue is that digitalWrite() is not an atomic or reentrant operation. One task could be part way through executing that function when a context switch occurs. The other task would then call it even through the previous call hadn’t finished. That’s sure to mess things up for a function that’s not designed to be reentrant. When two tasks share a resource (like a GPIO in this case), you need to assure exclusive control of that resource by one task at a time. One way to do this is with a mutex.

Also, your timing of the HIGH and LOW periods of the GPIO depends solely on that of FreeRTOS’s context switching. You should probably have a better-defined source of timing.

Finally, while each task is executing during its time slot, it’s is calling digitalWrite() hundreds (maybe thousands) of time in its tight loop while setting the GPIO to the same level.

I highly recommend that you read https://www.freertos.org/media/2018/FreeRTOS_Reference_Manual_V10.0.0.pdf. You can skip Chapters 2 & 3 for now.

There is nothing wrong with my code - it is an example and all I require of it is that two tasks work as RTOS suggests they should. It is meant to illustrate race conditions - the GPIO lines are simply used to show when one task starts and another finishes. Your comments are all correct but not relevant to the question I asked.

Actually, there is. I define "wrong" as fails to operate as intended, which can happen for the first reason I mentioned.

You clearly are wanting an argument - It operates as intended once the difficulty with starting the two threads was sorted, as it is a DEMO, it is supposed to show a race condition.
I am teaching a class that includes tasks and their dangers.