Тройная проверка блокировки? - PullRequest
2 голосов
/ 03 марта 2010

Итак, пока мы знаем, что двойная проверка блокировки как есть не работает в C ++, по крайней мере, не в переносимой форме.

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

Это прохождение вдохновлено шаблоном на странице 12 C ++ и опасностями двойной проверки блокировки , но пытается сделать это дешевле:

(pseudo code!)

struct Foo {
    bool childCreated[4];
    Mutex mutex[4];
    Foo child[4];

    void traverse (...) {
        ...
        if (!childCreated[c]) { 
            // get updated view
            #pragma flush childCreated[c]
            if (!childCreated[c]) { 
                ScopedLock sl (mutex[c]);
                if (!childCreated[c]) {
                    create (c);
                    #pragma flush childCreated[c]  
                    childCreated[c] = true;
                }
            }
        }
    }
}

Предполагается, что #pragma flush также будет служить жесткой последовательностью, когда компиляторам и процессорам не будет разрешено переупорядочивать операции между ними.

Какие проблемы вы видите?

изменить: Версия 2, пытаясь учесть ответ Владис (ввести третий сброс):

(pseudo code!)

struct Foo {
    bool childCreated[4];
    Mutex mutex[4];
    Foo child[4];

    void traverse (...) {
        ...
        if (!childCreated[c]) { 
            // get updated view
            #pragma flush childCreated[c]
            if (!childCreated[c]) { 
                ScopedLock sl (mutex[c]);
                #pragma flush childCreated[c]
                if (!childCreated[c]) {
                    create (c);
                    #pragma flush childCreated[c]
                    childCreated[c] = true;
                }
            }
        }
    }
}

edit: Версия 3, я как-то нахожу это довольно эквивалентным Версии 2, потому что я использую не дочерний элемент, а примитивный флаг для проверки на допустимость, в основном полагаясь на барьер памяти между ребенок и писать под этим флагом.

(pseudo code!)

struct Foo {
    bool childCreated[4];
    Mutex mutex[4];
    Foo child[4];

    void traverse (...) {
        ...
        if (!childCreated[c]) { 
            ScopedLock sl (mutex[c]);
            #pragma flush childCreated[c]
            if (!childCreated[c]) {
                create (c);
                #pragma flush childCreated[c]
                childCreated[c] = true;
            }
        }
    }
}

1 Ответ

3 голосов
/ 03 марта 2010

Кажется, что ваш шаблон не правильный.Рассмотрим случай, когда поток # 1 выполняется до первого #pragma flush.Затем элемент управления переключается на поток # 2, который включается и создает c, элемент управления возвращается непосредственно перед вторым #pragma flush.Теперь первый поток просыпается и заново создает дочернего элемента.

Редактировать: извините, неправильно: он не сможет взять блокировку.

Редактировать 2: нет, все еще правильно, потому чтозначение не будет сброшено в потоке # 1

...