Basic Task scheduling in FreeRTOS - scheduled-tasks

Ivé been playing with two tasks only to learn the basics of FreeRTOS but i have some confusing things happening I can not understand.
I have two Tasks with the same priority:
xTaskCreate( vTaskLED1, "LED1", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 1, NULL );
xTaskCreate( vTaskLED2, "LED2", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 1, NULL );
Then, in each task i just turn on the corresponding led in order to know thich task is running.
// Task 1 to be created.
void vTaskLED1(void *pvParameters)
{
for( ;; )
{
GPIO_PortToggle(BOARD_INITPINS_LED1_GPIO, BOARD_INITPINS_LED1_GPIO_PIN_MASK);
// Task code goes here.
}
}
// Task 2 to be created.
void vTaskLED2(void *pvParameters)
{
for( ;; )
{
GPIO_PortToggle(BOARD_INITPINS_LED2_GPIO, BOARD_INITPINS_LED2_GPIO_PIN_MASK);
// Task code goes here.
}
}
And as I understand, as I have configUSE_PREEMPTION On and configUSE_TIME_SLICING Off, at this point the only running task would be LED1 since I never blocked or suspended this task.
Then I tested adding a TaskYELD() at the end of task 1 (LED1), this resoulted in just task 2 running all time (because this is never blocked or suspended) after one run of task 1.
Ath this moment everything is ok, but when I started testing with vTaskDelay( 1000 / portTICK_PERIOD_MS ); in order to block task 1, I don't get why this task is running even if task 2 is never blocked or suspended.
I can't understand why at the first two cases Task 1 was always READY but never ran and why in the third case ir runs even if there is a second Task running that never gets blocked or suspended.

Related

FreeRTOS mutex/binary semaphore and deadlock

