Skip to content

Commit 15f84b3

Browse files
authored
fix accumulated bugs with stdlib initialization (#26241)
These were making it hard to turn off certain stdlibs, which in turn made it hard to test alternate configurations and compiler changes. And some environment variables weren't being handled well (or tested).
1 parent c7da537 commit 15f84b3

File tree

9 files changed

+122
-50
lines changed

9 files changed

+122
-50
lines changed

base/client.jl

+12-5
Original file line numberDiff line numberDiff line change
@@ -298,11 +298,6 @@ function exec_options(opts)
298298
# load ~/.julia/config/startup.jl file
299299
startup && load_julia_startup()
300300

301-
if repl || is_interactive
302-
# load interactive-only libraries
303-
eval(Main, :(using InteractiveUtils))
304-
end
305-
306301
# process cmds list
307302
for (cmd, arg) in cmds
308303
if cmd == 'e'
@@ -387,6 +382,18 @@ const REPL_MODULE_REF = Ref{Module}()
387382
# run the requested sort of evaluation loop on stdio
388383
function run_main_repl(interactive::Bool, quiet::Bool, banner::Bool, history_file::Bool, color_set::Bool)
389384
global active_repl
385+
# load interactive-only libraries
386+
if !isdefined(Main, :InteractiveUtils)
387+
try
388+
let InteractiveUtils = require(PkgId(UUID(0xb77e0a4c_d291_57a0_90e8_8db25a27a240), "InteractiveUtils"))
389+
eval(Main, :(const InteractiveUtils = $InteractiveUtils))
390+
eval(Main, :(using .InteractiveUtils))
391+
end
392+
catch ex
393+
@warn "Failed to insert InteractiveUtils into module Main" exception=(ex, catch_backtrace())
394+
end
395+
end
396+
390397
if interactive && isassigned(REPL_MODULE_REF)
391398
invokelatest(REPL_MODULE_REF[]) do REPL
392399
term_env = get(ENV, "TERM", @static Sys.iswindows() ? "" : "dumb")

base/initdefs.jl

+3-13
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ const DEPOT_PATH = String[]
4646
function init_depot_path(BINDIR = Sys.BINDIR)
4747
if haskey(ENV, "JULIA_DEPOT_PATH")
4848
depots = split(ENV["JULIA_DEPOT_PATH"], Sys.iswindows() ? ';' : ':')
49-
push!(empty!(DEPOT_PATH), map(expanduser, depots))
49+
append!(empty!(DEPOT_PATH), map(expanduser, depots))
5050
else
5151
push!(DEPOT_PATH, joinpath(homedir(), ".julia"))
5252
end
@@ -80,6 +80,7 @@ function show(io::IO, env::NamedEnv)
8080
end
8181

8282
function parse_env(env::Union{String,SubString{String}})
83+
isempty(env) && return Any[]
8384
env == "@" && return CurrentEnv()
8485
env == "@!" && return CurrentEnv(create=true)
8586
if env[1] == '@'
@@ -106,7 +107,7 @@ function parse_load_path(str::String)
106107
envs = Any[split(str, Sys.iswindows() ? ';' : ':');]
107108
for (i, env) in enumerate(envs)
108109
if '|' in env
109-
envs[i] = [parse_env(e) for e in split(env, '|')]
110+
envs[i] = Any[parse_env(e) for e in split(env, '|')]
110111
else
111112
envs[i] = parse_env(env)
112113
end
@@ -122,17 +123,6 @@ function init_load_path(BINDIR = Sys.BINDIR)
122123
push!(LOAD_PATH, abspath(BINDIR, "..", "share", "julia", "site", vers))
123124
end
124125

125-
function early_init()
126-
Sys._early_init()
127-
# make sure OpenBLAS does not set CPU affinity (#1070, #9639)
128-
ENV["OPENBLAS_MAIN_FREE"] = get(ENV, "OPENBLAS_MAIN_FREE",
129-
get(ENV, "GOTOBLAS_MAIN_FREE", "1"))
130-
if Sys.CPU_CORES > 8 && !("OPENBLAS_NUM_THREADS" in keys(ENV)) && !("OMP_NUM_THREADS" in keys(ENV))
131-
# Prevent openblas from starting too many threads, unless/until specifically requested
132-
ENV["OPENBLAS_NUM_THREADS"] = 8
133-
end
134-
end
135-
136126
const atexit_hooks = []
137127

138128
"""

base/logging.jl

+2-2
Original file line numberDiff line numberDiff line change
@@ -367,8 +367,6 @@ end
367367

368368
LogState(logger) = LogState(LogLevel(min_enabled_level(logger)), logger)
369369

370-
_global_logstate = LogState(NullLogger())
371-
372370
function current_logstate()
373371
logstate = current_task().logstate
374372
(logstate != nothing ? logstate : _global_logstate)::LogState
@@ -493,4 +491,6 @@ function handle_message(logger::SimpleLogger, level, message, _module, group, id
493491
nothing
494492
end
495493

494+
_global_logstate = LogState(SimpleLogger(Core.stderr, CoreLogging.Info))
495+
496496
end # CoreLogging

base/sysimg.jl

+21-8
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,6 @@ include("weakkeydict.jl")
324324
# Logging
325325
include("logging.jl")
326326
using .CoreLogging
327-
global_logger(SimpleLogger(Core.stderr, CoreLogging.Info))
328327

329328
# To limit dependency on rand functionality (implemented in the Random
330329
# module), Crand is used in file.jl, and could be used in error.jl
@@ -343,7 +342,7 @@ Crand(::Type{Float64}) = Crand(UInt32) / 2^32
343342
"""
344343
Csrand([seed])
345344
346-
Interface the the C `srand(seed)` function.
345+
Interface with the C `srand(seed)` function.
347346
"""
348347
Csrand(seed=floor(time())) = ccall(:srand, Cvoid, (Cuint,), seed)
349348

@@ -456,7 +455,7 @@ include("loading.jl")
456455
include("util.jl")
457456

458457
# set up depot & load paths to be able to find stdlib packages
459-
let BINDIR = ccall(:jl_get_julia_bindir, Any, ())
458+
let BINDIR = Sys.BINDIR
460459
init_depot_path(BINDIR)
461460
init_load_path(BINDIR)
462461
end
@@ -483,17 +482,28 @@ end_base_include = time_ns()
483482

484483
if is_primary_base_module
485484
function __init__()
485+
# try to ensuremake sure OpenBLAS does not set CPU affinity (#1070, #9639)
486+
if !haskey(ENV, "OPENBLAS_MAIN_FREE") && !haskey(ENV, "GOTOBLAS_MAIN_FREE")
487+
ENV["OPENBLAS_MAIN_FREE"] = "1"
488+
end
489+
# And try to prevent openblas from starting too many threads, unless/until specifically requested
490+
if !haskey(ENV, "OPENBLAS_NUM_THREADS") && !haskey(ENV, "OMP_NUM_THREADS")
491+
cpu_cores = Sys.CPU_CORES::Int
492+
if cpu_cores > 8 # always at most 8
493+
ENV["OPENBLAS_NUM_THREADS"] = "8"
494+
elseif haskey(ENV, "JULIA_CPU_CORES") # or exactly as specified
495+
ENV["OPENBLAS_NUM_THREADS"] = cpu_cores
496+
end # otherwise, trust that openblas will pick CPU_CORES anyways, without any intervention
497+
end
486498
# for the few uses of Crand in Base:
487499
Csrand()
488500
# Base library init
489501
reinit_stdio()
490-
Logging = root_module(PkgId(UUID(0x56ddb016_857b_54e1_b83d_db4d58db5568), "Logging"))
491-
global_logger(Logging.ConsoleLogger(stderr))
492502
Multimedia.reinit_displays() # since Multimedia.displays uses stdout as fallback
493-
early_init()
503+
# initialize loading
494504
init_depot_path()
495505
init_load_path()
496-
init_threadcall()
506+
nothing
497507
end
498508

499509
INCLUDE_STATE = 3 # include = include_relative
@@ -505,7 +515,6 @@ end # baremodule Base
505515

506516
using .Base
507517

508-
509518
# Ensure this file is also tracked
510519
pushfirst!(Base._included_files, (@__MODULE__, joinpath(@__DIR__, "sysimg.jl")))
511520

@@ -915,8 +924,12 @@ end
915924
end
916925
end
917926

927+
# Clear global state
928+
empty!(Core.ARGS)
929+
empty!(Base.ARGS)
918930
empty!(DEPOT_PATH)
919931
empty!(LOAD_PATH)
932+
@eval Base.Sys BINDIR = ""
920933

921934
let
922935
tot_time_userimg = @elapsed (Base.isfile("userimg.jl") && Base.include(Main, "userimg.jl"))

base/sysinfo.jl

+14-6
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,13 @@ export BINDIR,
2727

2828
import ..Base: show
2929

30+
global BINDIR = ccall(:jl_get_julia_bindir, Any, ())
3031
"""
3132
Sys.BINDIR
3233
3334
A string containing the full path to the directory containing the `julia` executable.
3435
"""
35-
BINDIR = ccall(:jl_get_julia_bindir, Any, ())
36-
37-
_early_init() = global BINDIR = ccall(:jl_get_julia_bindir, Any, ())
36+
:BINDIR
3837

3938
# helper to avoid triggering precompile warnings
4039

@@ -78,12 +77,21 @@ Standard word size on the current machine, in bits.
7877
const WORD_SIZE = Core.sizeof(Int) * 8
7978

8079
function __init__()
81-
global CPU_CORES =
82-
haskey(ENV,"JULIA_CPU_CORES") ? parse(Int,ENV["JULIA_CPU_CORES"]) :
83-
Int(ccall(:jl_cpu_cores, Int32, ()))
80+
env_cores = get(ENV, "JULIA_CPU_CORES", "")
81+
global CPU_CORES = if !isempty(env_cores)
82+
env_cores = tryparse(Int, env_cores)
83+
if !(env_cores isa Int && env_cores > 0)
84+
Core.print(Core.stderr, "WARNING: couldn't parse `JULIA_CPU_CORES` environment variable. Defaulting Sys.CPU_CORES to 1.\n")
85+
env_cores = 1
86+
end
87+
env_cores
88+
else
89+
Int(ccall(:jl_cpu_cores, Int32, ()))
90+
end
8491
global SC_CLK_TCK = ccall(:jl_SC_CLK_TCK, Clong, ())
8592
global CPU_NAME = ccall(:jl_get_cpu_name, Ref{String}, ())
8693
global JIT = ccall(:jl_get_JIT, Ref{String}, ())
94+
global BINDIR = ccall(:jl_get_julia_bindir, Any, ())
8795
end
8896

8997
mutable struct UV_cpu_info_t

base/threadcall.jl

+1-4
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,6 @@ function notify_fun(idx)
1010
return
1111
end
1212

13-
function init_threadcall()
14-
global c_notify_fun = cfunction(notify_fun, Cvoid, Tuple{Cint})
15-
end
16-
1713
"""
1814
@threadcall((cfunc, clib), rettype, (argtypes...), argvals...)
1915
@@ -65,6 +61,7 @@ end
6561
function do_threadcall(wrapper::Function, rettype::Type, argtypes::Vector, argvals::Vector)
6662
# generate function pointer
6763
fun_ptr = cfunction(wrapper, Int, Tuple{Ptr{Cvoid}, Ptr{Cvoid}})
64+
c_notify_fun = cfunction(notify_fun, Cvoid, Tuple{Cint})
6865

6966
# cconvert, root and unsafe_convert arguments
7067
roots = Any[]

stdlib/Distributed/test/distributed_exec.jl

+4-3
Original file line numberDiff line numberDiff line change
@@ -948,7 +948,7 @@ const get_num_threads = function() # anonymous so it will be serialized when cal
948948

949949
# OSX BLAS looks at an environment variable
950950
if Sys.isapple()
951-
return ENV["VECLIB_MAXIMUM_THREADS"]
951+
return tryparse(Cint, get(ENV, "VECLIB_MAXIMUM_THREADS", "1"))
952952
end
953953
end
954954

@@ -969,11 +969,12 @@ function test_blas_config(pid, expected)
969969
end
970970

971971
function test_add_procs_threaded_blas()
972-
if get_num_threads() === nothing
972+
master_blas_thread_count = get_num_threads()
973+
if master_blas_thread_count === nothing
973974
@warn "Skipping blas num threads tests due to unsupported blas version"
974975
return
975976
end
976-
master_blas_thread_count = get_num_threads()
977+
@test master_blas_thread_count <= 8 # check that Base set the environment variable in __init__ before LinearAlgebra dlopen'd it
977978

978979
# Test with default enable_threaded_blas false
979980
processes_added = addprocs_with_testenv(2)

stdlib/Logging/src/Logging.jl

+4
Original file line numberDiff line numberDiff line change
@@ -52,4 +52,8 @@ include("ConsoleLogger.jl")
5252
# 2. AbstractLogger message related functions:
5353
# handle_message, shouldlog, min_enabled_level, catch_exceptions,
5454

55+
function __init__()
56+
global_logger(ConsoleLogger(stderr))
57+
end
58+
5559
end

test/cmdlineargs.jl

+61-9
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ function writereadpipeline(input, exename)
1010
write(p.in, input)
1111
close(p.in)
1212
end
13-
return read(p.out, String)
13+
return (read(p.out, String), success(p))
1414
end
1515

1616
# helper function for returning stderr and stdout
@@ -25,6 +25,52 @@ function readchomperrors(exename::Cmd)
2525
end
2626

2727

28+
let exename = `$(Base.julia_cmd()) --sysimage-native-code=yes --startup-file=no`
29+
# tests for handling of ENV errors
30+
let v = writereadpipeline("println(\"REPL: \", @which(less), @isdefined(InteractiveUtils))",
31+
setenv(`$exename -i -E 'empty!(LOAD_PATH); @isdefined InteractiveUtils'`,
32+
"JULIA_LOAD_PATH"=>"", "JULIA_DEPOT_PATH"=>""))
33+
@test v[1] == "false\nREPL: InteractiveUtilstrue\n"
34+
@test v[2]
35+
end
36+
let v = writereadpipeline("println(\"REPL: \", InteractiveUtils)",
37+
setenv(`$exename -i -e 'const InteractiveUtils = 3'`,
38+
"JULIA_LOAD_PATH"=>";;;:::", "JULIA_DEPOT_PATH"=>";;;:::"))
39+
# TODO: ideally, `@which`, etc. would still work, but Julia can't handle `using $InterativeUtils`
40+
@test v[1] == "REPL: 3\n"
41+
@test v[2]
42+
end
43+
let v = readchomperrors(`$exename -i -e '
44+
empty!(LOAD_PATH)
45+
Base.unreference_module(Base.PkgId(Base.UUID(0xb77e0a4c_d291_57a0_90e8_8db25a27a240), "InteractiveUtils"))
46+
'`)
47+
# simulate not having a working version of InteractiveUtils,
48+
# make sure this is a non-fatal error and the REPL still loads
49+
@test v[1]
50+
@test isempty(v[2])
51+
@test startswith(v[3], """
52+
┌ Warning: Failed to insert InteractiveUtils into module Main
53+
│ exception =
54+
│ ArgumentError: Module InteractiveUtils not found in current path.
55+
│ Run `Pkg.add("InteractiveUtils")` to install the InteractiveUtils package.
56+
│ Stacktrace:
57+
""")
58+
end
59+
for nc in ("0", "-2", "x", "2x", " ")
60+
v = readchomperrors(setenv(`$exename -i -E 'Sys.CPU_CORES'`, "JULIA_CPU_CORES" => nc))
61+
@test v[1]
62+
@test v[2] == "1"
63+
@test v[3] == "WARNING: couldn't parse `JULIA_CPU_CORES` environment variable. Defaulting Sys.CPU_CORES to 1."
64+
end
65+
real_cores = string(ccall(:jl_cpu_cores, Int32, ()))
66+
for nc in ("1", " 1 ", " +1 ", " 0x1 ", "")
67+
v = readchomperrors(setenv(`$exename -i -E 'Sys.CPU_CORES'`, "JULIA_CPU_CORES" => nc))
68+
@test v[1]
69+
@test v[2] == (isempty(nc) ? real_cores : "1")
70+
@test isempty(v[3])
71+
end
72+
end
73+
2874
let exename = `$(Base.julia_cmd()) --sysimage-native-code=yes --startup-file=no`
2975
# --version
3076
let v = split(read(`$exename -v`, String), "julia version ")[end]
@@ -173,19 +219,25 @@ let exename = `$(Base.julia_cmd()) --sysimage-native-code=yes --startup-file=no`
173219

174220
# -g
175221
@test readchomp(`$exename -E "Base.JLOptions().debug_level" -g`) == "2"
176-
let code = read(`$exename -g0 -i -e "code_llvm(stdout, +, (Int64, Int64), false, true); exit()"`, String)
222+
let code = writereadpipeline("code_llvm(stdout, +, (Int64, Int64), false, true)", `$exename -g0`)
223+
@test code[2]
224+
code = code[1]
177225
@test contains(code, "llvm.module.flags")
178226
@test !contains(code, "llvm.dbg.cu")
179227
@test !contains(code, "int.jl")
180228
@test !contains(code, "Int64")
181229
end
182-
let code = read(`$exename -g1 -i -e "code_llvm(stdout, +, (Int64, Int64), false, true); exit()"`, String)
230+
let code = writereadpipeline("code_llvm(stdout, +, (Int64, Int64), false, true)", `$exename -g1`)
231+
@test code[2]
232+
code = code[1]
183233
@test contains(code, "llvm.module.flags")
184234
@test contains(code, "llvm.dbg.cu")
185235
@test contains(code, "int.jl")
186236
@test !contains(code, "Int64")
187237
end
188-
let code = read(`$exename -g2 -i -e "code_llvm(stdout, +, (Int64, Int64), false, true); exit()"`, String)
238+
let code = writereadpipeline("code_llvm(stdout, +, (Int64, Int64), false, true)", `$exename -g2`)
239+
@test code[2]
240+
code = code[1]
189241
@test contains(code, "llvm.module.flags")
190242
@test contains(code, "llvm.dbg.cu")
191243
@test contains(code, "int.jl")
@@ -486,11 +538,11 @@ end
486538

487539
# issue #6310
488540
let exename = `$(Base.julia_cmd()) --startup-file=no`
489-
@test writereadpipeline("2+2", exename) == "4\n"
490-
@test writereadpipeline("2+2\n3+3\n4+4", exename) == "4\n6\n8\n"
491-
@test writereadpipeline("", exename) == ""
492-
@test writereadpipeline("print(2)", exename) == "2"
493-
@test writereadpipeline("print(2)\nprint(3)", exename) == "23"
541+
@test writereadpipeline("2+2", exename) == ("4\n", true)
542+
@test writereadpipeline("2+2\n3+3\n4+4", exename) == ("4\n6\n8\n", true)
543+
@test writereadpipeline("", exename) == ("", true)
544+
@test writereadpipeline("print(2)", exename) == ("2", true)
545+
@test writereadpipeline("print(2)\nprint(3)", exename) == ("23", true)
494546
let infile = tempname()
495547
touch(infile)
496548
try

0 commit comments

Comments
 (0)