Call Function with External Stack

[中文]

Overview

A given function can be executed with a user-allocated stack space which is independent of current task stack. This mechanism can be used to save stack space wasted by tasks which call a common function with intensive stack usage such as printf. The given function can be called inside the shared stack space, which is a callback function deferred by calling esp_execute_shared_stack_function(), passing that function as a parameter.

Warning

esp_execute_shared_stack_function() does only minimal preparation of the provided shared stack memory. The function passed to it for execution on the shared stack space or any of that function's callees should not do any of the following:

  • Use thread-local storage

  • Call vTaskDelete(NULL) to delete the currently running task

Furthermore, backtraces will be wrong when called from the function running on the shared stack or any of its callees. The limitations are quite severe, so that we might deprecate esp_execute_shared_stack_function() in the future. If you have any use case which can only be implemented using esp_execute_shared_stack_function(), please open a GitHub Issue.

Usage

esp_execute_shared_stack_function() takes four arguments:

  • a mutex object allocated by the caller, which is used to protect if the same function shares its allocated stack

  • a pointer to the top of stack used for that function

  • the size of stack in bytes

  • a pointer to the shared stack function

The user-defined function is deferred as a callback and can be called using the user-allocated space without taking space from current task stack.

The usage may look like the code below:

void external_stack_function(void)
{
    printf("Executing this printf from external stack! \n");
}

//Let us suppose we want to call printf using a separated stack space
//allowing the app to reduce its stack size.
void app_main()
{
    //Allocate a stack buffer, from heap or as a static form:
    StackType_t *shared_stack = malloc(8192 * sizeof(StackType_t));
    assert(shared_stack != NULL);

    //Allocate a mutex to protect its usage:
    SemaphoreHandle_t printf_lock = xSemaphoreCreateMutex();
    assert(printf_lock != NULL);

    //Call the desired function using the macro helper:
    esp_execute_shared_stack_function(printf_lock,
                                    shared_stack,
                                    8192,
                                    external_stack_function);

    vSemaphoreDelete(printf_lock);
    free(shared_stack);
}

API Reference

Header File

Functions

void esp_execute_shared_stack_function(SemaphoreHandle_t lock, void *stack, size_t stack_size, shared_stack_function function)

Calls function on user defined shared stack space.

After returning, the original stack is used again.

Note

if either lock, stack or stack size is invalid, the expression will be called using the current stack.

Warning

This function does minimal preparation of the provided piece of memory (stack). DO NOT do any of the following in function or any of its callees:

  • Use Thread-local storage

  • Use the Floating-point unit on ESP32-P4

  • Use the AI co-processor on ESP32-P4

  • Call vTaskDelete(NULL) (deleting the currently running task) Furthermore, backtraces will be wrong when called from function or any of its callees. The limitations are quite sever, so that we might deprecate this function in the future. If you have any use case which can only be implemented using this function, please open an issue on github.

Parameters
  • lock -- Mutex object to protect in case of shared stack

  • stack -- Pointer to user allocated stack

  • stack_size -- Size of current stack in bytes

  • function -- pointer to the shared stack function to be executed

Macros

ESP_EXECUTE_EXPRESSION_WITH_STACK(lock, stack, stack_size, expression)

Type Definitions

typedef void (*shared_stack_function)(void)