Может ли это быть выполнено атомарно? - PullRequest
5 голосов
/ 24 марта 2011

Я хотел бы знать, возможно ли гарантировать, что line выполняется атомарно, учитывая, что это может быть выполнено как в контексте ISR, так и в контексте Main. Я работаю над ARM9 (LPC313x) и использую RealView 4 (armcc).

foo() { 
  ..
  stack_var = ++volatile_var; // line
  ..
}

Я ищу любую подпрограмму, например _atomic_ для C166, код прямой сборки и т. Д. Я бы предпочел не отключать прерывания.

Большое спасибо.

Ответы [ 2 ]

8 голосов
/ 24 марта 2011

Нет, я не думаю, что вы когда-либо можете ожидать, что ++volatile_var будет атомарным, даже если у вас нет задания. Для этого используйте подходящий атомарный примитив. Если ваш компилятор не предоставляет такого расширения, вы легко найдете короткий встроенный ассемблер для этого в сети. Я думаю, что ассемблерные инструкции - ldrex и strex для атомного обмена на руке.

Редактировать: похоже, что конкретный тип процессора, который запрашивается в вопросе, не реализует эти инструкции.

Редактировать: Следующее должно работать с gcc, для другого компилятора, вероятно, придется адаптировать __asm__ части.

inline
size_t arm_ldrex(size_t volatile*ptr) {
  size_t ret;
  __asm__ volatile ("ldrex %0,[%1]\t@ load exclusive\n"
                    : "=&r" (ret)
                    : "r" (ptr)
                    : "cc", "memory"
                    );
  return ret;
}

inline
_Bool arm_strex(size_t volatile*ptr, size_t val) {
  size_t error;
  __asm__ volatile ("strex %0,%1,[%2]\t@ store exclusive\n"
                    : "=&r" (error)
                    : "r" (val), "r" (ptr)
                    : "cc", "memory"
                    );
  return !error;
}

inline
size_t atomic_add_fetch(size_t volatile *object, size_t operand) {
  for (;;) {
    size_t oldval = arm_ldrex(object);
    size_t newval = oldval + operand;
    if (arm_strex(object, newval)) return newval;
  }
}
6 голосов
/ 26 марта 2011

При быстром просмотре макрос C166 _atomic_, похоже, использует инструкцию, которая эффективно маскирует прерывания на время заданного количества инструкций. Нет ничего прямо соответствующего этому в архитектуре ARM.

Конечно, вы можете использовать инструкцию swp (или __swp, встроенную в цепочку инструментов RealView) для реализации блокировки вокруг критической секции. Упомянутый в другом ответе ldrex / strex не существует в версии 5 архитектуры ARM, которая включает в себя процессоры ARM9. http://infocenter.arm.com/help/topic/com.arm.doc.dui0491c/CJAHDCHB.html и http://infocenter.arm.com/help/topic/com.arm.doc.dui0489c/Chdbbbai.html соответственно.

Упрощенная реализация блокировки вокруг этого (с использованием цепочки инструментов RealView) будет:

{
    /* Loop until lock acquired */
    while (__swp(LOCKED, &lockvar) == LOCKED);
    ..
    /* Critical section */
    ..
    lockvar = UNLOCKED;
}

Однако это приведет к тупику в контексте ISR, когда основной поток удерживает блокировку.

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

...