Win API Блокированные операции для 32-битного типа int - PullRequest
0 голосов
/ 03 сентября 2010

Если у нас есть:

__int32 some_var = 0;

Каков наилучший (если есть) способ вызова InterlockedExchange, InterlockedIncrement и других взаимосвязанных функций, для которых требуется LONG* для some_var?

Поскольку существует гарантия того, что LONG является 32-битным в любой Windows, возможно, просто пропустить (long*) some_var.Тем не менее, мне это кажется довольно уродливым, и я не могу найти подтверждение, что это безопасно.

Обратите внимание, я не могу изменить тип на long, потому что он не переносимый.Мне нужен ровно 32-битный тип.

Обновление: Некоторые исследования библиотек, которые предоставляют переносимые атомарные операции, показали, что никто не заботится о приведении типов.Некоторые примеры:

Apache Portable Runtime (APR) :

typedef WINBASEAPI apr_uint32_t (WINAPI * apr_atomic_win32_ptr_val_fn)
    (apr_uint32_t volatile *, 
     apr_uint32_t);

APR_DECLARE(apr_uint32_t) apr_atomic_add32(volatile apr_uint32_t *mem, apr_uint32_t val)
{
#if (defined(_M_IA64) || defined(_M_AMD64))
    return InterlockedExchangeAdd(mem, val);
#elif defined(__MINGW32__)
    return InterlockedExchangeAdd((long *)mem, val);
#else
    return ((apr_atomic_win32_ptr_val_fn)InterlockedExchangeAdd)(mem, val);
#endif
}

atomic_ops :

AO_INLINE AO_t
AO_fetch_and_sub1_full (volatile AO_t *p)
{
  return _InterlockedDecrement64((LONGLONG volatile *)p) + 1;
}

Ответы [ 6 ]

2 голосов
/ 03 сентября 2010

Ну, это камень и трудное место.Атомный инкремент - это деталь реализации платформы для тяжелых условий эксплуатации.Вот почему LONG typedef существует в первую очередь.Некоторые будущие операционные системы через 20 или 50 лет могут переопределить этот тип.Когда, скажем, 256-битные ядра являются общими и атомарные приращения работают по-разному.Кто знает.

Если вы хотите написать действительно переносимый код, вам следует использовать действительно переносимые типы.Как долго.И будет бремя Microsoft заставить его работать вместо вас.

Это будет 32-разрядное целое число в течение довольно долгого времени, я бы рекомендовал вам не беспокоиться об этом.1005 *

1 голос
/ 04 сентября 2010

Ну, __int32 не переносимый тип либо .Поэтому я предлагаю решить проблему с помощью typedef.В Windows вы можете сделать:

typedef LONG my_int32;

... и безопасно передать указатель на такой тип на InterlockedExchange().В других системах используйте там все 32-битный тип - например, если у них stdint.h, вы можете сделать:

typedef int32_t my_int32;
1 голос
/ 03 сентября 2010

Вы также можете изменить тип на long, оставив позади переносимость, потому что все "взаимосвязанные" семейства атомарных операций также не переносимы .

Кстати, как примечание, я подумал, что блокировка поддерживает целочисленную перегрузку. Возможно, только в .net.

0 голосов
/ 05 сентября 2010

Ганс Пассант выразил это очень хорошо:

"Атомный инкремент - это деталь реализации платформы для тяжелых условий."

Именно поэтому реализации предоставляют специфичные для типа перегрузки.

atomic_ops является одним из таких проектов.

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

В этом отношении происходит некоторая стандартизация, см., Например, на подобные вопросы ответили здесь и здесь .

0 голосов
/ 05 сентября 2010

Как ни странно, существует InterlockedExchange - Windows API, который принимает LONG * и _InterlockedExchange встроенный компилятор msvc, который занимает длинный *.

Поскольку была вызвана переносимость, я также создам ссылку на страницу Атомные свойства GCC .

Однако все хорошо: MSVC использует модель данных ILP32LLP64 для 32-битных сборок и LLP64 для 64-битных сборок. Наборы инструментов на основе GCC (такие как MinGW) существуют для окон и могут очень хорошо реализовать модель LP64 - что приводит к забаве! такие случаи, как «long» - 64 бита, но LONG - 32.

Если вы придерживаетесь компиляторов Microsoft, вам не о чем беспокоиться.

Итак, в заключение: 1. Передаваемое значение ДОЛЖНО быть квалифицировано как «volatile». 2. Поскольку вы (а) используете 32-битное количество (и это ваше требование) и (б) используете явно 32-битную форму API-интерфейса InterlockedXXX - его 100% безопасно просто выполнять кровавое приведение и выполнять его: InterlockedIncrement будет работать с 32-битным значением для всех битовых размеров, ваша переменная будет явно 32-битной для всех битовых размеров - даже при использовании разных моделей данных.

Актерский состав безопасен, не усложняйте вещи без причины.

0 голосов
/ 03 сентября 2010

Просто сделайте assert(sizeof(LONG) == sizeof(some_var)) и беспокойтесь о проблеме, только когда утверждение не выполнено YAGNI .Пока утверждение верно, вы можете использовать reinterpret_cast<LONG*>(&some_var).

...