iphone ios grand-central-dispatch

iphone - ¿Por qué este código causa "EXC_BAD_INSTRUCTION"?



ios grand-central-dispatch (3)

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

Cuando el programa se ejecuta en dispatch_release (aSemaphore) , causará "EXC_BAD_INSTRUCTION" y luego se bloqueará. ¿Por qué?


Probé este código y de hecho muere con instrucciones ilegales. Así que hice algunas excavaciones y descubrí que está muriendo en _dispatch_semaphore_dispose. Así que echemos un vistazo a lo que es (ARMv7 aquí, porque es fácil de entender):

__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 ...

Muere a las 0x40ae, que es una instrucción duff colocada allí para que se bloquee si el bge.n no nos hace saltar para saltar sobre ella.

La razón por la que está fallando es porque r0 debe ser menor que r1 . r0 y r1 se cargan desde la memoria en r4 + 32 que ha vuelto a subir la pila para descifrarlo. Creo que r4 es aSemaphore en el código de ejemplo, es decir, lo que pasó a dispatch_semaphore_release . El + 32 significa que está leyendo 32 bytes en la estructura a la que aSemaphore (es un puntero a una estructura dispatch_semaphore_s ). Entonces, en general, lo que hace es leer 4 bytes desde aSemaphore + 32 y ponerlos en r0 y leer 4 bytes desde aSemaphore + 36 y ponerlos en r1 .

La comparación está comparando efectivamente el valor de aSemaphore + 32 y aSemaphore + 36 . Leyendo qué dispatch_semaphore_create puedo ver que almacena el valor pasado tanto aSemaphore + 32 como aSemaphore + 36 . También encontré que dispatch_semaphore_wait y dispatch_semaphore_signal tocan el valor en aSemaphore + 32 , para incrementarlo y decrementarlo. Esto significa que la razón por la que se está rompiendo es porque el valor actual del semáforo es menor que el valor pasado en dispatch_semaphore_create . Por lo tanto, no puede deshacerse de un semáforo cuando el valor actual es menor que el valor con el que fue creado.

Si has leído aquí y has entendido mis divagaciones, ¡bien hecho! ¡Espero eso ayude!

ACTUALIZAR:

Probablemente sea mejor mirar la fuente (señalada por JustSid) aquí - http://opensource.apple.com/source/libdispatch/libdispatch-187.7/src/semaphore.c - mirando la función _dispatch_semaphore_dispose que vemos:

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

Así que sí, ahí lo tienes, ¡por eso se estrella!


Puedes crear un semáforo con valor cero, pero creo que será simplemente inútil. Tenía un campo de semáforo en una clase que causó que se bloquee en la desinicialización. Así es como lo arreglé (código Swift):

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

Un parche bastante incómodo, pero funciona!


Respuesta algo más sucinta: estás creando el semáforo con el valor incorrecto, debería ser cero. Crearlo con un valor de 1 significa que más tarde está liberando un semáforo que todavía está "en uso" y GCD está generando deliberadamente una instrucción ilegal para ayudarlo a depurar el hecho de que tiene un semáforo con más camareros.