Skip to content

Commit 6495270

Browse files
authored
Refactor julia initialization by finding and loading the sysimage earlier (#58231)
This changes the order of initialization slightly. The goal is to be able to initialize executables and share libraries without having to explicitly call init functions. This still doesn't address the thread safety
2 parents 748775b + fa4e1e0 commit 6495270

File tree

8 files changed

+302
-284
lines changed

8 files changed

+302
-284
lines changed

doc/src/manual/embedding.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ linking against `libjulia`.
5454
The first thing that must be done before calling any other Julia C function is to
5555
initialize Julia. This is done by calling `jl_init`, which tries to automatically determine
5656
Julia's install location. If you need to specify a custom location, or specify which system
57-
image to load, use `jl_init_with_image` instead.
57+
image to load, use `jl_init_with_image_file` or `jl_init_with_image_handle` instead.
5858
5959
The second statement in the test program evaluates a Julia statement using a call to `jl_eval_string`.
6060

src/init.c

+81-256
Original file line numberDiff line numberDiff line change
@@ -533,169 +533,6 @@ int jl_isabspath(const char *in) JL_NOTSAFEPOINT
533533
return 0; // relative path
534534
}
535535

536-
static char *absrealpath(const char *in, int nprefix)
537-
{ // compute an absolute realpath location, so that chdir doesn't change the file reference
538-
// ignores (copies directly over) nprefix characters at the start of abspath
539-
#ifndef _OS_WINDOWS_
540-
char *out = realpath(in + nprefix, NULL);
541-
if (out) {
542-
if (nprefix > 0) {
543-
size_t sz = strlen(out) + 1;
544-
char *cpy = (char*)malloc_s(sz + nprefix);
545-
memcpy(cpy, in, nprefix);
546-
memcpy(cpy + nprefix, out, sz);
547-
free(out);
548-
out = cpy;
549-
}
550-
}
551-
else {
552-
size_t sz = strlen(in + nprefix) + 1;
553-
if (in[nprefix] == PATHSEPSTRING[0]) {
554-
out = (char*)malloc_s(sz + nprefix);
555-
memcpy(out, in, sz + nprefix);
556-
}
557-
else {
558-
size_t path_size = JL_PATH_MAX;
559-
char *path = (char*)malloc_s(JL_PATH_MAX);
560-
if (uv_cwd(path, &path_size)) {
561-
jl_error("fatal error: unexpected error while retrieving current working directory");
562-
}
563-
out = (char*)malloc_s(path_size + 1 + sz + nprefix);
564-
memcpy(out, in, nprefix);
565-
memcpy(out + nprefix, path, path_size);
566-
out[nprefix + path_size] = PATHSEPSTRING[0];
567-
memcpy(out + nprefix + path_size + 1, in + nprefix, sz);
568-
free(path);
569-
}
570-
}
571-
#else
572-
// GetFullPathName intentionally errors if given an empty string so manually insert `.` to invoke cwd
573-
char *in2 = (char*)malloc_s(JL_PATH_MAX);
574-
if (strlen(in) - nprefix == 0) {
575-
memcpy(in2, in, nprefix);
576-
in2[nprefix] = '.';
577-
in2[nprefix+1] = '\0';
578-
in = in2;
579-
}
580-
DWORD n = GetFullPathName(in + nprefix, 0, NULL, NULL);
581-
if (n <= 0) {
582-
jl_error("fatal error: jl_options.image_file path too long or GetFullPathName failed");
583-
}
584-
char *out = (char*)malloc_s(n + nprefix);
585-
DWORD m = GetFullPathName(in + nprefix, n, out + nprefix, NULL);
586-
if (n != m + 1) {
587-
jl_error("fatal error: jl_options.image_file path too long or GetFullPathName failed");
588-
}
589-
memcpy(out, in, nprefix);
590-
free(in2);
591-
#endif
592-
return out;
593-
}
594-
595-
// create an absolute-path copy of the input path format string
596-
// formed as `joinpath(replace(pwd(), "%" => "%%"), in)`
597-
// unless `in` starts with `%`
598-
static const char *absformat(const char *in)
599-
{
600-
if (in[0] == '%' || jl_isabspath(in))
601-
return in;
602-
// get an escaped copy of cwd
603-
size_t path_size = JL_PATH_MAX;
604-
char path[JL_PATH_MAX];
605-
if (uv_cwd(path, &path_size)) {
606-
jl_error("fatal error: unexpected error while retrieving current working directory");
607-
}
608-
size_t sz = strlen(in) + 1;
609-
size_t i, fmt_size = 0;
610-
for (i = 0; i < path_size; i++)
611-
fmt_size += (path[i] == '%' ? 2 : 1);
612-
char *out = (char*)malloc_s(fmt_size + 1 + sz);
613-
fmt_size = 0;
614-
for (i = 0; i < path_size; i++) { // copy-replace pwd portion
615-
char c = path[i];
616-
out[fmt_size++] = c;
617-
if (c == '%')
618-
out[fmt_size++] = '%';
619-
}
620-
out[fmt_size++] = PATHSEPSTRING[0]; // path sep
621-
memcpy(out + fmt_size, in, sz); // copy over format, including nul
622-
return out;
623-
}
624-
625-
static void jl_resolve_sysimg_location(JL_IMAGE_SEARCH rel)
626-
{
627-
// this function resolves the paths in jl_options to absolute file locations as needed
628-
// and it replaces the pointers to `julia_bindir`, `julia_bin`, `image_file`, and output file paths
629-
// it may fail, print an error, and exit(1) if any of these paths are longer than JL_PATH_MAX
630-
//
631-
// note: if you care about lost memory, you should call the appropriate `free()` function
632-
// on the original pointer for each `char*` you've inserted into `jl_options`, after
633-
// calling `julia_init()`
634-
char *free_path = (char*)malloc_s(JL_PATH_MAX);
635-
size_t path_size = JL_PATH_MAX;
636-
if (uv_exepath(free_path, &path_size)) {
637-
jl_error("fatal error: unexpected error while retrieving exepath");
638-
}
639-
if (path_size >= JL_PATH_MAX) {
640-
jl_error("fatal error: jl_options.julia_bin path too long");
641-
}
642-
jl_options.julia_bin = (char*)malloc_s(path_size + 1);
643-
memcpy((char*)jl_options.julia_bin, free_path, path_size);
644-
((char*)jl_options.julia_bin)[path_size] = '\0';
645-
if (!jl_options.julia_bindir) {
646-
jl_options.julia_bindir = getenv("JULIA_BINDIR");
647-
if (!jl_options.julia_bindir) {
648-
jl_options.julia_bindir = dirname(free_path);
649-
}
650-
}
651-
if (jl_options.julia_bindir)
652-
jl_options.julia_bindir = absrealpath(jl_options.julia_bindir, 0);
653-
free(free_path);
654-
free_path = NULL;
655-
if (jl_options.image_file) {
656-
if (rel == JL_IMAGE_JULIA_HOME && !jl_isabspath(jl_options.image_file)) {
657-
// build time path, relative to JULIA_BINDIR
658-
free_path = (char*)malloc_s(JL_PATH_MAX);
659-
int n = snprintf(free_path, JL_PATH_MAX, "%s" PATHSEPSTRING "%s",
660-
jl_options.julia_bindir, jl_options.image_file);
661-
if (n >= JL_PATH_MAX || n < 0) {
662-
jl_error("fatal error: jl_options.image_file path too long");
663-
}
664-
jl_options.image_file = free_path;
665-
}
666-
if (jl_options.image_file)
667-
jl_options.image_file = absrealpath(jl_options.image_file, 0);
668-
if (free_path) {
669-
free(free_path);
670-
free_path = NULL;
671-
}
672-
}
673-
if (jl_options.outputo)
674-
jl_options.outputo = absrealpath(jl_options.outputo, 0);
675-
if (jl_options.outputji)
676-
jl_options.outputji = absrealpath(jl_options.outputji, 0);
677-
if (jl_options.outputbc)
678-
jl_options.outputbc = absrealpath(jl_options.outputbc, 0);
679-
if (jl_options.outputasm)
680-
jl_options.outputasm = absrealpath(jl_options.outputasm, 0);
681-
if (jl_options.machine_file)
682-
jl_options.machine_file = absrealpath(jl_options.machine_file, 0);
683-
if (jl_options.output_code_coverage)
684-
jl_options.output_code_coverage = absformat(jl_options.output_code_coverage);
685-
if (jl_options.tracked_path)
686-
jl_options.tracked_path = absrealpath(jl_options.tracked_path, 0);
687-
688-
const char **cmdp = jl_options.cmds;
689-
if (cmdp) {
690-
for (; *cmdp; cmdp++) {
691-
const char *cmd = *cmdp;
692-
if (cmd[0] == 'L') {
693-
*cmdp = absrealpath(cmd, 1);
694-
}
695-
}
696-
}
697-
}
698-
699536
JL_DLLEXPORT int jl_is_file_tracked(jl_sym_t *path)
700537
{
701538
const char* path_ = jl_symbol_name(path);
@@ -721,8 +558,85 @@ static void restore_fp_env(void)
721558
jl_error("Failed to configure floating point environment");
722559
}
723560
}
561+
static NOINLINE void _finish_jl_init_(jl_image_buf_t sysimage, jl_ptls_t ptls, jl_task_t *ct)
562+
{
563+
JL_TIMING(JULIA_INIT, JULIA_INIT);
564+
565+
if (sysimage.kind == JL_IMAGE_KIND_SO)
566+
jl_gc_notify_image_load(sysimage.data, sysimage.size);
567+
568+
if (jl_options.cpu_target == NULL)
569+
jl_options.cpu_target = "native";
570+
571+
// Parse image, perform relocations, and init JIT targets, etc.
572+
jl_image_t parsed_image = jl_init_processor_sysimg(sysimage, jl_options.cpu_target);
573+
574+
jl_init_codegen();
575+
jl_init_common_symbols();
576+
577+
if (sysimage.kind != JL_IMAGE_KIND_NONE) {
578+
// Load the .ji or .so sysimage
579+
jl_restore_system_image(&parsed_image, sysimage);
580+
} else {
581+
// No sysimage provided, init a minimal environment
582+
jl_init_types();
583+
jl_global_roots_list = (jl_genericmemory_t*)jl_an_empty_memory_any;
584+
jl_global_roots_keyset = (jl_genericmemory_t*)jl_an_empty_memory_any;
585+
}
586+
587+
jl_init_flisp();
588+
jl_init_serializer();
589+
590+
if (sysimage.kind == JL_IMAGE_KIND_NONE) {
591+
jl_top_module = jl_core_module;
592+
jl_init_intrinsic_functions();
593+
jl_init_primitives();
594+
jl_init_main_module();
595+
jl_load(jl_core_module, "boot.jl");
596+
jl_current_task->world_age = jl_atomic_load_acquire(&jl_world_counter);
597+
post_boot_hooks();
598+
}
599+
600+
if (jl_base_module == NULL) {
601+
// nthreads > 1 requires code in Base
602+
jl_atomic_store_relaxed(&jl_n_threads, 1);
603+
jl_n_markthreads = 0;
604+
jl_n_sweepthreads = 0;
605+
jl_n_gcthreads = 0;
606+
jl_n_threads_per_pool[JL_THREADPOOL_ID_INTERACTIVE] = 0;
607+
jl_n_threads_per_pool[JL_THREADPOOL_ID_DEFAULT] = 1;
608+
} else {
609+
jl_current_task->world_age = jl_atomic_load_acquire(&jl_world_counter);
610+
post_image_load_hooks();
611+
}
612+
jl_start_threads();
613+
jl_start_gc_threads();
614+
uv_barrier_wait(&thread_init_done);
615+
616+
jl_gc_enable(1);
617+
618+
if ((sysimage.kind != JL_IMAGE_KIND_NONE) &&
619+
(!jl_generating_output() || jl_options.incremental) && jl_module_init_order) {
620+
jl_array_t *init_order = jl_module_init_order;
621+
JL_GC_PUSH1(&init_order);
622+
jl_module_init_order = NULL;
623+
int i, l = jl_array_nrows(init_order);
624+
for (i = 0; i < l; i++) {
625+
jl_value_t *mod = jl_array_ptr_ref(init_order, i);
626+
jl_module_run_initializer((jl_module_t*)mod);
627+
}
628+
JL_GC_POP();
629+
}
630+
631+
if (jl_options.trim) {
632+
jl_entrypoint_mis = (arraylist_t *)malloc_s(sizeof(arraylist_t));
633+
arraylist_new(jl_entrypoint_mis, 0);
634+
}
635+
636+
if (jl_options.handle_signals == JL_OPTIONS_HANDLE_SIGNALS_ON)
637+
jl_install_sigint_handler();
638+
}
724639

