xStreamBufferReceive(…, portMAX_DELAY) Task Moved to Suspended and Never Wakes on Data

Did you try removing printf?

Then you should be able to write a minimal application to repro the problem: one task which sends data to the stream buffer and other task which receives the data from the stream buffer. If you can do that and share the application, I can give it a try on a different hardware.

For a test I changed original task code:

void task1_task(void *pvParameters) {
	(void) pvParameters;
	TickType_t xLastWakeTime;
	xLastWakeTime = xTaskGetTickCount();
	while (1) {
		vTaskDelayUntil(&xLastWakeTime, 60);
		FreeRTOS_printf(( "A%03u, %04u\n", (int16_t) DebugUARTbuffFreeBytesGet(), (uint16_t)uxTaskGetStackHighWaterMark(NULL) ));
		vLEDtoggle(GPIOA, GPIO_Pin_0);
	}
}

void task2_task(void *pvParameters) {
	(void) pvParameters;
	TickType_t xLastWakeTime;
	xLastWakeTime = xTaskGetTickCount();
	while (1) {
		vTaskDelayUntil(&xLastWakeTime, 61);
		FreeRTOS_printf(( "B%03u, %04u\n", (int16_t) DebugUARTbuffFreeBytesGet(), (uint16_t)uxTaskGetStackHighWaterMark(NULL) ));
		vLEDtoggle(GPIOA, GPIO_Pin_1);
	}
}

to simpler printing:

void task1_task(void *pvParameters) {
	(void) pvParameters;
	TickType_t xLastWakeTime;
	xLastWakeTime = xTaskGetTickCount();
	while (1) {
		vTaskDelayUntil(&xLastWakeTime, 60);
		__write(0,"tsk1\n",5);
		vLEDtoggle(GPIOA, GPIO_Pin_0);
	}
}

void task2_task(void *pvParameters) {
	(void) pvParameters;
	TickType_t xLastWakeTime;
	xLastWakeTime = xTaskGetTickCount();
	while (1) {
		vTaskDelayUntil(&xLastWakeTime, 61);
		__write(0,"tsk2\n",5);
		vLEDtoggle(GPIOA, GPIO_Pin_1);
	}
}

But the same behavior: the buffer is full but DebugUART is suspended with usNotifyState==taskNOTIFICATION_RECEIVED.

My hardware is somewhat new and probably different. Currently it’s WCH CH32V317 MCU with QuingKe4F CPU core. It lacks RISC-V mtime timer and it’s comparator but has SysTick like in ARM Cortex-M cores but 64 bit wide. So the context switching is done though timer interrupt or software interrupt (SWI) depending on the moment the switch is needed.
I can imagine that the problem is in the port itself. Something related to critical sections, task switching or tick timer interrupt control.
Not sure my example will not run on your hardware.
Can you describe the process of task notification to let me investigate my port problem (or RTOS problem if any)?

To fulfill your recommendation, I tested the stream sending from writing tasks directly:

void task1_task(void *pvParameters) {
	(void) pvParameters;
	TickType_t xLastWakeTime;
	xLastWakeTime = xTaskGetTickCount();
	while (1) {
		vTaskDelayUntil(&xLastWakeTime, 60);
		xStreamBufferSend(xDebugMsgStream, "tsk1\n", 5, 0);
		vLEDtoggle(GPIOA, GPIO_Pin_0);
	}
}

void task2_task(void *pvParameters) {
	(void) pvParameters;
	TickType_t xLastWakeTime;
	xLastWakeTime = xTaskGetTickCount();
	while (1) {
		vTaskDelayUntil(&xLastWakeTime, 61);
		xStreamBufferSend(xDebugMsgStream, "tsk2\n", 5, 0);
		vLEDtoggle(GPIOA, GPIO_Pin_1);
	}
}

No changes. Receiving task is eSuspened, usNotifyState==taskNOTIFICATION_RECEIVED, buffer is full.