I am new to FreeRTOS, so I started with what I think is a great tutorial, the one presented by Shawn Hymel. I'm also implementing the code that I'm writting in a ESP32 DevkitC V4.
However, I think that I don't understand the difference between binary semaphores and mutexes. When I run this code that tries to avoid deadlock between two tasks that use two mutexes to protect a critical section (as shown in the tutorial):
// Use only core 1 for demo purposes
#if CONFIG_FREERTOS_UNICORE
static const BaseType_t app_cpu = 0;
#else
static const BaseType_t app_cpu = 1;
#endif
//Settings
TickType_t mutex_timeout = 1000 / portTICK_PERIOD_MS;
//Timeout for any task that tries to take a mutex!
//Globals
static SemaphoreHandle_t mutex_1;
static SemaphoreHandle_t mutex_2;
//**********************************************************
//Tasks
//Task A (High priority)
void doTaskA(void*parameters){
while(1){
//Take mutex 1
if( xSemaphoreTake(mutex_1, mutex_timeout) == pdTRUE){
Serial.println("Task A took mutex 1");
vTaskDelay(1 / portTICK_PERIOD_MS);
//Take mutex 2
if(xSemaphoreTake(mutex_2, mutex_timeout) == pdTRUE){
Serial.println("Task A took mutex 2");
//Critical section protected by 2 mutexes
Serial.println("Task A doing work");
vTaskDelay(500/portTICK_PERIOD_MS); //simulate that critical section takes 500ms
} else {
Serial.println("Task A timed out waiting for mutex 2. Trying again...");
}
} else {
Serial.println("Task A timed out waiting for mutex 1. Trying again...");
}
//Return mutexes
xSemaphoreGive(mutex_2);
xSemaphoreGive(mutex_1);
Serial.println("Task A going to sleep");
vTaskDelay(500/portTICK_PERIOD_MS);
//Wait to let other task execute
}
}
//Task B (low priority)
void doTaskB(void * parameters){
while(1){
//Take mutex 2 and wait to force deadlock
if(xSemaphoreTake(mutex_2, mutex_timeout)==pdTRUE){
Serial.println("Task B took mutex 2");
vTaskDelay(1 / portTICK_PERIOD_MS);
if(xSemaphoreTake(mutex_1, mutex_timeout) == pdTRUE){
Serial.println("Task B took mutex 1");
//Critical section protected by 2 mutexes
Serial.println("Task B doing work");
vTaskDelay(500/portTICK_PERIOD_MS); //simulate that critical section takes 500ms
} else {
Serial.println("Task B timed out waiting for mutex 1");
}
} else {
Serial.println("Task B timed out waiting for mutex 2");
}
//Return mutexes
xSemaphoreGive(mutex_1);
xSemaphoreGive(mutex_2);
Serial.println("Task B going to sleep");
vTaskDelay(500/portTICK_PERIOD_MS);
//Wait to let other task execute
}
}
void setup(){
Serial.begin(115200);
vTaskDelay(1000 / portTICK_PERIOD_MS);
Serial.println();
Serial.println("---FreeRTOS Deadlock Demo---");
//create mutexes
mutex_1 = xSemaphoreCreateMutex();
mutex_2 = xSemaphoreCreateMutex();
//Start task A (high priority)
xTaskCreatePinnedToCore(doTaskA, "Task A", 1500, NULL, 2, NULL, app_cpu);
//Start task B (low priority)
xTaskCreatePinnedToCore(doTaskB, "Task B", 1500, NULL, 1, NULL, app_cpu);
vTaskDelete(NULL);
}
void loop(){
}
My ESP32 starts automatically rebooting after both tasks reach their first mutex in execution, displaying this message:
---FreeRTOS Deadlock Demo---
Task A took mutex 1
Task B took mutex 2
Task A timed out waiting for mutex 2. Trying again...
assert failed: xQueueGenericSend queue.c:832 (pxQueue->pcHead != ((void *)0) || pxQueue->u.xSemaphore.xMutexHolder == ((void *)0) || pxQueue->u.xSemaphore.xMutexHolder == xTaskGetCurrentTaskHandle())
I am unable to interpret the error. However, when I change the definition of the mutexes to binary semaphores in setup():
//create mutexes
mutex_1 = xSemaphoreCreateBinary();
mutex_2 = xSemaphoreCreateBinary();
The code runs fine in the ESP32. Would anyone please explain me why this happens? Many thanks and sorry if the question wasn't adequately made, as this is my first one.
One of the key differences between semaphores and mutexes is the concept of ownership. Semaphores, don't have a thread that owns them. A higher priority thread can acquire a semaphore even if a lower priority thread has already acquired it. On the other hand, mutexes are owned by the thread that acquires them and can only be released by that thread.
In your code above, mutex_1 is acquired by Task A and mutex_2 is acquired by Task B. At this point, Task A is trying to acquire mutex_2. When it is an actual mutex, Task A cannot acquire it since it is owned by Task B. If this were a semaphore, however, Task A could acquire it from Task B. Thus clearing the deadlock.
The error here plays into that. After task A times out waiting for mutex_2, it starts to release the mutexes. It can release mutex_1 no problem because it owns it. When it tries to release mutex_2, it cannot because it is not the owner. Thus the OS throws an error because a task shouldn't try to release a mutex it doesn't own.
If you want to read a little more about the differences between mutexes and semaphores, you can check out this article.

Workflow execution fails when a worker is restarted on the same workflow service client

