Лучший эквивалент для EnterCriticalSection в Mac OS X?

Какой лучший аналог? Я не нашел разумного решения для такой простой функции. Варианты, о которых я знаю:

1) MPEnterCriticalRegion - это, к сожалению, крайне неэффективно, вероятно, потому, что, несмотря на свое имя, он входит в режим ядра, поэтому для повторения блокировок требуется слишком много времени...

2) OSSpinLockLock - непригодный, т.к. видимо не рекурсивный. Если бы это было рекурсивно, это был бы правильный эквивалент.

3) pthread_mutex_lock - не пробовал, но на многое не рассчитываю, т.к. скорее всего будет просто эмулировать через критическую область или другой системный ресурс.


person Vojtěch Melda Meluzín    schedule 02.07.2014    source источник
comment
Вы можете легко создать рекурсивную оболочку вокруг нерекурсивной блокировки, поэтому я предполагаю № 2, если ваше резюме верно.   -  person Voo    schedule 02.07.2014
comment
Как? Я думал об этом, но в конце концов мне пришлось бы начать сравнивать идентификаторы потоков и т. Д. ... кажется более сложным, чем я думал изначально.   -  person Vojtěch Melda Meluzín    schedule 02.07.2014
comment
Да, вам нужно сравнить идентификаторы потоков и сохранить TID владельца, если поток получает блокировку, вот и все. 10 строк дополнительного кода сверху.   -  person Voo    schedule 03.07.2014
comment
Ну, я не уверен, что все так просто: 1) Если идентификатор потока совпадает -> ок, ничего страшного. 2) Если идентификатор потока не совпадает —> проблема, потому что несколько потоков могут иметь одно и то же, черт возьми, сохраненный идентификатор потока может быть даже в частично неподтвержденном состоянии, так что в экстремальной ситуации он может совпадать, даже если это не так. на самом деле совпадают. -› поэтому мне понадобится еще один замок для защиты тестового кода. Теперь вопрос, не будет ли дополнительных проблем...   -  person Vojtěch Melda Meluzín    schedule 03.07.2014


Ответы (1)


Предполагая, что у вас есть правильно работающая нерекурсивная блокировка, довольно легко получить эффективную рекурсивную блокировку (не знаю об API-интерфейсах Mac, так что это псевдокод):

class RecursiveLock {
public:
    void acquire() {
        auto tid = get_thread_id();
        if (owner == tid) { 
            lockCnt++;
        } else {
            AcquireLock(lock);
            owner = tid;
            lockCnt = 1;
        }
    }

    void release() {
        assert(owner == get_thread_id());
        lockCnt--;
        if (lockCnt == 0) {
            owner = 0;  // some illegal value for thread id
            ReleaseLock(lock);
        }
    }

private:
    int lockCnt;
    std::atomic<void*> owner;  
    void *lock;   // use whatever lock you like here
};

Рассуждать просто:

  • если tid == owner гарантируется, что мы уже получили блокировку.
  • если tid != owner либо кто-то еще удерживает блокировку, либо она свободна, в обоих случаях мы пытаемся получить блокировку и блокировать, пока не получим ее. После получения блокировки мы устанавливаем владельца на tid. Итак, какое-то время мы получали блокировку, но owner по-прежнему незаконны. Но нет проблем, так как незаконный tid также не будет сравниваться с tid любого реального потока, поэтому они также перейдут в ветку else и должны будут ждать, чтобы получить ее.

Обратите внимание на часть std::atomic — нам действительно нужны гарантии заказа для поля owner, чтобы сделать это законным. Если у вас нет С++ 11, используйте для этого встроенный компилятор.

person Voo    schedule 03.07.2014
comment
Ааааа, ты прав! Я слишком усложнял вещи! Благодарю вас! - person Vojtěch Melda Meluzín; 03.07.2014
comment
@Vojtěch также, если нет недопустимого значения идентификатора потока, вам понадобится дополнительное логическое значение. В этом случае логическое значение должно быть атомарным (точнее, семантикой получения освобождения) и устанавливать после tid владельца для получения, что тоже не имеет большого значения. - person Voo; 03.07.2014
comment
почему владелец должен быть атомарным, если он изменяется только а) потоком владельца и б) в то время как поток владельца владеет блокировкой, и единственный способ, которым он может быть установлен равным владельцу, - это сам владелец после блокировки приобретенный? - person mwag; 22.12.2015
comment
@mwag, потому что мы одновременно читаем и пишем в переменную. А читающие потоки не получают блокировку перед чтением, поэтому гарантии блокировки порядка памяти бесполезны, поэтому они нам нужны в любом случае. - person Voo; 22.12.2015