2015-02-06 33 views
5

Ho riscontrato un problema in cui una libreria condivisa chiamata con JNA sotto il molo si blocca quando si stampa su stderr.La libreria C condivisa (JNI) si blocca sotto il molo quando si scrive su stderr

Ho semplificato il problema per facilitarne la riproduzione creando innanzitutto una libreria condivisa C molto semplice che non fa altro che chiamare fprintf(stderr,"\n"); 100 volte, quindi restituire.

Sul lato java abbiamo un'istruzione di sincronizzazione su un blocco globale per assicurare che un solo thread si trovi nella libreria condivisa alla volta.

synchronized (lock) { 
    Foo.INSTANCE.shared_lib_function(); 
} 

schiero questo sotto pontile e fare continue richieste al pontile di chiamare la libreria condivisa alla fine (dopo meno di 100 richieste) Trovo che la libreria condivisa si blocca.

Usando jstack possiamo vedere il filo che è bloccato all'interno della chiamata alla libreria condivisa (classi sono stati rinominati):

Thread 5991: (state = BLOCKED) 
- com.whats.going.on.connector.MyFooCaller.callIt() @bci=55, line=105 (Interpreted frame) 
- com.whats.going.on.Controller.callSharedLib() @bci=101, line=71 (Interpreted frame) 
- com.whats.going.on.Controller$$FastClassBySpringCGLIB$$d6a0f4b3.invoke(int, java.lang.Object, java.lang.Object[]) @bci=72 (Interpreted frame) 
- org.springframework.cglib.proxy.MethodProxy.invoke(java.lang.Object, java.lang.Object[]) @bci=19, line=204 (Interpreted frame) 
- org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint() @bci=19, line=717 (Interpreted frame) 
- org.springframework.aop.framework.ReflectiveMethodInvocation.proceed() @bci=19, line=157 (Interpreted frame) 
- org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor.invoke(org.aopalliance.intercept.MethodInvocation) @bci=7, line=64 (Interpreted frame) 
- org.springframework.aop.framework.ReflectiveMethodInvocation.proceed() @bci=101, line=179 (Interpreted frame) 
- org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(java.lang.Object, java.lang.reflect.Method, java.lang.Object[], org.springframework.cglib.proxy.MethodProxy) @bci=112, line=653 (Interpreted frame) 

Utilizzando gdb io sono in grado di ottenere un backtrace da dentro il mio libreria condivisa:

#0 0x00007f1136ec153d in write() from /lib64/libc.so.6 
#1 0x00007f1136e57ad3 in _IO_new_file_write() from /lib64/libc.so.6 
#2 0x00007f1136e5799a in _IO_new_file_xsputn() from /lib64/libc.so.6 
#3 0x00007f1136e4da4d in fwrite() from /lib64/libc.so.6 
#4 0x00007f10ed2dc122 in shared_lib_function() at foo/bar.c:357 
#5 0x00007f10ed4d227c in ??() 
#6 0x000000000000000e in ??() 
#7 0x00007f110c2309c0 in ??() 
#8 0x00007f110c230700 in ??() 
#9 0x00007f10ed4d1ddf in ??() 
#10 0x0000000000000000 in ??() 

linea 357 è una linea fprintf().

Mi sono preoccupato che forse il problema era qualcosa che si bloccava solo leggendo da stdout e mai stderr. All'interno di java ho creato un thread che continua a stampare su stdout e stderr e posso vederli entrambi.

Ho anche provato a vedere cosa succederebbe se avessimo effettuato 100 chiamate a System.err.println(""); all'interno di java e, comunque, questo non ha causato il blocco di un thread in java.

Originariamente quando si accede questo stderr e stdout è stato reindirizzato con:

PrintStream errorLog = new PrintStream(new RolloverFileOutputStream(new Fil("yyyy_mm_dd.error.log").getCanonicalPath(), false, 90)); 
System.setErr(errorLog); 
System.setOut(errorLog); 

ero in grado di vedere ciò che la lib condivisa scriveva a stderr nel file di registro. Ho quindi rimosso il reindirizzamento di stderr e stdout e ho notato che non riuscivo più a vedere cosa stesse scrivendo la libreria condivisa su stderr, tuttavia potevo vedere che System.err.println() stava stampando.

Quando ho provato a chiamare la libreria condivisa all'interno di un test (senza pontile) non ero in grado di riprodurre il problema. Ho eseguito il mio test sia da Eclipse che da Maven. Ho anche provato a reindirizzare stderr e stdout come sopra, ma ho scoperto che solo le scritture su stderr e stdout all'interno di java venivano reindirizzate (ad esempio, fprintf() allo stderr all'interno della libreria condivisa continuava a comparire in eclissi o nella console).

versione Java:

java version "1.8.0_25" 
Java(TM) SE Runtime Environment (build 1.8.0_25-b17) 
Java HotSpot(TM) 64-Bit Server VM (build 25.25-b02, mixed mode) 

versione Jetty: 9.2.6.v20141205

+0

JNA fornisce ['Native.synchronizedLibrary()'] (http://twall.github.io/jna/4.1.0/com/sun/jna/Native.html#synchronizedLibrary (com.sun.jna.Library)) allo scopo di limitare l'accesso a una libreria nativa a una singola chiamata in qualsiasi momento. – technomage

risposta

0

Il problema che ho avuto è stato YAJSW stava solo leggendo System.out del pontile e System.err ancora non leggere dallo stdout o dallo stderr di una libreria condivisa che verrebbe eventualmente riempito se si verificava qualche errore.

La soluzione era quella di impostare wrapper.console.pipestreams = true, vedere http://yajsw.sourceforge.net/YAJSW%20Configuration%20Parameters.html

Redirezionare stdout e stderr con freopen ha funzionato nel frattempo. Grazie per l'aiuto.

0

Scrivendo a STDERR da JNI, non giocherà bello, con la maggior parte dei quadri di registrazione Java.

Opzione # 1: fissare il lato Java

Che cosa hai bisogno di fare:

  • Non utilizzare il modulo di registrazione predefinito di Jetty
  • Non utilizzare implementazione StdErrLog di Jetty
  • Configurare un framework di registrazione alternativo. iniziare con slf4j e configurarlo per utilizzare qualcosa come logback o log4j.
  • Configurare il framework di registrazione alternativo su NON acquisire STDERR.
  • Configurare il framework di registrazione alternativo in modo che NON scriva su STDERR, deve solo scrivere in un file di registro.

Opzione # 2: Fissare il JNI Side

Un'altra opzione è quella di scrivere il codice JNI mai uscita al STDERR di default o STDOUT flussi. Ma invece ottieni i riferimenti di handle di file su Java System.err e System.out e scrivi invece su quelli.

Un esempio di questo può essere trovato nel gruppo comp.lang.java.programmer a

https://groups.google.com/forum/#!msg/comp.lang.java.programmer/SUN7EEjk8AU/JWjGGaD0ey0J

+0

Joakim sta "scrivendo su STDERR da JNI, non funzionerà bene con la maggior parte dei framework di registrazione java" un problema noto? È stato segnalato? – Luke