Изменчивые переменные-члены против изменчивого объекта? - PullRequest
1 голос
/ 09 февраля 2020

Я пытаюсь реализовать очередь для нескольких производителей (через прерывание) и одного потребителя (через поток приложения) на встроенной цели в "MpscQueue.h" ниже.

Мне интересно, могу ли я безопасно удалите часть из volatile, использованную ниже (см. встроенные вопросы). Я также рассмотрел бы использование volatile std::array вместо C -тиля buffer_[], показанного ниже, но я не был уверен, смогу ли я доверять его реализации, чтобы соответствовать цели ниже. Третий вариант - пометить сам объект MpscQueue как volatile и квалифицировать соответствующие методы volatile, но не ясно, приведет ли это к тому, что все переменные-члены (и на что они указывают в случае указателей) будут рассматривается как изменчивый.

Есть ли какие-либо указания по этому поводу?

template<typename T, uint32_t depth>
class MpscQueue
{
public:
    MpscQueue(void);
    bool push(T& t);
    bool pop(T* const t);

private:
    T volatile buffer_[depth];  // Q1: is volatile unnecessary if never access buffer_[]?
    T volatile* const begin_;   // Q2: is volatile unnecessary if never access value
    T volatile* const end_;     //     via begin_/end_?
    T volatile* head_;          // volatile required so that thread always checks value
    T volatile* volatile tail_; // Q3: is 'T volatile' required so that ISR accounts
                                //     for other ISRs when setting value?
                                // Q4: is '* volatile' required so that ISR accounts
                                //     for other ISRs when checking pointer?
};

template<typename T, uint32_t depth>
MpscQueue<T, depth>::MpscQueue(void) :
    begin_(&buffer_[0]),
    end_(&buffer_[depth - 1]),
    head_(begin_),
    tail_(begin_)
{}

template<typename T, uint32_t depth>
bool MpscQueue<T, depth>::push(T& t)
{
    // "Multiple producer" ISRs can use this function to push at tail

    // Pseudo-code: if not full, *(tail_++) = t
}

template<typename T, uint32_t depth>
bool MpscQueue<T, depth>::pop(T* const t)
{
    // "Single consumer" thread can use this function to pop at head

    // Pseudo-code: if not empty, *t = *(head_++)
}

Редактировать: Чтобы сфокусировать вопрос в правильном направлении, позвольте мне уточнить, что я позаботился о безопасности потоков и здесь это не является частью вопроса.

Поскольку эта очередь является одиночным потребителем, не требуется безопасность потоков на стороне чтения / вывода. Со стороны write / pu sh безопасность потока между прерываниями будет обрабатываться путем установки всех соответствующих прерываний на один и тот же уровень приоритета (в противном случае будет использоваться блокировка).

Ответы [ 2 ]

1 голос
/ 09 февраля 2020

Как написано, код не защищен от прерываний - если прерывание происходит, когда основной поток выполняет чтение / всплывающее окно, возникает состояние гонки, и структура данных, вероятно, повреждена. Способ исправить это - блокировать прерывания в основном потоке всякий раз, когда он выполняет чтение / всплывающее окно. Если вы сделаете это (а функции, которые блокируют / разблокируют прерывания, являются барьерами памяти), все летучие компоненты становятся неактуальными и могут быть удалены.

Volatile в значительной степени бесполезно для синхронизации потоков - его основное использование - для взаимодействия с отображенными в память устройствами.

0 голосов
/ 10 февраля 2020

Вот что я буду делать с закрытыми переменными-членами, с обоснованием в комментариях:

T volatile buffer_[depth];  // will never touch buffer_[] via array handle,
                            //   but don't want compiler to optimize it out;
                            //   and technically, the elements are volatile due to push()
T volatile* const begin_;   // buffer_[] has elements of type 'T volatile', so
                            //   keep type of pointer consistent with what it points to
T volatile* const end_;     // "
T volatile* volatile head_; // value must be volatile, as unknown ISR thread will touch;
                            //   also, keep type of pointer consistent
                            // pointer should be volatile since ISRs will read outside
                            //   of "main" thread context
T volatile* volatile tail_; // value should be volatile since multiple ISRs will touch;
                            //   also, keep type of pointer consistent
                            // pointer should be volatile since multiple ISRs will touch

Если бы я использовал std::array вместо buffer_[], я не уверен, как бы я применял что не только элементы массива, но и базовые указатели / итераторы также являются нестабильными. Например, std::array<T volatile, uint32_t depth> volatile?

Если бы я сделал весь объект MpscQueue энергозависимым, я не уверен, каким образом я бы обеспечил, чтобы «волатильность» распространялась не только на сами указатели (т.е. * volatile), но также и к указанным значениям (т. е. T volatile* volatile вместо просто T* volatile).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...