esp-idf/components/driver/gptimer
morris 81a641d437 feat(gptimer): add gptimer linker.lf to control the placement of driver and hal
since the linker placement of the hal functions are controlled by the Kconfig in the driver component,
we should create the linker.lf in the driver component, not in the hal component.
2023-07-05 12:09:00 +08:00
..
include/driver gptimer: fix race condition between start and stop 2023-03-10 23:27:29 +08:00
gptimer_etm.c etm: update etm event task new API 2022-12-07 15:43:20 +08:00
gptimer_priv.c gptimer: fix race condition between start and stop 2023-03-10 23:27:29 +08:00
gptimer_priv.h gptimer: fix race condition between start and stop 2023-03-10 23:27:29 +08:00
gptimer.c driver: minor code clean up to pass coverity scan test 2023-06-10 16:21:12 +08:00
linker.lf feat(gptimer): add gptimer linker.lf to control the placement of driver and hal 2023-07-05 12:09:00 +08:00
README.md gptimer: fix race condition between start and stop 2023-03-10 23:27:29 +08:00

GPTimer Driver Design

State Transition

State transition is achieved by using the primitives provided by <stdatomic.h>.

stateDiagram-v2
    [*] --> init: gptimer_new_timer
    init --> enable: gptimer_enable
    enable --> init: gptimer_disable
    enable --> run: gptimer_start*
    run --> enable: gptimer_stop*
    init --> [*]: gptimer_del_timer

Other functions won't change the driver state. The functions above labeled with * are allowed to be used in the interrupt context.

Concurrency

There might be race conditions when the user calls the APIs from a thread and interrupt at the same time. e.g. a Task is just running the gptimer_start, and suddenly an interrupt occurs, where the user calls gptimer_stop for the same timer handle. Which is possible to make a "stopped" timer continue to run if the interrupt is returned before the Task.

stateDiagram-v2
    state Race-Condition {
        Thread --> gptimer_start
        state gptimer_start {
            state is_enabled <<choice>>
            [*] --> is_enabled: Enabled?
            is_enabled --> run_wait: yes
            is_enabled --> [*] : no
            run_wait --> run: call HAL/LL functions to start timer
            }
        --
        Interrupt --> gptimer_stop
        state gptimer_stop {
            state is_running <<choice>>
            [*] --> is_running: Running?
            is_running --> enable_wait: yes
            is_running --> [*] : no
            enable_wait --> enable: call HAL/LL functions to stop timer
        }
    }

By introducing a "middle" state like run_wait and enable_wait, we make sure that the timer is in a safe state before we start/stop it. And if the state is invalid, it can return an error code to the user.