Какова цель этого шаблона с использованием изменчивого указателя на "this"?

Недавно я встретил любопытное использование ключевого слова volatile в многопоточном коде С++. Чтобы отвлечь шаблон программирования, предположим, что есть объект управления, к которому обращается один производитель и несколько потребительских потоков:

class control_t {
    pthread_mutex_t control_lock;
    pthread_cond_t wake_cond;
    bool there_is_work_todo;

    control_t volatile* vthis() { return this; }
}

Потребительский поток выполняет следующие действия (c является энергонезависимым указателем на объект управления):

...
pthread_mutex_lock(c->control_lock)
while (!c->vthis()->there_is_work_todo) {
    pthread_cond_wait(&c->wake_cond, &c->control_lock);
}
...

Идея здесь в том, что потребительские потоки будут ждать, пока не будет выполнена какая-то работа, которую производитель сигнализирует через переменную wake_cond.

То, что я не понимаю здесь, - это то, почему доступ к объекту управления осуществляется с помощью летучего указателя на "this", который возвращается методом vthis(). Почему это?

Ответ 1

Использование volatile в многопоточном коде обычно подозрительно. volatile был разработан, чтобы избежать оптимизации чтения и записи в память, что полезно, когда такие чтения и записи происходят по специальным адресам, которые сопоставляются с аппаратными регистрами. См., Например, как volatile бесполезно предотвращать расы данных, и как это может быть (ab) использоваться как тип phantom...

Поскольку автор использовал правильную синхронизацию (мьютексы и переменные условия), использование volatile крайне подозрительно. Такое использование, как правило, связано с непониманием, наиболее распространенным среди таких языков, как Java, которые повторно использовали одно и то же ключевое слово с различной семантикой.

В C и С++ многопоточный код должен опираться на барьеры памяти, такие как те, которые вводятся в мьютексы и атомарные операции, которые гарантируют правильную синхронизацию значений по различным ядрам и кэшам ЦП.

Ответ 2

Использование volatile в этом коде дает либо поведение undefined, либо избыточное:

  • Если объект, на который указывает c, не является изменчивым, то доступ (включая чтение) с добавлением volatile статически считается причиной побочного эффекта (для размещения случаев, когда компилятор не может статически найти вне зависимости от того, действительно ли доступ использует изменчивый объект), но не требуется выполнять его любой ценой, поскольку побочный эффект на энергонезависимый объект не является наблюдаемым поведением.

  • Если объект, который вы вызываете vthis on, является volatile, тогда код имеет поведение undefined, потому что он обращается к изменчивому объекту через lvalue энергонезависимого типа до этого вызова в предыдущих строках.

Код, вероятно, полагается на реализацию, не оптимизирующую изменчивый доступ из-за совместимости с существующим кодом.