2024-04-29 13:51:34 -04:00

1109 lines
31 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* \file pros/rtos.h
* \ingroup c-rtos
*
* Contains declarations for the PROS RTOS kernel for use by typical VEX
* programmers.
*
* This file should not be modified by users, since it gets replaced whenever
* a kernel upgrade occurs.
*
* \copyright (c) 2017-2023, Purdue University ACM SIGBots.
* All rights reserved.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* \defgroup c-rtos RTOS Facilities C API
* \note Additional example code for this module can be found in its [Tutorial.](@ref multitasking)
*/
#ifndef _PROS_RTOS_H_
#define _PROS_RTOS_H_
#include <stdbool.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
namespace pros {
#endif
/// \ingroup c-rtos
/// \addtogroup c-rtos
/// @{
/// \name Macros
/// @{
/**
* The highest priority that can be assigned to a task.
*
* A task with this priority will always run if it is available to. Beware of
* deadlocks when using this priority.
*/
#define TASK_PRIORITY_MAX 16
/**
* The lowest priority that can be assigned to a task.
*
* This can cause severe performance problems and is generally not recommended
* that users use this priority.
*/
#define TASK_PRIORITY_MIN 1
/**
* The default task priority, which should be used for most tasks unless you
* have a specific need for a higher or lower priority task.
*
* The default tasks, such as autonomous(), are run with this priority
*/
#define TASK_PRIORITY_DEFAULT 8
/**
* The recommended stack size for a new task.
*
* This stack size is used for the default tasks such as autonomous(). This
* size is 8,192 words, or 32,768 bytes. This should be enough for the majority
* of tasks
*/
#define TASK_STACK_DEPTH_DEFAULT 0x2000
/**
* The minimal stack size for a task.
*
* This equates to 512 words, or 2,048 bytes.
*/
#define TASK_STACK_DEPTH_MIN 0x200
/**
* The maximum number of characters allowed in a task's name.
*/
#define TASK_NAME_MAX_LEN 32
/**
* The maximum timeout value that can be given to, for instance, a mutex grab.
*/
#define TIMEOUT_MAX ((uint32_t)0xffffffffUL)
/// @} Name: Macros
/// \name Typedefs
/// @{
/**
* An opaque type that pontis to a task handle. This is used for referencing a
* task.
*/
typedef void* task_t;
/**
* A pointer to a task's function.
*
* Such a function is called when a task starts, and exiting said function will
* terminate the task.
*/
typedef void (*task_fn_t)(void*);
/// @} Name: Typedefs
/// \name Enumerations
/// @{
/**
* The state of a task.
*/
typedef enum {
E_TASK_STATE_RUNNING = 0, /**< The task is actively executing. */
E_TASK_STATE_READY, /**< The task exists and is available to run, but is not currently running. */
E_TASK_STATE_BLOCKED, /**< The task is delayed or blocked by a mutex, semaphore, or I/O operation. */
E_TASK_STATE_SUSPENDED, /**< The task is supended using task_suspend. */
E_TASK_STATE_DELETED, /**< The task has been deleted using task_delete. */
E_TASK_STATE_INVALID /**< The task handle does not point to a current or past task.*/
} task_state_e_t;
/**
* brief The action to take when a task is notified.
*/
typedef enum {
E_NOTIFY_ACTION_NONE, /**< The tasks notification value will not be touched.*/
E_NOTIFY_ACTION_BITS, /**< The tasks notification value will be bitwise ORed with the new value.*/
E_NOTIFY_ACTION_INCR, /**< The tasks notification value will be incremented by one, effectively using it as a notification counter.*/
E_NOTIFY_ACTION_OWRITE, /**< The tasks notification value will be unconditionally set to the new value.*/
E_NOTIFY_ACTION_NO_OWRITE /**< The tasks notification value will be set to the new value if the task does not already have a pending notification.*/
} notify_action_e_t;
/// @} Name: Enumerations
/// \name Simple enum names
/// @{
#ifdef PROS_USE_SIMPLE_NAMES
#ifdef __cplusplus
#define TASK_STATE_RUNNING pros::E_TASK_STATE_RUNNING
#define TASK_STATE_READY pros::E_TASK_STATE_READY
#define TASK_STATE_BLOCKED pros::E_TASK_STATE_BLOCKED
#define TASK_STATE_SUSPENDED pros::E_TASK_STATE_SUSPENDED
#define TASK_STATE_DELETED pros::E_TASK_STATE_DELETED
#define TASK_STATE_INVALID pros::E_TASK_STATE_INVALID
#define NOTIFY_ACTION_NONE pros::E_NOTIFY_ACTION_NONE
#define NOTIFY_ACTION_BITS pros::E_NOTIFY_ACTION_BITS
#define NOTIFY_ACTION_INCR pros::E_NOTIFY_ACTION_INCR
#define NOTIFY_ACTION_OWRITE pros::E_NOTIFY_ACTION_OWRITE
#define NOTIFY_ACTION_NO_OWRITE pros::E_NOTIFY_ACTION_NO_OWRITE
#else
#define TASK_STATE_RUNNING E_TASK_STATE_RUNNING
#define TASK_STATE_READY E_TASK_STATE_READY
#define TASK_STATE_BLOCKED E_TASK_STATE_BLOCKED
#define TASK_STATE_SUSPENDED E_TASK_STATE_SUSPENDED
#define TASK_STATE_DELETED E_TASK_STATE_DELETED
#define TASK_STATE_INVALID E_TASK_STATE_INVALID
#define NOTIFY_ACTION_NONE E_NOTIFY_ACTION_NONE
#define NOTIFY_ACTION_BITS E_NOTIFY_ACTION_BITS
#define NOTIFY_ACTION_INCR E_NOTIFY_ACTION_INCR
#define NOTIFY_ACTION_OWRITE E_NOTIFY_ACTION_OWRITE
#define NOTIFY_ACTION_NO_OWRITE E_NOTIFY_ACTION_NO_OWRITE
#endif
#endif
/// @} Name: Simple enum names
/// \name Typedefs
/**
* A [mutex.](@ref multitasking)
*
* A mutex is a synchronization object that can be used to protect a shared
* resource from being accessed by multiple tasks at the same time. A mutex can
* be claimed by a task, which will prevent other tasks from claiming it until
* that task releases it.
*/
typedef void* mutex_t;
/// @} Name: Typedefs
/**
* The task handle of the currently running task.
*/
#ifdef __cplusplus
#define CURRENT_TASK ((pros::task_t)NULL)
#else
#define CURRENT_TASK ((task_t)NULL)
#endif
/// @} (add to group: c-rtos)
#ifdef __cplusplus
namespace c {
#endif
/// \ingroup c-rtos
/// \addtogroup c-rtos
/// @{
/**
* Gets the number of milliseconds since PROS initialized.
*
* \return The number of milliseconds since PROS initialized
*
* \b Example
* \code
* void opcontrol() {
* uint32_t now = millis();
* while (true) {
* // Do opcontrol things
* task_delay_until(&now, 2);
* }
* }
* \endcode
*/
uint32_t millis(void);
/**
* Gets the number of microseconds since PROS initialized,
*
* \return The number of microseconds since PROS initialized
*
* \b Example
* \code
* void opcontrol() {
* uint64_t now = micros();
* while (true) {
* // Do opcontrol things
* task_delay_until(&now, 2000);
* }
* }
* \endcode
*/
uint64_t micros(void);
/**
* Creates a new task and add it to the list of tasks that are ready to run.
*
* This function uses the following values of errno when an error state is
* reached:
* ENOMEM - The stack cannot be used as the TCB was not created.
*
* \param function
* Pointer to the task entry function
* \param parameters
* Pointer to memory that will be used as a parameter for the task being
* created. This memory should not typically come from stack, but rather
* from dynamically (i.e., malloc'd) or statically allocated memory.
* \param prio
* The priority at which the task should run.
* TASK_PRIO_DEFAULT plus/minus 1 or 2 is typically used.
* \param stack_depth
* The number of words (i.e. 4 * stack_depth) available on the task's
* stack. TASK_STACK_DEPTH_DEFAULT is typically sufficienct.
* \param name
* A descriptive name for the task. This is mainly used to facilitate
* debugging. The name may be up to 32 characters long.
*
* \return A handle by which the newly created task can be referenced. If an
* error occurred, NULL will be returned and errno can be checked for hints as
* to why task_create failed.
*
* \b Example
* \code
* void my_task_fn(void* param) {
* printf("Hello %s\n", (char*)param);
* // ...
* }
*
* void initialize() {
* task_t my_task = task_create(my_task_fn, (void*)"PROS", TASK_PRIORITY_DEFAULT,
* TASK_STACK_DEPTH_DEFAULT, "My Task");
* }
* \endcode
*/
task_t task_create(task_fn_t function, void* const parameters, uint32_t prio, const uint16_t stack_depth,
const char* const name);
/**
* Removes a task from the RTOS real time kernel's management. The task being
* deleted will be removed from all ready, blocked, suspended and event lists.
*
* Memory dynamically allocated by the task is not automatically freed, and
* should be freed before the task is deleted.
*
* \param task
* The handle of the task to be deleted. Passing NULL will cause the
* calling task to be deleted.
*
* \b Example
* \code
* void my_task_fn(void* param) {
* printf("Hello %s\n", (char*)param);
* // ...
* }
*
* void initialize() {
* task_t my_task = task_create(my_task_fn, (void*)"PROS", TASK_PRIORITY_DEFAULT,
* TASK_STACK_DEPTH_DEFAULT, "My Task");
* // Do other things
* task_delete(my_task);
* }
* \endcode
*/
void task_delete(task_t task);
/**
* Delays the current task for a given number of milliseconds.
*
* This is not the best method to have a task execute code at predefined
* intervals, as the delay time is measured from when the delay is requested.
* To delay cyclically, use task_delay_until().
*
* \param milliseconds
* The number of milliseconds to wait (1000 milliseconds per second)
*
* \b Example
* \code
* void opcontrol() {
* while (true) {
* // Do opcontrol things
* task_delay(2);
* }
* }
* \endcode
*/
void task_delay(const uint32_t milliseconds);
/**
* Delays the current task for a given number of milliseconds.
*
* This is not the best method to have a task execute code at predefined
* intervals, as the delay time is measured from when the delay is requested.
* To delay cyclically, use task_delay_until().
*
* \param milliseconds
* The number of milliseconds to wait (1000 milliseconds per second)
*
* \b Example
* \code
* void opcontrol() {
* while (true) {
* // Do opcontrol things
* delay(2);
* }
* }
* \endcode
*/
void delay(const uint32_t milliseconds);
/**
* Delays the current task until a specified time. This function can be used
* by periodic tasks to ensure a constant execution frequency.
*
* The task will be woken up at the time *prev_time + delta, and *prev_time will
* be updated to reflect the time at which the task will unblock.
*
* \param prev_time
* A pointer to the location storing the setpoint time. This should
* typically be initialized to the return value of millis().
* \param delta
* The number of milliseconds to wait (1000 milliseconds per second)
*
* \b Example
* \code
* void opcontrol() {
* uint32_t now = millis();
* while (true) {
* // Do opcontrol things
* task_delay_until(&now, 2);
* }
* }
* \endcode
*/
void task_delay_until(uint32_t* const prev_time, const uint32_t delta);
/**
* Gets the priority of the specified task.
*
* \param task
* The task to check
*
* \return The priority of the task
*
* \b Example
* \code
* void my_task_fn(void* param) {
* printf("Hello %s\n", (char*)param);
* // ...
* }
*
* void initialize() {
* task_t my_task = task_create(my_task_fn, (void*)"PROS", TASK_PRIORITY_DEFAULT,
* TASK_STACK_DEPTH_DEFAULT, "My Task");
* printf("Task Priority: %d\n", task_get_priority(my_task));
* }
* \endcode
*/
uint32_t task_get_priority(task_t task);
/**
* Sets the priority of the specified task.
*
* If the specified task's state is available to be scheduled (e.g. not blocked)
* and new priority is higher than the currently running task, a context switch
* may occur.
*
* \param task
* The task to set
* \param prio
* The new priority of the task
*
* \b Example
* \code
* void my_task_fn(void* ign) {
* // Do things
* }
*
* void opcontrol() {
* task_t my_task = task_create(my_task_fn, NULL, TASK_PRIORITY_DEFAULT,
* TASK_STACK_DEPTH_DEFAULT, "Example Task");
* task_set_priority(my_task, TASK_PRIORITY_DEFAULT + 1);
* }
* \endcode
*/
void task_set_priority(task_t task, uint32_t prio);
/**
* Gets the state of the specified task.
*
* \param task
* The task to check
*
* \return The state of the task
*
* \b Example
* \code
* void my_task_fn(void* param) {
* printf("Hello %s\n", (char*)param);
* // ...
* }
*
* void initialize() {
* task_t my_task = task_create(my_task_fn, (void*)"PROS", TASK_PRIORITY_DEFAULT,
* TASK_STACK_DEPTH_DEFAULT, "My Task");
* printf("Task's State: %d\n", task_get_state(my_task));
* }
* \endcode
*/
task_state_e_t task_get_state(task_t task);
/**
* Suspends the specified task, making it ineligible to be scheduled.
*
* \param task
* The task to suspend
*
* \b Example
* \code
* mutex_t counter_mutex;
* int counter = 0;
*
* void my_task_fn(void* param) {
* while(true) {
* mutex_take(counter_mutex, TIMEOUT_MAX);// Mutexes are used for protecting shared resources
* counter++;
* mutex_give(counter_mutex);
* pros::delay(10);
* }
* }
*
* void opcontrol() {
* task_t task = task_create(my_task_fn, NULL, TASK_PRIORITY_DEFAULT,;
*
* while(true) {
* mutex_take(counter_mutex, TIMEOUT_MAX);
* if(counter > 100) {
* task_suspepend(task);
* }
* mutex_give(counter_mutex);
* pros::delay(10);
* }
* }
* \endcode
*/
void task_suspend(task_t task);
/**
* Resumes the specified task, making it eligible to be scheduled.
*
* \param task
* The task to resume
*
* \b Example
* \code
* void my_task_fn(void* param) {
* while(true) {
* // Do stuff
* delay(10);
* }
* }
*
* task_t task;
*
* void initialize() {
* task = task_create(my_task_fn, NULL, TASK_PRIORITY_DEFAULT,
* TASK_STACK_DEPTH_DEFAULT, "My Task");
* }
*
* void autonomous() {
* task_resume(task);
*
* // Run autonomous , then suspend the task so it doesn't interfere run
*
* // outside of autonomous or opcontrol
* task_suspend(task);
* }
*
* void opcontrol() {
* task_resume(task);
* // Opctonrol code here
* task_suspend(task);
* }
*
* \endcode
*/
void task_resume(task_t task);
/**
* Gets the number of tasks the kernel is currently managing, including all
* ready, blocked, or suspended tasks. A task that has been deleted, but not yet
* reaped by the idle task will also be included in the count. Tasks recently
* created may take one context switch to be counted.
*
* \return The number of tasks that are currently being managed by the kernel.
*
* \b Example
* \code
* void my_task_fn(void* param) {
* printf("Hello %s\n", (char*)param);
* // ...
* }
*
* void initialize() {
* task_t my_task = task_create(my_task_fn, (void*)"PROS", TASK_PRIORITY_DEFAULT,
* TASK_STACK_DEPTH_DEFAULT, "My Task");
* printf("Number of Running Tasks: %d\n", task_get_count());
* }
* \endcode
*/
uint32_t task_get_count(void);
/**
* Gets the name of the specified task.
*
* \param task
* The task to check
*
* \return A pointer to the name of the task
*
* \b Example
* \code
* void my_task_fn(void* param) {
* printf("Hello %s\n", (char*)param);
* // ...
* }
*
* void initialize() {
* task_t my_task = task_create(my_task_fn, (void*)"PROS", TASK_PRIORITY_DEFAULT,
* TASK_STACK_DEPTH_DEFAULT, "My Task");
* printf("Task Name: %d\n", task_get_name(my_task));
* }
* \endcode
*/
char* task_get_name(task_t task);
/**
* Gets a task handle from the specified name
*
* The operation takes a relatively long time and should be used sparingly.
*
* \param name
* The name to query
*
* \return A task handle with a matching name, or NULL if none were found.
*
* \b Example
* \code
* void my_task_fn(void* param) {
* printf("Hello %s\n", (char*)param);
* // ...
* }
*
* void initialize() {
* task_t my_task = task_create(my_task_fn, (void*)"PROS", TASK_PRIORITY_DEFAULT,
* TASK_STACK_DEPTH_DEFAULT, "My Task");
* // Do other things
* task_delete(task_get_by_name("My Task"));
* }
* \endcode
*/
task_t task_get_by_name(const char* name);
/**
* Get the currently running task handle. This could be useful if a task
* wants to tell another task about itself.
*
* \return The currently running task handle.
*
* \b Example
* \code
* void my_task_fn(void* param) {
* task_t this_task = task_get_current();
* if (task_get_state(this_take) == E_TASK_STATE_RUNNING) {
* printf("This task is currently running\n");
* }
* // ...
* }
*
* void initialize() {
* task_t my_task = task_create(my_task_fn, (void*)"PROS", TASK_PRIORITY_DEFAULT,
* TASK_STACK_DEPTH_DEFAULT, "My Task");
* }
* \endcode
*/
task_t task_get_current();
/**
* Sends a simple notification to task and increments the notification counter.
*
* \param task
* The task to notify
*
* \return Always returns true.
*
* \b Example
* \code
* void my_task_fn(void* ign) {
* while(task_notify_take(true) == 0) {
* // Code while waiting
* }
* puts("I was unblocked!");
* }
*
* void opcontrol() {
* task_t my_task = task_create(my_task_fn, NULL, TASK_PRIORITY_DEFAULT,
* TASK_STACK_DEPTH_DEFAULT, "Notify me! Task");
* while(true) {
* if(controller_get_digital(CONTROLLER_MASTER, DIGITAL_L1)) {
* task_notify(my_task);
* }
* }
* }
* \endcode
*/
uint32_t task_notify(task_t task);
/**
*
* Utilizes task notifications to wait until specified task is complete and deleted,
* then continues to execute the program. Analogous to std::thread::join in C++.
*
* \param task
* The handle of the task to wait on.
*
* \return void
*
* \b Example
* \code
* void my_task_fn(void* ign) {
* lcd_print(1, "%s running", task_get_name(NULL));
* task_delay(1000);
* lcd_print(2, "End of %s", task_get_name(NULL));
* }
*
* void opcontrol() {
* task_t my_task = task_create(my_task_fn, NULL, TASK_PRIORITY_DEFAULT,
* TASK_STACK_DEPTH_DEFAULT, "Example Task");
* lcd_set_text(0, "Running task.");
* task_join(my_task);
* lcd_set_text(3, "Task completed.");
* }
* \endcode
*/
void task_join(task_t task);
/**
* Sends a notification to a task, optionally performing some action. Will also
* retrieve the value of the notification in the target task before modifying
* the notification value.
*
* \param task
* The task to notify
* \param value
* The value used in performing the action
* \param action
* An action to optionally perform on the receiving task's notification
* value
* \param prev_value
* A pointer to store the previous value of the target task's
* notification, may be NULL
*
* \return Dependent on the notification action.
* For NOTIFY_ACTION_NO_WRITE: return 0 if the value could be written without
* needing to overwrite, 1 otherwise.
* For all other NOTIFY_ACTION values: always return 0
*
* \b Example
* \code
* void my_task_fn(void* param) {
* while(true) {
* // Wait until we have been notified 20 times before running the code
* if(task_notify_take(false, TIMEOUT_MAX) == 20) {
* // ... Code to do stuff here ...
*
* // Reset the notification counter
* task_notify_take(true, TIMEOUT_MAX);
* }
* delay(10);
* }
* }
*
* void opcontrol() {
* task_t task = task_create(my_task_fn, NULL, TASK_PRIORITY_DEFAULT,
* TASK_STACK_DEPTH_DEFAULT, "My Task");
*
* int count = 0;
*
* while(true) {
* if(controller_get_digital(CONTROLLER_MASTER, DIGITAL_L1)) {
* task_notify_ext(task, 1, NOTIFY_ACTION_INCREMENT, &count);
* }
*
* delay(20);
* }
* }
* \endcode
*/
uint32_t task_notify_ext(task_t task, uint32_t value, notify_action_e_t action, uint32_t* prev_value);
/**
* Waits for a notification to be nonzero.
*
* See https://pros.cs.purdue.edu/v5/tutorials/topical/notifications.html for
* details.
*
* \param clear_on_exit
* If true (1), then the notification value is cleared.
* If false (0), then the notification value is decremented.
* \param timeout
* Specifies the amount of time to be spent waiting for a notification
* to occur.
*
* \return The value of the task's notification value before it is decremented
* or cleared
*
* \b Example
* \code
* void my_task_fn(void* ign) {
* task_t current_task = task_get_current();
* while(task_notify_take(current_task, true, TIMEOUT_MAX)) {
* puts("I was unblocked!");
* }
* }
*
* void opcontrol() {
* task_t my_task = task_create(my_task_fn, NULL, TASK_PRIORITY_DEFAULT,
* TASK_STACK_DEPTH_DEFAULT, "Notify me! Task");
* while(true) {
* if(controller_get_digital(CONTROLLER_MASTER, DIGITAL_L1)) {
* task_notify(my_task);
* }
* }
* }
* \endcode
*/
uint32_t task_notify_take(bool clear_on_exit, uint32_t timeout);
/**
* Clears the notification for a task.
*
* See https://pros.cs.purdue.edu/v5/tutorials/topical/notifications.html for
* details.
*
* \param task
* The task to clear
*
* \return False if there was not a notification waiting, true if there was
*
* \b Example
* \code
* void my_task_fn(void* param) {
* task_t task = task_get_current();
* while(true) {
* printf("Waiting for notification...\n");
* printf("Got a notification: %d\n", task_notify_take(task, false, TIMEOUT_MAX));
*
* task_notify_clear(task);
* delay(10):
* }
* }
*
* void opcontrol() {
* task_t task = task_create(my_task_fn, NULL, TASK_PRIORITY_DEFAULT,
* TASK_STACK_DEPTH_DEFAULT, "My Task");
*
* while(true) {
* if(controller_get_digital(CONTROLLER_MASTER, DIGITAL_L1)) {
* task_notify(task);
* }
* delay(10);
* }
* }
* \endcode
*/
bool task_notify_clear(task_t task);
/**
* Creates a mutex.
*
* See https://pros.cs.purdue.edu/v5/tutorials/topical/multitasking.html#mutexes
* for details.
*
* \return A handle to a newly created mutex. If an error occurred, NULL will be
* returned and errno can be checked for hints as to why mutex_create failed.
*
* \b Example
* \code
* // Global variables for the robot's odometry, which the rest of the robot's
* // subsystems will utilize
* double odom_x = 0.0;
* double odom_y = 0.0;
* double odom_heading = 0.0;
*
* // This mutex protects the odometry data. Whenever we read or write to the
* // odometry data, we should make copies into the local variables, and read
* // all 3 values at once to avoid errors.
* mutex_t odom_mutex;
*
* void odom_task(void* param) {
* while(true) {
* // First we fetch the odom coordinates from the previous iteration of the
* // odometry task. These are put into local variables so that we can
* // keep the size of the critical section as small as possible. This lets
* // other tasks that need to use the odometry data run until we need to
* // update it again.
* mutex_take(odom_mutex, MAX_DELAY);
* double x_old = odom_x;
* double y_old = odom_y;
* double heading_old = odom_heading;
* mutex_give(odom_mutex);
*
* double x_new = 0.0;
* double y_new = 0.0;
* double heading_new = 0.0;
*
* // --- Calculate new pose for the robot here ---
*
* // Now that we have the new pose, we can update the global variables
* mutex_take(odom_mutex, MAX_DELAY);
* odom_x = x_new;
* odom_y = y_new;
* odom_heading = heading_new;
* mutex_give(odom_mutex);
*
* delay(10);
* }
* }
*
* void chassis_task(void* param) {
* while(true) {
* // Here we copy the current odom values into local variables so that
* // we can use them without worrying about the odometry task changing say,
* // the y value right after we've read the x. This ensures our values are
* // sound.
* mutex_take(odom_mutex, MAX_DELAY);
* double current_x = odom_x;
* double current_y = odom_y;
* double current_heading = odom_heading;
* mutex_give(odom_mutex);
*
* // ---- Move the robot using the current locations goes here ----
*
* delay(10);
* }
* }
*
* void initialize() {
* odom_mutex = mutex_create();
*
* task_create(odom_task, NULL, TASK_PRIORITY_DEFAULT, TASK_STACK_DEPTH_DEFAULT, "Odometry Task");
* task_create(chassis_task, NULL, TASK_PRIORITY_DEFAULT, TASK_STACK_DEPTH_DEFAULT, "Chassis Task");
* }
* \endcode
*/
mutex_t mutex_create(void);
/**
* Takes and locks a mutex, waiting for up to a certain number of milliseconds
* before timing out.
*
* See https://pros.cs.purdue.edu/v5/tutorials/topical/multitasking.html#mutexes
* for details.
*
* \param mutex
* Mutex to attempt to lock.
* \param timeout
* Time to wait before the mutex becomes available. A timeout of 0 can
* be used to poll the mutex. TIMEOUT_MAX can be used to block
* indefinitely.
*
* \return True if the mutex was successfully taken, false otherwise. If false
* is returned, then errno is set with a hint about why the the mutex
* couldn't be taken.
*
* \b Example
* \code
* // Global variables for the robot's odometry, which the rest of the robot's
* // subsystems will utilize
* double odom_x = 0.0;
* double odom_y = 0.0;
* double odom_heading = 0.0;
*
* // This mutex protects the odometry data. Whenever we read or write to the
* // odometry data, we should make copies into the local variables, and read
* // all 3 values at once to avoid errors.
* mutex_t odom_mutex;
*
* void odom_task(void* param) {
* while(true) {
* // First we fetch the odom coordinates from the previous iteration of the
* // odometry task. These are put into local variables so that we can
* // keep the size of the critical section as small as possible. This lets
* // other tasks that need to use the odometry data run until we need to
* // update it again.
* mutex_take(odom_mutex, MAX_DELAY);
* double x_old = odom_x;
* double y_old = odom_y;
* double heading_old = odom_heading;
* mutex_give(odom_mutex);
*
* double x_new = 0.0;
* double y_new = 0.0;
* double heading_new = 0.0;
*
* // --- Calculate new pose for the robot here ---
*
* // Now that we have the new pose, we can update the global variables
* mutex_take(odom_mutex, MAX_DELAY);
* odom_x = x_new;
* odom_y = y_new;
* odom_heading = heading_new;
* mutex_give(odom_mutex);
*
* delay(10);
* }
* }
*
* void chassis_task(void* param) {
* while(true) {
* // Here we copy the current odom values into local variables so that
* // we can use them without worrying about the odometry task changing say,
* // the y value right after we've read the x. This ensures our values are
* // sound.
* mutex_take(odom_mutex, MAX_DELAY);
* double current_x = odom_x;
* double current_y = odom_y;
* double current_heading = odom_heading;
* mutex_give(odom_mutex);
*
* // ---- Move the robot using the current locations goes here ----
*
* delay(10);
* }
* }
*
* void initialize() {
* odom_mutex = mutex_create();
*
* task_create(odom_task, NULL, TASK_PRIORITY_DEFAULT, TASK_STACK_DEPTH_DEFAULT, "Odometry Task");
* task_create(chassis_task, NULL, TASK_PRIORITY_DEFAULT, TASK_STACK_DEPTH_DEFAULT, "Chassis Task");
* }
* \endcode
*/
bool mutex_take(mutex_t mutex, uint32_t timeout);
/**
* Unlocks a mutex.
*
* See https://pros.cs.purdue.edu/v5/tutorials/topical/multitasking.html#mutexes
* for details.
*
* \param mutex
* Mutex to unlock.
*
* \return True if the mutex was successfully returned, false otherwise. If
* false is returned, then errno is set with a hint about why the mutex
* couldn't be returned.
*
* \b Example
* \code
* // Global variables for the robot's odometry, which the rest of the robot's
* // subsystems will utilize
* double odom_x = 0.0;
* double odom_y = 0.0;
* double odom_heading = 0.0;
*
* // This mutex protects the odometry data. Whenever we read or write to the
* // odometry data, we should make copies into the local variables, and read
* // all 3 values at once to avoid errors.
* mutex_t odom_mutex;
*
* void odom_task(void* param) {
* while(true) {
* // First we fetch the odom coordinates from the previous iteration of the
* // odometry task. These are put into local variables so that we can
* // keep the size of the critical section as small as possible. This lets
* // other tasks that need to use the odometry data run until we need to
* // update it again.
* mutex_take(odom_mutex, MAX_DELAY);
* double x_old = odom_x;
* double y_old = odom_y;
* double heading_old = odom_heading;
* mutex_give(odom_mutex);
*
* double x_new = 0.0;
* double y_new = 0.0;
* double heading_new = 0.0;
*
* // --- Calculate new pose for the robot here ---
*
* // Now that we have the new pose, we can update the global variables
* mutex_take(odom_mutex, MAX_DELAY);
* odom_x = x_new;
* odom_y = y_new;
* odom_heading = heading_new;
* mutex_give(odom_mutex);
*
* delay(10);
* }
* }
*
* void chassis_task(void* param) {
* while(true) {
* // Here we copy the current odom values into local variables so that
* // we can use them without worrying about the odometry task changing say,
* // the y value right after we've read the x. This ensures our values are
* // sound.
* mutex_take(odom_mutex, MAX_DELAY);
* double current_x = odom_x;
* double current_y = odom_y;
* double current_heading = odom_heading;
* mutex_give(odom_mutex);
*
* // ---- Move the robot using the current locations goes here ----
*
* delay(10);
* }
* }
*
* void initialize() {
* odom_mutex = mutex_create();
*
* task_create(odom_task, NULL, TASK_PRIORITY_DEFAULT, TASK_STACK_DEPTH_DEFAULT, "Odometry Task");
* task_create(chassis_task, NULL, TASK_PRIORITY_DEFAULT, TASK_STACK_DEPTH_DEFAULT, "Chassis Task");
* }
* \endcode
*/
bool mutex_give(mutex_t mutex);
/**
* Deletes a mutex
*
* \param mutex
* Mutex to unlock.
*
* \b Example
* \code
* mutex_t mutex = mutex_create();
* // Acquire the mutex; other tasks using this command will wait until the mutex is released
* // timeout can specify the maximum time to wait, or MAX_DELAY to wait forever
* // If the timeout expires, "false" will be returned, otherwise "true"
* mutex_take(mutex, MAX_DELAY);
* // do some work
* // Release the mutex for other tasks
* mutex_give(mutex);
* // Delete the mutex
* mutex_delete(mutex);
* \endcode
*/
void mutex_delete(mutex_t mutex);
/// @} Add to group: c-rtos
#ifdef __cplusplus
} // namespace c
} // namespace pros
}
#endif
#endif // _PROS_RTOS_H_