Problem using graphviz with JNI

Hello all,

I am a Java developer and I want to use graphviz native libraries with Java. For this I use JNI. This is my C code for JNI method:

  (JNIEnv *env, jobject object, jobjectArray arguments) {
    int argc = env->GetArrayLength(arguments);
    char *argv[argc];
    jstring strings[argc];

    for (int i = 0; i < argc; i++) {
        jstring strArg = (jstring) env->GetObjectArrayElement(arguments, i);
        const char *charArg = env->GetStringUTFChars(strArg, 0);
        strings[i] = strArg;
        argv[i] = (char*) charArg;
    }

    GVC_t *gvc = gvContext();
    gvParseArgs(gvc, argc, argv);

    graph_t *g, *prev = NULL;
    while ((g = gvNextInputGraph(gvc))) {
        if (prev) {
            gvFreeLayout(gvc, prev);
            agclose(prev);
        }
        gvLayoutJobs(gvc, g);
        gvRenderJobs(gvc, g);
        prev = g;
    }
    gvFinalize(gvc);
    int result = gvFreeContext(gvc);

    for (int i = 0; i < argc; i++) {
        env->ReleaseStringUTFChars(strings[i], argv[i]);
    }
    return result;
}

And I have three tests for this method. I can use these test in any order but the following statement is always true - image is generated for first test, image is NOT generated for the second test and JVM crashes for the third test with the following stack:

C  [libc.so.6+0x18b6a7]
C  [libc.so.6+0x7d022]  (vfprintf-internal.c:2377)
C  [libcgraph.so.6.0.0+0x68ed]  agerr_va+0xe6
C  [libcgraph.so.6.0.0+0x6a15]  agerr+0xb9
C  [graphvizj+0x5f767]  gvNextInputGraph+0xbb

From the fact that test order can be different but result is always the same I can conclude that state is somewhere saved between tests.

Could anyone say what I am doing wrong and how to fix it?

After some tests I created this issue .

FWIW this the code I use when linking natively (not for JNI, but for WebAssembly):

Note: src is the DOT string and only contains a single graph.

    GVC_t *gvc = gvContextPlugins(lt_preloaded_symbols, true);

    agseterr(AGERR);
    agseterrf(vizErrorf);
    agreadline(1);

    Agraph_t *graph = agmemread(src);
    if (graph)
    {
        char *data = NULL;
        unsigned int length;

        gvLayout(gvc, graph, engine);
        gvRenderData(gvc, graph, format, &data, &length);
        m_result = data;
        gvFreeRenderData(data);
        gvFreeLayout(gvc, graph);
        agclose(graph);
    }

    gvFinalize(gvc);
    gvFreeContext(gvc);

Over in Graphviz issues, it’s pointed out that apparently gvParseArgs() has a bug and isn’t reentrant (if people still use that word?)

@schmoo2k Could you show full chain from your non C code to Graphviz? I mean you code → … → … → Graphviz wasm.

The code is here: GitHub - GordonSmith/hpcc-js-wasm: HPCC-Systems Web-Assembly (JavaScript) but its somewhat complicated (and somewhat outdated). Currently it goes like this:

  1. You write an IDL file: hpcc-js-wasm/src-cpp/graphviz/main.idl at WASI_WIT · GordonSmith/hpcc-js-wasm · GitHub
  2. The IDL file generates additional c++ wrappers and corresponding JavaScript loaders
  3. The C++ and generated wrappers get compiled to wasm
  4. The generated JavaScript code loads the wasm and wires up all the wasm methods so they can be called from JavaScript.

Note: The above has been obsoleted by WASI and WIT Components - which does a similar thing, but in a more “standard” way and isn’t limited to C++ “Guests” and JS “Hosts”

I’m not exactly sure what we are trying to figure out now. Over in gitlab, there’s a merge request to fix the bug that started this. I believe @PavelTurk then asked if Graphviz was threadsafe, and we said Definitely Not. @PavelTurk said Then what about WASM Graphviz. The example is a single shot, anyway, maybe can’t answer that question.