diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index aa63399c0e155..e97afc54549a1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -190,7 +190,7 @@ For new functionality and other substantial changes, add a brief summary to `NEW API additions and deprecations, and minor behavior changes are allowed in minor version releases. For documented features that are part of the public API, a compatibility note should be added into -the manual or the docstring. It should state the Julia minor version that made changed the behavior +the manual or the docstring. It should state the Julia minor version that changed the behavior and have a brief message describing the change. At the moment, this should always be done with the following `compat` admonition diff --git a/NEWS.md b/NEWS.md index 4d757a20e247d..c96b1f28594c9 100644 --- a/NEWS.md +++ b/NEWS.md @@ -14,6 +14,9 @@ Language changes Multi-threading changes ----------------------- +* All system-level I/O operations (e.g. files and sockets) are now thread-safe. + This does not include subtypes of `IO` that are entirely in-memory, e.g. `IOBuffer`. + ([#32309], [#32174], [#31981], [#32421]). Build system changes -------------------- diff --git a/base/Base.jl b/base/Base.jl index efd9e6887dd03..7c5274405668b 100644 --- a/base/Base.jl +++ b/base/Base.jl @@ -198,7 +198,6 @@ include("c.jl") # Core I/O include("io.jl") -include("iostream.jl") include("iobuffer.jl") # strings & printing @@ -264,6 +263,7 @@ function randn end # I/O include("libuv.jl") include("asyncevent.jl") +include("iostream.jl") include("stream.jl") include("filesystem.jl") using .Filesystem diff --git a/base/channels.jl b/base/channels.jl index 1cb6b5386f960..3653ec9c1a2b4 100644 --- a/base/channels.jl +++ b/base/channels.jl @@ -304,8 +304,8 @@ function put_unbuffered(c::Channel, v) finally unlock(c) end - # unfair version of: schedule(taker, v); yield() - yield(taker, v) # immediately give taker a chance to run, but don't block the current task + schedule(taker, v) + yield() # immediately give taker a chance to run, but don't block the current task return v end diff --git a/base/io.jl b/base/io.jl index 047862ec227b3..d339b773da42b 100644 --- a/base/io.jl +++ b/base/io.jl @@ -226,6 +226,80 @@ function unsafe_read(s::IO, p::Ptr{UInt8}, n::UInt) nothing end +function peek(s::IO) + mark(s) + try read(s, UInt8) + finally + reset(s) + end +end + +# Generic `open` methods + +""" + open_flags(; keywords...) -> NamedTuple + +Compute the `read`, `write`, `create`, `truncate`, `append` flag value for +a given set of keyword arguments to [`open`](@ref) a [`NamedTuple`](@ref). +""" +function open_flags(; + read :: Union{Bool,Nothing} = nothing, + write :: Union{Bool,Nothing} = nothing, + create :: Union{Bool,Nothing} = nothing, + truncate :: Union{Bool,Nothing} = nothing, + append :: Union{Bool,Nothing} = nothing, +) + if write === true && read !== true && append !== true + create === nothing && (create = true) + truncate === nothing && (truncate = true) + end + + if truncate === true || append === true + write === nothing && (write = true) + create === nothing && (create = true) + end + + write === nothing && (write = false) + read === nothing && (read = !write) + create === nothing && (create = false) + truncate === nothing && (truncate = false) + append === nothing && (append = false) + + return ( + read = read, + write = write, + create = create, + truncate = truncate, + append = append, + ) +end + +""" + open(f::Function, args...; kwargs....) + +Apply the function `f` to the result of `open(args...; kwargs...)` and close the resulting file +descriptor upon completion. + +# Examples +```jldoctest +julia> open("myfile.txt", "w") do io + write(io, "Hello world!") + end; + +julia> open(f->read(f, String), "myfile.txt") +"Hello world!" + +julia> rm("myfile.txt") +``` +""" +function open(f::Function, args...; kwargs...) + io = open(args...; kwargs...) + try + f(io) + finally + close(io) + end +end # Generic wrappers around other IO objects abstract type AbstractPipe <: IO end diff --git a/base/iostream.jl b/base/iostream.jl index 981bd83813bfe..70b08d84f7aa9 100644 --- a/base/iostream.jl +++ b/base/iostream.jl @@ -15,11 +15,11 @@ mutable struct IOStream <: IO ios::Array{UInt8,1} name::AbstractString mark::Int64 + lock::ReentrantLock - IOStream(name::AbstractString, buf::Array{UInt8,1}) = new(pointer(buf), buf, name, -1) + IOStream(name::AbstractString, buf::Array{UInt8,1}) = new(pointer(buf), buf, name, -1, ReentrantLock()) end -# TODO: delay adding finalizer, e.g. for memio with a small buffer, or -# in the case where we take! it. + function IOStream(name::AbstractString, finalize::Bool) buf = zeros(UInt8,sizeof_ios_t) x = IOStream(name, buf) @@ -42,11 +42,11 @@ to synchronous `File`'s and `IOStream`'s not to any of the asynchronous streams. fd(s::IOStream) = Int(ccall(:jl_ios_fd, Clong, (Ptr{Cvoid},), s.ios)) stat(s::IOStream) = stat(fd(s)) -close(s::IOStream) = ccall(:ios_close, Cvoid, (Ptr{Cvoid},), s.ios) -isopen(s::IOStream) = ccall(:ios_isopen, Cint, (Ptr{Cvoid},), s.ios)!=0 +close(s::IOStream) = @lock_nofail s.lock ccall(:ios_close, Cvoid, (Ptr{Cvoid},), s.ios) +isopen(s::IOStream) = ccall(:ios_isopen, Cint, (Ptr{Cvoid},), s.ios) != 0 function flush(s::IOStream) sigatomic_begin() - bad = ccall(:ios_flush, Cint, (Ptr{Cvoid},), s.ios) != 0 + bad = @lock_nofail s.lock ccall(:ios_flush, Cint, (Ptr{Cvoid},), s.ios) != 0 sigatomic_end() systemerror("flush", bad) end @@ -83,7 +83,8 @@ julia> String(take!(io)) ``` """ function truncate(s::IOStream, n::Integer) - systemerror("truncate", ccall(:ios_trunc, Cint, (Ptr{Cvoid}, Csize_t), s.ios, n) != 0) + err = @lock_nofail s.lock ccall(:ios_trunc, Cint, (Ptr{Cvoid}, Csize_t), s.ios, n) != 0 + systemerror("truncate", err) return s end @@ -103,7 +104,7 @@ julia> read(io, Char) ``` """ function seek(s::IOStream, n::Integer) - ret = ccall(:ios_seek, Int64, (Ptr{Cvoid}, Int64), s.ios, n) + ret = @lock_nofail s.lock ccall(:ios_seek, Int64, (Ptr{Cvoid}, Int64), s.ios, n) systemerror("seek", ret == -1) ret < -1 && error("seek failed") return s @@ -137,7 +138,8 @@ seekstart(s::IO) = seek(s,0) Seek a stream to its end. """ function seekend(s::IOStream) - systemerror("seekend", ccall(:ios_seek_end, Int64, (Ptr{Cvoid},), s.ios) != 0) + err = @lock_nofail s.lock ccall(:ios_seek_end, Int64, (Ptr{Cvoid},), s.ios) != 0 + systemerror("seekend", err) return s end @@ -159,7 +161,7 @@ julia> read(io, Char) ``` """ function skip(s::IOStream, delta::Integer) - ret = ccall(:ios_skip, Int64, (Ptr{Cvoid}, Int64), s.ios, delta) + ret = @lock_nofail s.lock ccall(:ios_skip, Int64, (Ptr{Cvoid}, Int64), s.ios, delta) systemerror("skip", ret == -1) ret < -1 && error("skip failed") return s @@ -191,12 +193,13 @@ julia> position(io) ``` """ function position(s::IOStream) - pos = ccall(:ios_pos, Int64, (Ptr{Cvoid},), s.ios) + pos = @lock_nofail s.lock ccall(:ios_pos, Int64, (Ptr{Cvoid},), s.ios) systemerror("position", pos == -1) return pos end -eof(s::IOStream) = ccall(:ios_eof_blocking, Cint, (Ptr{Cvoid},), s.ios)!=0 +_eof_nolock(s::IOStream) = ccall(:ios_eof_blocking, Cint, (Ptr{Cvoid},), s.ios) != 0 +eof(s::IOStream) = @lock_nofail s.lock _eof_nolock(s) ## constructing and opening streams ## @@ -217,44 +220,6 @@ function fdio(name::AbstractString, fd::Integer, own::Bool=false) end fdio(fd::Integer, own::Bool=false) = fdio(string(""), fd, own) -""" - open_flags(; keywords...) -> NamedTuple - -Compute the `read`, `write`, `create`, `truncate`, `append` flag value for -a given set of keyword arguments to [`open`](@ref) a [`NamedTuple`](@ref). -""" -function open_flags(; - read :: Union{Bool,Nothing} = nothing, - write :: Union{Bool,Nothing} = nothing, - create :: Union{Bool,Nothing} = nothing, - truncate :: Union{Bool,Nothing} = nothing, - append :: Union{Bool,Nothing} = nothing, -) - if write === true && read !== true && append !== true - create === nothing && (create = true) - truncate === nothing && (truncate = true) - end - - if truncate === true || append === true - write === nothing && (write = true) - create === nothing && (create = true) - end - - write === nothing && (write = false) - read === nothing && (read = !write) - create === nothing && (create = false) - truncate === nothing && (truncate = false) - append === nothing && (append = false) - - return ( - read = read, - write = write, - create = create, - truncate = truncate, - append = append, - ) -end - """ open(filename::AbstractString; keywords...) -> IOStream @@ -351,52 +316,36 @@ function open(fname::AbstractString, mode::AbstractString) throw(ArgumentError("invalid open mode: $mode")) end -""" - open(f::Function, args...; kwargs....) - -Apply the function `f` to the result of `open(args...; kwargs...)` and close the resulting file -descriptor upon completion. - -# Examples -```jldoctest -julia> open("myfile.txt", "w") do io - write(io, "Hello world!") - end; - -julia> open(f->read(f, String), "myfile.txt") -"Hello world!" - -julia> rm("myfile.txt") -``` -""" -function open(f::Function, args...; kwargs...) - io = open(args...; kwargs...) - try - f(io) - finally - close(io) - end -end - ## low-level calls ## function write(s::IOStream, b::UInt8) iswritable(s) || throw(ArgumentError("write failed, IOStream is not writeable")) - Int(ccall(:ios_putc, Cint, (Cint, Ptr{Cvoid}), b, s.ios)) + Int(@lock_nofail s.lock ccall(:ios_putc, Cint, (Cint, Ptr{Cvoid}), b, s.ios)) end function unsafe_write(s::IOStream, p::Ptr{UInt8}, nb::UInt) iswritable(s) || throw(ArgumentError("write failed, IOStream is not writeable")) - return Int(ccall(:ios_write, Csize_t, (Ptr{Cvoid}, Ptr{Cvoid}, Csize_t), s.ios, p, nb)) + return Int(@lock_nofail s.lock ccall(:ios_write, Csize_t, (Ptr{Cvoid}, Ptr{Cvoid}, Csize_t), s.ios, p, nb)) end # num bytes available without blocking -bytesavailable(s::IOStream) = ccall(:jl_nb_available, Int32, (Ptr{Cvoid},), s.ios) - -readavailable(s::IOStream) = read!(s, Vector{UInt8}(undef, bytesavailable(s))) +bytesavailable(s::IOStream) = @lock_nofail s.lock ccall(:jl_nb_available, Int32, (Ptr{Cvoid},), s.ios) + +function readavailable(s::IOStream) + lock(s.lock) + nb = ccall(:jl_nb_available, Int32, (Ptr{Cvoid},), s.ios) + a = Vector{UInt8}(undef, nb) + nr = ccall(:ios_readall, Csize_t, (Ptr{Cvoid}, Ptr{Cvoid}, Csize_t), s, a, nb) + if nr != nb + unlock(s.lock) + throw(EOFError()) + end + unlock(s.lock) + return a +end function read(s::IOStream, ::Type{UInt8}) - b = ccall(:ios_getc, Cint, (Ptr{Cvoid},), s.ios) + b = @lock_nofail s.lock ccall(:ios_getc, Cint, (Ptr{Cvoid},), s.ios) if b == -1 throw(EOFError()) end @@ -405,7 +354,15 @@ end if ENDIAN_BOM == 0x04030201 function read(s::IOStream, T::Union{Type{Int16},Type{UInt16},Type{Int32},Type{UInt32},Type{Int64},Type{UInt64}}) - return ccall(:jl_ios_get_nbyte_int, UInt64, (Ptr{Cvoid}, Csize_t), s.ios, sizeof(T)) % T + n = sizeof(T) + lock(s.lock) + if ccall(:jl_ios_buffer_n, Cint, (Ptr{Cvoid}, Csize_t), s.ios, n) != 0 + unlock(s.lock) + throw(EOFError()) + end + x = ccall(:jl_ios_get_nbyte_int, UInt64, (Ptr{Cvoid}, Csize_t), s.ios, n) % T + unlock(s.lock) + return x end read(s::IOStream, ::Type{Float16}) = reinterpret(Float16, read(s, Int16)) @@ -414,8 +371,8 @@ read(s::IOStream, ::Type{Float64}) = reinterpret(Float64, read(s, Int64)) end function unsafe_read(s::IOStream, p::Ptr{UInt8}, nb::UInt) - if ccall(:ios_readall, Csize_t, - (Ptr{Cvoid}, Ptr{Cvoid}, Csize_t), s, p, nb) != nb + nr = @lock_nofail s.lock ccall(:ios_readall, Csize_t, (Ptr{Cvoid}, Ptr{Cvoid}, Csize_t), s, p, nb) + if nr != nb throw(EOFError()) end nothing @@ -424,24 +381,25 @@ end ## text I/O ## take!(s::IOStream) = - ccall(:jl_take_buffer, Vector{UInt8}, (Ptr{Cvoid},), s.ios) + @lock_nofail s.lock ccall(:jl_take_buffer, Vector{UInt8}, (Ptr{Cvoid},), s.ios) function readuntil(s::IOStream, delim::UInt8; keep::Bool=false) - ccall(:jl_readuntil, Array{UInt8,1}, (Ptr{Cvoid}, UInt8, UInt8, UInt8), s.ios, delim, 0, !keep) + @lock_nofail s.lock ccall(:jl_readuntil, Array{UInt8,1}, (Ptr{Cvoid}, UInt8, UInt8, UInt8), s.ios, delim, 0, !keep) end # like readuntil, above, but returns a String without requiring a copy function readuntil_string(s::IOStream, delim::UInt8, keep::Bool) - ccall(:jl_readuntil, Ref{String}, (Ptr{Cvoid}, UInt8, UInt8, UInt8), s.ios, delim, 1, !keep) + @lock_nofail s.lock ccall(:jl_readuntil, Ref{String}, (Ptr{Cvoid}, UInt8, UInt8, UInt8), s.ios, delim, 1, !keep) end function readline(s::IOStream; keep::Bool=false) - ccall(:jl_readuntil, Ref{String}, (Ptr{Cvoid}, UInt8, UInt8, UInt8), s.ios, '\n', 1, keep ? 0 : 2) + @lock_nofail s.lock ccall(:jl_readuntil, Ref{String}, (Ptr{Cvoid}, UInt8, UInt8, UInt8), s.ios, '\n', 1, keep ? 0 : 2) end function readbytes_all!(s::IOStream, b::Array{UInt8}, nb) olb = lb = length(b) nr = 0 + @lock_nofail s.lock begin GC.@preserve b while nr < nb if lb < nr+1 lb = max(65536, (nr+1) * 2) @@ -449,7 +407,8 @@ function readbytes_all!(s::IOStream, b::Array{UInt8}, nb) end nr += Int(ccall(:ios_readall, Csize_t, (Ptr{Cvoid}, Ptr{Cvoid}, Csize_t), s.ios, pointer(b, nr+1), min(lb-nr, nb-nr))) - eof(s) && break + _eof_nolock(s) && break + end end if lb > olb && lb > nr resize!(b, max(olb, nr)) # shrink to just contain input data if was resized @@ -462,8 +421,11 @@ function readbytes_some!(s::IOStream, b::Array{UInt8}, nb) if nb > olb resize!(b, nb) end + local nr + @lock_nofail s.lock begin nr = GC.@preserve b Int(ccall(:ios_read, Csize_t, (Ptr{Cvoid}, Ptr{Cvoid}, Csize_t), s.ios, pointer(b), nb)) + end lb = length(b) if lb > olb && lb > nr resize!(b, max(olb, nr)) # shrink to just contain input data if was resized @@ -495,7 +457,7 @@ function read(s::IOStream) Int64(0) end if sz > 0 - pos = ccall(:ios_pos, Int64, (Ptr{Cvoid},), s.ios) + pos = position(s) if pos > 0 sz -= pos end @@ -528,13 +490,5 @@ end ## peek ## function peek(s::IOStream) - ccall(:ios_peekc, Cint, (Ptr{Cvoid},), s) -end - -function peek(s::IO) - mark(s) - try read(s, UInt8) - finally - reset(s) - end + @lock_nofail s.lock ccall(:ios_peekc, Cint, (Ptr{Cvoid},), s) end diff --git a/base/lock.jl b/base/lock.jl index 8df4e43aeccbf..60066220b744e 100644 --- a/base/lock.jl +++ b/base/lock.jl @@ -164,6 +164,28 @@ function trylock(f, l::AbstractLock) return false end +macro lock(l, expr) + quote + temp = $(esc(l)) + lock(temp) + try + $(esc(expr)) + finally + unlock(temp) + end + end +end + +macro lock_nofail(l, expr) + quote + temp = $(esc(l)) + lock(temp) + val = $(esc(expr)) + unlock(temp) + val + end +end + @eval Threads begin """ Threads.Condition([lock]) diff --git a/src/codegen.cpp b/src/codegen.cpp index 796ae8f6b6f93..f4ae5dbc41f7b 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -148,7 +148,7 @@ extern void _chkstk(void); #define DISABLE_FLOAT16 // llvm state -JL_DLLEXPORT LLVMContext jl_LLVMContext; +JL_DLLEXPORT LLVMContext &jl_LLVMContext = *(new LLVMContext()); TargetMachine *jl_TargetMachine; extern JITEventListener *CreateJuliaJITEventListener(); @@ -160,7 +160,7 @@ Module *shadow_output; #define jl_Module ctx.f->getParent() #define jl_builderModule(builder) (builder).GetInsertBlock()->getParent()->getParent() -static DataLayout jl_data_layout(""); +static DataLayout &jl_data_layout = *(new DataLayout("")); // types static Type *T_jlvalue; @@ -1392,7 +1392,7 @@ void jl_generate_fptr(jl_code_instance_t *output) codeinst->rettype_const = ucache->rettype_const; if (codeinst->rettype_const) jl_gc_wb(codeinst, codeinst->rettype_const); - codeinst->invoke = ucache->invoke; + jl_atomic_store_release(&codeinst->invoke, ucache->invoke); JL_UNLOCK(&codegen_lock); return; } @@ -1423,15 +1423,15 @@ void jl_generate_fptr(jl_code_instance_t *output) assert(specptr != NULL); // the fptr should be cached somewhere also if (codeinst->invoke == NULL) { - codeinst->specptr.fptr = specptr; - codeinst->invoke = fptr; + jl_atomic_store_release(&codeinst->specptr.fptr, specptr); + jl_atomic_store_release(&codeinst->invoke, fptr); } if (codeinst != output && output->invoke == NULL) { output->specptr = codeinst->specptr; output->rettype_const = codeinst->rettype_const; if (output->rettype_const) jl_gc_wb(output, output->rettype_const); - output->invoke = fptr; + jl_atomic_store_release(&output->invoke, fptr); } JL_UNLOCK(&codegen_lock); // Might GC } diff --git a/src/debuginfo.cpp b/src/debuginfo.cpp index 6aef69e15ade3..ed4eba43a79b9 100644 --- a/src/debuginfo.cpp +++ b/src/debuginfo.cpp @@ -394,10 +394,14 @@ class JuliaJITEventListener: public JITEventListener } } // now process these in order, so we ensure the closure values are updated before enabling the invoke pointer + // TODO: this sets these pointers a bit too early, allowing other threads to see + // the addresses before the code has been filled in. + /* for (auto &def : def_spec) def.first->specptr.fptr = (void*)def.second; for (auto &def : def_invoke) def.first->invoke = (jl_callptr_t)def.second; + */ uv_rwlock_wrunlock(&threadsafe); jl_gc_safe_leave(ptls, gc_state); } diff --git a/src/gc.c b/src/gc.c index c1d8e022f1e27..9bc0f603b60e6 100644 --- a/src/gc.c +++ b/src/gc.c @@ -571,42 +571,14 @@ static int prev_sweep_full = 1; // Full collection heuristics static int64_t live_bytes = 0; static int64_t promoted_bytes = 0; +static int64_t last_full_live = 0; // live_bytes after last full collection +static int64_t last_live_bytes = 0; // live_bytes at last collection +static int64_t grown_heap_age = 0; // # of collects since live_bytes grew and remained #ifdef __GLIBC__ // maxrss at last malloc_trim static int64_t last_trim_maxrss = 0; #endif -static int64_t last_full_live_ub = 0; -static int64_t last_full_live_est = 0; -// upper bound and estimated live object sizes -// This heuristic should be really unlikely to trigger. -// However, this should be simple enough to trigger a full collection -// when it's necessary if other heuristics are messed up. -// It is also possible to take the total memory available into account -// if necessary. -STATIC_INLINE int gc_check_heap_size(int64_t sz_ub, int64_t sz_est) -{ - if (__unlikely(!last_full_live_ub || last_full_live_ub > sz_ub)) { - last_full_live_ub = sz_ub; - } - else if (__unlikely(last_full_live_ub * 3 / 2 < sz_ub)) { - return 1; - } - if (__unlikely(!last_full_live_est || last_full_live_est > sz_est)) { - last_full_live_est = sz_est; - } - else if (__unlikely(last_full_live_est * 2 < sz_est)) { - return 1; - } - return 0; -} - -STATIC_INLINE void gc_update_heap_size(int64_t sz_ub, int64_t sz_est) -{ - last_full_live_ub = sz_ub; - last_full_live_est = sz_est; -} - static void gc_sync_cache_nolock(jl_ptls_t ptls, jl_gc_mark_cache_t *gc_cache) JL_NOTSAFEPOINT { const int nbig = gc_cache->nbig_obj; @@ -2834,20 +2806,25 @@ static int _jl_gc_collect(jl_ptls_t ptls, int full) int large_frontier = nptr*sizeof(void*) >= default_collect_interval; // many pointers in the intergen frontier => "quick" mark is not quick int sweep_full; int recollect = 0; + // trigger a full collection if the number of live bytes doubles since the last full + // collection and then remains at least that high for a while. + if (grown_heap_age == 0) { + if (live_bytes > 2 * last_full_live) + grown_heap_age = 1; + } + else if (live_bytes >= last_live_bytes) { + grown_heap_age++; + } if ((full || large_frontier || ((not_freed_enough || promoted_bytes >= gc_num.interval) && (promoted_bytes >= default_collect_interval || prev_sweep_full)) || - gc_check_heap_size(live_sz_ub, live_sz_est)) && + grown_heap_age > 1) && gc_num.pause > 1) { - gc_update_heap_size(live_sz_ub, live_sz_est); recollect = full; if (large_frontier) gc_num.interval = last_long_collect_interval; if (not_freed_enough || large_frontier) { - if (gc_num.interval < default_collect_interval) { - gc_num.interval = default_collect_interval; - } - else if (gc_num.interval <= 2*(max_collect_interval/5)) { + if (gc_num.interval <= 2*(max_collect_interval/5)) { gc_num.interval = 5 * (gc_num.interval / 2); } } @@ -2856,7 +2833,12 @@ static int _jl_gc_collect(jl_ptls_t ptls, int full) promoted_bytes = 0; } else { - gc_num.interval = default_collect_interval / 2; + // reset interval to default, or at least half of live_bytes + int64_t half = live_bytes/2; + if (default_collect_interval < half && half <= max_collect_interval) + gc_num.interval = half; + else + gc_num.interval = default_collect_interval; sweep_full = gc_sweep_always_full; } if (sweep_full) @@ -2910,9 +2892,14 @@ static int _jl_gc_collect(jl_ptls_t ptls, int full) gc_time_sweep_pause(gc_end_t, actual_allocd, live_bytes, estimate_freed, sweep_full); gc_num.full_sweep += sweep_full; - prev_sweep_full = sweep_full; gc_num.allocd = -(int64_t)gc_num.interval; + last_live_bytes = live_bytes; live_bytes += -gc_num.freed + gc_num.since_sweep; + if (prev_sweep_full) { + last_full_live = live_bytes; + grown_heap_age = 0; + } + prev_sweep_full = sweep_full; gc_num.pause += !recollect; gc_num.total_time += pause; gc_num.since_sweep = 0; diff --git a/src/gf.c b/src/gf.c index 8cc8a3ae32bc2..5cd8fd9805602 100644 --- a/src/gf.c +++ b/src/gf.c @@ -295,7 +295,7 @@ JL_DLLEXPORT jl_code_instance_t *jl_set_method_inferred( codeinst->invoke = NULL; if ((const_flags & 1) != 0) { assert(const_flags & 2); - codeinst->invoke = jl_fptr_const_return; + jl_atomic_store_release(&codeinst->invoke, jl_fptr_const_return); } codeinst->specptr.fptr = NULL; if (jl_is_method(mi->def.method)) @@ -1742,7 +1742,7 @@ jl_code_instance_t *jl_compile_method_internal(jl_method_instance_t *mi, size_t 0, 1, ~(size_t)0); codeinst->specptr = unspec->specptr; codeinst->rettype_const = unspec->rettype_const; - codeinst->invoke = unspec->invoke; + jl_atomic_store_release(&codeinst->invoke, unspec->invoke); return codeinst; } } @@ -1750,7 +1750,7 @@ jl_code_instance_t *jl_compile_method_internal(jl_method_instance_t *mi, size_t if (!jl_code_requires_compiler(src)) { jl_code_instance_t *codeinst = jl_set_method_inferred(mi, (jl_value_t*)jl_any_type, NULL, NULL, 0, 1, ~(size_t)0); - codeinst->invoke = jl_fptr_interpret_call; + jl_atomic_store_release(&codeinst->invoke, jl_fptr_interpret_call); return codeinst; } if (jl_options.compile_enabled == JL_OPTIONS_COMPILE_OFF) { @@ -1790,7 +1790,7 @@ jl_code_instance_t *jl_compile_method_internal(jl_method_instance_t *mi, size_t 0, 1, ~(size_t)0); codeinst->specptr = ucache->specptr; codeinst->rettype_const = ucache->rettype_const; - codeinst->invoke = ucache->invoke; + jl_atomic_store_release(&codeinst->invoke, ucache->invoke); return codeinst; } } diff --git a/src/jitlayers.h b/src/jitlayers.h index 464625ec2467e..8eabb5efb2ccd 100644 --- a/src/jitlayers.h +++ b/src/jitlayers.h @@ -191,7 +191,7 @@ class JuliaOJIT { SymbolTableT LocalSymbolTable; }; extern JuliaOJIT *jl_ExecutionEngine; -JL_DLLEXPORT extern LLVMContext jl_LLVMContext; +JL_DLLEXPORT extern LLVMContext &jl_LLVMContext; Pass *createLowerPTLSPass(bool imaging_mode); Pass *createCombineMulAddPass(); diff --git a/src/sys.c b/src/sys.c index ded24078bc05c..7c6311feea8cd 100644 --- a/src/sys.c +++ b/src/sys.c @@ -315,16 +315,21 @@ JL_DLLEXPORT jl_value_t *jl_readuntil(ios_t *s, uint8_t delim, uint8_t str, uint return (jl_value_t*)a; } -JL_DLLEXPORT uint64_t jl_ios_get_nbyte_int(ios_t *s, const size_t n) +JL_DLLEXPORT int jl_ios_buffer_n(ios_t *s, const size_t n) { - assert(n <= 8); size_t space, ret; do { space = (size_t)(s->size - s->bpos); ret = ios_readprep(s, n); if (space == ret && ret < n) - jl_eof_error(); - } while(ret < n); + return 1; + } while (ret < n); + return 0; +} + +JL_DLLEXPORT uint64_t jl_ios_get_nbyte_int(ios_t *s, const size_t n) +{ + assert(n <= 8); uint64_t x = 0; uint8_t *buf = (uint8_t*)&s->buf[s->bpos]; if (n == 8) { @@ -545,7 +550,7 @@ JL_DLLEXPORT const char *jl_pathname_for_handle(void *handle) #elif defined(_OS_WINDOWS_) - wchar_t *pth16 = (wchar_t*)malloc(32768); // max long path length + wchar_t *pth16 = (wchar_t*)malloc(32768 * sizeof(*pth16)); // max long path length DWORD n16 = GetModuleFileNameW((HMODULE)handle,pth16,32768); if (n16 <= 0) { free(pth16); diff --git a/test/threads_exec.jl b/test/threads_exec.jl index a2e20dee695cf..05e060188eb93 100644 --- a/test/threads_exec.jl +++ b/test/threads_exec.jl @@ -665,3 +665,12 @@ let timeout = 300 # this test should take about 1-10 seconds end close(t) # stop the watchdog end + +# issue #32575 +let ch = Channel{Char}(0), t + t = Task(()->for v in "hello" put!(ch, v) end) + t.sticky = false + bind(ch, t) + schedule(t) + @test String(collect(ch)) == "hello" +end