diff --git a/src/lib/parameters/CMakeLists.txt b/src/lib/parameters/CMakeLists.txt index 7e9ce681e5..d2cd47a129 100644 --- a/src/lib/parameters/CMakeLists.txt +++ b/src/lib/parameters/CMakeLists.txt @@ -156,7 +156,7 @@ add_custom_target(parameters_header DEPENDS px4_parameters.hpp) set(SRCS) -list(APPEND SRCS parameters.cpp) +list(APPEND SRCS parameters.cpp atomic_transaction.cpp) if(BUILD_TESTING) list(APPEND SRCS param_translation_unit_tests.cpp) diff --git a/src/lib/parameters/ConstLayer.h b/src/lib/parameters/ConstLayer.h new file mode 100644 index 0000000000..0edca5ff59 --- /dev/null +++ b/src/lib/parameters/ConstLayer.h @@ -0,0 +1,82 @@ +/**************************************************************************** + * + * Copyright (c) 2023 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 + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +#pragma once + +#include "ParamLayer.h" + +class ConstLayer : public ParamLayer +{ +public: + + ConstLayer() = default; + + bool store(param_t param, param_value_u value) override + { + return false; + } + + bool contains(param_t param) const override + { + return param < PARAM_COUNT; + } + + param_value_u get(param_t param) const override + { + if (param >= PARAM_COUNT) { + return {0}; + } + + return px4::parameters[param].val; + } + + void reset(param_t param) override + { + // Do nothing + } + + void refresh(param_t param) override + { + // Do nothing + } + + int size() const override + { + return PARAM_COUNT; + } + + int byteSize() const override + { + return PARAM_COUNT * sizeof(param_info_s); + } +}; diff --git a/src/lib/parameters/DynamicSparseLayer.h b/src/lib/parameters/DynamicSparseLayer.h new file mode 100644 index 0000000000..1491f53360 --- /dev/null +++ b/src/lib/parameters/DynamicSparseLayer.h @@ -0,0 +1,232 @@ +/**************************************************************************** + * + * Copyright (c) 2023 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 + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +#pragma once + +#include "ParamLayer.h" + +#include + +class DynamicSparseLayer : public ParamLayer +{ +public: + DynamicSparseLayer(ParamLayer *parent, int n_prealloc = 32, int n_grow = 4) : ParamLayer(parent), + _n_slots(n_prealloc), _n_grow(n_grow) + { + Slot *slots = (Slot *)malloc(sizeof(Slot) * n_prealloc); + + if (slots == nullptr) { + PX4_ERR("Failed to allocate memory for dynamic sparse layer"); + _n_slots = 0; + return; + } + + for (int i = 0; i < _n_slots; i++) { + slots[i] = {UINT16_MAX, param_value_u{}}; + } + + _slots.store(slots); + } + + virtual ~DynamicSparseLayer() + { + if (_slots.load()) { + free(_slots.load()); + } + } + + bool store(param_t param, param_value_u value) override + { + AtomicTransaction transaction; + Slot *slots = _slots.load(); + + const int index = _getIndex(param); + + if (index < _next_slot) { // already exists + slots[index].value = value; + + } else if (_next_slot < _n_slots) { + slots[_next_slot++] = {param, value}; + _sort(); + + } else { + if (!_grow(transaction)) { + return false; + } + + _slots.load()[_next_slot++] = {param, value}; + _sort(); + } + + return true; + } + + bool contains(param_t param) const override + { + const AtomicTransaction transaction; + return _getIndex(param) < _next_slot; + } + + param_value_u get(param_t param) const override + { + const AtomicTransaction transaction; + Slot *slots = _slots.load(); + + const int index = _getIndex(param); + + if (index < _next_slot) { // exists in our data structure + return slots[index].value; + } + + return _parent->get(param); + } + + void reset(param_t param) override + { + const AtomicTransaction transaction; + int index = _getIndex(param); + Slot *slots = _slots.load(); + + if (index < _next_slot) { + slots[index] = {UINT16_MAX, param_value_u{}}; + _sort(); + _next_slot--; + } + } + + void refresh(param_t param) override + { + _parent->refresh(param); + } + + int size() const override + { + return _next_slot; + } + + int byteSize() const override + { + return _n_slots * sizeof(Slot); + } + +private: + struct Slot { + param_t param; + param_value_u value; + }; + + static int _slotCompare(const void *a, const void *b) + { + return ((int)((Slot *)a)->param) - ((int)((Slot *)b)->param); + } + + void _sort() + { + qsort(_slots.load(), _n_slots, sizeof(Slot), _slotCompare); + } + + int _getIndex(param_t param) const + { + int left = 0; + int right = _next_slot - 1; + Slot *slots = _slots.load(); + + while (left <= right) { + int mid = (left + right) / 2; + + if (slots[mid].param == param) { + return mid; + + } else if (slots[mid].param < param) { + left = mid + 1; + + } else { + right = mid - 1; + } + } + + return _next_slot; + } + + bool _grow(AtomicTransaction &transaction) + { + if (_n_slots == 0) { + return false; + } + + int max_retries = 5; + + // As malloc uses locking, so we need to re-enable IRQ's during malloc/free and + // then atomically exchange the buffer + while (_next_slot >= _n_slots && max_retries-- > 0) { + Slot *previous_slots = nullptr; + Slot *new_slots = nullptr; + + do { + previous_slots = _slots.load(); + transaction.unlock(); + + if (new_slots) { + free(new_slots); + } + + new_slots = (Slot *) malloc(sizeof(Slot) * (_n_slots + _n_grow)); + transaction.lock(); + + if (new_slots == nullptr) { + return false; + } + + } while (!_slots.compare_exchange(&previous_slots, new_slots)); + + memcpy(new_slots, previous_slots, sizeof(Slot) * _n_slots); + + for (int i = _n_slots; i < _n_slots + _n_grow; i++) { + new_slots[i] = {UINT16_MAX, param_value_u{}}; + } + + _n_slots += _n_grow; + + transaction.unlock(); + free(previous_slots); + transaction.lock(); + } + + return _next_slot < _n_slots; + } + + int _next_slot = 0; + int _n_slots = 0; + const int _n_grow; + px4::atomic _slots{nullptr}; +}; diff --git a/src/lib/parameters/ExhaustiveLayer.h b/src/lib/parameters/ExhaustiveLayer.h new file mode 100644 index 0000000000..9b91c12d9c --- /dev/null +++ b/src/lib/parameters/ExhaustiveLayer.h @@ -0,0 +1,119 @@ +/**************************************************************************** + * + * Copyright (c) 2023 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 + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +#pragma once + +#include +#include "ParamLayer.h" + +class ExhaustiveLayer : public ParamLayer +{ +public: + ExhaustiveLayer(ParamLayer *parent) : ParamLayer(parent) + { + // refresh all values + for (param_t i = 0; i < PARAM_COUNT; i++) { + _values[i] = parent->get(i); + } + } + + bool store(param_t param, param_value_u value) override + { + if (param >= PARAM_COUNT) { + return false; + } + + { + const AtomicTransaction transaction; + _values[param] = value; + _ownership_set.set(param); + } + + return true; + } + + bool contains(param_t param) const override + { + return param < PARAM_COUNT && _ownership_set[param]; + } + + param_value_u get(param_t param) const override + { + if (param >= PARAM_COUNT) { + return {0}; + } + + const AtomicTransaction transaction; + // We assume to have the correct values for all params, even without ownership. + // We expect that refresh was called when underlying defaults changed + return _values[param]; + } + + void reset(param_t param) override + { + if (param >= PARAM_COUNT) { + return; + } + + const AtomicTransaction transaction; + _values[param] = _parent->get(param); + _ownership_set.set(param, false); + } + + void refresh(param_t param) override + { + _parent->refresh(param); + // in case we don't have ownership, and it changed below, we have to refresh our cache. + { + const AtomicTransaction transaction; + + if (!contains(param)) { + _values[param] = _parent->get(param); + } + } + } + + int size() const override + { + return _ownership_set.count(); + } + + int byteSize() const override + { + return PARAM_COUNT * sizeof(param_value_u); + } + +private: + param_value_u _values[PARAM_COUNT]; + px4::AtomicBitset _ownership_set; +}; diff --git a/src/lib/parameters/ParamLayer.h b/src/lib/parameters/ParamLayer.h new file mode 100644 index 0000000000..3a3d5be87e --- /dev/null +++ b/src/lib/parameters/ParamLayer.h @@ -0,0 +1,72 @@ +/**************************************************************************** + * + * Copyright (c) 2023 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 + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +#ifndef PX4_PARAMLAYER_H +#define PX4_PARAMLAYER_H + +#include "atomic_transaction.h" +#include "param.h" +#include + +class ParamLayer +{ +public: + static constexpr param_t PARAM_COUNT = sizeof(px4::parameters) / sizeof(param_info_s); + + ParamLayer(ParamLayer *parent = nullptr) : _parent(parent) {} + + ParamLayer(const ParamLayer &) = delete; + ParamLayer &operator=(const ParamLayer &) = delete; + ParamLayer(ParamLayer &&) = delete; + ParamLayer &operator=(ParamLayer &&) = delete; + + + virtual bool store(param_t param, param_value_u value) = 0; + + virtual bool contains(param_t param) const = 0; + + virtual param_value_u get(param_t param) const = 0; + + virtual void reset(param_t param) = 0; + + virtual void refresh(param_t param) = 0; + + virtual int size() const = 0; + + virtual int byteSize() const = 0; + +protected: + ParamLayer *const _parent; +}; + +#endif //PX4_PARAMLAYER_H diff --git a/src/lib/parameters/StaticSparseLayer.h b/src/lib/parameters/StaticSparseLayer.h new file mode 100644 index 0000000000..98f0c73267 --- /dev/null +++ b/src/lib/parameters/StaticSparseLayer.h @@ -0,0 +1,156 @@ +/**************************************************************************** + * + * Copyright (c) 2023 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 + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +#pragma once + +#include +#include "ParamLayer.h" + +template +class StaticSparseLayer : public ParamLayer +{ +public: + StaticSparseLayer(ParamLayer *parent) : ParamLayer(parent) + { + for (int i = 0; i < N_SLOTS; i++) { + _slots[i] = {UINT16_MAX, param_value_u{}}; + } + } + + virtual ~StaticSparseLayer() = default; + + bool store(param_t param, param_value_u value) override + { + const AtomicTransaction transaction; + const int index = _getIndex(param); + + if (index < _next_slot) { // already exists + _slots[index].value = value; + + } else if (_next_slot < N_SLOTS) { + _slots[_next_slot++] = {param, value}; + _sort(); + + } else { + return false; + } + + return true; + } + + bool contains(param_t param) const override + { + const AtomicTransaction transaction; + return _getIndex(param) < _next_slot; + } + + param_value_u get(param_t param) const override + { + const AtomicTransaction transaction; + const int index = _getIndex(param); + + if (index < _next_slot) { // exists in this layer + return _slots[index].value; + } + + return _parent->get(param); + } + + void reset(param_t param) override + { + const AtomicTransaction transaction; + int index = _getIndex(param); + + if (index < _next_slot) { + _slots[index] = {UINT16_MAX, param_value_u{}}; + _sort(); + _next_slot--; + } + } + + void refresh(param_t param) override + { + _parent->refresh(param); + } + + int size() const override + { + return _next_slot; + } + + int byteSize() const override + { + return N_SLOTS * sizeof(Slot); + } + +private: + struct Slot { + param_t param; + param_value_u value; + }; + + static int _slotCompare(const void *a, const void *b) + { + return ((int)((Slot *)a)->param) - ((int)((Slot *)b)->param); + } + + void _sort() + { + qsort(_slots, N_SLOTS, sizeof(Slot), &_slotCompare); + } + + int _getIndex(param_t param) const + { + int left = 0; + int right = _next_slot - 1; + + while (left <= right) { + int mid = (left + right) / 2; + + if (_slots[mid].param == param) { + return mid; + + } else if (_slots[mid].param < param) { + left = mid + 1; + + } else { + right = mid - 1; + } + } + + return _next_slot; + } + + Slot _slots[N_SLOTS]; + int _next_slot = 0; +}; diff --git a/src/lib/parameters/atomic_transaction.cpp b/src/lib/parameters/atomic_transaction.cpp new file mode 100644 index 0000000000..5b1b21a761 --- /dev/null +++ b/src/lib/parameters/atomic_transaction.cpp @@ -0,0 +1,38 @@ +/**************************************************************************** + * + * Copyright (c) 2023 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 + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +#include "atomic_transaction.h" + +#ifdef __PX4_POSIX +_MutexHolder AtomicTransaction::_mutex_holder = _MutexHolder {}; +#endif diff --git a/src/lib/parameters/atomic_transaction.h b/src/lib/parameters/atomic_transaction.h new file mode 100644 index 0000000000..6079746416 --- /dev/null +++ b/src/lib/parameters/atomic_transaction.h @@ -0,0 +1,105 @@ +/**************************************************************************** + * + * Copyright (c) 2023 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 + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +#pragma once + +#ifdef __PX4_NUTTX +#include "px4_platform_common/micro_hal.h" +#endif + +#ifdef __PX4_POSIX +#include + +class _MutexHolder +{ +public: + pthread_mutex_t _mutex; + pthread_mutexattr_t _mutex_attr; + + _MutexHolder() + { + pthread_mutexattr_init(&_mutex_attr); + pthread_mutexattr_settype(&_mutex_attr, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(&_mutex, &_mutex_attr); + } + + ~_MutexHolder() + { + pthread_mutex_destroy(&_mutex); + } +}; +#endif + + +class AtomicTransaction +{ +private: +#ifdef __PX4_NUTTX + irqstate_t _irq_state; +#endif + +#ifdef __PX4_POSIX + static _MutexHolder _mutex_holder; +#endif + +public: + AtomicTransaction() + { + lock(); + } + + ~AtomicTransaction() + { + unlock(); + } + + void lock() + { +#ifdef __PX4_NUTTX + _irq_state = px4_enter_critical_section(); +#endif +#ifdef __PX4_POSIX + pthread_mutex_lock(&_mutex_holder._mutex); +#endif + } + + void unlock() + { +#ifdef __PX4_NUTTX + px4_leave_critical_section(_irq_state); +#endif +#ifdef __PX4_POSIX + pthread_mutex_unlock(&_mutex_holder._mutex); +#endif + } +}; diff --git a/src/lib/parameters/flashparams/CMakeLists.txt b/src/lib/parameters/flashparams/CMakeLists.txt index 23cbd92234..9b7ac3f9c6 100644 --- a/src/lib/parameters/flashparams/CMakeLists.txt +++ b/src/lib/parameters/flashparams/CMakeLists.txt @@ -42,7 +42,7 @@ add_library(flashparams flashparams.cpp ) -add_dependencies(flashparams prebuild_targets) +add_dependencies(flashparams prebuild_targets parameters_header) target_compile_definitions(flashparams PRIVATE -DMODULE_NAME="flashparams") target_compile_options(flashparams PRIVATE diff --git a/src/lib/parameters/flashparams/flashparams.cpp b/src/lib/parameters/flashparams/flashparams.cpp index b309207850..16bc24b5eb 100644 --- a/src/lib/parameters/flashparams/flashparams.cpp +++ b/src/lib/parameters/flashparams/flashparams.cpp @@ -46,6 +46,7 @@ #include #include #include +#include #include #include @@ -54,7 +55,6 @@ #include -#include "../uthash/utarray.h" #include #include "flashparams.h" #include "flashfs.h" @@ -78,7 +78,6 @@ struct param_wbuf_s { static int param_export_internal(param_filter_func filter) { - struct param_wbuf_s *s = nullptr; bson_encoder_s encoder{}; int result = -1; @@ -86,28 +85,22 @@ param_export_internal(param_filter_func filter) bson_encoder_init_buf(&encoder, nullptr, 0); - /* no modified parameters -> we are done */ - if (param_values == nullptr) { - result = 0; - goto out; - } - - while ((s = (struct param_wbuf_s *)utarray_next(param_values, s)) != nullptr) { + for (param_t param = 0; param < user_config.PARAM_COUNT; param++) { int32_t i; float f; - if (filter && !filter(s->param)) { + if (filter && !filter(param)) { continue; } /* append the appropriate BSON type object */ - switch (param_type(s->param)) { + switch (param_type(param)) { case PARAM_TYPE_INT32: - i = s->val.i; + i = user_config.get(param).i; - if (bson_encoder_append_int32(&encoder, param_name(s->param), i)) { + if (bson_encoder_append_int32(&encoder, param_name(param), i)) { debug("BSON append failed for '%s'", param_name(s->param)); goto out; } @@ -115,9 +108,9 @@ param_export_internal(param_filter_func filter) break; case PARAM_TYPE_FLOAT: - f = s->val.f; + f = user_config.get(param).f; - if (bson_encoder_append_double(&encoder, param_name(s->param), f)) { + if (bson_encoder_append_double(&encoder, param_name(param), (double)f)) { debug("BSON append failed for '%s'", param_name(s->param)); goto out; } diff --git a/src/lib/parameters/flashparams/flashparams.h b/src/lib/parameters/flashparams/flashparams.h index 2620e5e91d..667b464767 100644 --- a/src/lib/parameters/flashparams/flashparams.h +++ b/src/lib/parameters/flashparams/flashparams.h @@ -49,6 +49,7 @@ #include #include #include +#include "../DynamicSparseLayer.h" __BEGIN_DECLS @@ -57,9 +58,9 @@ __BEGIN_DECLS * the param_values and 2 functions to be global */ -__EXPORT extern UT_array *param_values; +__EXPORT extern DynamicSparseLayer user_config; __EXPORT int param_set_external(param_t param, const void *val, bool mark_saved, bool notify_changes); -__EXPORT const void *param_get_value_ptr_external(param_t param); +__EXPORT void param_get_external(param_t param, void *val); /* The interface hooks to the Flash based storage. The caller is responsible for locking */ __EXPORT int flash_param_save(param_filter_func filter); diff --git a/src/lib/parameters/parameters.cpp b/src/lib/parameters/parameters.cpp index 84a5da7682..30c5e3dd2a 100644 --- a/src/lib/parameters/parameters.cpp +++ b/src/lib/parameters/parameters.cpp @@ -60,7 +60,7 @@ #include #include #include -#include "uthash/utarray.h" +#include using namespace time_literals; @@ -69,6 +69,13 @@ using namespace time_literals; #include #include +#include "ExhaustiveLayer.h" +#include "ConstLayer.h" +#include "DynamicSparseLayer.h" +#include "StaticSparseLayer.h" + +#include "atomic_transaction.h" + /* Include functions common to user and kernel sides */ #include "parameters_common.cpp" @@ -96,103 +103,27 @@ static px4::atomic_bool autosave_scheduled{false}; static bool autosave_disabled = false; static px4::AtomicBitset params_active; // params found -static px4::AtomicBitset params_changed; // params non-default -static px4::Bitset params_custom_default; // params with runtime default value static px4::AtomicBitset params_unsaved; -// Storage for modified parameters. -struct param_wbuf_s { - union param_value_u val; - param_t param; -}; - -/** flexible array holding modified parameter values */ -UT_array *param_values{nullptr}; -UT_array *param_custom_default_values{nullptr}; - -const UT_icd param_icd = {sizeof(param_wbuf_s), nullptr, nullptr, nullptr}; +static ConstLayer firmware_defaults; +static DynamicSparseLayer runtime_defaults{&firmware_defaults}; +DynamicSparseLayer user_config{&runtime_defaults}; /** parameter update topic handle */ static orb_advert_t param_topic = nullptr; static unsigned int param_instance = 0; -// the following implements an RW-lock using 2 semaphores (used as mutexes). It gives -// priority to readers, meaning a writer could suffer from starvation, but in our use-case -// we only have short periods of reads and writes are rare. -static px4_sem_t param_sem; ///< this protects against concurrent access to param_values -static int reader_lock_holders = 0; -static px4_sem_t reader_lock_holders_lock; ///< this protects against concurrent access to reader_lock_holders - static perf_counter_t param_export_perf; static perf_counter_t param_find_perf; static perf_counter_t param_get_perf; static perf_counter_t param_set_perf; -static px4_sem_t param_sem_save; ///< this protects against concurrent param saves (file or flash access). -///< we use a separate lock to allow concurrent param reads and saves. -///< a param_set could still be blocked by a param save, because it -///< needs to take the reader lock - -/** lock the parameter store for read access */ -static void -param_lock_reader() -{ - do {} while (px4_sem_wait(&reader_lock_holders_lock) != 0); - - ++reader_lock_holders; - - if (reader_lock_holders == 1) { - // the first reader takes the lock, the next ones are allowed to just continue - do {} while (px4_sem_wait(¶m_sem) != 0); - } - - px4_sem_post(&reader_lock_holders_lock); -} - -/** lock the parameter store for write access */ -static void -param_lock_writer() -{ - do {} while (px4_sem_wait(¶m_sem) != 0); -} - -/** unlock the parameter store */ -static void -param_unlock_reader() -{ - do {} while (px4_sem_wait(&reader_lock_holders_lock) != 0); - - --reader_lock_holders; - - if (reader_lock_holders == 0) { - // the last reader releases the lock - px4_sem_post(¶m_sem); - } - - px4_sem_post(&reader_lock_holders_lock); -} - -/** unlock the parameter store */ -static void -param_unlock_writer() -{ - px4_sem_post(¶m_sem); -} - -/** assert that the parameter store is locked */ -static void -param_assert_locked() -{ - /* XXX */ -} +static pthread_mutex_t file_mutex = + PTHREAD_MUTEX_INITIALIZER; ///< this protects against concurrent param saves (file or flash access). void param_init() { - px4_sem_init(¶m_sem, 0, 1); - px4_sem_init(¶m_sem_save, 0, 1); - px4_sem_init(&reader_lock_holders_lock, 0, 1); - param_export_perf = perf_alloc(PC_ELAPSED, "param: export"); param_find_perf = perf_alloc(PC_COUNT, "param: find"); param_get_perf = perf_alloc(PC_COUNT, "param: get"); @@ -203,48 +134,6 @@ param_init() #endif } -/** - * Compare two modified parameter structures to determine ordering. - * - * This function is suitable for passing to qsort or bsearch. - */ -static int -param_compare_values(const void *a, const void *b) -{ - struct param_wbuf_s *pa = (struct param_wbuf_s *)a; - struct param_wbuf_s *pb = (struct param_wbuf_s *)b; - - if (pa->param < pb->param) { - return -1; - } - - if (pa->param > pb->param) { - return 1; - } - - return 0; -} - -/** - * Locate the modified parameter structure for a parameter, if it exists. - * - * @param param The parameter being searched. - * @return The structure holding the modified value, or - * nullptr if the parameter has not been modified. - */ -static param_wbuf_s * -param_find_changed(param_t param) -{ - param_assert_locked(); - - if (params_changed[param] && (param_values != nullptr)) { - param_wbuf_s key{}; - key.param = param; - return (param_wbuf_s *)utarray_find(param_values, &key, param_compare_values); - } - - return nullptr; -} void param_notify_changes() @@ -256,8 +145,8 @@ param_notify_changes() pup.find_count = perf_event_count(param_find_perf); pup.export_count = perf_event_count(param_export_perf); pup.active = params_active.count(); - pup.changed = params_changed.count(); - pup.custom_default = params_custom_default.count(); + pup.changed = user_config.size(); + pup.custom_default = runtime_defaults.size(); pup.timestamp = hrt_absolute_time(); if (param_topic == nullptr) { @@ -372,51 +261,6 @@ param_value_unsaved(param_t param) return handle_in_range(param) ? params_unsaved[param] : false; } -/** - * Obtain a pointer to the storage allocated for a parameter. - * - * @param param The parameter whose storage is sought. - * @return A pointer to the parameter value, or nullptr - * if the parameter does not exist. - */ -static const void * -param_get_value_ptr(param_t param) -{ - param_assert_locked(); - - if (handle_in_range(param)) { - /* work out whether we're fetching the default or a written value */ - struct param_wbuf_s *s = param_find_changed(param); - - if (s != nullptr) { - return &s->val; - - } else { - if (params_custom_default[param] && param_custom_default_values) { - // get default from custom default storage - param_wbuf_s key{}; - key.param = param; - param_wbuf_s *pbuf = (param_wbuf_s *)utarray_find(param_custom_default_values, &key, param_compare_values); - - if (pbuf != nullptr) { - return &pbuf->val; - } - } - - // otherwise return static default value - switch (param_type(param)) { - case PARAM_TYPE_INT32: - return &px4::parameters[param].val.i; - - case PARAM_TYPE_FLOAT: - return &px4::parameters[param].val.f; - } - } - } - - return nullptr; -} - int param_get(param_t param, void *val) { @@ -434,28 +278,18 @@ param_get(param_t param, void *val) int result = PX4_ERROR; if (val) { - if (!params_changed[param] && !params_custom_default[param]) { - // if parameter is unchanged (static default value) copy immediately and avoid locking - switch (param_type(param)) { - case PARAM_TYPE_INT32: - memcpy(val, &px4::parameters[param].val.i, sizeof(px4::parameters[param].val.i)); - return PX4_OK; - case PARAM_TYPE_FLOAT: - memcpy(val, &px4::parameters[param].val.f, sizeof(px4::parameters[param].val.f)); - return PX4_OK; - } + auto retrieve_value = user_config.get(param); + + switch (param_type(param)) { + case PARAM_TYPE_INT32: + *(int32_t *)val = retrieve_value.i; + return PX4_OK; + + case PARAM_TYPE_FLOAT: + *(float *)val = retrieve_value.f; + return PX4_OK; } - - param_lock_reader(); - const void *v = param_get_value_ptr(param); - - if (v) { - memcpy(val, v, param_size(param)); - result = PX4_OK; - } - - param_unlock_reader(); } return result; @@ -470,26 +304,13 @@ param_get_default_value_internal(param_t param, void *default_val) } if (default_val) { - if (params_custom_default[param] && param_custom_default_values) { - // get default from custom default storage - param_wbuf_s key{}; - key.param = param; - param_wbuf_s *pbuf = (param_wbuf_s *)utarray_find(param_custom_default_values, &key, param_compare_values); - - if (pbuf != nullptr) { - memcpy(default_val, &pbuf->val, param_size(param)); - return PX4_OK; - } - } - - // otherwise return static default value switch (param_type(param)) { case PARAM_TYPE_INT32: - memcpy(default_val, &px4::parameters[param].val.i, param_size(param)); + *(int32_t *) default_val = runtime_defaults.get(param).i; return PX4_OK; case PARAM_TYPE_FLOAT: - memcpy(default_val, &px4::parameters[param].val.f, param_size(param)); + *(float *) default_val = runtime_defaults.get(param).f; return PX4_OK; } } @@ -504,26 +325,7 @@ param_get_default_value(param_t param, void *default_val) return PX4_ERROR; } - int ret = 0; - - if (!params_custom_default[param]) { - // return static default value - switch (param_type(param)) { - case PARAM_TYPE_INT32: - memcpy(default_val, &px4::parameters[param].val.i, sizeof(px4::parameters[param].val.i)); - return PX4_OK; - - case PARAM_TYPE_FLOAT: - memcpy(default_val, &px4::parameters[param].val.f, sizeof(px4::parameters[param].val.f)); - return PX4_OK; - } - - } else { - param_lock_reader(); - ret = param_get_default_value_internal(param, default_val); - param_unlock_reader(); - } - + int ret = param_get_default_value_internal(param, default_val); return ret; } @@ -533,49 +335,20 @@ bool param_value_is_default(param_t param) return true; } - if (!params_changed[param] && !params_custom_default[param]) { - // no value saved and no custom default + if (!user_config.contains(param)) { + // if user config does not contain it, consider it default. return true; } else { - // the param_values dynamic array might carry things that have been set - // back to default, so we don't rely on the params_changed bitset here + // compare with default value switch (param_type(param)) { case PARAM_TYPE_INT32: { - param_lock_reader(); - int32_t default_value = 0; - - if (param_get_default_value_internal(param, &default_value) == PX4_OK) { - const void *v = param_get_value_ptr(param); - - if (v) { - bool unchanged = (*static_cast(v) == default_value); - param_unlock_reader(); - return unchanged; - } - } - - param_unlock_reader(); + return user_config.get(param).i == runtime_defaults.get(param).i; } - break; case PARAM_TYPE_FLOAT: { - param_lock_reader(); - float default_value = 0; - - if (param_get_default_value_internal(param, &default_value) == PX4_OK) { - const void *v = param_get_value_ptr(param); - - if (v) { - bool unchanged = (fabsf(*static_cast(v) - default_value) <= FLT_EPSILON); - param_unlock_reader(); - return unchanged; - } - } - - param_unlock_reader(); + return user_config.get(param).f - runtime_defaults.get(param).f < FLT_EPSILON; } - break; } } @@ -602,11 +375,12 @@ autosave_worker(void *arg) } } - param_lock_writer(); - last_autosave_timestamp = hrt_absolute_time(); - autosave_scheduled.store(false); - disabled = autosave_disabled; - param_unlock_writer(); + { + const AtomicTransaction transaction; + last_autosave_timestamp = hrt_absolute_time(); + autosave_scheduled.store(false); + disabled = autosave_disabled; + } if (disabled) { return; @@ -653,15 +427,14 @@ param_autosave() void param_control_autosave(bool enable) { - param_lock_writer(); + AtomicTransaction transaction; + autosave_disabled = !enable; if (!enable && autosave_scheduled.load()) { - work_cancel(LPWORK, &autosave_work); autosave_scheduled.store(false); + transaction.unlock(); + work_cancel(LPWORK, &autosave_work); } - - autosave_disabled = !enable; - param_unlock_writer(); } static int @@ -679,84 +452,49 @@ param_set_internal(param_t param, const void *val, bool mark_saved, bool notify_ int result = -1; bool param_changed = false; - - param_lock_writer(); + bool param_store_success = false; perf_begin(param_set_perf); - // create the parameter store if it doesn't exist - if (param_values == nullptr) { - utarray_new(param_values, ¶m_icd); + switch (param_type(param)) { + case PARAM_TYPE_INT32: { + if (user_config.get(param).i != *(int32_t *)val) { + param_changed = true; + } - // mark all parameters unchanged (default) - params_changed.reset(); - params_unsaved.reset(); + param_store_success = user_config.store(param, param_value_u{.i = *(int32_t *)val}); + params_unsaved.set(param, !mark_saved); + break; + } + + case PARAM_TYPE_FLOAT: { + if (fabsf(user_config.get(param).f - * (float *) val) > FLT_EPSILON) { + param_changed = true; + } + + param_store_success = user_config.store(param, param_value_u{.f = *(float *) val}); + params_unsaved.set(param, !mark_saved); + break; + } + + default: { + PX4_ERR("param_set invalid param type for %s", param_name(param)); + break; + } } - if (param_values == nullptr) { - PX4_ERR("failed to allocate modified values array"); - goto out; + if (!param_store_success) { + PX4_ERR("param_set failed to store param %s", param_name(param)); + result = PX4_ERROR; } else { - param_wbuf_s *s = param_find_changed(param); - - if (s == nullptr) { - /* construct a new parameter */ - param_wbuf_s buf{}; - buf.param = param; - - param_changed = true; - - /* add it to the array and sort */ - utarray_push_back(param_values, &buf); - utarray_sort(param_values, param_compare_values); - params_changed.set(param, true); - - /* find it after sorting */ - s = param_find_changed(param); - } - - if (s == nullptr) { - PX4_ERR("error param_values storage slot invalid"); - - } else { - /* update the changed value */ - switch (param_type(param)) { - case PARAM_TYPE_INT32: - if (s->val.i != *(int32_t *)val) { - s->val.i = *(int32_t *)val; - param_changed = true; - } - - params_changed.set(param, true); - params_unsaved.set(param, !mark_saved); - result = PX4_OK; - break; - - case PARAM_TYPE_FLOAT: - if (fabsf(s->val.f - * (float *)val) > FLT_EPSILON) { - s->val.f = *(float *)val; - param_changed = true; - } - - params_changed.set(param, true); - params_unsaved.set(param, !mark_saved); - result = PX4_OK; - break; - - default: - PX4_ERR("param_set invalid param type for %s", param_name(param)); - break; - } - } - - if ((result == PX4_OK) && param_changed && !mark_saved) { // this is false when importing parameters - param_autosave(); - } + result = PX4_OK; + } + + if ((result == PX4_OK) && param_changed && !mark_saved) { // this is false when importing parameters + param_autosave(); } -out: perf_end(param_set_perf); - param_unlock_writer(); /* * If we set something, now that we have unlocked, go ahead and advertise that @@ -775,9 +513,9 @@ int param_set_external(param_t param, const void *val, bool mark_saved, bool not return param_set_internal(param, val, mark_saved, notify_changes); } -const void *param_get_value_ptr_external(param_t param) +void param_get_external(param_t param, void *val) { - return param_get_value_ptr(param); + param_get(param, val); } #endif @@ -821,90 +559,46 @@ int param_set_default_value(param_t param, const void *val) int result = PX4_ERROR; - param_lock_writer(); - - if (param_custom_default_values == nullptr) { - utarray_new(param_custom_default_values, ¶m_icd); - - // mark all parameters unchanged (default) - params_custom_default.reset(); - - if (param_custom_default_values == nullptr) { - PX4_ERR("failed to allocate custom default values array"); - param_unlock_writer(); - return PX4_ERROR; - } - } // check if param being set to default value bool setting_to_static_default = false; switch (param_type(param)) { case PARAM_TYPE_INT32: - setting_to_static_default = (px4::parameters[param].val.i == *(int32_t *)val); + setting_to_static_default = (firmware_defaults.get(param).i == *(int32_t *)val); break; case PARAM_TYPE_FLOAT: - setting_to_static_default = (fabsf(px4::parameters[param].val.f - * (float *)val) <= FLT_EPSILON); + setting_to_static_default = (fabsf(firmware_defaults.get(param).f - * (float *)val) <= FLT_EPSILON); break; } - // find if custom default value is already set - param_wbuf_s *s = nullptr; - - { - param_wbuf_s key{}; - key.param = param; - s = (param_wbuf_s *)utarray_find(param_custom_default_values, &key, param_compare_values); - } - if (setting_to_static_default) { - if (s != nullptr) { - // param in memory and set to non-default value, clear - int pos = utarray_eltidx(param_custom_default_values, s); - utarray_erase(param_custom_default_values, pos, 1); - } + runtime_defaults.reset(param); - // do nothing if param not already set and being set to default - params_custom_default.set(param, false); result = PX4_OK; } else { - if (s == nullptr) { - // construct a new parameter default value - param_wbuf_s buf{}; - buf.param = param; - - // add it to the array and sort - utarray_push_back(param_custom_default_values, &buf); - utarray_sort(param_custom_default_values, param_compare_values); - - // find it after sorting - s = (param_wbuf_s *)utarray_find(param_custom_default_values, &buf, param_compare_values); - } - - if (s != nullptr) { - // update the default value - switch (param_type(param)) { - case PARAM_TYPE_INT32: - s->val.i = *(int32_t *)val; - params_custom_default.set(param, true); - result = PX4_OK; - break; - - case PARAM_TYPE_FLOAT: - s->val.f = *(float *)val; - params_custom_default.set(param, true); - result = PX4_OK; - break; - - default: + switch (param_type(param)) { + case PARAM_TYPE_INT32: { + const bool store_ok = runtime_defaults.store(param, param_value_u{.i = *(int32_t *) val}); + user_config.refresh(param); + result = store_ok ? PX4_OK : PX4_ERROR; break; } + + case PARAM_TYPE_FLOAT: { + const bool store_ok = runtime_defaults.store(param, param_value_u{.f = *(float *) val}); + user_config.refresh(param); + result = store_ok ? PX4_OK : PX4_ERROR; + break; + } + + default: + break; } } - param_unlock_writer(); if ((result == PX4_OK) && param_used(param)) { // send notification if param is already in use @@ -914,38 +608,23 @@ int param_set_default_value(param_t param, const void *val) return result; } -static int param_reset_internal(param_t param, bool notify = true) +static int param_reset_internal(param_t param, bool notify = true, bool autosave = true) { - param_wbuf_s *s = nullptr; - bool param_found = false; - - param_lock_writer(); + bool param_found = user_config.contains(param); if (handle_in_range(param)) { - /* look for a saved value */ - s = param_find_changed(param); - - /* if we found one, erase it */ - if (s != nullptr) { - int pos = utarray_eltidx(param_values, s); - utarray_erase(param_values, pos, 1); - } - - params_changed.set(param, false); - params_unsaved.set(param, true); - - param_found = true; + user_config.reset(param); } - param_autosave(); + if (autosave) { + param_autosave(); + } - param_unlock_writer(); - - if (s != nullptr && notify) { + if (param_found && notify) { param_notify_changes(); } - return (!param_found); + return param_found; } int param_reset(param_t param) { return param_reset_internal(param, true); } @@ -954,23 +633,14 @@ int param_reset_no_notification(param_t param) { return param_reset_internal(par static void param_reset_all_internal(bool auto_save) { - param_lock_writer(); - - if (param_values != nullptr) { - utarray_free(param_values); - - params_changed.reset(); + for (param_t param = 0; handle_in_range(param); param++) { + param_reset_internal(param, false, false); } - /* mark as reset / deleted */ - param_values = nullptr; - if (auto_save) { param_autosave(); } - param_unlock_writer(); - param_notify_changes(); } @@ -1097,17 +767,19 @@ static int param_verify(int fd); int param_save_default() { PX4_DEBUG("param_save_default"); + + // take the file lock + if (pthread_mutex_trylock(&file_mutex) != 0) { + PX4_ERR("param_save_default: file lock failed (already locked)"); + return PX4_ERROR; + } + int shutdown_lock_ret = px4_shutdown_lock(); if (shutdown_lock_ret != 0) { PX4_ERR("px4_shutdown_lock() failed (%i)", shutdown_lock_ret); } - // take the file lock - do {} while (px4_sem_wait(¶m_sem_save) != 0); - - param_lock_reader(); - int res = PX4_ERROR; const char *filename = param_get_default_file(); @@ -1174,8 +846,7 @@ int param_save_default() } } - param_unlock_reader(); - px4_sem_post(¶m_sem_save); + pthread_mutex_unlock(&file_mutex); if (shutdown_lock_ret == 0) { px4_shutdown_unlock(); @@ -1336,9 +1007,10 @@ param_export(const char *filename, param_filter_func filter) } // take the file lock - do {} while (px4_sem_wait(¶m_sem_save) != 0); - - param_lock_reader(); + if (pthread_mutex_trylock(&file_mutex) != 0) { + PX4_ERR("param_export: file lock failed (already locked)"); + return PX4_ERROR; + } int fd = ::open(filename, O_RDWR | O_CREAT, PX4_O_MODE_666); int result = PX4_ERROR; @@ -1354,8 +1026,7 @@ param_export(const char *filename, param_filter_func filter) perf_end(param_export_perf); - param_unlock_reader(); - px4_sem_post(¶m_sem_save); + pthread_mutex_unlock(&file_mutex); if (shutdown_lock_ret == 0) { px4_shutdown_unlock(); @@ -1370,7 +1041,6 @@ static int param_export_internal(int fd, param_filter_func filter) PX4_DEBUG("param_export_internal"); int result = -1; - param_wbuf_s *s = nullptr; bson_encoder_s encoder{}; uint8_t bson_buffer[256]; @@ -1383,50 +1053,42 @@ static int param_export_internal(int fd, param_filter_func filter) goto out; } - // no modified parameters, export empty BSON document - if (param_values == nullptr) { - result = 0; - goto out; - } - - while ((s = (struct param_wbuf_s *)utarray_next(param_values, s)) != nullptr) { - if (filter && !filter(s->param)) { + for (param_t param = 0; handle_in_range(param); param++) { + if (filter && !filter(param)) { continue; } // don't export default values - switch (param_type(s->param)) { + switch (param_type(param)) { case PARAM_TYPE_INT32: { - int32_t default_value = 0; - param_get_default_value_internal(s->param, &default_value); + int32_t default_value = runtime_defaults.get(param).i; - if (s->val.i == default_value) { - PX4_DEBUG("skipping %s %" PRIi32 " export", param_name(s->param), default_value); + if (user_config.get(param).i == default_value) { + PX4_DEBUG("skipping %s %" PRIi32 " export", param_name(param), default_value); continue; } } break; case PARAM_TYPE_FLOAT: { - float default_value = 0; - param_get_default_value_internal(s->param, &default_value); + float default_value = runtime_defaults.get(param).f; - if (fabsf(s->val.f - default_value) <= FLT_EPSILON) { - PX4_DEBUG("skipping %s %.3f export", param_name(s->param), (double)default_value); + if (fabsf(user_config.get(param).f - default_value) <= FLT_EPSILON) { + PX4_DEBUG("skipping %s %.3f export", param_name(param), (double)default_value); continue; } } break; } - const char *name = param_name(s->param); - const size_t size = param_size(s->param); + const char *name = param_name(param); + const size_t size = param_size(param); /* append the appropriate BSON type object */ - switch (param_type(s->param)) { + switch (param_type(param)) { case PARAM_TYPE_INT32: { - const int32_t i = s->val.i; - PX4_DEBUG("exporting: %s (%d) size: %lu val: %" PRIi32, name, s->param, (long unsigned int)size, i); + const int32_t i = user_config.get(param).i; + PX4_DEBUG("exporting: %s (%d) size: %lu val: %" PRIi32, name, param, (long unsigned int)size, i); if (bson_encoder_append_int32(&encoder, name, i) != 0) { PX4_ERR("BSON append failed for '%s'", name); @@ -1436,8 +1098,8 @@ static int param_export_internal(int fd, param_filter_func filter) break; case PARAM_TYPE_FLOAT: { - const double f = (double)s->val.f; - PX4_DEBUG("exporting: %s (%d) size: %lu val: %.3f", name, s->param, (long unsigned int)size, (double)f); + const double f = (double)user_config.get(param).f; + PX4_DEBUG("exporting: %s (%d) size: %lu val: %.3f", name, param, (long unsigned int)size, (double)f); if (bson_encoder_append_double(&encoder, name, f) != 0) { PX4_ERR("BSON append failed for '%s'", name); @@ -1447,7 +1109,7 @@ static int param_export_internal(int fd, param_filter_func filter) break; default: - PX4_ERR("%s unrecognized parameter type %d, skipping export", name, param_type(s->param)); + PX4_ERR("%s unrecognized parameter type %d, skipping export", name, param_type(param)); } } @@ -1606,7 +1268,7 @@ param_foreach(void (*func)(void *arg, param_t param), void *arg, bool only_chang for (param = 0; handle_in_range(param); param++) { /* if requested, skip unchanged values */ - if (only_changed && (param_find_changed(param) == nullptr)) { + if (only_changed && (!user_config.contains(param))) { continue; } @@ -1622,8 +1284,6 @@ uint32_t param_hash_check() { uint32_t param_hash = 0; - param_lock_reader(); - /* compute the CRC32 over all string param names and 4 byte values */ for (param_t param = 0; handle_in_range(param); param++) { if (!param_used(param) || param_is_volatile(param)) { @@ -1631,13 +1291,12 @@ uint32_t param_hash_check() } const char *name = param_name(param); - const void *val = param_get_value_ptr(param); + auto value = user_config.get(param).i; + const void *val = (void *)&value; param_hash = crc32part((const uint8_t *)name, strlen(name), param_hash); param_hash = crc32part((const uint8_t *)val, param_size(param), param_hash); } - param_unlock_reader(); - return param_hash; } @@ -1658,16 +1317,12 @@ void param_print_status() #endif /* FLASH_BASED_PARAMS */ - if (param_values != nullptr) { - PX4_INFO("storage array: %d/%d elements (%zu bytes total)", - utarray_len(param_values), param_values->n, param_values->n * sizeof(UT_icd)); - } + PX4_INFO("storage array: %d/%d elements (%zu bytes total)", + user_config.size(), firmware_defaults.size(), (size_t)user_config.byteSize()); - if (param_custom_default_values != nullptr) { - PX4_INFO("storage array (custom defaults): %d/%d elements (%zu bytes total)", - utarray_len(param_custom_default_values), param_custom_default_values->n, - param_custom_default_values->n * sizeof(UT_icd)); - } + + PX4_INFO("storage array (custom defaults): %d/%d elements (%zu bytes total)", + runtime_defaults.size(), firmware_defaults.size(), (size_t)runtime_defaults.byteSize()); PX4_INFO("auto save: %s", autosave_disabled ? "off" : "on"); diff --git a/src/lib/parameters/uthash/doc/utarray.txt b/src/lib/parameters/uthash/doc/utarray.txt deleted file mode 100644 index 37830f1244..0000000000 --- a/src/lib/parameters/uthash/doc/utarray.txt +++ /dev/null @@ -1,376 +0,0 @@ -utarray: dynamic array macros for C -=================================== -Troy D. Hanson -v1.9.5, November 2011 - -include::sflogo.txt[] -include::topnav_utarray.txt[] - -Introduction ------------- -include::toc.txt[] - -A set of general-purpose dynamic array macros for C structures are included with -uthash in `utarray.h`. To use these macros in your own C program, just -copy `utarray.h` into your source directory and use it in your programs. - - #include "utarray.h" - -The dynamic array supports basic operations such as push, pop, and erase on the -array elements. These array elements can be any simple datatype or structure. -The array <> are based loosely on the C++ STL vector methods. - -Internally the dynamic array contains a contiguous memory region into which -the elements are copied. This buffer is grown as needed using `realloc` to -accomodate all the data that is pushed into it. - -Download -~~~~~~~~ -To download the `utarray.h` header file, follow the link on the -http://uthash.sourceforge.net[uthash home page]. - -BSD licensed -~~~~~~~~~~~~ -This software is made available under the -link:license.html[revised BSD license]. -It is free and open source. - -Platforms -~~~~~~~~~ -The 'utarray' macros have been tested on: - - * Linux, - * Mac OS X, - * Windows, using Visual Studio 2008 and Visual Studio 2010 - -Usage ------ - -Declaration -~~~~~~~~~~~ - -The array itself has the data type `UT_array`, regardless of the type of -elements to be stored in it. It is declared like, - - UT_array *nums; - -New and free -~~~~~~~~~~~~ -The next step is to create the array using `utarray_new`. Later when you're -done with the array, `utarray_free` will free it and all its elements. - -Push, pop, etc -~~~~~~~~~~~~~~ -The central features of the utarray involve putting elements into it, taking -them out, and iterating over them. There are several <> -to pick from that deal with either single elements or ranges of elements at a -time. In the examples below we will use only the push operation to insert -elements. - -Elements --------- - -Support for dynamic arrays of integers or strings is especially easy. These are -best shown by example: - -Integers -~~~~~~~~ -This example makes a utarray of integers, pushes 0-9 into it, then prints it. -Lastly it frees it. - -.Integer elements -------------------------------------------------------------------------------- -#include -#include "utarray.h" - -int main() { - UT_array *nums; - int i, *p; - - utarray_new(nums,&ut_int_icd); - for(i=0; i < 10; i++) utarray_push_back(nums,&i); - - for(p=(int*)utarray_front(nums); - p!=NULL; - p=(int*)utarray_next(nums,p)) { - printf("%d\n",*p); - } - - utarray_free(nums); - - return 0; -} -------------------------------------------------------------------------------- - -The second argument to `utarray_push_back` is always a 'pointer' to the type -(so a literal cannot be used). So for integers, it is an `int*`. - -Strings -~~~~~~~ -In this example we make a utarray of strings, push two strings into it, print -it and free it. - -.String elements -------------------------------------------------------------------------------- -#include -#include "utarray.h" - -int main() { - UT_array *strs; - char *s, **p; - - utarray_new(strs,&ut_str_icd); - - s = "hello"; utarray_push_back(strs, &s); - s = "world"; utarray_push_back(strs, &s); - p = NULL; - while ( (p=(char**)utarray_next(strs,p))) { - printf("%s\n",*p); - } - - utarray_free(strs); - - return 0; -} -------------------------------------------------------------------------------- - -In this example, since the element is a `char*`, we pass a pointer to it -(`char**`) as the second argument to `utarray_push_back`. Note that "push" makes -a copy of the source string and pushes that copy into the array. - -About UT_icd -~~~~~~~~~~~~ - -Arrays be made of any type of element, not just integers and strings. The -elements can be basic types or structures. Unless you're dealing with integers -and strings (which use pre-defined `ut_int_icd` and `ut_str_icd`), you'll need -to define a `UT_icd` helper structure. This structure contains everything that -utarray needs to initialize, copy or destruct elements. - - typedef struct { - size_t sz; - init_f *init; - ctor_f *copy; - dtor_f *dtor; - } UT_icd; - -The three function pointers `init`, `copy`, and `dtor` have these prototypes: - - typedef void (ctor_f)(void *dst, const void *src); - typedef void (dtor_f)(void *elt); - typedef void (init_f)(void *elt); - -The `sz` is just the size of the element being stored in the array. - -The `init` function will be invoked whenever utarray needs to initialize an -empty element. This only happens as a byproduct of `utarray_resize` or -`utarray_extend_back`. If `init` is `NULL`, it defaults to zero filling the -new element using memset. - -The `copy` function is used whenever an element is copied into the array. -It is invoked during `utarray_push_back`, `utarray_insert`, `utarray_inserta`, -or `utarray_concat`. If `copy` is `NULL`, it defaults to a bitwise copy using -memcpy. - -The `dtor` function is used to clean up an element that is being removed from -the array. It may be invoked due to `utarray_resize`, `utarray_pop_back`, -`utarray_erase`, `utarray_clear`, `utarray_done` or `utarray_free`. If the -elements need no cleanup upon destruction, `dtor` may be `NULL`. - -Scalar types -~~~~~~~~~~~~ - -The next example uses `UT_icd` with all its defaults to make a utarray of -`long` elements. This example pushes two longs, prints them, and frees the -array. - -.long elements -------------------------------------------------------------------------------- -#include -#include "utarray.h" - -UT_icd long_icd = {sizeof(long), NULL, NULL, NULL }; - -int main() { - UT_array *nums; - long l, *p; - utarray_new(nums, &long_icd); - - l=1; utarray_push_back(nums, &l); - l=2; utarray_push_back(nums, &l); - - p=NULL; - while( (p=(long*)utarray_next(nums,p))) printf("%ld\n", *p); - - utarray_free(nums); - return 0; -} -------------------------------------------------------------------------------- - -Structures -~~~~~~~~~~ - -Structures can be used as utarray elements. If the structure requires no -special effort to initialize, copy or destruct, we can use `UT_icd` with all -its defaults. This example shows a structure that consists of two integers. Here -we push two values, print them and free the array. - -.Structure (simple) -------------------------------------------------------------------------------- -#include -#include "utarray.h" - -typedef struct { - int a; - int b; -} intpair_t; - -UT_icd intpair_icd = {sizeof(intpair_t), NULL, NULL, NULL}; - -int main() { - - UT_array *pairs; - intpair_t ip, *p; - utarray_new(pairs,&intpair_icd); - - ip.a=1; ip.b=2; utarray_push_back(pairs, &ip); - ip.a=10; ip.b=20; utarray_push_back(pairs, &ip); - - for(p=(intpair_t*)utarray_front(pairs); - p!=NULL; - p=(intpair_t*)utarray_next(pairs,p)) { - printf("%d %d\n", p->a, p->b); - } - - utarray_free(pairs); - return 0; -} -------------------------------------------------------------------------------- - -The real utility of `UT_icd` is apparent when the elements of the utarray are -structures that require special work to initialize, copy or destruct. - -For example, when a structure contains pointers to related memory areas that -need to be copied when the structure is copied (and freed when the structure is -freed), we can use custom `init`, `copy`, and `dtor` members in the `UT_icd`. - -Here we take an example of a structure that contains an integer and a string. -When this element is copied (such as when an element is pushed into the array), -we want to "deep copy" the `s` pointer (so the original element and the new -element point to their own copies of `s`). When an element is destructed, we -want to "deep free" its copy of `s`. Lastly, this example is written to work -even if `s` has the value `NULL`. - -.Structure (complex) -------------------------------------------------------------------------------- -#include -#include -#include "utarray.h" - -typedef struct { - int a; - char *s; -} intchar_t; - -void intchar_copy(void *_dst, const void *_src) { - intchar_t *dst = (intchar_t*)_dst, *src = (intchar_t*)_src; - dst->a = src->a; - dst->s = src->s ? strdup(src->s) : NULL; -} - -void intchar_dtor(void *_elt) { - intchar_t *elt = (intchar_t*)_elt; - if (elt->s) free(elt->s); -} - -UT_icd intchar_icd = {sizeof(intchar_t), NULL, intchar_copy, intchar_dtor}; - -int main() { - UT_array *intchars; - intchar_t ic, *p; - utarray_new(intchars, &intchar_icd); - - ic.a=1; ic.s="hello"; utarray_push_back(intchars, &ic); - ic.a=2; ic.s="world"; utarray_push_back(intchars, &ic); - - p=NULL; - while( (p=(intchar_t*)utarray_next(intchars,p))) { - printf("%d %s\n", p->a, (p->s ? p->s : "null")); - } - - utarray_free(intchars); - return 0; -} - -------------------------------------------------------------------------------- - -[[operations]] -Reference ---------- -This table lists all the utarray operations. These are loosely based on the C++ -vector class. - -Operations -~~~~~~~~~~ - -[width="100%",cols="50 /* size_t */ -#include /* memset, etc */ -#include /* exit */ - -// FIXME: this needs to be checked: we need to handle OOM properly instead of just exiting -#define oom() system_exit(-1) - -typedef void (ctor_f)(void *dst, const void *src); -typedef void (dtor_f)(void *elt); -typedef void (init_f)(void *elt); -typedef struct { - size_t sz; - init_f *init; - ctor_f *copy; - dtor_f *dtor; -} UT_icd; - -typedef struct { - unsigned i,n;/* i: index of next available slot, n: num slots */ - UT_icd icd; /* initializer, copy and destructor functions */ - char *d; /* n slots of size icd->sz*/ -} UT_array; - -#define utarray_init(a,_icd) do { \ - memset(a,0,sizeof(UT_array)); \ - (a)->icd=*_icd; \ -} while(0) - -#define utarray_done(a) do { \ - if ((a)->n) { \ - if ((a)->icd.dtor) { \ - size_t _ut_i; \ - for(_ut_i=0; _ut_i < (a)->i; _ut_i++) { \ - (a)->icd.dtor(utarray_eltptr(a,_ut_i)); \ - } \ - } \ - free((a)->d); \ - } \ - (a)->n=0; \ -} while(0) - -#define utarray_new(a,_icd) do { \ - a=(UT_array*)malloc(sizeof(UT_array)); \ - utarray_init(a,_icd); \ -} while(0) - -#define utarray_free(a) do { \ - utarray_done(a); \ - free(a); \ -} while(0) - -#define utarray_reserve(a,by) do { \ - if (((a)->i+by) > ((a)->n)) { \ - while(((a)->i+by) > ((a)->n)) { (a)->n = ((a)->n ? (2*(a)->n) : 8); } \ - if ( ((a)->d=(char*)realloc((a)->d, (a)->n*(a)->icd.sz)) == NULL) oom(); \ - } \ -} while(0) - -#define utarray_push_back(a,p) do { \ - utarray_reserve(a,1); \ - if ((a)->icd.copy) { (a)->icd.copy( _utarray_eltptr(a,(a)->i++), p); } \ - else { memcpy(_utarray_eltptr(a,(a)->i++), p, (a)->icd.sz); }; \ -} while(0) - -#define utarray_pop_back(a) do { \ - if ((a)->icd.dtor) { (a)->icd.dtor( _utarray_eltptr(a,--((a)->i))); } \ - else { (a)->i--; } \ -} while(0) - -#define utarray_extend_back(a) do { \ - utarray_reserve(a,1); \ - if ((a)->icd.init) { (a)->icd.init(_utarray_eltptr(a,(a)->i)); } \ - else { memset(_utarray_eltptr(a,(a)->i),0,(a)->icd.sz); } \ - (a)->i++; \ -} while(0) - -#define utarray_len(a) ((a)->i) - -#define utarray_eltptr(a,j) (((j) < (a)->i) ? _utarray_eltptr(a,j) : NULL) -#define _utarray_eltptr(a,j) ((char*)((a)->d + ((a)->icd.sz*(j) ))) - -#define utarray_insert(a,p,j) do { \ - utarray_reserve(a,1); \ - if (j > (a)->i) break; \ - if ((j) < (a)->i) { \ - memmove( _utarray_eltptr(a,(j)+1), _utarray_eltptr(a,j), \ - ((a)->i - (j))*((a)->icd.sz)); \ - } \ - if ((a)->icd.copy) { (a)->icd.copy( _utarray_eltptr(a,j), p); } \ - else { memcpy(_utarray_eltptr(a,j), p, (a)->icd.sz); }; \ - (a)->i++; \ -} while(0) - -#define utarray_inserta(a,w,j) do { \ - if (utarray_len(w) == 0) break; \ - if (j > (a)->i) break; \ - utarray_reserve(a,utarray_len(w)); \ - if ((j) < (a)->i) { \ - memmove(_utarray_eltptr(a,(j)+utarray_len(w)), \ - _utarray_eltptr(a,j), \ - ((a)->i - (j))*((a)->icd.sz)); \ - } \ - if ((a)->icd.copy) { \ - size_t _ut_i; \ - for(_ut_i=0;_ut_i<(w)->i;_ut_i++) { \ - (a)->icd.copy(_utarray_eltptr(a,j+_ut_i), _utarray_eltptr(w,_ut_i)); \ - } \ - } else { \ - memcpy(_utarray_eltptr(a,j), _utarray_eltptr(w,0), \ - utarray_len(w)*((a)->icd.sz)); \ - } \ - (a)->i += utarray_len(w); \ -} while(0) - -#define utarray_resize(dst,num) do { \ - size_t _ut_i; \ - if (dst->i > (size_t)(num)) { \ - if ((dst)->icd.dtor) { \ - for(_ut_i=num; _ut_i < dst->i; _ut_i++) { \ - (dst)->icd.dtor(utarray_eltptr(dst,_ut_i)); \ - } \ - } \ - } else if (dst->i < (size_t)(num)) { \ - utarray_reserve(dst,num-dst->i); \ - if ((dst)->icd.init) { \ - for(_ut_i=dst->i; _ut_i < num; _ut_i++) { \ - (dst)->icd.init(utarray_eltptr(dst,_ut_i)); \ - } \ - } else { \ - memset(_utarray_eltptr(dst,dst->i),0,(dst)->icd.sz*(num-dst->i)); \ - } \ - } \ - dst->i = num; \ -} while(0) - -#define utarray_concat(dst,src) do { \ - utarray_inserta((dst),(src),utarray_len(dst)); \ -} while(0) - -#define utarray_erase(a,pos,len) do { \ - if ((a)->icd.dtor) { \ - size_t _ut_i; \ - for(_ut_i=0; _ut_i < len; _ut_i++) { \ - (a)->icd.dtor(utarray_eltptr((a),pos+_ut_i)); \ - } \ - } \ - if ((a)->i > (pos+len)) { \ - memmove( _utarray_eltptr((a),pos), _utarray_eltptr((a),pos+len), \ - (((a)->i)-(pos+len))*((a)->icd.sz)); \ - } \ - (a)->i -= (len); \ -} while(0) - -#define utarray_renew(a,u) do { \ - if (a) utarray_clear(a); \ - else utarray_new((a),(u)); \ -} while(0) - -#define utarray_clear(a) do { \ - if ((a)->i > 0) { \ - if ((a)->icd.dtor) { \ - size_t _ut_i; \ - for(_ut_i=0; _ut_i < (a)->i; _ut_i++) { \ - (a)->icd.dtor(utarray_eltptr(a,_ut_i)); \ - } \ - } \ - (a)->i = 0; \ - } \ -} while(0) - -#define utarray_sort(a,cmp) do { \ - qsort((a)->d, (a)->i, (a)->icd.sz, cmp); \ -} while(0) - -#define utarray_find(a,v,cmp) bsearch((v),(a)->d,(a)->i,(a)->icd.sz,cmp) - -#define utarray_front(a) (((a)->i) ? (_utarray_eltptr(a,0)) : NULL) -#define utarray_next(a,e) (((e)==NULL) ? utarray_front(a) : ((((a)->i) > (utarray_eltidx(a,e)+1)) ? _utarray_eltptr(a,utarray_eltidx(a,e)+1) : NULL)) -#define utarray_prev(a,e) (((e)==NULL) ? utarray_back(a) : ((utarray_eltidx(a,e) > 0) ? _utarray_eltptr(a,utarray_eltidx(a,e)-1) : NULL)) -#define utarray_back(a) (((a)->i) ? (_utarray_eltptr(a,(a)->i-1)) : NULL) -#define utarray_eltidx(a,e) (((char*)(e) >= (char*)((a)->d)) ? (((char*)(e) - (char*)((a)->d))/(a)->icd.sz) : -1) - -/* last we pre-define a few icd for common utarrays of ints and strings */ -static void utarray_str_cpy(void *dst, const void *src) { - char **_src = (char**)src, **_dst = (char**)dst; - *_dst = (*_src == NULL) ? NULL : strdup(*_src); -} -static void utarray_str_dtor(void *elt) { - char **eltc = (char**)elt; - if (*eltc) free(*eltc); -} -static const UT_icd ut_str_icd _UNUSED_ = {sizeof(char*),NULL,utarray_str_cpy,utarray_str_dtor}; -static const UT_icd ut_int_icd _UNUSED_ = {sizeof(int),NULL,NULL,NULL}; -static const UT_icd ut_ptr_icd _UNUSED_ = {sizeof(void*),NULL,NULL,NULL}; - - -#endif /* UTARRAY_H */