2015-12-03 3 views
3

В коде C У меня есть 64-разрядное значение в общей памяти, к которому обращаются различные процессы, подключенные к этой общей памяти. В настоящее время чтение и запись покрываются спин-блокировкой, но в компиляторах, где выровненное 64-битное считывание гарантировано не «порвано» (т. Е. Чтение является атомарным), я мог бы опустить покрытие спин-блокинга с «слегка устаревшей» версии значение прекрасное. Код должен быть способен скомпилировать на самых разных аппаратных средствах (включая 32 и 64-разрядные машины: Intel, AMD, Sparc Solaris, IBM Power7 и Power8 и многие другие), операционные среды (включая Linux, Windows и HP-UX) и компиляторы (gcc и clang, многие из которых - C89 или новее); хотя было бы замечательно, если бы я «запустил» на то, чтобы держать шпильки в средах, где безопасность их упускания не могла быть положительно подтверждена. Конечно, более безопасные платформы, на которых я могу определить, что это безопасно, тем лучше.Есть ли #define, чтобы определить, будет ли чтение согласованного 64-битного значения атомарным?

Что является самым безопасным и самым портативным способом определения того, гарантированы ли такие выровненные 64-разрядные чтения из одной 64-разрядной записи?

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

На моей машине, в src/include/pg_config.h:

/* src/include/pg_config.h. Generated from pg_config.h.in by configure. */ 
/* src/include/pg_config.h.in. Generated from configure.in by autoheader. */ 

[...] 

/* Define to 1 if `long int' works and is 64 bits. */ 
#define HAVE_LONG_INT_64 1 

Много таких констант времени компиляции определяются на основании того, что обнаруживается, когда ./configure запускается.

В src/include/c.h:

/* 
* 64-bit integers 
*/ 
#ifdef HAVE_LONG_INT_64 
/* Plain "long int" fits, use it */ 

#ifndef HAVE_INT64 
typedef long int int64; 
#endif 
#ifndef HAVE_UINT64 
typedef unsigned long int uint64; 
#endif 
#elif defined(HAVE_LONG_LONG_INT_64) 
/* We have working support for "long long int", use that */ 

#ifndef HAVE_INT64 
typedef long long int int64; 
#endif 
#ifndef HAVE_UINT64 
typedef unsigned long long int uint64; 
#endif 
#else 
/* neither HAVE_LONG_INT_64 nor HAVE_LONG_LONG_INT_64 */ 
#error must have a working 64-bit integer datatype 
#endif 

Код я пытаюсь улучшить ниже. oldSnapshotControl - это указатель на структуру в общей памяти, а threshold_timestamp - это поле в этой структуре, определяемое как int64. Обратите внимание на блок XXX в комментарии к функции.

/* 
* Get timestamp through which vacuum may have processed based on last stored 
* value for threshold_timestamp. 
* 
* XXX: If we can trust a read of an int64 value to be atomic, we can skip the 
* spinlock here. 
*/ 
int64 
GetOldSnapshotThresholdTimestamp(void) 
{ 
    int64  threshold_timestamp; 

    SpinLockAcquire(&oldSnapshotControl->mutex_threshold); 
    threshold_timestamp = oldSnapshotControl->threshold_timestamp; 
    SpinLockRelease(&oldSnapshotControl->mutex_threshold); 

    return threshold_timestamp; 
} 
+0

Поиск 'stdatomic.h'. Лучше использовать '_Atomic' .. – Olaf

+0

stdatomic.h и _Atomic, похоже, не присутствуют в gcc, так что это не очень портативно. – kgrittn

+0

Очень портативный, так как он является частью стандарта C11. Используйте последнюю версию gcc (начиная с 4.9.something), и вы ее найдете. Если вы используете внешний заголовок, вы получите поддержку для них также в gcc 4.7 и 4.8. Этот файл использует встроенные функции для предоставления '_Atomic' и требуемых определений. IIRC, это BSD-порт (хотя он не зависит от BSD). – Olaf

ответ

0

Перед С11, нет чтения любого типа данных, но, возможно, sig_atomic_t не guarateed атомарных (в том смысле, что ее решения), это просто не часть языка, и C в этой версии может никогда не гарантируйте это. И есть множество платформ вокруг, где на самом деле это не будет атомным.

С C11 у вас есть атомы (в более широком смысле), включенные в язык, и более новая версия gcc и clang поддерживают эту функцию. Это путь, если вам нужен переносимый код, который будет будущим доказательством.

Если вы удовлетворены меньшей переносимостью, вы можете использовать встроенные расширения gcc, начиная с __sync_, которые в основном выполняют атомарные операции по основным целым типам. Здесь у вас есть макросы __GCC_HAVE_SYNC_COMPARE_AND_SWAP_XX с XX заменены на ширину в количестве байтов, которые говорят вам, поддерживается ли такая операция.

Эти расширения gcc существуют в течение долгого времени, а также clang имеют их.

+0

Просто, чтобы быть ясным, здание с компилятором C11 не * запрещено *; но код должен компилироваться без предупреждения или ошибки на C89. Существует множество констант, которые мы определяем по-разному на основе аппаратного обеспечения ОС. или компилятор. – kgrittn

+0

Я не заинтересован в покрытии атомных CAS и т. Д. - это уже сделано в этом коде. Как ни странно, то, что не было, обрабатывает случай простого чтения или записи без барьера памяти в атомном режиме, где это возможно. Не кажется, что __sync_ является необходимым или полезным с этим. – kgrittn

0

Вы можете использовать sig_atomic_t

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

от C89 дальше.В заголовке <stdint.h> следующие макросы определены

SIG_ATOMIC_MIN 
SIG_ATOMIC_MAX 

однако, только из C99 (компилятор может иметь аналогичные реализации определенных макросов). Затем просто #if SIG_ATOMIC_MAX == 64BIT_MAX; 64BIT_MAX можно найти, используя макросы, определенные реализацией, limits.h или оба, с парой #if -s и #define-s.

Хотя вы гарантированно sig_atomic_t будет атомарным, он может быть меньше 64 бит: на моей 64-битной платформе sig_atomic_t по-прежнему int, например.

Возможно, самое простое решение будет основываться на внешних инструментах (например, cmake, autotools и т. Д.) И заставить их найти это для вас.