Skip to content

Commit dd17a4f

Browse files
topolaritygbaraldi
authored andcommitted
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
1 parent 66ade55 commit dd17a4f

File tree

8 files changed

+302
-284
lines changed

8 files changed

+302
-284
lines changed

doc/src/manual/embedding.md

Lines changed: 1 addition & 1 deletion
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

Lines changed: 81 additions & 256 deletions
Original file line numberDiff line numberDiff line change
@@ -534,169 +534,6 @@ int jl_isabspath(const char *in) JL_NOTSAFEPOINT
534534
return 0; // relative path
535535
}
536536

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

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

728642
JL_DLLEXPORT int jl_default_debug_info_kind;
729643
JL_DLLEXPORT jl_cgparams_t jl_default_cgparams = {
@@ -751,12 +665,12 @@ static void init_global_mutexes(void) {
751665
JL_MUTEX_INIT(&profile_show_peek_cond_lock, "profile_show_peek_cond_lock");
752666
}
753667

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

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

956781
#ifdef __cplusplus

src/jl_exported_funcs.inc

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,9 +240,11 @@
240240
XX(jl_hrtime) \
241241
XX(jl_idtable_rehash) \
242242
XX(jl_init) \
243+
XX(jl_init_) \
243244
XX(jl_init_options) \
244245
XX(jl_init_restored_module) \
245-
XX(jl_init_with_image) \
246+
XX(jl_init_with_image_file) \
247+
XX(jl_init_with_image_handle) \
246248
XX(jl_install_sigint_handler) \
247249
XX(jl_instantiate_type_in_env) \
248250
XX(jl_instantiate_unionall) \

0 commit comments

Comments
 (0)