Okay. It was not too hard.
Here is main.c which is demonstrating the problem in my environment:

/*
 * main.c
 *
 *  Created on: Aug 15, 2025
 *      Author: nikolaypo
 */

#define DebugUART_PRIO 5
#define TASK1_TASK_PRIO 4
#define TASK2_TASK_PRIO 3

#define TASK1_STK_SIZE 256
#define TASK2_STK_SIZE 256
#define DebugUART_STK_SIZE 256 //Stack size for debug background output handler

//#define DebugUARTtxSize 320 //Size of debug UART Tx buffer
//#define xBufferSizeBytes DebugUARTtxSize

#include "FreeRTOS.h"
#include "task.h"
#include "stream_buffer.h"
#include "message_buffer.h"

//Static tasks memory
TaskHandle_t xDebugUART; //Debug background output handler for RTOS tasks
StaticTask_t xDebugUART_TCB; //Task data structure
StackType_t puxDbgUARTstkBuf[DebugUART_STK_SIZE]; //Static task stack buffer

TaskHandle_t Task1Task_Handler;
StaticTask_t xTask1Buffer;
StackType_t uxTask1StackBuffer[TASK1_STK_SIZE];

TaskHandle_t Task2Task_Handler;
StaticTask_t xTask2Buffer;
StackType_t uxTask2StackBuffer[TASK2_STK_SIZE];


//FreeRTOS debug messages static stream buffer
uint8_t pucDbgMsgBufStor[320/*xBufferSizeBytes*/]; //Debug message buffer storage area
StaticMessageBuffer_t xDbgMsgBufStruct; //Debug message buffer structure
StreamBufferHandle_t xDebugMsgStream; //Debug message buffer handler

//UART message buffer receiver task stuff

static const char HelloString[] = "\nUART started\n"; //Printed each time the UART is configured or re-configured

void task1_task(void *pvParameters) {
	(void) pvParameters;
	TickType_t xLastWakeTime;
	xLastWakeTime = xTaskGetTickCount();
	while (1) {
		vTaskDelayUntil(&xLastWakeTime, 60);
		xStreamBufferSend(xDebugMsgStream, "tsk1\n", 5, 0);
	}
}

void task2_task(void *pvParameters) {
	(void) pvParameters;
	TickType_t xLastWakeTime;
	xLastWakeTime = xTaskGetTickCount();
	while (1) {
		vTaskDelayUntil(&xLastWakeTime, 61);
		xStreamBufferSend(xDebugMsgStream, "tsk2\n", 5, 0);
	}
}

void vUART_DMAtx(void *pvParameters) {
	(void) pvParameters;
	char Buff[320/*xBufferSizeBytes*/];
	xStreamBufferSend(xDebugMsgStream, (const void*) HelloString, sizeof(HelloString) - 1, 0); //Send helloString to fill the buffer by first message
	for (;;) {
		size_t Size = xStreamBufferReceive(xDebugMsgStream, (void*) Buff, sizeof(Buff), portMAX_DELAY); //Wait for next message
		if (Size > 0) { //Sanity check for size of data requested for a transmission
			//Some data need to be transferred
			asm volatile ("NOP");
			//I'm getting here only first time after calling xStreamBufferSend() above at first entering to vUART_DMAtx()
		} else {
			//Size of request is zero or there is no request. Do nothing.
		}
	} //Repeat forever
}

