java — ошибки сегментации в JNA / BridJ и т. д.

Я несколько раз перестраивал свой проект Java / C ++, используя JNI, JNA, BridJ и JavaCPP, и каждый раз, когда сталкиваюсь со случайными (непредсказуемыми) ошибками сегментации. Я проверил, что исполняемый файл на чистом C ++, использующий эту библиотеку, никогда не вызывает ошибок сегментации, а в случае BridJ сузил его до сборщика мусора Java, явно вызвав его.

Одна мысль заключалась в том, что эти библиотеки создают объекты указателя на стороне Java, которые вызывают free или же delete когда они получают мусор (через finalize) вместо обработки указателей, которые C ++ возвращает как заимствованные ссылки, как они должны делать в этом приложении.

Но я попробовал один дополнительный тест (не представлен ниже): я превратил каждый указатель в API в int64_t (long в Java) и явно приведен как соответствующий тип в C ++. Я все еще вижу редкие сегфо.

Поэтому у меня широкий вопрос: что может быть причиной этой проблемы? Я приму ответы, используя JNA или BridJ, потому что я могу легко переключаться между этими двумя. Я думаю, что мне не хватает фундаментальной проблемы, потому что эта проблема настолько распространена во всех библиотеках, которые я пробовал.

Для конкретности, вот моя версия JNA. Я связываюсь с библиотекой CERN ROOT. RootTreeReader.h — это:

#ifndef ROOTTREEREADER_H
#define ROOTTREEREADER_H

#include <TFile.h>
#include <TTreeReader.h>
#include <TTreeReaderValue.h>
#include <TTreeReaderArray.h>

using namespace ROOT::Internal;

extern "C" {
TFile *newFile(const char *fileLocation);
TTreeReader *newReader(TFile *file, const char *treeLocation);
bool readerNext(TTreeReader *reader);

TTreeReaderValueBase *newValue_float(TTreeReader *reader, const char *name);

float getValue_float(TTreeReaderValueBase *value);
}

#endif // ROOTTREEREADER_H

RootTreeReader.cpp — это:

#include <string>

#include "RootTreeReader.h"
TFile *newFile(const char *fileLocation) {
return TFile::Open(fileLocation);
}

TTreeReader *newReader(TFile *file, const char *treeLocation) {
return new TTreeReader(treeLocation, file);
}

bool readerNext(TTreeReader *reader) {
return reader->Next();
}

TTreeReaderValueBase *newValue_float(TTreeReader *reader, const char *name) {
return new TTreeReaderValue<float>(*reader, name);
}

float getValue_float(TTreeReaderValueBase *value) {
return *((float*)value->GetAddress());
}

Их Makefile

all: RootTreeReader.cpp
mkdir -p ../../../target/native/linux-x86-64
g++ RootTreeReader.cpp -o ../../../target/native/linux-x86-64/libRootTreeReader.so \
-fPIC -shared \
-Wl,--no-as-needed $(shell root-config --cflags --ldflags --libs) -lTreePlayer

JNAerator генерирует следующий файл RootTreeReaderLibrary.java:

