Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ipc_task() can wake up blocking task early. (IDFGH-6939) #8559

Closed
pickaxprograms opened this issue Mar 11, 2022 · 1 comment
Closed

ipc_task() can wake up blocking task early. (IDFGH-6939) #8559

pickaxprograms opened this issue Mar 11, 2022 · 1 comment
Assignees
Labels
Resolution: Done Issue is done internally Status: Done Issue is done internally

Comments

@pickaxprograms
Copy link

pickaxprograms commented Mar 11, 2022

  • Development Kit: none
  • Module or chip used: ESP32-WROVER
  • IDF version : v4.4-12-ga0404a9ed2
  • Build System: idf.py
  • Compiler version: xtensa-esp32-elf-gcc (crosstool-NG esp-2021r2-patch3) 8.4.0
  • Operating System: Windows
  • (Windows only) environment type: WSL
  • Using an IDE?: No

Problem Description

If two different threads on the same core attempt an esp_ipc_call() at the same time it is possible for one to be woken early.

Steps to reproduce

In two different threads on the same core,
esp_ipc_call()
esp_ipc_call_blocking()
If the function in esp_ipc_call() takes a while to run or blocks, a second thread calling esp_ipc_call_blocking() can be woken before it should be.

Theoretical analysis:

static void IRAM_ATTR ipc_task(void* arg)
{
...
            // Original code
            if (s_ipc_wait[cpuid] == IPC_WAIT_FOR_START) {
                xSemaphoreGive(s_ipc_ack[cpuid]);
            }
            (*func)(arg);
            if (s_ipc_wait[cpuid] == IPC_WAIT_FOR_END) {
                xSemaphoreGive(s_ipc_ack[cpuid]);
            }
...
}

The first pass in ipc_task() handling the esp_ipc_call() will give the ack semaphore (as s_ipc_wait[]=IPC_WAIT_FOR_START) and then execute (*func)(arg). When the ack semaphore is given, that esp_ipc_call() can exit and the call to esp_ipc_call_blocking() can now update s_ipc_wait[] and s_ipc_ack[]. When (*func)(arg) returns, it checks the new value of s_ipc_wait[] which is now IPC_WAIT_FOR_END and then gives the new s_ipc_ack[] before the the (*func)() from esp_ipc_call_blocking() had a chance to be called. Both of these need to be cached first.

        esp_ipc_wait_t ipc_wait = s_ipc_wait[cpuid];
        SemaphoreHandle_t ack = s_ipc_ack[cpuid];

        if (ipc_wait == IPC_WAIT_FOR_START) {
            xSemaphoreGive(ack);
        }
        (*func)(arg);
        if (ipc_wait == IPC_WAIT_FOR_END) {
            xSemaphoreGive(ack);
        }
@espressif-bot espressif-bot added the Status: Opened Issue is new label Mar 11, 2022
@github-actions github-actions bot changed the title ipc_task() can wake up blocking task early. ipc_task() can wake up blocking task early. (IDFGH-6939) Mar 11, 2022
@espressif-bot espressif-bot added Status: In Progress Work is in progress and removed Status: Opened Issue is new labels Mar 16, 2022
@KonstantinKondrashov
Copy link
Collaborator

Hi @pickaxprograms!
Thank you for noticing this. I have created a fix, it should be merged soon (backports will also be provided).

@espressif-bot espressif-bot added Resolution: NA Issue resolution is unavailable Status: Done Issue is done internally Resolution: Done Issue is done internally and removed Status: In Progress Work is in progress Resolution: NA Issue resolution is unavailable labels Mar 31, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Resolution: Done Issue is done internally Status: Done Issue is done internally
Projects
None yet
Development

No branches or pull requests

3 participants