725-
static NOINLINE void _finish_julia_init(JL_IMAGE_SEARCH rel, jl_ptls_t ptls, jl_task_t *ct);
726640

727641
JL_DLLEXPORT int jl_default_debug_info_kind;
728642
JL_DLLEXPORT jl_cgparams_t jl_default_cgparams = {
@@ -750,12 +664,12 @@ static void init_global_mutexes(void) {
750664
JL_MUTEX_INIT(&profile_show_peek_cond_lock, "profile_show_peek_cond_lock");
751665
}
752666

753-
JL_DLLEXPORT void julia_init(JL_IMAGE_SEARCH rel)
667+
JL_DLLEXPORT void jl_init_(jl_image_buf_t sysimage)
754668
{
755669
// initialize many things, in no particular order
756670
// but generally running from simple platform things to optional
757671
// configuration features
758-
jl_init_timing();
672+
759673
// Make sure we finalize the tls callback before starting any threads.
760674
(void)jl_get_pgcstack();
761675

@@ -860,96 +774,7 @@ JL_DLLEXPORT void julia_init(JL_IMAGE_SEARCH rel)
860774
jl_task_t *ct = jl_init_root_task(ptls, stack_lo, stack_hi);
861775
#pragma GCC diagnostic pop
862776
JL_GC_PROMISE_ROOTED(ct);
863-
_finish_julia_init(rel, ptls, ct);
864-
}
865-
866-
static NOINLINE void _finish_julia_init(JL_IMAGE_SEARCH rel, jl_ptls_t ptls, jl_task_t *ct)
867-
{
868-
JL_TIMING(JULIA_INIT, JULIA_INIT);
869-
jl_resolve_sysimg_location(rel);
870-
871-
// loads sysimg if available, and conditionally sets jl_options.cpu_target
872-
jl_image_buf_t sysimage = { JL_IMAGE_KIND_NONE };
873-
if (rel == JL_IMAGE_IN_MEMORY) {
874-
sysimage = jl_set_sysimg_so(jl_exe_handle);
875-
jl_options.image_file = jl_options.julia_bin;
876-
}
877-
else if (jl_options.image_file)
878-
sysimage = jl_preload_sysimg(jl_options.image_file);
879-
880-
if (sysimage.kind == JL_IMAGE_KIND_SO)
881-
jl_gc_notify_image_load(sysimage.data, sysimage.size);
882-
883-
if (jl_options.cpu_target == NULL)
884-
jl_options.cpu_target = "native";
885-
886-
// Parse image, perform relocations, and init JIT targets, etc.
887-
jl_image_t parsed_image = jl_init_processor_sysimg(sysimage, jl_options.cpu_target);
888-
889-
jl_init_codegen();
890-
jl_init_common_symbols();
891-
892-
if (sysimage.kind != JL_IMAGE_KIND_NONE) {
893-
// Load the .ji or .so sysimage
894-
jl_restore_system_image(&parsed_image, sysimage);
895-
} else {
896-
// No sysimage provided, init a minimal environment
897-
jl_init_types();
898-
jl_global_roots_list = (jl_genericmemory_t*)jl_an_empty_memory_any;
899-
jl_global_roots_keyset = (jl_genericmemory_t*)jl_an_empty_memory_any;
900-
}
901-
902-
jl_init_flisp();
903-
jl_init_serializer();
904-
905-
if (sysimage.kind == JL_IMAGE_KIND_NONE) {
906-
jl_top_module = jl_core_module;
907-
jl_init_intrinsic_functions();
908-
jl_init_primitives();
909-
jl_init_main_module();
910-
jl_load(jl_core_module, "boot.jl");
911-
jl_current_task->world_age = jl_atomic_load_acquire(&jl_world_counter);
912-
post_boot_hooks();
913-
}
914-
915-
if (jl_base_module == NULL) {
916-
// nthreads > 1 requires code in Base
917-
jl_atomic_store_relaxed(&jl_n_threads, 1);
918-
jl_n_markthreads = 0;
919-
jl_n_sweepthreads = 0;
920-
jl_n_gcthreads = 0;
921-
jl_n_threads_per_pool[JL_THREADPOOL_ID_INTERACTIVE] = 0;
922-
jl_n_threads_per_pool[JL_THREADPOOL_ID_DEFAULT] = 1;
923-
} else {
924-
jl_current_task->world_age = jl_atomic_load_acquire(&jl_world_counter);
925-
post_image_load_hooks();
926-
}
927-
jl_start_threads();
928-
jl_start_gc_threads();
929-
uv_barrier_wait(&thread_init_done);
930-
931-
jl_gc_enable(1);
932-
933-
if ((sysimage.kind != JL_IMAGE_KIND_NONE) &&
934-
(!jl_generating_output() || jl_options.incremental) && jl_module_init_order) {
935-
jl_array_t *init_order = jl_module_init_order;
936-
JL_GC_PUSH1(&init_order);
937-
jl_module_init_order = NULL;
938-
int i, l = jl_array_nrows(init_order);
939-
for (i = 0; i < l; i++) {
940-
jl_value_t *mod = jl_array_ptr_ref(init_order, i);
941-
jl_module_run_initializer((jl_module_t*)mod);
942-
}
943-
JL_GC_POP();
944-
}
945-
946-
if (jl_options.trim) {
947-
jl_entrypoint_mis = (arraylist_t *)malloc_s(sizeof(arraylist_t));
948-
arraylist_new(jl_entrypoint_mis, 0);
949-
}
950-
951-
if (jl_options.handle_signals == JL_OPTIONS_HANDLE_SIGNALS_ON)
952-
jl_install_sigint_handler();
777+
_finish_jl_init_(sysimage, ptls, ct);
953778
}
954779

955780
#ifdef __cplusplus

src/jl_exported_funcs.inc

+3-1
Original file line numberDiff line numberDiff line change
@@ -235,9 +235,11 @@
235235
XX(jl_hrtime) \
236236
XX(jl_idtable_rehash) \
237237
XX(jl_init) \
238+
XX(jl_init_) \
238239
XX(jl_init_options) \
239240
XX(jl_init_restored_module) \
240-
XX(jl_init_with_image) \
241+
XX(jl_init_with_image_file) \
242+
XX(jl_init_with_image_handle) \
241243
XX(jl_install_sigint_handler) \
242244
XX(jl_instantiate_type_in_env) \
243245
XX(jl_instantiate_unionall) \

0 commit comments

Comments
 (0)