Threads abstraction (#3423)

* Threads and synchronization abstractions for paparazzi.
* Test module for the threads abstraction.
* Replace old mutexes by the new ones.

---------

Co-authored-by: Fabien-B <Fabien-B@github.com>
This commit is contained in:
Fabien-B
2025-02-03 14:05:16 +01:00
committed by GitHub
parent e851238972
commit 02b08b08c4
22 changed files with 674 additions and 93 deletions
+1 -1
View File
@@ -9,7 +9,7 @@
</description>
</doc>
<dep>
<depends>mcu,math,state_interface,@actuators|@intermcu,settings|@no_settings,@datalink,@telemetry</depends>
<depends>mcu,threads,math,state_interface,@actuators|@intermcu,settings|@no_settings,@datalink,@telemetry</depends>
<provides>core</provides>
</dep>
<header>
+37
View File
@@ -0,0 +1,37 @@
<!DOCTYPE module SYSTEM "module.dtd">
<module name="thd_test" dir="sensors" task="sensors">
<doc>
<description>
A module to test threads functionnalities, like threads, mutexes, semaphores and so on.
A thread follow the syracuse suit, cadenced by the main ap thread from a periodic
function signaling a binary semaphore.
The counter thread and the AP thread exchange data protected by a mutex.
The counter thread is joined by the AP thread when the suit reach 1.
The counter thread can be restarted by setting the initial value from the settings: Threads test/thd_test_start_value
</description>
</doc>
<settings>
<dl_settings>
<dl_settings NAME="Threads test">
<dl_setting var="thd_test_start_value" min="1" step="1" max="100" header="modules/sensors/thd_test" handler="syracuse_restart"/>
</dl_settings>
</dl_settings>
</settings>
<dep>
<depends>threads</depends>
</dep>
<header>
<file name="thd_test.h"/>
</header>
<init fun="thd_test_init()"/>
<periodic fun="thd_test_periodic()" freq="5"/>
<makefile>
<file name="thd_test.c"/>
<test>
<define name="USE_UART0"/>
<define name="DOWNLINK_TRANSPORT" value="pprz_tp"/>
<define name="DOWNLINK_DEVICE" value="uart0"/>
</test>
</makefile>
</module>
+20
View File
@@ -0,0 +1,20 @@
<!DOCTYPE module SYSTEM "module.dtd">
<module name="threads" dir="core" task="core">
<doc>
<description>
Provide threads and synchronization primitives.
</description>
</doc>
<dep>
</dep>
<header>
<file name="threads.h"/>
</header>
<makefile>
<file_arch name="threads_arch.c"/>
<test>
</test>
</makefile>
</module>
@@ -0,0 +1,76 @@
/*
* Copyright (C) 2025 The Paparazzi Team
*
* This file is part of paparazzi.
*
* Paparazzi is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* See LICENSE file for the full license version, or see http://www.gnu.org/licenses/
*/
#include "modules/core/threads.h"
#include "modules/core/threads_arch.h"
#include "stdbool.h"
int pprz_mtx_init(pprz_mutex_t* mtx) {
chMtxObjectInit(&mtx->mtx);
return 0;
}
int pprz_mtx_lock(pprz_mutex_t* mtx) {
chMtxLock(&mtx->mtx);
return 0;
}
int pprz_mtx_trylock(pprz_mutex_t* mtx) {
return chMtxTryLock(&mtx->mtx) ? 0 : -1;
}
int pprz_mtx_unlock(pprz_mutex_t* mtx) {
chMtxUnlock(&mtx->mtx);
return 0;
}
void pprz_bsem_init(pprz_bsem_t* bsem, bool taken) {
chBSemObjectInit(&bsem->bsem, taken);
}
void pprz_bsem_wait(pprz_bsem_t* bsem) {
chBSemWait(&bsem->bsem);
}
void pprz_bsem_signal(pprz_bsem_t* bsem) {
chBSemSignal(&bsem->bsem);
}
int pprz_thread_create(pprz_thread_t* thread, size_t size, const char *name, uint8_t prio, void (*pf)(void *), void* arg) {
*thread = chThdCreateFromHeap(NULL, THD_WORKING_AREA_SIZE(size), name, prio, pf, arg);
if(*thread == NULL) {
return -1;
}
return 0;
}
void pprz_thread_exit(void *retval) {
chThdExit((msg_t)retval);
}
int pprz_thread_join(pprz_thread_t* thread, void** retval) {
*retval = (void*)chThdWait(*thread);
return 0;
}
int pprz_thread_tryjoin(pprz_thread_t* thread, void** retval) {
if(chThdTerminatedX(*thread)) {
return pprz_thread_join(thread, retval);
}
return -1;
}
@@ -0,0 +1,31 @@
/*
* Copyright (C) 2025 The Paparazzi Team
*
* This file is part of paparazzi.
*
* Paparazzi is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* See LICENSE file for the full license version, or see http://www.gnu.org/licenses/
*/
#pragma once
#include <ch.h>
#define PPRZ_NORMAL_PRIO NORMALPRIO
#define THREADS_ATTRIBUTES
struct pprzMutex {
mutex_t mtx;
};
struct pprzBSem {
binary_semaphore_t bsem;
};
typedef thread_t* pprz_thread_t;
@@ -0,0 +1,96 @@
/*
* Copyright (C) 2025 The Paparazzi Team
*
* This file is part of paparazzi.
*
* Paparazzi is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* See LICENSE file for the full license version, or see http://www.gnu.org/licenses/
*/
#include "modules/core/threads.h"
#include "modules/core/threads_arch.h"
#include "stdbool.h"
#include <semaphore.h>
#include <stdlib.h>
int pprz_mtx_init(pprz_mutex_t* mtx) {
return pthread_mutex_init(&mtx->mtx, NULL);
}
int pprz_mtx_lock(pprz_mutex_t* mtx) {
return pthread_mutex_lock(&mtx->mtx);
}
int pprz_mtx_trylock(pprz_mutex_t* mtx) {
return pthread_mutex_trylock(&mtx->mtx);
}
int pprz_mtx_unlock(pprz_mutex_t* mtx) {
return pthread_mutex_unlock(&mtx->mtx);
}
void pprz_bsem_init(pprz_bsem_t* bsem, bool taken) {
pthread_mutex_init(&bsem->mtx, NULL);
sem_init(&bsem->sem, 0, taken ? 0: 1);
}
void pprz_bsem_wait(pprz_bsem_t* bsem) {
sem_wait(&bsem->sem);
}
void pprz_bsem_signal(pprz_bsem_t* bsem) {
int val;
pthread_mutex_lock(&bsem->mtx);
if(!sem_getvalue(&bsem->sem, &val)) {
if(val < 1) {
sem_post(&bsem->sem);
}
}
pthread_mutex_unlock(&bsem->mtx);
}
// stub function argument: the thread function to run with its argument.
struct stub_arg
{
void (*func)(void*);
void* arg;
};
// stub function, ran in a new thread
static void* stub(void* arg) {
struct stub_arg* sa = (struct stub_arg*) arg;
sa->func(sa->arg);
free(sa);
return NULL;
}
int pprz_thread_create(pprz_thread_t* thread, size_t size, const char *name, uint8_t prio, void (*func)(void*), void* arg) {
(void)size;
(void)prio;
struct stub_arg *sa = malloc(sizeof(struct stub_arg)); // allocate a stub arg. Must be allocated on the heap
sa->func = func;
sa->arg = arg;
int ret = pthread_create(thread, NULL, stub, (void*)sa); // launch the stub in a new thread
if(ret) {return ret;}
return pthread_setname_np(*thread, name);
}
void pprz_thread_exit(void *retval) {
pthread_exit(retval);
}
int pprz_thread_join(pprz_thread_t* thread, void** retval) {
return pthread_join(*thread, retval);
}
int pprz_thread_tryjoin(pprz_thread_t* thread, void** retval) {
return pthread_tryjoin_np(*thread, retval);
}
@@ -0,0 +1,33 @@
/*
* Copyright (C) 2025 The Paparazzi Team
*
* This file is part of paparazzi.
*
* Paparazzi is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* See LICENSE file for the full license version, or see http://www.gnu.org/licenses/
*/
#pragma once
#include <pthread.h>
#include <semaphore.h>
#define PPRZ_NORMAL_PRIO 128
#define THREADS_ATTRIBUTES
struct pprzMutex {
pthread_mutex_t mtx;
};
struct pprzBSem {
pthread_mutex_t mtx;
sem_t sem;
};
typedef pthread_t pprz_thread_t;
+1
View File
@@ -0,0 +1 @@
../../../linux/modules/core/threads_arch.c
+1
View File
@@ -0,0 +1 @@
../../../linux/modules/core/threads_arch.h
@@ -0,0 +1,56 @@
/*
* Copyright (C) 2025 The Paparazzi Team
*
* This file is part of paparazzi.
*
* Paparazzi is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* See LICENSE file for the full license version, or see http://www.gnu.org/licenses/
*/
#include "modules/core/threads.h"
#include "modules/core/threads_arch.h"
#include "stdbool.h"
int pprz_mtx_init(pprz_mutex_t* mtx) {
(void)mtx;
return 0;
}
int pprz_mtx_lock(pprz_mutex_t* mtx) {
(void)mtx;
return 0;
}
int pprz_mtx_trylock(pprz_mutex_t* mtx) {
(void)mtx;
(void)mtx;
return 0;
}
int pprz_mtx_unlock(pprz_mutex_t* mtx) {
(void)mtx;
return 0;
}
void pprz_bsem_init(pprz_bsem_t* bsem, bool taken) {
bsem->value = taken ? 0: 1;
}
void pprz_bsem_wait(pprz_bsem_t* bsem) {
while (bsem->value == 0)
{
// active wait
asm("NOP");
}
bsem->value = 0;
}
void pprz_bsem_signal(pprz_bsem_t* bsem) {
bsem->value = 1;
}
@@ -0,0 +1,29 @@
/*
* Copyright (C) 2025 The Paparazzi Team
*
* This file is part of paparazzi.
*
* Paparazzi is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* See LICENSE file for the full license version, or see http://www.gnu.org/licenses/
*/
#pragma once
#define PPRZ_NORMAL_PRIO NORMALPRIO
#define THREADS_ATTRIBUTES __attribute__((error("Threads cannot be used in STM32 bare metal ARCH.")))
struct pprzMutex {
int dummy; // avoid warning: empty declaration
};
struct pprzBSem {
volatile int value;
};
typedef int pprz_thread_t;
+2 -2
View File
@@ -186,7 +186,7 @@ void vPoint(float fPlaneEast, float fPlaneNorth, float fPlaneAltitude,
if (cam_mode == CAM_MODE_STABILIZED || cam_mode == CAM_MODE_RC) {
// protect acces to fbw state
PPRZ_MUTEX_LOCK(fbw_state_mtx);
pprz_mtx_lock(&fbw_state_mtx);
/*######################################## TILT CONTROL INPUT #############################################*/
#ifdef CAM_TILT_NEUTRAL
@@ -266,7 +266,7 @@ void vPoint(float fPlaneEast, float fPlaneNorth, float fPlaneAltitude,
#endif //#ifdef CAM_PAN_NEUTRAL
/*######################################## END OF PAN CONTROL INPUT #############################################*/
PPRZ_MUTEX_UNLOCK(fbw_state_mtx);
pprz_mtx_unlock(&fbw_state_mtx);
// Bound Pan and Tilt angles.
if (cam_theta > RadOfDeg(CAM_TILT_MAX)) {
+75
View File
@@ -0,0 +1,75 @@
/*
* Copyright (C) 2025 The Paparazzi Team
*
* This file is part of paparazzi.
*
* Paparazzi is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* See LICENSE file for the full license version, or see http://www.gnu.org/licenses/
*/
#pragma once
#include "stdbool.h"
#include "modules/core/threads_arch.h"
#include "stdint.h"
#include <stddef.h>
typedef struct pprzMutex pprz_mutex_t;
typedef struct pprzBSem pprz_bsem_t;
int pprz_mtx_init(pprz_mutex_t* mtx);
int pprz_mtx_lock(pprz_mutex_t* mtx);
/**
* @brief Performs a nonblocking lock on the mutex.
* @return 0 if successful
*/
int pprz_mtx_trylock(pprz_mutex_t* mtx);
int pprz_mtx_unlock(pprz_mutex_t* mtx);
void pprz_bsem_init(pprz_bsem_t* bsem, bool taken);
void pprz_bsem_wait(pprz_bsem_t* bsem);
void pprz_bsem_signal(pprz_bsem_t* bsem);
/**
* @brief Creates a new thread whose stack is dynamically allocated.
* @param[in] size size of the stack. Unused on linux at the moment.
* @param[in] prio the priority level for the new thread. Unused on linux at the moment. NORMALPRIO
* @param[in] pf the thread function
* @param[in] arg an argument passed to the thread function. It can be
* @p NULL.
* @param[out] thread pointer to a pprz_thread_t that will be writen with the handle to the thread
* @return Return 0 on success, or a platform dependant error code.
*
* @api
*/
int pprz_thread_create(pprz_thread_t* thread, size_t size, const char *name, uint8_t prio, void (*func)(void*), void* arg) THREADS_ATTRIBUTES;
/**
* @brief Exit the current thread
* @param[out] retval The thread's return value
*/
void pprz_thread_exit(void *retval) THREADS_ATTRIBUTES;
/**
* @brief Wait for the thread to terminate.
* @note Warning ! This is a blocking function, do not use it in the main AP thread !
* @param[in] thread The thread to be joined
* @param[out] retval The thread's return value will be written to the variable pointed by retval
*/
int pprz_thread_join(pprz_thread_t* thread, void** retval) THREADS_ATTRIBUTES;
/**
* @brief Performs a nonblocking join with the thread.
* @param[in] thread The thread to be joined
* @param[out] retval The thread's return value will be written to the variable pointed by retval.
* must not be used if this fuction did not returned 0.
* @return 0 if the thread successfully joined.
*/
int pprz_thread_tryjoin(pprz_thread_t* thread, void** retval) THREADS_ATTRIBUTES;
@@ -30,12 +30,12 @@
#include "mcu_periph/uart.h"
#include <string.h>
#include "led.h"
#include "modules/core/threads.h"
struct syslink_dl syslink;
/** Protect syslink TX with Mutex when using RTOS */
#include "pprz_mutex.h"
PPRZ_MUTEX(syslink_tx_mtx);
pprz_mutex_t syslink_tx_mtx;
/** Send a syslink message
*/
@@ -220,7 +220,7 @@ static void handle_raw(syslink_message_t *msg)
// send next raw message if fifo is not empty
if (syslink.tx_extract_idx != syslink.tx_insert_idx) {
PPRZ_MUTEX_LOCK(syslink_tx_mtx);
pprz_mtx_lock(&syslink_tx_mtx);
syslink_message_t msg_raw;
msg_raw.type = SYSLINK_RADIO_RAW;
memcpy(&msg_raw.length, &syslink.msg_tx[syslink.tx_extract_idx], sizeof(crtp_message_t));
@@ -230,7 +230,7 @@ static void handle_raw(syslink_message_t *msg)
if (syslink.tx_extract_idx == CRTP_BUF_LEN) {
syslink.tx_extract_idx = 0;
}
PPRZ_MUTEX_UNLOCK(syslink_tx_mtx);
pprz_mtx_unlock(&syslink_tx_mtx);
}
}
@@ -293,7 +293,7 @@ static int syslink_check_free_space(struct syslink_dl *s, long *fd UNUSED, uint1
}
int space = CRTP_MAX_DATA_SIZE * (slots - 1);
if (space >= len) {
PPRZ_MUTEX_LOCK(syslink_tx_mtx);
pprz_mtx_lock(&syslink_tx_mtx);
return space;
}
return 0;
@@ -342,7 +342,7 @@ static void syslink_put_byte(struct syslink_dl *s, long fd, const uint8_t b)
// send_message is not needed as messages are stored in a fifo
static void syslink_send_message(struct syslink_dl *s UNUSED, long fd UNUSED)
{
PPRZ_MUTEX_UNLOCK(syslink_tx_mtx); // release mutex
pprz_mtx_lock(&syslink_tx_mtx); // release mutex
}
static uint8_t syslink_getch(struct syslink_dl *s)
@@ -390,7 +390,7 @@ void syslink_dl_init(void)
syslink.device.get_byte = (get_byte_t) syslink_getch;
// init mutex if needed
PPRZ_MUTEX_INIT(syslink_tx_mtx);
pprz_mtx_init(&syslink_tx_mtx);
}
/** Periodic function */
+7 -7
View File
@@ -144,7 +144,7 @@ static void start_message(struct pprzlink_msg *msg,
long fd __attribute__((unused)),
uint8_t payload_len __attribute__((unused)))
{
PPRZ_MUTEX_LOCK(get_trans(msg)->mtx_tx); // lock mutex
pprz_mtx_lock(&get_trans(msg)->mtx_tx); // lock mutex
memset(get_trans(msg)->tx_msg, 0, TRANSPORT_PAYLOAD_LEN);// erase message data
get_trans(msg)->tx_msg_idx = 0;// reset index
}
@@ -205,7 +205,7 @@ static void end_message(struct pprzlink_msg *msg, long fd)
break;
}
// unlock mutex
PPRZ_MUTEX_UNLOCK(get_trans(msg)->mtx_tx);
pprz_mtx_unlock(&get_trans(msg)->mtx_tx);
}
/**
@@ -219,7 +219,7 @@ void gec_encapsulate_and_send_msg(struct pprzlink_msg *msg, long fd)
get_trans(msg)->pprz_tp.trans_tx.put_bytes(msg, fd, DL_TYPE_UINT8,
DL_FORMAT_SCALAR, get_trans(msg)->tx_msg, get_trans(msg)->tx_msg_idx);
// unlock mutex
PPRZ_MUTEX_UNLOCK(get_trans(msg)->mtx_tx);
pprz_mtx_unlock(&get_trans(msg)->mtx_tx);
get_trans(msg)->pprz_tp.trans_tx.end_message(msg, fd);
}
@@ -263,7 +263,7 @@ static void start_message(struct gec_transport *trans,
long fd __attribute__((unused)),
uint8_t payload_len __attribute__((unused)))
{
PPRZ_MUTEX_LOCK(trans->mtx_tx); // lock mutex
pprz_mtx_lock(&trans->mtx_tx); // lock mutex
memset(trans->tx_msg, 0, TRANSPORT_PAYLOAD_LEN); // erase message data
trans->tx_msg_idx = 0; // reset index
}
@@ -283,7 +283,7 @@ static void end_message(struct gec_transport *trans, struct link_device *dev,
break;
}
// unlock mutex
PPRZ_MUTEX_UNLOCK(trans->mtx_tx);
pprz_mtx_unlock(&trans->mtx_tx);
}
void gec_encapsulate_and_send_msg(struct gec_transport *trans,
@@ -293,7 +293,7 @@ void gec_encapsulate_and_send_msg(struct gec_transport *trans,
trans->pprz_tp.trans_tx.put_bytes(trans, dev, fd, DL_TYPE_UINT8,
DL_FORMAT_SCALAR, trans->tx_msg, trans->tx_msg_idx);
// unlock mutex
PPRZ_MUTEX_UNLOCK(trans->mtx_tx);
pprz_mtx_unlock(&trans->mtx_tx);
trans->pprz_tp.trans_tx.end_message(trans, dev, fd);
}
@@ -335,7 +335,7 @@ void gec_transport_init(struct gec_transport *t)
t->trans_tx.overrun = (overrun_t) overrun;
t->trans_tx.count_bytes = (count_bytes_t) count_bytes;
t->trans_tx.impl = (void *)(t);
PPRZ_MUTEX_INIT(t->mtx_tx); // init mutex, check if correct pointer
pprz_mtx_init(&t->mtx_tx); // init mutex, check if correct pointer
// add whitelist messages
gec_add_to_whitelist(&(t->whitelist), KEY_EXCHANGE_MSG_ID_UAV);
+2 -2
View File
@@ -29,7 +29,7 @@
#include "pprzlink/pprzlink_transport.h"
#include "pprzlink/pprz_transport.h"
#include "modules/datalink/gec/gec.h"
#include "pprz_mutex.h"
#include "modules/core/threads.h"
#include "mcu_periph/uart.h"
#if USE_USB_SERIAL || USE_USB_SERIAL_DEBUG
@@ -84,7 +84,7 @@ struct gec_transport {
struct gec_whitelist whitelist;
// optional mutex
PPRZ_MUTEX(mtx_tx);
pprz_mutex_t mtx_tx;
};
/** PPRZ transport structure */
+5 -4
View File
@@ -46,11 +46,12 @@
#include "modules/datalink/datalink.h"
#include "modules/datalink/extra_pprz_dl.h"
#include "pprz_mutex.h"
#include "modules/core/threads.h"
extern pprz_mutex_t copilot_cam_snapshot_mtx;
extern pprz_mutex_t copilot_cam_payload_mtx;
extern pprz_mutex_t copilot_status_mtx;
PPRZ_MUTEX_DECL(copilot_cam_snapshot_mtx);
PPRZ_MUTEX_DECL(copilot_cam_payload_mtx);
PPRZ_MUTEX_DECL(copilot_status_mtx);
struct CameraPayload {
float timestamp;
+18 -18
View File
@@ -52,9 +52,9 @@ struct CameraPayload cam_payload;
struct CameraSnapshot cam_snapshot;
struct CopilotStatus copilot_status;
PPRZ_MUTEX(copilot_cam_snapshot_mtx);
PPRZ_MUTEX(copilot_cam_payload_mtx);
PPRZ_MUTEX(copilot_status_mtx);
pprz_mutex_t copilot_cam_snapshot_mtx;
pprz_mutex_t copilot_cam_payload_mtx;
pprz_mutex_t copilot_status_mtx;
/** Init function */
void copilot_init(void)
@@ -67,15 +67,15 @@ void copilot_init(void)
memset(&cam_snapshot, 0, sizeof(cam_snapshot));
memset(&copilot_status, 0, sizeof(copilot_status));
PPRZ_MUTEX_INIT(copilot_cam_snapshot_mtx);
PPRZ_MUTEX_INIT(copilot_cam_payload_mtx);
PPRZ_MUTEX_INIT(copilot_status_mtx);
pprz_mtx_init(&copilot_cam_snapshot_mtx);
pprz_mtx_init(&copilot_cam_payload_mtx);
pprz_mtx_init(&copilot_status_mtx);
}
/** Periodic function */
void copilot_periodic(void)
{
PPRZ_MUTEX_LOCK(copilot_cam_snapshot_mtx);
pprz_mtx_lock(&copilot_cam_snapshot_mtx);
if (send_cam_snapshot) {
// send down to GCS
DOWNLINK_SEND_CAMERA_SNAPSHOT(DefaultChannel, DefaultDevice,
@@ -85,9 +85,9 @@ void copilot_periodic(void)
send_cam_snapshot = false;
}
PPRZ_MUTEX_UNLOCK(copilot_cam_snapshot_mtx);
pprz_mtx_unlock(&copilot_cam_snapshot_mtx);
PPRZ_MUTEX_LOCK(copilot_cam_payload_mtx);
pprz_mtx_lock(&copilot_cam_payload_mtx);
if (send_cam_payload) {
// NOTE: to send the message over the EXTRA_DL port
// use "DOWNLINK_SEND_CAMERA_PAYLOAD(extra_pprz_tp, EXTRA_DOWNLINK_DEVICE,"
@@ -99,9 +99,9 @@ void copilot_periodic(void)
send_cam_payload = false;
}
PPRZ_MUTEX_UNLOCK(copilot_cam_payload_mtx);
pprz_mtx_unlock(&copilot_cam_payload_mtx);
PPRZ_MUTEX_LOCK(copilot_status_mtx);
pprz_mtx_lock(&copilot_status_mtx);
// send down to GCS
if (send_copilot_status) {
DOWNLINK_SEND_COPILOT_STATUS(DefaultChannel, DefaultDevice,
@@ -111,7 +111,7 @@ void copilot_periodic(void)
send_copilot_status = false;
}
PPRZ_MUTEX_UNLOCK(copilot_status_mtx);
pprz_mtx_unlock(&copilot_status_mtx);
}
@@ -126,7 +126,7 @@ void copilot_periodic(void)
*/
void copilot_parse_cam_snapshot_dl(uint8_t *buf)
{
PPRZ_MUTEX_LOCK(copilot_cam_snapshot_mtx);
pprz_mtx_lock(&copilot_cam_snapshot_mtx);
// copy CAMERA_SNAPSHOT message and mark it to be sent
cam_snapshot.cam_id = DL_CAMERA_SNAPSHOT_DL_camera_id(buf);
@@ -138,7 +138,7 @@ void copilot_parse_cam_snapshot_dl(uint8_t *buf)
send_cam_snapshot = true;
PPRZ_MUTEX_UNLOCK(copilot_cam_snapshot_mtx);
pprz_mtx_unlock(&copilot_cam_snapshot_mtx);
}
/**
@@ -146,7 +146,7 @@ void copilot_parse_cam_snapshot_dl(uint8_t *buf)
*/
void copilot_parse_cam_payload_dl(uint8_t *buf)
{
PPRZ_MUTEX_LOCK(copilot_cam_payload_mtx);
pprz_mtx_lock(&copilot_cam_payload_mtx);
cam_payload.timestamp = DL_CAMERA_PAYLOAD_DL_timestamp(buf);
cam_payload.used_mem = DL_CAMERA_PAYLOAD_DL_used_memory(buf);
@@ -156,7 +156,7 @@ void copilot_parse_cam_payload_dl(uint8_t *buf)
send_cam_payload = true;
PPRZ_MUTEX_UNLOCK(copilot_cam_payload_mtx);
pprz_mtx_unlock(&copilot_cam_payload_mtx);
}
/**
@@ -164,7 +164,7 @@ void copilot_parse_cam_payload_dl(uint8_t *buf)
*/
void copilot_parse_copilot_status_dl(uint8_t *buf)
{
PPRZ_MUTEX_LOCK(copilot_status_mtx);
pprz_mtx_lock(&copilot_status_mtx);
copilot_status.timestamp = DL_COPILOT_STATUS_DL_timestamp(buf);
copilot_status.used_mem = DL_COPILOT_STATUS_DL_used_memory(buf);
@@ -174,7 +174,7 @@ void copilot_parse_copilot_status_dl(uint8_t *buf)
send_copilot_status = true;
PPRZ_MUTEX_UNLOCK(copilot_status_mtx);
pprz_mtx_unlock(&copilot_status_mtx);
}
/**
+126
View File
@@ -0,0 +1,126 @@
/*
* Copyright (C) 2025 fab <fab@github.com>
*
* This file is part of paparazzi
*
* paparazzi is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* paparazzi is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with paparazzi; see the file COPYING. If not, see
* <http://www.gnu.org/licenses/>.
*/
/** @file "modules/sensors/thd_test.c"
* @author fab <fab@github.com>
* A module to test threads functionnalities, like threads, mutexes, semaphores and so on.
*/
#include "modules/sensors/thd_test.h"
#include "modules/core/threads.h"
#include "led.h"
#include "modules/datalink/telemetry.h"
#include <stdio.h>
float thd_test_start_value;
pprz_bsem_t bsem;
pprz_mutex_t mtx; // mutex protecting syracuse
static int syracuse = 14;
// Remember if the thread has already been joined or not.
// From https://linux.die.net/man/3/pthread_join:
// "Joining with a thread that has previously been joined results in undefined behavior."
static bool joined = true;
static void test_thd(void*);
pprz_thread_t thd_handle;
void thd_test_syracuse_restart(float s) {
// try changing syracuse
// non blocking because we are in the main loop.
// If it doesn't work, just try again!
if(!pprz_mtx_trylock(&mtx)) {
syracuse = (int)s;
thd_test_start_value = s;
pprz_mtx_unlock(&mtx);
// if it was stopped, restart it!
if(joined) {
if(!pprz_thread_create(&thd_handle, 512, "test", PPRZ_NORMAL_PRIO+1, test_thd, NULL)) {
joined = false;
}
}
} else {
char err[] = "Could not lock";
DOWNLINK_SEND_INFO_MSG(DefaultChannel, DefaultDevice, strlen(err), err);
}
}
void thd_test_init(void)
{
pprz_bsem_init(&bsem, true); // init binary semaphore
pprz_mtx_init(&mtx); // init mutex
// create thread, and remember to try joining it.
if(!pprz_thread_create(&thd_handle, 512, "test", PPRZ_NORMAL_PRIO, test_thd, NULL)) {
joined = false;
}
}
void thd_test_periodic(void)
{
if(!joined) {
size_t ret;
char msg[30];
size_t len = 0;
if(!pprz_thread_tryjoin(&thd_handle, (void**)(&ret))) {
// try joining the thread. If successful, send its exit value.
len = snprintf(msg, sizeof(msg), "Finished in %zu steps!", ret);
joined = true;
} else {
// the thread is still running, print the current value of the syracuse suit (mutex protected)
// then signal the binary semaphore to allow the thread to resume execution
if(!pprz_mtx_trylock(&mtx)) {
len = snprintf(msg, sizeof(msg), "%d", syracuse);
pprz_mtx_unlock(&mtx);
pprz_bsem_signal(&bsem);
}
}
// send the message to the ground (current value or end message)
if(len > 0) {
DOWNLINK_SEND_INFO_MSG(DefaultChannel, DefaultDevice, len, msg);
}
}
}
static void test_thd(void* arg) {
(void)arg;
size_t count = 0;
while(syracuse != 1) {
pprz_bsem_wait(&bsem); // wait to be woken up by the AP thread
pprz_mtx_lock(&mtx);
if(syracuse%2) {
syracuse = 3*syracuse + 1;
} else {
syracuse = syracuse / 2;
}
pprz_mtx_unlock(&mtx);
count++;
}
pprz_thread_exit((void*)((size_t)count));
}
+37
View File
@@ -0,0 +1,37 @@
/*
* Copyright (C) 2025 fab <fab@github.com>
*
* This file is part of paparazzi
*
* paparazzi is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* paparazzi is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with paparazzi; see the file COPYING. If not, see
* <http://www.gnu.org/licenses/>.
*/
/** @file "modules/sensors/thd_test.h"
* @author fab <fab@github.com>
* A module to test threads functionnalities, like threads, mutexes, semaphores and so on.
*/
#ifndef THD_TEST_H
#define THD_TEST_H
extern void thd_test_init(void);
extern void thd_test_periodic(void);
extern void thd_test_event(void);
void thd_test_syracuse_restart(float s);
extern float thd_test_start_value;
#endif // THD_TEST_H
-52
View File
@@ -1,52 +0,0 @@
/*
* Copyright (C) 2016 Gautier Hattenberger <gautier.hattenberger@enac.fr>
*
* This file is part of paparazzi.
*
* paparazzi is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* paparazzi is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with paparazzi; see the file COPYING. If not, see
* <http://www.gnu.org/licenses/>.
*/
/**
* @file pprz_mutex.h
*
* Utility functions and macros to abstract some RTOS functionalities
* such as mutexes.
*/
#ifndef PPRZ_MUTEX_H
#define PPRZ_MUTEX_H
#if USE_CHIBIOS_RTOS
#include <ch.h>
#define PPRZ_MUTEX(_mtx) mutex_t _mtx
#define PPRZ_MUTEX_DECL(_mtx) extern mutex_t _mtx
#define PPRZ_MUTEX_INIT(_mtx) chMtxObjectInit(&(_mtx))
#define PPRZ_MUTEX_LOCK(_mtx) chMtxLock(&(_mtx))
#define PPRZ_MUTEX_UNLOCK(_mtx) chMtxUnlock(&(_mtx))
#else // no RTOS
#define PPRZ_MUTEX(_mtx)
#define PPRZ_MUTEX_DECL(_mtx)
#define PPRZ_MUTEX_INIT(_mtx) {}
#define PPRZ_MUTEX_LOCK(_mtx) {}
#define PPRZ_MUTEX_UNLOCK(_mtx) {}
#endif
#endif
@@ -0,0 +1,14 @@
#pragma once
#define PPRZ_NORMAL_PRIO 128
#define THREADS_ATTRIBUTES
struct pprzMutex {
};
struct pprzBSem {
};
typedef struct{} pprz_thread_t;