circuitpython/supervisor/background_callback.h
Scott Shawcroft d2860b58b0
Check background pending before sleep
There is a race between when we run background tasks and when we
sleep. If an interrupt happens between the two, then we may delay
executing the background task. On some ports we checked this for
TinyUSB already. On iMX RT, we didn't which caused USB issues.
This PR makes it more generic for all background tasks including
USB.

Fixes #5086 and maybe others.
2021-08-19 12:18:13 -07:00

94 lines
3.8 KiB
C

/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2020 Jeff Epler for Adafruit Industries
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef CIRCUITPY_INCLUDED_SUPERVISOR_BACKGROUND_CALLBACK_H
#define CIRCUITPY_INCLUDED_SUPERVISOR_BACKGROUND_CALLBACK_H
#include <stdbool.h>
/** Background callbacks are a linked list of tasks to call in the background.
*
* Include a member of type `background_callback_t` inside an object
* which needs to queue up background work, and zero-initialize it.
*
* To schedule the work, use background_callback_add, with fun as the
* function to call and data pointing to the object itself.
*
* Next time run_background_tasks_if_tick is called, the callback will
* be run and removed from the linked list.
*
* Queueing a task that is already queued does nothing. Unconditionally
* re-queueing it from its own background task will cause it to run during the
* very next background-tasks invocation, leading to a CircuitPython freeze, so
* don't do that.
*
* background_callback_add can be called from interrupt context.
*/
typedef void (*background_callback_fun)(void *data);
typedef struct background_callback {
background_callback_fun fun;
void *data;
struct background_callback *next;
struct background_callback *prev;
} background_callback_t;
/* Add a background callback for which 'fun' and 'data' were previously set */
void background_callback_add_core(background_callback_t *cb);
/* Add a background callback to the given function with the given data. When
* the callback involves an object on the GC heap, the 'data' must be a pointer
* to that object itself, not an internal pointer. Otherwise, it can be the
* case that no other references to the object itself survive, and the object
* becomes garbage collected while an outstanding background callback still
* exists.
*/
void background_callback_add(background_callback_t *cb, background_callback_fun fun, void *data);
/* Run all background callbacks. Normally, this is done by the supervisor
* whenever the list is non-empty */
void background_callback_run_all(void);
/* True when a background callback is pending. Helpful for checking background state when
* interrupts are disabled. */
bool background_callback_pending(void);
/* During soft reset, remove all pending callbacks and clear the critical section flag */
void background_callback_reset(void);
/* Sometimes background callbacks must be blocked. Use these functions to
* bracket the section of code where this is the case. These calls nest, and
* begins must be balanced with ends.
*/
void background_callback_begin_critical_section(void);
void background_callback_end_critical_section(void);
/*
* Background callbacks may stop objects from being collected
*/
void background_callback_gc_collect(void);
#endif