В настоящее время я работаю с микроконтроллерами Atmel AVR (gcc), но хотел бы, чтобы ответ применим к миру микроконтроллеров в целом, то есть обычно однопоточным, но с прерываниями.
Я знаю, как использовать volatile
в коде C при доступе к переменной, которая может быть изменена в ISR. Например:
uint8_t g_pushIndex = 0;
volatile uint8_t g_popIndex = 0;
uint8_t g_values[QUEUE_SIZE];
void waitForEmptyQueue()
{
bool isQueueEmpty = false;
while (!isQueueEmpty)
{
// Disable interrupts to ensure atomic access.
cli();
isQueueEmpty = (g_pushIndex == g_popIndex);
sei();
}
}
ISR(USART_UDRE_vect) // some interrupt routine
{
// Interrupts are disabled here.
if (g_pushIndex == g_popIndex)
{
usart::stopTransfer();
}
else
{
uint8_t value = g_values[g_popIndex++];
g_popIndex &= MASK;
usart::transmit(value);
}
}
Поскольку g_popIndex модифицируется внутри ISR и доступен вне ISR, он должен быть объявлен volatile
, чтобы указать компилятору не оптимизировать доступ к памяти для этой переменной. Обратите внимание, что, если я не ошибаюсь, g_pushIndex
и g_values
не нужно объявлять volatile
, поскольку они не изменяются ISR.
Я хочу инкапсулировать код, связанный с очередью, внутри класса, чтобы его можно было повторно использовать:
class Queue
{
public:
Queue()
: m_pushIndex(0)
, m_popIndex(0)
{
}
inline bool isEmpty() const
{
return (m_pushIndex == m_popIndex);
}
inline uint8_t pop()
{
uint8_t value = m_values[m_popIndex++];
m_popIndex &= MASK;
return value;
}
// other useful functions here...
private:
uint8_t m_pushIndex;
uint8_t m_popIndex;
uint8_t m_values[QUEUE_SIZE];
};
Queue g_queue;
void waitForEmptyQueue()
{
bool isQueueEmpty = false;
while (!isQueueEmpty)
{
// Disable interrupts to ensure atomic access.
cli();
isQueueEmpty = g_queue.isEmpty();
sei();
}
}
ISR(USART_UDRE_vect) // some interrupt routine
{
// Interrupts are disabled here.
if (g_queue.isEmpty())
{
usart::stopTransfer();
}
else
{
usart::transmit(g_queue.pop());
}
}
Приведенный выше код, возможно, более читабелен. Однако что в этом случае делать с volatile
?
1) Это еще нужно? Обеспечивает ли вызов метода Queue::isEmpty()
неоптимизированный доступ к g_queue.m_popIndex
, даже если функция объявлена inline
? Сомневаюсь. Я знаю, что компиляторы используют эвристику, чтобы определить, не следует ли оптимизировать доступ, но мне не нравится полагаться на такую эвристику в качестве общего решения.
2) Я думаю, что рабочее (и эффективное) решение - объявить член Queue::m_popIndex
volatile
внутри определения класса. Однако мне не нравится это решение, потому что разработчик класса Queue
должен точно знать, как он будет использоваться, чтобы узнать, какая переменная-член должна быть volatile
. Он не будет хорошо масштабироваться с будущими изменениями кода. Кроме того, все Queue
экземпляры теперь будут иметь volatile
член, даже если некоторые из них не используются внутри ISR.
3) Если посмотреть на класс Queue
, как если бы он был встроенным, я думаю, что естественным решением было бы объявить сам глобальный экземпляр g_queue
как volatile
, поскольку он модифицируется в ISR и доступен вне ISR. Однако это не работает, потому что для volatile
объектов можно вызывать только volatile
функции. Внезапно все функции-члены Queue
должны быть объявлены volatile
(а не только const
или те, которые используются внутри ISR). Опять же, как разработчик Queue
может знать это заранее? Кроме того, это наказывает всех Queue
пользователей. По-прежнему существует возможность дублировать все функции-члены и иметь в классе как volatile
, так и не volatile
перегрузки, так что пользователи, не являющиеся volatile
, не будут наказаны. Не очень.
4) Класс Queue
можно создать по шаблону для класса политики, который может дополнительно добавлять volatile
ко всем своим переменным-членам только при необходимости. Опять же, разработчик класса должен знать это заранее, и решение сложнее для понимания, ну да ладно.
Мне любопытно узнать, не хватает ли мне более простого решения этой проблемы. В качестве примечания, я компилирую без поддержки C ++ 11/14 (пока).
cli
иsei
барьеры памяти? - person David Schwartz   schedule 04.03.2015