# How to embed multithreaded Julia in a graphical macOS app

**URL:** <https://discourse.julialang.org/t/how-to-embed-multithreaded-julia-in-a-graphical-macos-app/111637>\
**Category:** General Usage\
**Tags:** question, embedding\
**Created:** [March 15, 2024, 1:07am UTC](https://discourse.julialang.org/t/how-to-embed-multithreaded-julia-in-a-graphical-macos-app/111637 "2024-03-15T01:07:24Z")\
**Posts on this page:** 15\
**Page:** 1

<div class="post-metadata">

**Author:** ![droodman](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/droodman/32/26652_2.png) [@droodman](https://discourse.julialang.org/u/droodman)\
**Post date:** [March 15, 2024, 1:07am UTC](https://discourse.julialang.org/t/how-to-embed-multithreaded-julia-in-a-graphical-macos-app/111637/1 "2024-03-15T01:07:24Z")

</div>

I wrote a [C++ plugin](https://github.com/droodman/julia.ado/) for the statistical software Stata to give it access to Julia. Stata is a GUI program, so in macOS, it is normally not launched from a shell. The problem I have is giving the user the option to launch it with nthreads \> 1.

This is hard because on the Mac, I believe that graphical programs normally have no access to environment variables. So setting JULIA\_NUM\_THREADS is normally useless.

And the `jl_init()` function that launches an embedded copy of Julia takes no arguments. So I see no way to pass the equivalent of an “-t 8” command line option at launch.

Opening a terminal window and typing `open -a StataMP` does work, for then the graphical program is launched in a context with environment variables defined by .zshenv. But I would rather the user didn’t have to learn such tricks.

Is there a way to pass the equivalent of command line arguments to Julia when embedding it?

---

<div class="post-metadata">

**Author:** ![mkitti](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mkitti/32/12459_2.png) [@mkitti](https://discourse.julialang.org/u/mkitti)\
**Post date:** [March 15, 2024, 1:41am UTC](https://discourse.julialang.org/t/how-to-embed-multithreaded-julia-in-a-graphical-macos-app/111637/2 "2024-03-15T01:41:32Z")

</div>

I think you want to modify jl\_options as defined in this header.

> <https://github.com/JuliaLang/julia/blob/3c4af03cf2c69fbd695daab05f1ba595856100cb/src/jloptions.h#L17>

`jl_options` is a global declared in julia.h:

> <https://github.com/JuliaLang/julia/blob/3c4af03cf2c69fbd695daab05f1ba595856100cb/src/julia.h#L2450>

---

<div class="post-metadata">

**Author:** ![droodman](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/droodman/32/26652_2.png) [@droodman](https://discourse.julialang.org/u/droodman)\
**Post date:** [March 15, 2024, 2:45am UTC](https://discourse.julialang.org/t/how-to-embed-multithreaded-julia-in-a-graphical-macos-app/111637/3 "2024-03-15T02:45:58Z")

</div>

> [@mkitti](#):
>
> `jl_options`

Oh interesting, thank you. But I’m still struggling.

I’m loading Julia dynamically, at runtime.  
Simplifying somewhat, I first load libjulia with a command like

```julia
hDLL = dlopen(fulllibpath, RTLD_LAZY);

```

Then I get a pointer to jl\_options:

```julia
JL_options = (jl_options_t *)dlsym(hDLL, "jl_options");

```

Then I set nthreads to 8:

```julia
JL_options->nthreads = 8;

```

And then I call `jl_init()`…and I get a seg fault.

So I’m not sure what to do, or what I’m doing wrong. It doesn’t crash if I skip the nthreads assignment.

---

<div class="post-metadata">

**Author:** ![mkitti](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mkitti/32/12459_2.png) [@mkitti](https://discourse.julialang.org/u/mkitti)\
**Post date:** [March 15, 2024, 2:57am UTC](https://discourse.julialang.org/t/how-to-embed-multithreaded-julia-in-a-graphical-macos-app/111637/4 "2024-03-15T02:57:41Z")

</div>

I don’t think you need to use `dlsym`. If you include julia.h and then `dlopen` libjulia, then you should be able to access the symbol.

The other option is to use `jl_parse_opts`.

---

<div class="post-metadata">

**Author:** ![droodman](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/droodman/32/26652_2.png) [@droodman](https://discourse.julialang.org/u/droodman)\
**Post date:** [March 15, 2024, 3:53am UTC](https://discourse.julialang.org/t/how-to-embed-multithreaded-julia-in-a-graphical-macos-app/111637/5 "2024-03-15T03:53:05Z")

</div>

I don’t think you need to use `dlsym` . If you include julia.h and then `dlopen` libjulia, then you should be able to access the symbol.

OK, I tried that and got a compiler error:

```julia
unresolved external symbol __imp_jl_options

```

Perhaps that is related to the fact that I’m not linking to libjulia till runtime.

As for `jl_parse_opts`, I tried the following. It didn’t crash but also didn’t affect nthreads:

```julia
            int ac = 1;
            char* av = (char*)"--threads 8";
            char** avp = &av;
            JL_parse_opts(&ac, &avp);

```

I tried calling `JL_parse_opts` (my alias for the dynamically loaded `jl_parse_opts()`) both before and after `JL_init()`. Neither way did anything…

---

<div class="post-metadata">

**Author:** ![mkitti](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mkitti/32/12459_2.png) [@mkitti](https://discourse.julialang.org/u/mkitti)\
**Post date:** [March 15, 2024, 4:08am UTC](https://discourse.julialang.org/t/how-to-embed-multithreaded-julia-in-a-graphical-macos-app/111637/6 "2024-03-15T04:08:30Z")

</div>

I see what you are doing.

Maybe call `jl_init_options`?

> <https://github.com/JuliaLang/julia/blob/67cdb9bf70a39e62992276cb3923e928c2e8b637/src/jloptions.c#L39>

---

<div class="post-metadata">

**Author:** ![mkitti](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mkitti/32/12459_2.png) [@mkitti](https://discourse.julialang.org/u/mkitti)\
**Post date:** [March 15, 2024, 4:19am UTC](https://discourse.julialang.org/t/how-to-embed-multithreaded-julia-in-a-graphical-macos-app/111637/7 "2024-03-15T04:19:26Z")

</div>

Looking at how pyjulia and PythonCall do it, they both go through `jl_parse_opts`.

> <https://github.com/JuliaPy/PythonCall.jl/blob/6975f7c5d1f08fc4db785e40d1041492346a21c1/pysrc/juliacall/ __init__.py#L183-L187>

You might also need to set `nthreadpools` if you are going to set it directly.

> <https://github.com/JuliaLang/julia/blob/67cdb9bf70a39e62992276cb3923e928c2e8b637/src/jloptions.c#L507C24-L507C37>

---

<div class="post-metadata">

**Author:** ![droodman](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/droodman/32/26652_2.png) [@droodman](https://discourse.julialang.org/u/droodman)\
**Post date:** [March 15, 2024, 10:52pm UTC](https://discourse.julialang.org/t/how-to-embed-multithreaded-julia-in-a-graphical-macos-app/111637/8 "2024-03-15T22:52:59Z")

</div>

> [@mkitti](#):
>
> `jl_parse_opts`.

Looking at those examples is a good idea. You’re right that’s how they do it. Still haven’t figured it out how to make it work for me.

---

<div class="post-metadata">

**Author:** ![mkitti](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mkitti/32/12459_2.png) [@mkitti](https://discourse.julialang.org/u/mkitti)\
**Post date:** [March 16, 2024, 2:12am UTC](https://discourse.julialang.org/t/how-to-embed-multithreaded-julia-in-a-graphical-macos-app/111637/9 "2024-03-16T02:12:16Z")

</div>

Here’s a demonstration that seems to work for me on nightly:

```julia
#include "julia.h"

int main() {
    // configure jl_options
    jl_options.nthreadpools = 1;
    jl_options.nthreads = 8;
    int16_t ntpp[] = {jl_options.nthreads};
    jl_options.nthreads_per_pool = ntpp;

    // initialize Julia
    jl_init();
    jl_eval_string("println(\"Number of threads:\")");
    jl_eval_string("println(Threads.nthreads())");
    jl_atexit_hook(0);

    return 0;
}

```

To compile I ran this:

```julia
./julia usr/share/julia/julia-config.jl --allflags | xargs gcc embed_threads_demo.c -o embed_threads_demo

```

Here’s the output of julia-config.jl:

```julia
$ ./julia usr/share/julia/julia-config.jl --allflags 
-std=gnu11 -I'/home/mkitti/src/julia/usr/include/julia' -fPIC -L'/home/mkitti/src/julia/usr/lib' -Wl,--export-dynamic -Wl,-rpath,'/home/mkitti/src/julia/usr/lib' -Wl,-rpath,'/home/mkitti/src/julia/usr/lib/julia' -ljulia

```

ldd reports the following:

```julia
$ ldd ./embed_threads_demo 
	linux-vdso.so.1 (0x00007ffe1a002000)
	libjulia.so.1.12 => /home/mkitti/src/julia/usr/lib/libjulia.so.1.12 (0x00007f4e5987e000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f4e59600000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f4e598ae000)

```

I then get the following result:

```julia
$ ./embed_threads_demo 
Number of threads:
8

```

Let me see if I can figure out how to do this with `jl_parse_opts`.

---

<div class="post-metadata">

**Author:** ![mkitti](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mkitti/32/12459_2.png) [@mkitti](https://discourse.julialang.org/u/mkitti)\
**Post date:** [March 16, 2024, 3:01am UTC](https://discourse.julialang.org/t/how-to-embed-multithreaded-julia-in-a-graphical-macos-app/111637/10 "2024-03-16T03:01:29Z")

</div>

Here’s the version via `jl_parse_opts`:

```julia
#include "julia.h"

int main(int org_argc, char* org_argv[]) {
    // parse options
    int argc = 2;
    char** argv = malloc(sizeof(char*)*argc);
    // first argument is the name of the executable
    argv[0] = org_argv[0];
    argv[1] = "--threads=6";
    jl_parse_opts(&argc, &argv);

    // initialize Julia
    jl_init();
    jl_eval_string("@info \"debug\" Threads.nthreads()");
    jl_atexit_hook(0);

    return 0;
}

```

It is compiled in the same way as above.

```julia
$ ./julia usr/share/julia/julia-config.jl --allflags | xargs gcc embed_threads_demo_parse_args.c -o embed_threads_demo_parse_args

$ ./embed_threads_demo_parse_args 
┌ Info: debug
│ Threads.nthreads() = 6
└ @ Main none:1

```

---

<div class="post-metadata">

**Author:** ![mkitti](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mkitti/32/12459_2.png) [@mkitti](https://discourse.julialang.org/u/mkitti)\
**Post date:** [March 16, 2024, 3:31am UTC](https://discourse.julialang.org/t/how-to-embed-multithreaded-julia-in-a-graphical-macos-app/111637/11 "2024-03-16T03:31:19Z")

</div>

Finally, here’s how to do this via `dlopen`.

```julia
#include <stdlib.h>
#include <dlfcn.h>

int main(int org_argc, char* org_argv[]) {
    // dynamically load libjulia
    const char* libjulia_path = "usr/lib/libjulia.so";
    void* jlptr = dlopen(libjulia_path, RTLD_LAZY);
    void (*jl_parse_opts)(int*, char***) = dlsym(jlptr, "jl_parse_opts");
    void (*jl_init)() = dlsym(jlptr, "jl_init");
    void* (*jl_eval_string)(const char*) = dlsym(jlptr, "jl_eval_string");
    void (*jl_atexit_hook)(int) = dlsym(jlptr, "jl_atexit_hook");

    // parse options
    int argc = 2;
    char** argv = malloc(sizeof(char*)*argc);
    // first argument is the name of the executable
    argv[0] = org_argv[0];
    argv[1] = "--threads=4";

    // parse arguments
    jl_parse_opts(&argc, &argv);

    // initialize Julia
    jl_init();
    jl_eval_string("@info \"debug\" Threads.nthreads()");
    jl_atexit_hook(0);

    return 0;
}

```

I compile and run as follows.

```julia
$ gcc embed_threads_demo_parse_args_dlopen.c -o embed_threads_demo_parse_args_dlopen -ldl

$ ./embed_threads_demo_parse_args_dlopen 
┌ Info: debug
│ Threads.nthreads() = 4
└ @ Main none:1

```

---

<div class="post-metadata">

**Author:** ![Palli](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/palli/32/3380_2.png) [@Palli](https://discourse.julialang.org/u/Palli)\
**Post date:** [March 16, 2024, 11:14am UTC](https://discourse.julialang.org/t/how-to-embed-multithreaded-julia-in-a-graphical-macos-app/111637/12 "2024-03-16T11:14:14Z")

</div>

You probably should be using Jluna (since you’re calling from C++, it improves on the C API of Julia for C++, maybe even you should use this if your code were C code?):

> <https://github.com/Clemapfel/jluna/blob/537d42330a0821ca714f3ee9a8a4a00ebba6ea54/docs/multi_threading.md?plain=1#L64>

---

<div class="post-metadata">

**Author:** ![droodman](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/droodman/32/26652_2.png) [@droodman](https://discourse.julialang.org/u/droodman)\
**Post date:** [March 16, 2024, 11:39am UTC](https://discourse.julialang.org/t/how-to-embed-multithreaded-julia-in-a-graphical-macos-app/111637/13 "2024-03-16T11:39:59Z")

</div>

I find the installation process for that overwhelmingly complicated, especially if I have to do it in three platforms. I’m developing for Windows, Linux, and macOS, and I didn’t know all of them well.

---

<div class="post-metadata">

**Author:** ![Palli](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/palli/32/3380_2.png) [@Palli](https://discourse.julialang.org/u/Palli)\
**Post date:** [March 16, 2024, 12:16pm UTC](https://discourse.julialang.org/t/how-to-embed-multithreaded-julia-in-a-graphical-macos-app/111637/14 "2024-03-16T12:16:54Z")

</div>

I wouldn’t know, I’ve not even used Jluna for one platform! I just wanted to help, if the C API directly (or some other wrapper helps) is enough for you then great! It seems you have good help already, reading the thread. It just seemed to me that Jluna is the go-to-solution for C++, though it may be overkill for you, or just for enabling multi-threading.

It seems sensible that GUI apps on macOS do not have access to ENV vars, and I believe that’s also done by now on Windows, at least for some types of GUI apps.

But in either case, it seems like ENV vars may work, in the sense that setting them from within your app, for further processing from it, for its benefit, seems still sensible, and I don’t see a reason why it shouldn’t work or be blocked.

It seems if I’m reading the right code, that’s what Jluna does:

> <https://github.com/Clemapfel/jluna/blob/537d42330a0821ca714f3ee9a8a4a00ebba6ea54/.src/safe_utilities.inl#L105>

so if it doesn’t work it will be surprising for users, and I and others would like to know! About Jluna being “overwhelmingly complicated” (to install), it’s at least meant to be a better C++ API, and the only one claiming that. Whatever you do, I would like to know how it goes, if you or other can embed into a larger (cross-platform) GUI app.

If you were just making a cross-platform (GUI) app, with Julia as the only language, or the main one, I would point you to AppBundler.jl. I’m not sure it helps you since you do not start with Julia (or how well it would fit with Jluna, probably not going to hurt, nor help much), and call e.g. C++/Stata (could you?). I’m not sure, maybe it can also help if Julia is being called? It’s a rather new project, I would like to know more about app building across languages (and operating systems), people claim difficult for Python alone, harder for Julia alone, and even more combining (on recent Reddit thread). Tools are likely not ready for that (by default), but might be improved…

I’m sure someone has embedded Julia before, for any of the platforms (should be supported on all), even with threading, not sure anyone has done for all three yet, or a GUI app. It seems like necessarily more complex, or at repeated compilation 3x. Do you think Jluna could do something to make it better? About changing code, or just adding docs?

---

<div class="post-metadata">

**Author:** ![droodman](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/droodman/32/26652_2.png) [@droodman](https://discourse.julialang.org/u/droodman)\
**Post date:** [March 16, 2024, 1:52pm UTC](https://discourse.julialang.org/t/how-to-embed-multithreaded-julia-in-a-graphical-macos-app/111637/16 "2024-03-16T13:52:53Z")

</div>

Thank you @mkitti. This is tremendously helpful. The key thing I learned is “first argument is the name of the executable”. I was putting “–threads=7” in argv[0].  
In my case I am compiling to a shared library, not an executable, so technically the caller is Stata. But I found I can just set argv[0] to 0 in your example and it still works.

It is now possible for a Stata user to type `jl start, threads(7)` and launch Julia with 7 threads.