int main(void) {
	//Create background debug stream buffer for sending debug messages to UART
	xDebugMsgStream = xStreamBufferCreateStatic(320/*xBufferSizeBytes*/, 1, pucDbgMsgBufStor, &xDbgMsgBufStruct);

	/* create tasks */
	xDebugUART = xTaskCreateStatic(vUART_DMAtx, (const char*) "DebugUART", DebugUART_STK_SIZE, (void*) 1, DebugUART_PRIO, puxDbgUARTstkBuf, &xDebugUART_TCB);

	Task1Task_Handler = xTaskCreateStatic((TaskFunction_t) task1_task, (const char*) "task1", (uint16_t) TASK1_STK_SIZE, (void*) NULL,
			(UBaseType_t) TASK1_TASK_PRIO, uxTask1StackBuffer, &xTask1Buffer);

	Task2Task_Handler = xTaskCreateStatic((TaskFunction_t) task2_task, (const char*) "task2", (uint16_t) TASK2_STK_SIZE, (void*) NULL,
			(UBaseType_t) TASK2_TASK_PRIO, uxTask2StackBuffer, &xTask2Buffer);

	/* Start the RTOS scheduler. */
	vTaskStartScheduler();
	for (;;) {
		asm volatile ("NOP");
		//Shouldn't run at here
	}
}

UPD: archived file is attached.
StreamBufferNotifyFailureTest.zip (1.4 KB)

This tiny test even without hardware initialization and interrupt enabling except the port code exhibits the same behavior: xDebugUART task is in eSuspended state and usNotifyState value is 2 which is taskNOTIFICATION_RECEIVED.
What should I check in my port to debugging that?

I do not know if this still applies to the current state of the problem report, but note that both critical sections and Suspend/ResumeAll are like nuclear war heads shooting at robins and should be avoided. It seems as if your use case cries for muteces. By suspending ALL tasks, you a) risk very subtle deadlocks even between tasks that do not care about the resources you need to protect and b) do not have any control over where all of your tasks are in their control flow.

Thanks for an opinion. Currently I flattened the code to absolute minimum with no critical sections and with no suspending on user part. But the problem persists

you should definitely check for the return values of your calls to xStreamBufferSend() to get an idea of how the buffer fills and whether you run into conditions where the receiver starves out or something the like. Do NOT use debug output for that but in-memory counters or message arrays that you can evaluate posthumously.

Didn’t catch your point. The problem is stream buffer send function didn’t wake-up suspended task which is subscribed to this stream buffer reading. So the buffer gets more and more filled until become completely full.
I need not to check buffer send result because if the buffer is full, it’s okay to drop the last message. There will be no workaround for that situation. I need short, fast write which is not blocking the task which wants to print debug message.
My single concern is the receiver task didn’t get running nor when fresh message arrived nor the buffer become overflowed.

well is the body of the loop of your “DMATx” really empty (only the nop code) or do you really output something via _write (which is probably some eco sytem provided function)? If the latter, how do you know if your task is suspended on the buffer receive and not somewhere within write? Note that a stack backtrace may be unreliable due to optization/inlining/folding. If the implementation of _write has internal deadlock potential, you may be barking up a wrong tree.

My suggestion was to record the flow of events in a single serialized linear memory array. If you record every incident before after each read and write, you may be able to observe a pattern which may clarify the understanding for how you get into that situation.

The problem is very stable. I need just reach this “NOP” instruction getting a notification for new message but none.
I disabled optimizations - no luck.

what do you mean “reach the nop” but none? If you reach the branch that contains the nop it implies that the read from the message buffer returned a value > 0, what makes you think you did not get a message?

I set a breakpoint at this NOP instruction. It doesn’t break second time.
The problem now is not to run full-feature system but to get a notification about data arrival. At this stage it’s enough to get this break point to trigger.
If you’re still not understanding, the last example is specailly reduced code with none functionality except testing the stream buffer notification to a reader.
A “NOP” assembly instruction is neutral, no-operation instruction which does nothing (except wasting command cycle), does not disturb CPU register, doesn’t modify anything so it is convenient to be used for dedicated position breakpoint.
When someone need to place a breakpoint after one function but before another, he can place asm volatile (“NOP”); instruction. That is it.

Hi Nikolay,

