2016-03-03 2 views
5

Поскольку мой компилятор все еще не поддерживает C++ 11 и std :: atomic, я вынужден реализовать его вручную через пару ldrex-strex.Atomic int64_t на ARM Cortex M3

Мой вопрос: какой правильный способ «атомарно» читать-модифицировать-писать int64_t с помощью ldrex и strex?

Простое решение, как это не похоже на работу (один из STREXW возвращает 1 все время):

volatile int64_t value; 
int64_t temp; 

do 
{ 
    int32_t low = __LDREXW((uint32_t *)&value); 
    int32_t high = __LDREXW(((uint32_t *)&value)+1); 

    temp = (int64_t)low | ((int64_t)high<<32); 
    temp++;  

} while(__STREXW(temp, (uint32_t *)&value) | __STREXW(temp>>32, ((uint32_t *)&value)+1)); 

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

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

+1

Это GCC? рассмотрите встроенную атомию GCC? – user3528438

+0

GCC для ARM теперь поддерживает std :: atomic. Нет, это не GCC, это Keil armcc. – Amomum

+0

См. [ARM AN321 - Cortex-M барьеры памяти] (http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dai0321a/BIHEADII.html). Atomic - это немного перегруженное слово. Вещи не могут произойти атомарно во вселенной. Они являются атомными по сравнению с некоторыми наборами электроники; mainline/interrupts, SMP, устройство на автобусе и т. д. В чем же ваша проблема? Вы должны это описать лучше. –

ответ

4

Это никогда не сработает, потому что вы не можете развести эксклюзивы таким образом. В реализации, локальный эксклюзивный монитор Cortex-M3 даже не отслеживает адрес - the exclusive reservation granule is the entire address space - поэтому предположение о слежении за каждым словом отдельно уже недействительно. Тем не менее, вы не должны даже рассмотреть любые детали реализации, так как архитектура уже явно исключает обратно к спине strex:

If two STREX instructions are executed without an intervening LDREX the second STREX returns a status value of 1. This means that:

  • Every STREX must have a preceding LDREX associated with it in a given thread of execution.
  • It is not necessary for every LDREX to have a subsequent STREX .

С Cortex-M3 (и ARMv7-M в целом) Безразлично» t есть ldrexd, как ARMv7-A, вам придется либо использовать отдельный замок для управления всеми обращениями к переменной, либо просто отключить прерывания вокруг read-modify-write. Если это вообще возможно, было бы лучше переделать вещи, не требующие атомарного 64-битного типа, в первую очередь, поскольку вы все равно только достигнете атомарности по отношению к другим потокам на одном ядре - вы просто не можете сделать 64 с точки зрения внешнего агента, как контроллер DMA.

+0

Я боялся этого. Итак, если я получаю это прямо сейчас, _any_ ldrex сбрасывает эксклюзивный монитор для _every other_ ldrex? – Amomum

+0

Спасибо, теперь я понял! – Amomum

+0

@Amomum Из-за внешнего вида, v7-M даже не имеет различий в локальном/глобальном мониторе - он читается как сокращенная версия конечного автомата локального монитора v7-A. Я предполагаю, что на практике аппаратное обеспечение, вероятно, даже не беспокоит отслеживание адреса и просто очищает исключительное состояние от любого доступа к памяти. – Notlikethat

1

Я бы просто посмотрел, как это делается gcc, и используйте одни и те же последовательности команд.

GCC 4.8.2 претензий реализовать std::atomic<int64_t> с is_lock_free() возвращает истину, даже с -mcpu=cortex-m3. К сожалению, на самом деле это не работает. Он делает код, который не связан или не работает, потому что нет реализации вспомогательных функций, которые он пытается использовать. (Спасибо @ Notlikethat за это.)

Here's the test code I tried. См. Старую версию этого ответа, если эта ссылка мертва. Я оставляю этот ответ на случай, если эта идея полезна для всех в связанных случаях, где gcc делает сделать полезный код.

+0

Я пробовал то, что вы предложили, с gcc 5.2.1 для руки и получил ошибку компоновщика: «e: \ gcc-arm-none-eabi-5_2-2015q4-20151219-win32 \ arm-none-eabi \ include \ C++ \ 5.2. 1 \ bits/atomic_base.h: 514: неопределенная ссылка на '__atomic_fetch_add_8 '" при создании std :: atomic ; std :: atomic работал должным образом. Выглядит вполне законно. – Amomum

+0

@Amomum: ваша gcc-установка обычно работает, когда ей нужны вспомогательные функции из 'libgcc.a'? Это либо ошибка gcc, либо неправильно установленная проблема с компилятором. 64-битное деление, скорее всего, вызовет аналогичную вспомогательную функцию. –

+0

разделение uint64_t вызывает __aeabi_uldivmod, как и ожидалось. Я не думаю, что это была ошибка, может быть, нет никакого способа реализовать атомный uint64_t?Совершенно нормально, что для некоторых архитектур не требуется uint8_t (если CHAR_BITS больше 8); поэтому я не думаю, что отсутствие __atomic_fetch_add_8 - это большая проблема. – Amomum