Atomic test-and-set для микроконтроллера LPC1788

Я работаю с микроконтроллером NXP LPC1788 и разрабатываю многопоточное приложение на C. В части своего приложения я определяю пользовательскую структуру данных связанного списка. Раньше у меня были проблемы с моей программой из-за одновременного доступа к определенному списку, который я, кажется, решил, реализовав метод «получения блокировки» и метод «снятия блокировки» для списков, которые потоки могут вызывать перед доступом к самому списку.

Я сделал это, добавив член данных sema в структуру списка:

typedef struct linked_list
{
  list_node_t *head;
  list_node_t *tail;
  uint32_t len;
  NODE_ITEM_TYPE_T itemType;
  uint32_t itemSize;
  uint8_t sema;
} linked_list_t;

Мой метод «получения блокировки» приведен ниже:

void LIST_AcquireLock(linked_list_t *list)
{
  while(list->sema);
  list->sema = 1;
}

Мой метод «разблокировки блокировки» приведен ниже:

void LIST_ReleaseLock(linked_list_t *list)
{
  list->sema = 0;
}

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

Однако, чтобы быть более уверенным в том, что это работает, мне было интересно, есть ли способ реализовать подход «тестируй и установи». LPC1788 использует версию набора инструкций Thumb, специфичную для микроконтроллеров Cortex-M3, которую можно найти по адресу здесь или в пользователь руководство на странице 918+.

Однако, просматривая его, я не могу найти ничего похожего на инструкцию «проверить и установить». может я просто не замечаю.

В идеале я хотел бы иметь что-то вроде этого:

void LIST_AcquireLock(linked_list_t *list)
{
  do{
    while(list->sema);
  } while(TestAndSet(list->sema));
}

ИЗМЕНИТЬ

Основываясь на ответе Немо, я попытался сделать следующее:

void LIST_AcquireLock(linked_list_t *list)
{
  // Wait until lock seems free.
  while(list->sema);

  // Make sure lock is actually free.
  do {

    // If the semaphore is locked, we continue.
    // OTHERWISE we try to lock it ourselves.
    if(__LDREXB(&(list->sema))) continue;

    // If __STREXB returns 1, then another thread might have accessed that
    // memory location and we can't be sure the lock operation is atomic,
    // so try the locking procedure again.
  } while(__STREXB(1, &(list->sema)));
}

Если это полезно, это соответствующий код сборки:

LIST_AcquireLock:
??LIST_AcquireLock_0:
       0x56de: 0x7d01         LDRB      R1, [R0, #0x14]
       0x56e0: 0x2900         CMP       R1, #0
       0x56e2: 0xd1fc         BNE.N     ??LIST_AcquireLock_0    ; 0x56de
??LIST_AcquireLock_1:
       0x56e4: 0xf110 0x0114  ADDS.W    R1, R0, #20             ; 0x14
       0x56e8: 0xe8d1 0x1f4f  LDREXB    R1, [R1]
       0x56ec: 0xb2c9         UXTB      R1, R1
       0x56ee: 0x2900         CMP       R1, #0
??LIST_AcquireLock_2:
       0x56f0: 0xf110 0x0114  ADDS.W    R1, R0, #20             ; 0x14
       0x56f4: 0x2201         MOVS      R2, #1
       0x56f6: 0xe8c1 0x2f43  STREXB    R3, R2, [R1]
       0x56fa: 0x2b00         CMP       R3, #0
       0x56fc: 0xd1f2         BNE.N     ??LIST_AcquireLock_1    ; 0x56e4
       0x56fe: 0x4770         BX        LR

У меня возникли проблемы с воспроизведением проблем с одновременным доступом (при условии, что у меня были проблемы с одновременным доступом), поэтому я не уверен, что это работает.


person Tagc    schedule 02.09.2014    source источник
comment
doulos.com/knowhow/arm/Hints_and_Tips/Implementing_Semaphores   -  person Hans Passant    schedule 02.09.2014
comment
infocenter.arm.com/help /index.jsp?topic=/com.arm.doc.dai0321a/   -  person Hans Passant    schedule 02.09.2014
comment
Спасибо за ссылки, почитаю.   -  person Tagc    schedule 02.09.2014


Ответы (1)


ARM использует для атомарных операций парадигму «связанная с загрузкой/эксклюзивным хранилищем». См. этот вопрос и Раздел 39.2.4.8 руководства пользователя, на который вы ссылаетесь, для получения подробной информации.

[Обновлять]

На основе кода в ссылке При условии @HansPassant, я бы предложил внести небольшие изменения в ваш распорядок дня:

void LIST_AcquireLock(linked_list_t *list)
{
  // Wait until lock seems free.
  //while(list->sema); // unnecessary

  // Make sure lock is actually free.
  do {

    // If the semaphore is locked, we continue.
    // OTHERWISE we try to lock it ourselves.
    if(__LDREXB(&(list->sema))) continue;

    // If __STREXB returns 1, then another thread might have accessed that
    // memory location and we can't be sure the lock operation is atomic,
    // so try the locking procedure again.
  } while(__STREXB(1, &(list->sema)));

  // Ensure CPU does not reorder any memory accesses across lock acquisition.
  __DMB();
}

__DMB(), вероятно, не имеет значения для очень простых ядер ARM, но определенно необходим для более сложных. Современные процессоры имеют сложные модели памяти.

person Nemo    schedule 02.09.2014
comment
Спасибо! Прежде чем я отмечу это как принятое, не могли бы вы просто взглянуть на мою реализацию (редактирование в исходном сообщении), просто чтобы подтвердить, что у меня есть правильная идея. - person Tagc; 02.09.2014
comment
Да, ваш код выглядит нормально... почти. Вы должны прочитать ссылки, предоставленные @HansPassant, особенно ссылку с arm.com ... Вам нужна инструкция __DNB() сразу после получения блокировки, чтобы гарантировать, что ЦП не изменит порядок доступа к памяти во время операции блокировки. (Возможно, это не проблема для очень простых ядер ARM, но в целом это определенно проблема.) - person Nemo; 02.09.2014