there is no need to explain nop to me. I have developed for embedded for well over 30 years now, and I know nop as well as I know dsb, isb, ldrex and strex and the like (if you need details, you can buy the book I wrote about it). My last (because you are apparently not interested in constructive support, so I will bail out of this discussion now) advice to you is to replace the inline nop with an inline bkpt (if this is an ARM based processor, other platforms may have functionally equivalent instructions) which will force a break into a debugger if attached. Be advised that the relationship between source code and machine code can be very remote, so setting breakpoints on the debugger level is a game of guessing.

Also, you should be more careful about your wording. When you wrote “The problem is very stable. I need just reach this “NOP” instruction getting a notification for new message but none.” this leaves some room for interpretation. I read this to mean that you DO reach instruction, so formulating precisely what happens helps if you want (unpaid) help.

As a final observation - in the call stack that @aggarg focused on in this post: xStreamBufferReceive(…, portMAX_DELAY) Task Moved to Suspended and Never Wakes on Data - #16 by aggarg , it looks as if your xStreamBufferSend() call happens WITHIN the _write function - again, debuggers can not be trusted to provide the real call stack, but if that is the case, have you ensured that the control flow makes it even to BEFORE the call - in other words, are you sure that the real hang is not somewhere else in the call chain that prevents the code even entering the path? The suggestion I made earlier - keeping counters and inline trace point markers to follow the serialized control flow - can detect these things reliably.

1 Like

RAc, thank you for your input.

I’m stepping through the code with disassembly enabled. It seems to me that, at least with no optimizations enabled the machine code is quite straightforward.

The CPU is RISC-V. As I wrote before. I don"t know which is an instruction to make a breakpoint to a debugger for given core nor sure it is supported by supplied tools. A breakpoint at NOP instruction is proven to be reliable way to break the execution for a debugging in my environment with these CPUs.

Just for avoidance of a complexity, I prepared very simple test source code which can be executed from basic FreeRTOS project with no peripherals. Let’s examine it’s behavior because it exhibits the same problem.
I wish to step through all the processes but is struggling of a lack of knowledge about notifications under the hood of FreeRTOS.

I made the following 2 changes to your code:

  1. Moved the receive buffer out of the vUART_DMAtx task as it may cause stack overflows.
  2. Wrapped the calls to xStreamBufferSend with vTaskSuspendAll/xTaskResumeAll to serialize calls to xStreamBufferSend. This requirement is described on the following page: xStreamBufferSend() - FreeRTOS™.
#define DebugUART_PRIO 5
#define TASK1_TASK_PRIO 4
#define TASK2_TASK_PRIO 3

#define TASK1_STK_SIZE 256
#define TASK2_STK_SIZE 256
#define DebugUART_STK_SIZE 256 //Stack size for debug background output handler

//#define DebugUARTtxSize 320 //Size of debug UART Tx buffer
//#define xBufferSizeBytes DebugUARTtxSize

#include "FreeRTOS.h"
#include "task.h"
#include "stream_buffer.h"
#include "message_buffer.h"

//Static tasks memory
TaskHandle_t xDebugUART; //Debug background output handler for RTOS tasks
StaticTask_t xDebugUART_TCB; //Task data structure
StackType_t puxDbgUARTstkBuf[DebugUART_STK_SIZE]; //Static task stack buffer

TaskHandle_t Task1Task_Handler;
StaticTask_t xTask1Buffer;
StackType_t uxTask1StackBuffer[TASK1_STK_SIZE];

TaskHandle_t Task2Task_Handler;
StaticTask_t xTask2Buffer;
StackType_t uxTask2StackBuffer[TASK2_STK_SIZE];


//FreeRTOS debug messages static stream buffer
uint8_t pucDbgMsgBufStor[320/*xBufferSizeBytes*/]; //Debug message buffer storage area
StaticMessageBuffer_t xDbgMsgBufStruct; //Debug message buffer structure
StreamBufferHandle_t xDebugMsgStream; //Debug message buffer handler

