У меня есть распределитель, который выполняет упрощенную атомику для отслеживания количества байтов, выделенных в данный момент. Они просто складываются и вычитаются, поэтому мне не нужна никакая синхронизация между потоками, кроме гарантии того, что изменения являются атомарными.
Однако я иногда хочу проверить количество выделенных байтов (например, при закрытии программы) и хочу убедиться, что все ожидающие записи зафиксированы. Я предполагаю, что мне нужен полный барьер памяти в этом случае, чтобы предотвратить перемещение любых предыдущих записей после барьера и предотвратить перемещение следующего чтения перед барьером.
Вопрос в следующем: Как правильно обеспечить, чтобы расслабленные атомарные записи выполнялись перед чтением? Мой текущий код правильный? (Предположим, что функции и типы отображаются в конструкции библиотеки std, как и ожидалось.)
void* Allocator::Alloc(size_t bytes, size_t alignment)
{
void* p = AlignedAlloc(bytes, alignment);
AtomicFetchAdd(&allocatedBytes, AlignedMsize(p), MemoryOrder::Relaxed);
return p;
}
void Allocator::Free(void* p)
{
AtomicFetchSub(&allocatedBytes, AlignedMsize(p), MemoryOrder::Relaxed);
AlignedFree(p);
}
size_t Allocator::GetAllocatedBytes()
{
AtomicThreadFence(MemoryOrder::AcqRel);
return AtomicLoad(&allocatedBytes, MemoryOrder::Relaxed);
}
И некоторые определения типов для контекста
enum struct MemoryOrder
{
Relaxed = 0,
Consume = 1,
Acquire = 2,
Release = 3,
AcqRel = 4,
SeqCst = 5,
};
struct Allocator
{
void* Alloc (size_t bytes, size_t alignment);
void Free (void* p);
size_t GetAllocatedBytes();
Atomic<size_t> allocatedBytes = { 0 };
};
Я не хочу просто по умолчанию использовать последовательную согласованность, так как пытаюсь лучше понять порядок памяти.
Часть, которая действительно меня сбивает с толку, заключается в том, что в стандарте под [atomics.fences]
все пункты говорят о захвате / атомном операторе, синхронизирующемся с разблокировочным / атомным оператором. Для меня совершенно непонятно, будет ли синхронный забор / атомная операция синхронизироваться с расслабленной атомной операцией в другом потоке. Если функция забора AcqRel буквально отображается на инструкцию mfence, похоже, что приведенный выше код будет в порядке. Однако мне трудно убедить себя, что стандарт гарантирует это. А именно,
4 Атомная операция А, которая является операцией освобождения атомарного
объект M синхронизируется с ограждением получения B, если существует
атомарная операция X на M такая, что X упорядочивается перед B и читает
значение, записанное A, или значение, записанное любым побочным эффектом в
последовательность выпуска во главе с А.
Это, кажется, проясняет, что забор не будет синхронизироваться с расслабленными атомными записями. С другой стороны, полное ограждение - это и ограждение освобождения, и ограждение приобретения, поэтому оно должно синхронизироваться с самим собой, верно?
2 Защитное ограждение A синхронизируется с ограждением приобретения B, если оно есть
существуют атомарные операции X и Y, обе работают на некотором атомном объекте
M, так что A секвенируется до X, X модифицирует M, Y секвенируется
перед B, и Y читает значение, записанное X или значение, записанное любым
побочный эффект в гипотетической последовательности релиза X возглавил бы, если бы
были операции освобождения.
Сценарий описан как
- Непоследовательные записи
- Ограждение
- X атомная запись
- Y атомное чтение
- B приобрести забор
- Непоследовательные чтения (здесь будут видны непоследовательные записи)
Тем не менее, в моем случае у меня нет атомарной записи + атомарного чтения в качестве сигнала между потоками, и ограничение выпуска происходит с ограничением получения в потоке B. Так что на самом деле происходит
- Непоследовательные записи
- Ограждение
- Б приобрести забор
- Непоследовательные чтения
Очевидно, что если забор исполняется до того, как начинается неупорядоченная запись, это гонка, и все ставки отменены. Но мне кажется, что если ограждение выполняется после того, как начинается неупорядоченная запись, но до того, как оно будет зафиксировано, оно будет вынуждено завершиться до того, как начнется непоследовательное чтение. Это именно то, что я хочу, но я не могу понять, гарантировано ли это по стандарту.