Почему этот код вызывает "EXC_BAD_INSTRUCTION"? - PullRequest
20 голосов
/ 27 ноября 2011
dispatch_semaphore_t aSemaphore = dispatch_semaphore_create(1);        
dispatch_semaphore_wait(aSemaphore, DISPATCH_TIME_FOREVER);
dispatch_release(aSemaphore);

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

Ответы [ 3 ]

40 голосов
/ 28 ноября 2011

Я пробовал этот код, и он действительно умирает с недопустимой инструкцией. Поэтому я немного покопался и обнаружил, что он умирает в _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");
}

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

17 голосов
/ 28 ноября 2011

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

1 голос
/ 07 мая 2015

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

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

Довольно неловкий патч, но он работает!

...