//UART message buffer receiver task stuff

static const char HelloString[] = "\nUART started\n"; //Printed each time the UART is configured or re-configured
static char recvBuff[320/*xBufferSizeBytes*/];

void task1_task(void *pvParameters) {
	(void) pvParameters;
	TickType_t xLastWakeTime;
	xLastWakeTime = xTaskGetTickCount();
	while (1) {
		vTaskDelayUntil(&xLastWakeTime, 60);
        vTaskSuspendAll();
        {
            xStreamBufferSend(xDebugMsgStream, "tsk1\n", 5, 0);
        }
        xTaskResumeAll();
	}
}

void task2_task(void *pvParameters) {
	(void) pvParameters;
	TickType_t xLastWakeTime;
	xLastWakeTime = xTaskGetTickCount();
	while (1) {
		vTaskDelayUntil(&xLastWakeTime, 61);
        vTaskSuspendAll();
        {
            xStreamBufferSend(xDebugMsgStream, "tsk2\n", 5, 0);
        }
        xTaskResumeAll();
	}
}

void vUART_DMAtx(void *pvParameters) {
	(void) pvParameters;
	xStreamBufferSend(xDebugMsgStream, (const void*) HelloString, sizeof(HelloString) - 1, 0); //Send helloString to fill the buffer by first message
	for (;;) {
		size_t Size = xStreamBufferReceive(xDebugMsgStream, (void*) recvBuff, sizeof(recvBuff), portMAX_DELAY); //Wait for next message
		if (Size > 0) { //Sanity check for size of data requested for a transmission
			//Some data need to be transferred
			asm volatile ("NOP");
			//I'm getting here only first time after calling xStreamBufferSend() above at first entering to vUART_DMAtx()
		} else {
			//Size of request is zero or there is no request. Do nothing.
		}
	} //Repeat forever
}

void app_main(void) {
	//Create background debug stream buffer for sending debug messages to UART
	xDebugMsgStream = xStreamBufferCreateStatic(320/*xBufferSizeBytes*/, 1, pucDbgMsgBufStor, &xDbgMsgBufStruct);

	/* create tasks */
	xDebugUART = xTaskCreateStatic(vUART_DMAtx, (const char*) "DebugUART", DebugUART_STK_SIZE, (void*) 1, DebugUART_PRIO, puxDbgUARTstkBuf, &xDebugUART_TCB);

	Task1Task_Handler = xTaskCreateStatic((TaskFunction_t) task1_task, (const char*) "task1", (uint16_t) TASK1_STK_SIZE, (void*) NULL,
			(UBaseType_t) TASK1_TASK_PRIO, uxTask1StackBuffer, &xTask1Buffer);

	Task2Task_Handler = xTaskCreateStatic((TaskFunction_t) task2_task, (const char*) "task2", (uint16_t) TASK2_STK_SIZE, (void*) NULL,
			(UBaseType_t) TASK2_TASK_PRIO, uxTask2StackBuffer, &xTask2Buffer);

	/* Start the RTOS scheduler. */
	vTaskStartScheduler();
	for (;;) {
		asm volatile ("NOP");
		//Shouldn't run at here
	}
}

With the above changes, the code works on my hardware and I see that the breakpoint on asm volatile ("NOP"); in the vUART_DMAtx task gets hit repeatedly.

I’d suggested to blink an LED or increment a volatile int instead of NOP just to ensure that the compiler is not optimizing it out.

Trying to dig my problem deeper.
When xStreamBufferReceive() is called when there is no data in the buffer, stream buffer code does next:

  1. In xTaskNotifyStateClearIndexed() clears task notify state from 2 (taskNOTIFICATION_RECEIVED) to 0 (taskNOT_WAITING_NOTIFICATION), see line 1104 of stream_buffer.c:

  1. Sets stream pointer xTaskWaitingToReceive to receiving task TCB (see lins 1107 and 1108).