We're in the process of writing a .NET Cadence client and ran into an issue while unit testing workflows. When we start a worker, execute a workflow, stop the worker, start it again, and then try and execute another workflow, the first workflow completes, but any workflow after the first hangs during the client.ExecuteWorkflow() call, eventually failing with a START_TO_CLOSE timeout. I replicated this behavior by munging the greetings cadence-samples workflow. See the loop in func main():
package main
import (
"context"
"time"
"go.uber.org/cadence/client"
"go.uber.org/cadence/worker"
"go.uber.org/zap"
"github.com/pborman/uuid"
"github.com/samarabbas/cadence-samples/cmd/samples/common"
)
// This needs to be done as part of a bootstrap step when the process starts.
// The workers are supposed to be long running.
func startWorkers(h *common.SampleHelper) worker.Worker {
// Configure worker options.
workerOptions := worker.Options{
MetricsScope: h.Scope,
Logger: h.Logger,
}
return h.StartWorkers(h.Config.DomainName, ApplicationName, workerOptions)
}
func startWorkflow(h *common.SampleHelper) client.WorkflowRun {
workflowOptions := client.StartWorkflowOptions{
ID: "greetings_" + uuid.New(),
TaskList: ApplicationName,
ExecutionStartToCloseTimeout: time.Minute,
DecisionTaskStartToCloseTimeout: time.Minute,
}
return h.StartWorkflow(workflowOptions, SampleGreetingsWorkflow)
}
func main() {
// setup the SampleHelper
var h common.SampleHelper
h.SetupServiceConfig()
// Loop:
// - start a worker
// - start a workflow
// - block and wait for workflow result
// - stop the worker
for i := 0; i < 3; i++ {
// start the worker
// execute the workflow
workflowWorker := startWorkers(&h)
workflowRun := startWorkflow(&h)
// create context
// get workflow result
var result string
ctx, cancel := context.WithCancel(context.Background())
err := workflowRun.Get(ctx, &result)
if err != nil {
panic(err)
}
// log the result
h.Logger.Info("Workflow Completed", zap.String("Result", result))
// stop the worker
// cancel the context
workflowWorker.Stop()
cancel()
}
}
This is not a blocking issue and will probably not come up in production.
Background:
We (Jeff Lill and I) noticed this issue during unit testing workflows in our .NET Cadence client. When we run our workflow tests individually they all pass, but when we run multiple at a time (sequentially, not in parallel), we see the behavior described above. This is because of the cleanup done in the .NET Cadence client dispose() method called after a test completes (pass or fail). One of the dispose behaviors is to stop workers created during a test. When the next test runs, new workers are created using the same workflow service client, and this is where the issue arises.

Scala Monix , how to kill running or scheduled Task

Im using Monix for asynchronous task workflow.
How do we kill a running Task ?
Task{ println("sleep")
Thread.sleep(200)
println("effect") }
.doOnCancel(Task(println("canceled")))
.timeout(100.milli) // timeout will do cancel
.runOnComplete(println)
#> Failure(java.util.concurrent.TimeoutException: Task timed-out after 100 milliseconds of inactivity)
sleep
canceled
effect <--- what !? , task is running. Isn't it canceled !?
My current solution is ugly in my opinion(the flag checking hinders the code reusing):
var flag=true
Task{
println("sleep")
Thread.sleep(200)
if (flag)
println("effect")
}
.doOnCancel(Task{ flag=false; println("canceled") })
.timeout(100.milli) // timeout will do cancel
If it is impossible, how do we kill a scheduled while not-yet-ran Task ?
My failed attempt is :
Task{ println("sleep"); Thread.sleep(200) }
.map{ _ => println("effect") }
.doOnCancel(Task(println("canceled")))
.timeout(100.milli) // timeout will do cancel
.runOnComplete(println)
Sadly it still shows the effect after the cancel happened. I hope that the scheduled and not-yet-ran Task can be canceled (the .map(...) is another Task, right?)
If you don't use Thread.sleep (which messes with the internals of Monix), but Task.sleep, things are working just fine.
Task
.defer {
println("start")
Task.sleep(1000.millis)
}
.map(_ => println("effect"))
.timeout(10.millis)
.doOnCancel(Task(println("canceling")))
Now, the question is what's your actual use case, because I'm sure you used Thread.sleep just for illustration purposes.
I found one of solutions if it is chain of tasks:
Task{println("start");Thread.sleep(1000)}
.asyncBoundary
.map{_=> println("effect")}
.doOnCancel(Task(println("canceling")))
.timeout(10.milli)
.executeWithOptions(_.enableAutoCancelableRunLoops)
.runOnComplete(println)
reference: https://github.com/monix/monix/issues/226
But I hope there are easy way to interrupt task instead of using closure or split and chaining tasks.

State machine program design in FreeRTOS - vTaskStartScheduler in a switch statement