package org.dianahep.scaroot;
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.NativeLibrary;
import com.sun.jna.Pointer;
import com.sun.jna.PointerType;
/**
* JNA Wrapper for library <b>RootTreeReader</b><br>
* This file was autogenerated by <a href="http://jnaerator.googlecode.com/">JNAerator</a>,<br>
* a tool written by <a href="http://ochafik.com/">Olivier Chafik</a> that <a href="http://code.google.com/p/jnaerator/wiki/CreditsAndLicense">uses a few opensource projects.</a>.<br>
* For help, please visit <a href="http://nativelibs4java.googlecode.com/">NativeLibs4Java</a> , <a href="http://rococoa.dev.java.net/">Rococoa</a>, or <a href="http://jna.dev.java.net/">JNA</a>.
*/
public interface RootTreeReaderLibrary extends Library {
public static final String JNA_LIBRARY_NAME = "RootTreeReader";
public static final NativeLibrary JNA_NATIVE_LIB = NativeLibrary.getInstance(RootTreeReaderLibrary.JNA_LIBRARY_NAME);
public static final RootTreeReaderLibrary INSTANCE = (RootTreeReaderLibrary)Native.loadLibrary(RootTreeReaderLibrary.JNA_LIBRARY_NAME, RootTreeReaderLibrary.class);
/**
* Original signature : <code>TFile* newFile(const char*)</code><br>
* <i>native declaration : src/main/cpp/RootTreeReader.h:5</i><br>
* @deprecated use the safer methods {@link #newFile(java.lang.String)} and {@link #newFile(com.sun.jna.Pointer)} instead
*/
@Deprecated
RootTreeReaderLibrary.TFile newFile(Pointer fileLocation);
/**
* Original signature : <code>TFile* newFile(const char*)</code><br>
* <i>native declaration : src/main/cpp/RootTreeReader.h:5</i>
*/
RootTreeReaderLibrary.TFile newFile(String fileLocation);
/**
* Original signature : <code>TTreeReader* newReader(TFile*, const char*)</code><br>
* <i>native declaration : src/main/cpp/RootTreeReader.h:6</i><br>
* @deprecated use the safer methods {@link #newReader(org.dianahep.scaroot.RootTreeReaderLibrary.TFile, java.lang.String)} and {@link #newReader(org.dianahep.scaroot.RootTreeReaderLibrary.TFile, com.sun.jna.Pointer)} instead
*/
@Deprecated
RootTreeReaderLibrary.TTreeReader newReader(RootTreeReaderLibrary.TFile file, Pointer treeLocation);
/**
* Original signature : <code>TTreeReader* newReader(TFile*, const char*)</code><br>
* <i>native declaration : src/main/cpp/RootTreeReader.h:6</i>
*/
RootTreeReaderLibrary.TTreeReader newReader(RootTreeReaderLibrary.TFile file, String treeLocation);
/**
* Original signature : <code>bool readerNext(TTreeReader*)</code><br>
* <i>native declaration : src/main/cpp/RootTreeReader.h:7</i>
*/
byte readerNext(RootTreeReaderLibrary.TTreeReader reader);
/**
* Original signature : <code>TTreeReaderValueBase* newValue_float(TTreeReader*, const char*)</code><br>
* <i>native declaration : src/main/cpp/RootTreeReader.h:9</i><br>
* @deprecated use the safer methods {@link #newValue_float(org.dianahep.scaroot.RootTreeReaderLibrary.TTreeReader, java.lang.String)} and {@link #newValue_float(org.dianahep.scaroot.RootTreeReaderLibrary.TTreeReader, com.sun.jna.Pointer)} instead
*/
@Deprecated
RootTreeReaderLibrary.TTreeReaderValueBase newValue_float(RootTreeReaderLibrary.TTreeReader reader, Pointer name);
/**
* Original signature : <code>TTreeReaderValueBase* newValue_float(TTreeReader*, const char*)</code><br>
* <i>native declaration : src/main/cpp/RootTreeReader.h:9</i>
*/
RootTreeReaderLibrary.TTreeReaderValueBase newValue_float(RootTreeReaderLibrary.TTreeReader reader, String name);
/**
* Original signature : <code>float getValue_float(TTreeReaderValueBase*)</code><br>
* <i>native declaration : src/main/cpp/RootTreeReader.h:11</i>
*/
float getValue_float(RootTreeReaderLibrary.TTreeReaderValueBase value);
public static class TFile extends PointerType {
public TFile(Pointer address) {
super(address);
}
public TFile() {
super();
}
};
public static class TTreeReader extends PointerType {
public TTreeReader(Pointer address) {
super(address);
}
public TTreeReader() {
super();
}
};
public static class TTreeReaderValueBase extends PointerType {
public TTreeReaderValueBase(Pointer address) {
super(address);
}
public TTreeReaderValueBase() {
super();
}
};
}

и я называю это так:

Native.setProtected(true)
println("Native.isProtected", Native.isProtected)   // it's true on Linux; I've tried this with and without

val lib = RootTreeReaderLibrary.INSTANCE

println("one")
val file = lib.newFile("TrackResonanceNtuple.root")
println("two")
val reader = lib.newReader(file, "TrackResonanceNtuple/twoMuon")
println("three")
val mass = lib.newValue_float(reader, "mass_mumu")
println("four")
var counter = 0
while (lib.readerNext(reader) > 0) {
val num = lib.getValue_float(mass)
println(num)
counter += 1
// if (counter % 1000 == 0) {
//   println("gc start")
//   System.gc()
//   println("gc end")
// }
}
println("five")

Это сегфо редко с или без явных вызовов сборщика мусора. BridJ версия этого сегфаулта редко без и часто с явными вызовами сборщика мусора.

2

Решение

Быстрое исправление:

export LD_PRELOAD=/path/to/libjsig.so

Проблема в том, что CERN ROOT пытается установить обработчики для тех же сигналов, что и JDK. Oracle рекомендует предварительно загрузить libjsig.so, который предоставляет «средство цепочки сигналов», для решения следующих проблем:

https://docs.oracle.com/javase/6/docs/technotes/guides/vm/signal-chaining.html

Редактировать от Джима:

Специально для платформы ROOT вы можете отключить обработку сигналов, вызвав

gSystem->ResetSignals();

Обсуждение можно найти на этой доске объявлений ROOT: https://root.cern.ch/phpBB3/viewtopic.php?t=8231

4

Другие решения

Других решений пока нет …