mirror of
https://gitee.com/mirrors_PX4/PX4-Autopilot.git
synced 2026-11-12 05:21:35 +08:00
ModuleBase add common base type and cleanup
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
/****************************************************************************
|
||||
*
|
||||
* Copyright (c) 2017 PX4 Development Team. All rights reserved.
|
||||
* Copyright (c) 2017-2020 PX4 Development Team. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
@@ -44,13 +44,23 @@
|
||||
#include <px4_platform_common/atomic.h>
|
||||
#include <px4_platform_common/time.h>
|
||||
#include <px4_platform_common/log.h>
|
||||
#include <px4_platform_common/posix.h>
|
||||
#include <px4_platform_common/tasks.h>
|
||||
#include <systemlib/px4_macros.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include <containers/List.hpp>
|
||||
#include <containers/LockGuard.hpp>
|
||||
|
||||
#include <cstring>
|
||||
|
||||
/** @var task_id_is_work_queue Value to indicate if the task runs on the work queue. */
|
||||
static constexpr const int task_id_is_work_queue = -2;
|
||||
|
||||
class ModuleBaseInterface;
|
||||
extern List<ModuleBaseInterface *> px4_modules_list;
|
||||
|
||||
/**
|
||||
* @brief This mutex protects against race conditions during startup & shutdown of modules.
|
||||
* There could be one mutex per module instantiation, but to reduce the memory footprint
|
||||
@@ -59,6 +69,123 @@
|
||||
*/
|
||||
extern pthread_mutex_t px4_modules_mutex;
|
||||
|
||||
class ModuleBaseInterface : public ListNode<ModuleBaseInterface *>
|
||||
{
|
||||
public:
|
||||
ModuleBaseInterface(const char *name) :
|
||||
_module_name(name)
|
||||
{
|
||||
px4_modules_list.add(this);
|
||||
}
|
||||
|
||||
virtual ~ModuleBaseInterface()
|
||||
{
|
||||
px4_modules_list.remove(this);
|
||||
}
|
||||
|
||||
const char *get_name() const { return _module_name; }
|
||||
|
||||
/**
|
||||
* @brief Print the status if the module is running. This can be overridden by the module to provide
|
||||
* more specific information.
|
||||
* @return Returns 0 iff successful, -1 otherwise.
|
||||
*/
|
||||
virtual int print_status()
|
||||
{
|
||||
PX4_INFO_RAW("[%s] running", _module_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Tells the module to stop (used from outside or inside the module thread).
|
||||
*/
|
||||
virtual void request_stop() { _task_should_exit.store(true); }
|
||||
|
||||
int task_id() const { return _task_id.load(); }
|
||||
void set_task_id(int id) { _task_id.store(id); }
|
||||
|
||||
/**
|
||||
* @brief Main loop method for modules running in their own thread. Called from run_trampoline().
|
||||
* This method must return when should_exit() returns true.
|
||||
*/
|
||||
virtual void run() {};
|
||||
|
||||
bool running() { return (task_id() != -1); }
|
||||
|
||||
/**
|
||||
* @brief Checks if the module should stop (used within the module thread).
|
||||
* @return Returns True iff successful, false otherwise.
|
||||
*/
|
||||
bool should_exit() const { return _task_should_exit.load(); }
|
||||
|
||||
protected:
|
||||
|
||||
/**
|
||||
* @brief lock_module Mutex to lock the module thread.
|
||||
*/
|
||||
static void lock_module() { pthread_mutex_lock(&px4_modules_mutex); }
|
||||
|
||||
/**
|
||||
* @brief unlock_module Mutex to unlock the module thread.
|
||||
*/
|
||||
static void unlock_module() { pthread_mutex_unlock(&px4_modules_mutex); }
|
||||
|
||||
const char *_module_name;
|
||||
|
||||
/** @var _task_should_exit Boolean flag to indicate if the task should exit. */
|
||||
px4::atomic_bool _task_should_exit{false};
|
||||
|
||||
/** @var _task_id The task handle: -1 = invalid, otherwise task is assumed to be running. */
|
||||
px4::atomic_int _task_id{-1};
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Get module address by name.
|
||||
* Caller responsible for locking.
|
||||
* @param name The module name.
|
||||
* @return Returns address if found, nullptr otherwise.
|
||||
*/
|
||||
ModuleBaseInterface *moduleInstance(const char *name);
|
||||
|
||||
/**
|
||||
* @brief Check if module is running (by name).
|
||||
* Caller responsible for locking.
|
||||
* @param name The module name.
|
||||
* @return Returns true if running, false otherwise.
|
||||
*/
|
||||
bool moduleRunning(const char *name);
|
||||
|
||||
/**
|
||||
* @brief Request module stop by name.
|
||||
* @param name The module name.
|
||||
* @return Returns 0 on success, -1 otherwise.
|
||||
*/
|
||||
int moduleStop(const char *name);
|
||||
|
||||
/**
|
||||
* @brief Check if module is running (by name).
|
||||
* @param name The module name.
|
||||
* @return Returns 0 on success, -1 otherwise.
|
||||
*/
|
||||
int moduleStatus(const char *name);
|
||||
|
||||
/**
|
||||
* @brief Delete module by name.
|
||||
* @param name The module name.
|
||||
*/
|
||||
void moduleExitAndCleanup(const char *name);
|
||||
|
||||
/**
|
||||
* @brief Wait until module is running (by name).
|
||||
* @param name The module name.
|
||||
* @return Returns true if running, false otherwise.
|
||||
*/
|
||||
bool moduleWaitUntilRunning(const char *name);
|
||||
|
||||
void modulesStatusAll();
|
||||
void modulesStopAll();
|
||||
|
||||
/**
|
||||
* @class ModuleBase
|
||||
* Base class for modules, implementing common functionality,
|
||||
@@ -75,9 +202,8 @@ extern pthread_mutex_t px4_modules_mutex;
|
||||
* static int task_spawn(int argc, char *argv[])
|
||||
* {
|
||||
* // call px4_task_spawn_cmd() with &run_trampoline as startup method
|
||||
* // optional: wait until _object is not null, which means the task got initialized (use a timeout)
|
||||
* // set _task_id and return 0
|
||||
* // on error return != 0 (and _task_id must be -1)
|
||||
* // optional: wait until object is not null, which means the task got initialized (use a timeout)
|
||||
* // on error return != 0
|
||||
* }
|
||||
*
|
||||
* static T *instantiate(int argc, char *argv[])
|
||||
@@ -104,19 +230,24 @@ extern pthread_mutex_t px4_modules_mutex;
|
||||
* - custom_command & print_usage as above
|
||||
* static int task_spawn(int argc, char *argv[]) {
|
||||
* // parse the arguments
|
||||
* // set _object (here or from the work_queue() callback)
|
||||
* // set object (here or from the work_queue() callback)
|
||||
* // call work_queue() (with a custom cycle trampoline)
|
||||
* // optional: wait until _object is not null, which means the task got initialized (use a timeout)
|
||||
* // set _task_id to task_id_is_work_queue and return 0
|
||||
* // on error return != 0 (and _task_id must be -1)
|
||||
* // instance->set_task_id(task_id_is_work_queue); and return 0
|
||||
* // on error return != 0
|
||||
* }
|
||||
*/
|
||||
template<class T>
|
||||
class ModuleBase
|
||||
class ModuleBase : public ModuleBaseInterface
|
||||
{
|
||||
public:
|
||||
ModuleBase() : _task_should_exit{false} {}
|
||||
virtual ~ModuleBase() {}
|
||||
ModuleBase() : ModuleBaseInterface(get_name_static())
|
||||
{}
|
||||
|
||||
virtual ~ModuleBase() = default;
|
||||
|
||||
#if defined(MODULE_NAME)
|
||||
static constexpr const char *get_name_static() { return MODULE_NAME; }
|
||||
#endif // MODULE_NAME
|
||||
|
||||
/**
|
||||
* @brief main Main entry point to the module that should be
|
||||
@@ -132,6 +263,7 @@ public:
|
||||
strcmp(argv[1], "help") == 0 ||
|
||||
strcmp(argv[1], "info") == 0 ||
|
||||
strcmp(argv[1], "usage") == 0) {
|
||||
|
||||
return T::print_usage();
|
||||
}
|
||||
|
||||
@@ -141,18 +273,14 @@ public:
|
||||
}
|
||||
|
||||
if (strcmp(argv[1], "status") == 0) {
|
||||
return status_command();
|
||||
return moduleStatus(get_name_static());
|
||||
}
|
||||
|
||||
if (strcmp(argv[1], "stop") == 0) {
|
||||
return stop_command();
|
||||
return moduleStop(get_name_static());
|
||||
}
|
||||
|
||||
lock_module(); // Lock here, as the method could access _object.
|
||||
int ret = T::custom_command(argc - 1, argv + 1);
|
||||
unlock_module();
|
||||
|
||||
return ret;
|
||||
return T::custom_command(argc - 1, argv + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -175,15 +303,19 @@ public:
|
||||
argv += 1;
|
||||
#endif
|
||||
|
||||
// lock module, instantiate will create module object and add to list
|
||||
lock_module();
|
||||
T *object = T::instantiate(argc, argv);
|
||||
_object.store(object);
|
||||
|
||||
if (object) {
|
||||
object->set_task_id(px4_getpid());
|
||||
unlock_module();
|
||||
object->run();
|
||||
|
||||
} else {
|
||||
PX4_ERR("failed to instantiate object");
|
||||
ret = -1;
|
||||
unlock_module();
|
||||
}
|
||||
|
||||
exit_and_cleanup();
|
||||
@@ -201,9 +333,9 @@ public:
|
||||
static int start_command_base(int argc, char *argv[])
|
||||
{
|
||||
int ret = 0;
|
||||
lock_module();
|
||||
LockGuard lg(px4_modules_mutex);
|
||||
|
||||
if (is_running()) {
|
||||
if (moduleRunning(get_name_static())) {
|
||||
ret = -1;
|
||||
PX4_ERR("Task already running");
|
||||
|
||||
@@ -215,102 +347,14 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
unlock_module();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Stops the command, ('command stop'), checks if it is running and if it is, request the module to stop
|
||||
* and waits for the task to complete.
|
||||
* @return Returns 0 iff successful, -1 otherwise.
|
||||
*/
|
||||
static int stop_command()
|
||||
{
|
||||
int ret = 0;
|
||||
lock_module();
|
||||
|
||||
if (is_running()) {
|
||||
T *object = _object.load();
|
||||
|
||||
if (object) {
|
||||
object->request_stop();
|
||||
|
||||
unsigned int i = 0;
|
||||
|
||||
do {
|
||||
unlock_module();
|
||||
px4_usleep(10000); // 10 ms
|
||||
lock_module();
|
||||
|
||||
if (++i > 500 && _task_id != -1) { // wait at most 5 sec
|
||||
PX4_ERR("timeout, forcing stop");
|
||||
|
||||
if (_task_id != task_id_is_work_queue) {
|
||||
px4_task_delete(_task_id);
|
||||
}
|
||||
|
||||
_task_id = -1;
|
||||
|
||||
delete _object.load();
|
||||
_object.store(nullptr);
|
||||
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
} while (_task_id != -1);
|
||||
|
||||
} else {
|
||||
// In the very unlikely event that can only happen on work queues,
|
||||
// if the starting thread does not wait for the work queue to initialize,
|
||||
// and inside the work queue, the allocation of _object fails
|
||||
// and exit_and_cleanup() is not called, set the _task_id as invalid.
|
||||
_task_id = -1;
|
||||
}
|
||||
}
|
||||
|
||||
unlock_module();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handle 'command status': check if running and call print_status() if it is
|
||||
* @return Returns 0 iff successful, -1 otherwise.
|
||||
*/
|
||||
static int status_command()
|
||||
{
|
||||
int ret = -1;
|
||||
lock_module();
|
||||
|
||||
if (is_running() && _object.load()) {
|
||||
T *object = _object.load();
|
||||
ret = object->print_status();
|
||||
|
||||
} else {
|
||||
PX4_INFO("not running");
|
||||
}
|
||||
|
||||
unlock_module();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Print the status if the module is running. This can be overridden by the module to provide
|
||||
* more specific information.
|
||||
* @return Returns 0 iff successful, -1 otherwise.
|
||||
*/
|
||||
virtual int print_status()
|
||||
{
|
||||
PX4_INFO("running");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Main loop method for modules running in their own thread. Called from run_trampoline().
|
||||
* This method must return when should_exit() returns true.
|
||||
*/
|
||||
virtual void run()
|
||||
{
|
||||
}
|
||||
virtual void run() {}
|
||||
|
||||
/**
|
||||
* @brief Returns the status of the module.
|
||||
@@ -318,118 +362,36 @@ public:
|
||||
*/
|
||||
static bool is_running()
|
||||
{
|
||||
return _task_id != -1;
|
||||
LockGuard lg(px4_modules_mutex);
|
||||
return moduleRunning(get_name_static());
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
/**
|
||||
* @brief Tells the module to stop (used from outside or inside the module thread).
|
||||
*/
|
||||
virtual void request_stop()
|
||||
{
|
||||
_task_should_exit.store(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if the module should stop (used within the module thread).
|
||||
* @return Returns True iff successful, false otherwise.
|
||||
*/
|
||||
bool should_exit() const
|
||||
{
|
||||
return _task_should_exit.load();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Exits the module and delete the object. Called from within the module's thread.
|
||||
* For work queue modules, this needs to be called from the derived class in the
|
||||
* cycle method, when should_exit() returns true.
|
||||
*/
|
||||
static void exit_and_cleanup()
|
||||
{
|
||||
// Take the lock here:
|
||||
// - if startup fails and we're faster than the parent thread, it will set
|
||||
// _task_id and subsequently it will look like the task is running.
|
||||
// - deleting the object must take place inside the lock.
|
||||
lock_module();
|
||||
|
||||
delete _object.load();
|
||||
_object.store(nullptr);
|
||||
|
||||
_task_id = -1; // Signal a potentially waiting thread for the module to exit that it can continue.
|
||||
unlock_module();
|
||||
}
|
||||
static void exit_and_cleanup() { moduleExitAndCleanup(get_name_static()); }
|
||||
|
||||
/**
|
||||
* @brief Waits until _object is initialized, (from the new thread). This can be called from task_spawn().
|
||||
* @return Returns 0 iff successful, -1 on timeout or otherwise.
|
||||
*/
|
||||
static int wait_until_running()
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
do {
|
||||
/* Wait up to 1s. */
|
||||
px4_usleep(2500);
|
||||
|
||||
} while (!_object.load() && ++i < 400);
|
||||
|
||||
if (i == 400) {
|
||||
PX4_ERR("Timed out while waiting for thread to start");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
static int wait_until_running() { return moduleWaitUntilRunning(get_name_static()); }
|
||||
|
||||
/**
|
||||
* @brief Get the module's object instance, (this is null if it's not running).
|
||||
*/
|
||||
static T *get_instance()
|
||||
{
|
||||
return (T *)_object.load();
|
||||
LockGuard lg(px4_modules_mutex);
|
||||
return (T *)moduleInstance(get_name_static());
|
||||
}
|
||||
|
||||
/**
|
||||
* @var _object Instance if the module is running.
|
||||
* @note There will be one instance for each template type.
|
||||
*/
|
||||
static px4::atomic<T *> _object;
|
||||
|
||||
/** @var _task_id The task handle: -1 = invalid, otherwise task is assumed to be running. */
|
||||
static int _task_id;
|
||||
|
||||
/** @var task_id_is_work_queue Value to indicate if the task runs on the work queue. */
|
||||
static constexpr const int task_id_is_work_queue = -2;
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief lock_module Mutex to lock the module thread.
|
||||
*/
|
||||
static void lock_module()
|
||||
{
|
||||
pthread_mutex_lock(&px4_modules_mutex);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief unlock_module Mutex to unlock the module thread.
|
||||
*/
|
||||
static void unlock_module()
|
||||
{
|
||||
pthread_mutex_unlock(&px4_modules_mutex);
|
||||
}
|
||||
|
||||
/** @var _task_should_exit Boolean flag to indicate if the task should exit. */
|
||||
px4::atomic_bool _task_should_exit{false};
|
||||
};
|
||||
|
||||
template<class T>
|
||||
px4::atomic<T *> ModuleBase<T>::_object{nullptr};
|
||||
|
||||
template<class T>
|
||||
int ModuleBase<T>::_task_id = -1;
|
||||
|
||||
|
||||
#endif /* __cplusplus */
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user