I have a program design question in FreeRTOS:
I have a state machine with 4 states, and 6 tasks. In each state, different tasks must be executed, excepting Task1, which is always active:
State 1: Task1, Task2, Task3
State 2: Task1, Task2, Task3, Task4
State 3: Task1, Task5
State 4: Task1, Task6
Task1, Task3, Task4, Task5 and Task6 are periodic, and each one reads a different sensor.
Task2 is aperiodic, it sends a GPRS alarm only if a threshold is reached.
The switching between the states is determined by events from the sensor input of each task.
The initial approach for the design of main() is to have a switch to control the states, and depending on the state, suspend and activate the corresponding tasks:
void main ()
{
/* initialisation of hw and variables*/
system_init();
/* creates FreeRTOS tasks and suspends all tasks except Task1*/
task_create();
/* Start the scheduler so FreeRTOS runs the tasks */
vTaskStartScheduler();
while(true)
{
switch STATE:
case 1:
suspend(Task4, Task5, Task6);
activate(Task2, Task3);
break;
case 2:
suspend(Task5, Task6);
activate(Task2, Task3, Task4);
break;
case 3:
suspend(Task2, Task3, Task4, Task6);
activate(Task5);
break;
case 4:
suspend(Task2, Task3, Task4, Task5);
activate(Task6);
break;
}
}
My question is: where should I call vTaskStartScheduler(), in relation with the switch? It seems to me that in this code, once the vTaskStartScheduler is called, the program will never enter the switch statement.
Should I create another task always active to control the state machine, which has the previous while and switch statements inside, such as the following pseudocode?
task_control()
{
while(true)
{
switch STATE:
case 1:
suspend(Task4, Task5, Task6);
execute(Task2, Task3);
and so on...
}
}
Any advice will be much appreciated...
To answer your question, vTaskStartScheduler() will, as the name suggests, start the scheduler. Any code after it will only execute when the scheduler is stopped, which in most cases is when the program ends, so never. This is why your switch won't run.
As you have already eluded to, for your design you could use a 'main' task to control the others. You need to have created this and registered it with the scheduler before calling vTaskStartScheduler().
On a side note, if you do go with this approach, you only want to suspend/resume your tasks on first entry into a state, not on every iteration of the 'main' task.
For example:
static bool s_first_state_entry = true;
task_control()
{
while (true)
{
switch (STATE)
{
case 1:
if (s_first_state_entry)
{
// Only do this stuff once
s_first_state_entry = false;
suspend(Task4, Task5, Task6);
execute(Task2, Task3);
}
// Do this stuff on every iteration
// ...
break;
default:
break;
}
}
}
void set_state(int state)
{
STATE = state;
s_first_state_entry = true;
}
As Ed King metioned, your solution contains a major design flaw. That is - after starting scheduler, no code that's specified after it in the main function will ever execute until the scheduler is stopped.
I suggest implementing your state logic in Idle task (remember to include delays in your tasks not to starve the Idle hook from processing time). Idle task could block and unblock the tasks depending on the current state by menas of semaphores. Remember though, that the Idle hook is a task with a lowest possible priority, so be careful when designing your system. The solution suggested by me may be completely wrong when the tasks consume most of the processing time not allowing the Idle task to switch states.
Alternatively, you can create a superior task as mentioned by Ed King, with the highest priority.
To be honest, everything depends on what the task are really doing.

boost::asio io_service stop specific thread

I've got a boost::asio based thread pool running on N threads.
It used mainly for IO tasks (DB data storing/retreival). It also launches self-diagnostic timer job to check how 'busy' pool is (calculates ms diff between 'time added' and 'time handler called')
So the question is - is there any way to stop M of N threads ( for cases when load is very low and pool does not need so many threads).
When the load is high (determined by diagnostic task) then new thread is added:
_workers.emplace_back(srv::unique_ptr<srv::thread>(new srv::thread([this]
{
_service.run();
})));
(srv namespace is used to switch quickly between boost and std)
But when 'peak load' is passed I need some way to stop additional threads. Is there any solution for this?
What you are looking for is a way to interrupt a thread that is waiting on the io_service. You can implement some sort of interruption mechanism using exceptions.
class worker_interrupted : public std::runtime_error
{
public:
worker_interrupted()
: runtime_error("thread interrupted") {}
};
_workers.emplace_back(srv::unique_ptr<srv::thread>(new srv::thread([this]
{
try
{
_service.run();
}
catch (const worker_interrupted& intrruption)
{
// thread function exits gracefully.
}
})));
You could then just use io_service::post to enqueue a completion handler which just throws worker_interrupted exception.

Categories