Подтвердить что ты не робот

Почему этот код вызывает "EXC_BAD_INSTRUCTION"?

dispatch_semaphore_t aSemaphore = dispatch_semaphore_create(1);        
dispatch_semaphore_wait(aSemaphore, DISPATCH_TIME_FOREVER);
dispatch_release(aSemaphore);

Когда программа работает до dispatch_release (aSemaphore), это вызовет "EXC_BAD_INSTRUCTION", а затем сбой. Почему?

4b9b3361

Ответ 1

Я пробовал этот код, и он действительно умирает с незаконной инструкцией. Так что я сделал рытье и обнаружил, что он умирает в _dispatch_semaphore_dispose. Так что давайте посмотрим, что это (ARMv7 здесь, потому что это легко понять!):

__dispatch_semaphore_dispose:
000040a0            b590        push    {r4, r7, lr}
000040a2            4604        mov     r4, r0
000040a4            af01        add     r7, sp, #4
000040a6        e9d40108        ldrd    r0, r1, [r4, #32]
000040aa            4288        cmp     r0, r1
000040ac            da00        bge.n   0x40b0
000040ae            defe        trap
...

Он умирает при 0x40ae, который является инструкцией по даффу, помещенной туда, чтобы он сработал, если bge.n не заставляет нас перепрыгивать через него.

Причина, по которой это происходит, состоит в том, что r0 должен быть меньше r1. r0 и r1 загружаются из памяти в r4 + 32, которая вернулась в стек, чтобы понять это. Я думаю, что r4 есть aSemaphore в примере кода, т.е. предмет, переданный в dispatch_semaphore_release. + 32 означает, что он считывает 32 байта в структуру, на которую указывает aSemaphore (это указатель на структуру dispatch_semaphore_s). Таким образом, общее, что он делает, читает 4 байта из aSemaphore + 32 и помещает их в r0 и читает 4 байта из aSemaphore + 36 и помещает их в r1.

Затем сравнение эффективно сравнивает значение aSemaphore + 32 и aSemaphore + 36. Чтение того, что dispatch_semaphore_create я могу видеть, что он сохраняет значение, переданное как для aSemaphore + 32, так и aSemaphore + 36. Я также обнаружил, что dispatch_semaphore_wait и dispatch_semaphore_signal коснитесь значения в aSemaphore + 32, чтобы увеличить и уменьшить его. Это означает, что причина его разлома заключается в том, что текущее значение семафора меньше значения, переданного в dispatch_semaphore_create. Таким образом, вы не можете распоряжаться семафором, когда текущее значение меньше значения, которое было создано с помощью.

Если вы читали здесь и понимали мои промахи, то хорошо сделали! Надеюсь, это поможет!

UPDATE:

Вероятно, лучше посмотреть на источник (указанный JustSid) здесь - http://opensource.apple.com/source/libdispatch/libdispatch-187.7/src/semaphore.c - глядя на функцию _dispatch_semaphore_dispose, которую мы видим:

if (dsema->dsema_value < dsema->dsema_orig) {
    DISPATCH_CLIENT_CRASH("Semaphore/group object deallocated while in use");
}

Итак, да, вот и все, вот почему он падает!

Ответ 2

Несколько более краткий ответ: вы создаете семафор с неправильным значением, он должен быть равен нулю. Создание его со значением 1 означает, что вы позже выпускаете семафор, который все еще "используется", и GCD намеренно генерирует незаконную инструкцию, чтобы помочь вам отладить тот факт, что у вас есть семафор с большим количеством официантов на нем.

Ответ 3

Вы можете создать семафор с нулевым значением, но я считаю, что это будет просто бесполезно. У меня было поле семафора в классе, из-за которого он рушился при деинициализации. Вот как я его исправил (код Swift):

deinit {
  while (dispatch_semaphore_signal(semaphore) != 0) {}
}

А неудобный патч, но он работает!