At this point the receiving task is still running and is “subscribed” for stream buffer data arrival with none notify state.

  1. Next inside xStreamBufferReceive() some kind of blocking or sudpending should occur, see line 1121, function xTaskNotifyWaitIndexed(). If stepping into, the xTaskGenericNotifyWait() function of task.c is called. At the line 7739 of tasks.c it checks that receiver task is not already “taskNOTIFICATION_RECEIVED” (2) which is true due to point 1 above.
    As I’m understanding, the pxStreamBuffer->xTaskWaitingToReceive = NULL; at the line 1122 is executing AFTER next stream buffer data arrival and the task become running because this code flow is receiving task context. So it’s normal to wait for a notification (indefinite time in my case) then remove receiving task for waiting to receive subscriber until the buffer discovered to be empty again.
  2. Then xTaskGenericNotifyWait() performs clearing notification value at an entry (line 7744, irrelevant for my case, not used) then sets the receiver task as taskWAITING_NOTIFICATION (1).
    At this point all is perfect: The receiver task is in WAITING_NOTIFICATION state (see point 3 above), is in running state (is executing it’s last CPU instructions before suspending) and is subscribed for stream buffer data arrival notification (a pointer to receiving task TCB is set in buffer handle):

  1. Inside of xTaskGenericNotifyWait(), at the line 7771 of tasks.c, by the function prvAddCurrentTaskToDelayedList() the receiving task is placed into delayed list. In this function the receiver task is removed from ready list at the line 8434 of tasks.c. Still looks normal.
  2. At the line 8452 of tasks.c, in prvAddCurrentTaskToDelayedList() function, receiving task get placed into suspended list. At this point all is correct. Receiving task now is in eSuspended list, keeps a subscription for stream buffer event as as a receiving task. An receiving task waiting state is taskWAITING_NOTIFICATION (1). So far, so good.
  3. At the line 7781 of tasks.c inside of xTaskGenericNotifyWait() function, a software interrupt is prended to force the sheduler execution. But a few instructions later an entrance to critical section at the line 7790 is occurred. At this moment the parameters of stream buffer and buffer reading task I’m watching remaining the same as in point 6. OK.
  4. At the line 7805 of tasks.c, in case new buffer notification arrival the receiving task notification is tested for a notification received (2). But it is (1), so false. Return value of xTaskGenericNotifyWait() become false.
  5. At the line of 7818 of tasks.c, receiving task state is changed from taskWAITING_NOTIFICATION (1) to taskNOT_WAITING_NOTIFICATION (0):

As I can see, the problem is that receiving task didn’t get actually blocked. See points 6..9 in the post above.
When task is set eSuspended and waiting buffer notification, this condition test (is true):

        if( ( xShouldBlock == pdTRUE ) && ( xAlreadyYielded == pdFALSE ) )
        {
            taskYIELD_WITHIN_API();
        }

should initiate a context switch before entering critical section at the line 7790 of tasks.c to switch-out receiving task in favor of other ready for running tasks. But this not is the case.
taskYIELD_WITHIN_API() is pending software interrupt which is used for sheduling. But it interrupt didn’t fire for some reason.
Due to missed context switching, next execution of critical section between lines 7790 and 7820, removing taskWAITING_NOTIFICATION state from receiving task which prevents it’s wake-up upon next buffer data arrivals.

After context switch failure, the code of receiving task which is nominally in eSuspended state continues to execute. And after exiting xTaskGenericNotifyWait() function and (assuming the task is running) line 1122 of stream_buffer.c removes the receiving task from stream buffer subscription:

            pxStreamBuffer->xTaskWaitingToReceive = NULL;

All of these is done during the first tick of RTOS SysTick timer. I watched on xTickCount.
I’m gad to see the root of the buffer problem. The digging in what is the cause of context switch miss is my new task.

Thank you for watching!
Stay tuned!