[java] El hilo se atasca en BlockingQueue.take sin motivo aparente


Answers

Question

Me encontré con un problema realmente extraño que no puedo entender en absoluto. Un poco de historia de fondo primero:

Estoy intentando ejecutar JavaScriptCore y usarlo como lenguaje de scripting para una aplicación de Android. El problema es que el tamaño de pila en el hilo principal es bastante limitado en versiones anteriores de Android (algo así como 12k en API 16). Sin embargo, todavía me gustaría llamar a JS en el hilo principal, hacer que vuelva a llamar para solicitar cosas y tener todo eso sincrónico. No hay problema - Voy a sacar un par de channe ... khm ... SynchronousQueues y rebotar la ejecución hacia adelante y hacia atrás. Así es como se ve mi código.

Es bastante simple: cada vez que algo llama diferir, rebota en el otro hilo y continúa desde allí. El único problema es, bueno, no funciona. En un caso de uso real con la ejecución de código Javascript, falla bastante confiablemente en algún momento, aunque no en el mismo lugar para el emulador y los diferentes dispositivos. Logcat siempre se ve bastante inofensivo:

I/JavaScriptCore: Lockstep [Main]: Defer
I/JavaScriptCore: Lockstep [Main]: Send EXECUTE_FUNC
I/JavaScriptCore: Lockstep [Background]: Receive EXECUTE_FUNC
I/JavaScriptCore: Lockstep [Background]: Defer
I/JavaScriptCore: Lockstep [Background]: Send EXECUTE_FUNC

Sin embargo, ese segundo EJECUTAR nunca se recibe por main, aunque se realice el put. Por lo que yo entiendo, eso ni siquiera debería ser posible con las colas sincrónicas. Al mirar el volcado de hilo, el hilo de fondo está esperando en el ciclo de ejecución para el siguiente mensaje, mientras que el principal está estacionado en incoming.take. No hay otros hilos que interactúen con esto.

En uno de mis dispositivos podría configurar un punto de interrupción condicional para el momento exacto en que esto deja de funcionar, y podría detenerlo justo cuando MAIN está esperando ese mensaje de EJECUTAR. El mensaje no es nulo, el foregroundQueue en ese punto está funcionando, puedo sondearlo con o sin tiempo de espera de Android Studio, tomar su tamaño, lo que sea. Tan pronto como paso, cuelgan todas las operaciones.

Por supuesto, sospechaba chanchullos de JNI, pero no hay volcados de memoria, fallas de segmentación o advertencias en Logcat.

Además, no es solo tomar, incluso si lo hago con una espera realmente sucia y ocupada:

Message msg = incoming.poll();
if(msg == null) {
 Thread.sleep(20);
 continue;
}

Main está atascado en la encuesta, el hilo de fondo sigue alegremente en la otra fila cada 20 milisegundos.

Intenté anidar difers con un factorial realmente perezoso que le gusta dormir mucho y no tiene problemas para llegar a 200 de profundidad, a pesar de los desbordamientos de enteros:

LockstepThread t = new LockstepThread();

int deferredFactoriel(final int n) {
  if(n == 0) {
    return 1;
  }
  return n * t.defer(new Functor<Integer>() {
    @Override
     public Integer call() {
       try {
         Thread.sleep(20);
       } catch (InterruptedException e) {
         e.printStackTrace();
       }
       return deferredFactoriel(n-1);
    }
  });
}

@Override
public void onCreate() {

  super.onCreate();

  for(int i=0; i<200; ++i) {
    Log.i("Test", i+"! = " + deferredFactoriel(i));
  }

...

Lo que probablemente sea más extraño de todos es que no importa la sincronización que uso. SynchronizedQueue, ArrayBlockingQueue, LinkedBlocking queue - siempre falla en el mismo lugar con el mismo volcado de hilo. Demonios, incluso hice mi propio intercambiador solo para ver que no me estoy volviendo loco y todavía me atascó de la misma manera.

Así que sí, estoy completamente perplejo. ¿Alguna idea de qué está pasando? Cualquier ayuda con la depuración esto sería muy apreciada.




Links