diff --git a/.travis.yml b/.travis.yml index 20c1212e75fd5..f0c0ab8791dac 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,14 +1,31 @@ language: cpp -os: - - linux - - osx -env: - - ARCH="i686" - - ARCH="x86_64" +sudo: false matrix: - exclude: - - os: osx + include: + - os: linux env: ARCH="i686" + addons: + apt: + packages: + - binutils:i386 + - gcc:i386 + - g++:i386 + - make:i386 + - cpp:i386 + - libssl-dev:i386 + - gfortran:i386 + - os: linux + env: ARCH="x86_64" + addons: + apt: + packages: + - gfortran + - os: osx + env: ARCH="x86_64" +cache: + directories: + - $TRAVIS_BUILD_DIR/deps-i686 + - $TRAVIS_BUILD_DIR/deps-x86_64 notifications: email: false irc: @@ -23,28 +40,17 @@ notifications: before_install: - make check-whitespace - if [ `uname` = "Linux" ]; then - sudo apt-get install jq -y; contrib/travis_fastfail.sh || exit 1; - BUILDOPTS="-j3 USEGCC=1 LLVM_CONFIG=llvm-config-3.3 VERBOSE=1 USE_BLAS64=0 FORCE_ASSERTIONS=1 STAGE2_DEPS=utf8proc"; - for lib in LLVM SUITESPARSE ARPACK BLAS FFTW LAPACK GMP MPFR LIBUNWIND OPENLIBM; do - export BUILDOPTS="$BUILDOPTS USE_SYSTEM_$lib=1"; - done; - sudo add-apt-repository ppa:staticfloat/julia-deps -y; - sudo apt-get update -qq -y; + BUILDOPTS="-j3 VERBOSE=1 FORCE_ASSERTIONS=1"; if [ "$ARCH" = "i686" ]; then export BUILDOPTS="$BUILDOPTS MARCH=pentium4"; - sudo apt-get remove libblas3gf liblapack3gf libarmadillo2 -y; - sudo apt-get install binutils:i386 -y; - sudo apt-get install gcc:i386 g++:i386 make:i386 cpp:i386 g++-4.6:i386 gcc-4.6:i386 libssl-dev:i386 patchelf:i386 gfortran:i386 llvm-3.3-dev:i386 libsuitesparse-dev:i386 libopenblas-dev:i386 libopenblas-base:i386 libblas-dev:i386 liblapack-dev:i386 liblapack3:i386 libarpack2-dev:i386 libarpack2:i386 libfftw3-dev:i386 libgmp-dev:i386 libpcre3-dev:i386 libunwind7-dev:i386 libopenlibm-dev:i386 libmpfr-dev:i386 -y; - else - sudo apt-get install patchelf gfortran llvm-3.3-dev libsuitesparse-dev libopenblas-dev liblapack-dev libarpack2-dev libfftw3-dev libgmp-dev libpcre3-dev libunwind7-dev libopenlibm-dev libmpfr-dev -y; fi; elif [ `uname` = "Darwin" ]; then - brew tap staticfloat/julia; - brew rm --force $(brew deps --HEAD julia); brew update; brew install -v jq; contrib/travis_fastfail.sh || exit 1; + brew tap staticfloat/julia; + brew rm --force $(brew deps --HEAD julia); brew install -v --only-dependencies --HEAD julia; BUILDOPTS="-j3 USECLANG=1 LLVM_CONFIG=$(brew --prefix llvm33-julia)/bin/llvm-config-3.3 VERBOSE=1 USE_BLAS64=0 SUITESPARSE_INC=-I$(brew --prefix suite-sparse-julia)/include FORCE_ASSERTIONS=1 STAGE2_DEPS=utf8proc"; BUILDOPTS="$BUILDOPTS LIBBLAS=-lopenblas LIBBLASNAME=libopenblas LIBLAPACK=-lopenblas LIBLAPACKNAME=libopenblas"; @@ -55,9 +61,11 @@ before_install: export DYLD_FALLBACK_LIBRARY_PATH="/usr/local/lib:/lib:/usr/lib:$(brew --prefix openblas-julia)/lib:$(brew --prefix suite-sparse-julia)/lib:$(brew --prefix arpack-julia)/lib"; make $BUILDOPTS -C contrib -f repackage_system_suitesparse4.make; fi + - git clone -q git://git.kitenet.net/moreutils script: + - if [ -n "`ls deps-$ARCH`" ]; then cp -a deps-$ARCH/* deps; fi - make $BUILDOPTS -C base version_git.jl.phony - - git clone -q git://git.kitenet.net/moreutils + - make $BUILDOPTS NO_GIT=1 -C deps > deps.log || cat deps.log - make $BUILDOPTS NO_GIT=1 JULIA_SYSIMG_BUILD_FLAGS="--output-ji ../usr/lib/julia/sys.ji" prefix=/tmp/julia install | moreutils/ts -s "%.s" - if [ `uname` = "Darwin" ]; then for name in suitesparseconfig spqr umfpack colamd cholmod amd suitesparse_wrapper; do @@ -68,6 +76,13 @@ script: - cp /tmp/julia/lib/julia/sys.ji local.ji && /tmp/julia/bin/julia -J local.ji -e 'true' && /tmp/julia/bin/julia-debug -J local.ji -e 'true' && rm local.ji - /tmp/julia/bin/julia -e 'versioninfo()' - export JULIA_CPU_CORES=2 && cd /tmp/julia/share/julia/test && /tmp/julia/bin/julia --check-bounds=yes runtests.jl all && /tmp/julia/bin/julia --check-bounds=yes runtests.jl pkg - - cd - && mv julia2 julia - - sudo dmesg - - echo "Ready for packaging..." + - cd `dirname $TRAVIS_BUILD_DIR` && mv julia2 julia && rm -f julia/deps/julia-env/src/*/*/*.pyc + - case $TRAVIS_PULL_REQUEST-$TRAVIS_BRANCH in + false-master | false-release*) + cd julia && rm -rf deps-$ARCH && mkdir -p deps-$ARCH && + for i in $(git ls-files -o --directory deps); do + mv $i deps-$ARCH; + done;; + esac +# uncomment the following if failures are suspected to be due to the out-of-memory killer +# - dmesg diff --git a/Make.inc b/Make.inc index a8e3c9b40284d..da01e384fa68e 100644 --- a/Make.inc +++ b/Make.inc @@ -932,6 +932,7 @@ VERBOSE = 0 endif WARNCOLOR="\033[33;1m" +ENDCOLOR="\033[0m" ifeq ($(VERBOSE), 0) @@ -946,7 +947,6 @@ JULIACOLOR="\033[32;1m" SRCCOLOR="\033[33m" BINCOLOR="\033[37;1m" JULCOLOR="\033[34;1m" -ENDCOLOR="\033[0m" GOAL=$(subst ','\'',$(subst $(abspath $(JULIAHOME))/,,$(abspath $@))) diff --git a/README.arm.md b/README.arm.md index 7bfddb132ae62..0c55d5ef0b091 100644 --- a/README.arm.md +++ b/README.arm.md @@ -50,7 +50,7 @@ If you run into issues building LLVM, see these notes: # Raspberry Pi The Raspberry Pi ARM CPU type is not detected by LLVM. -Before starting the build, it is recommended to do `export JULIA_CPU_ARCH=arm1176jzf-s` +Before starting the build, it is recommended to do `export JULIA_CPU_TARGET=arm1176jzf-s` at the shell to tune the generated code for your CPU architecture. # Raspberry Pi 2 diff --git a/base/REPLCompletions.jl b/base/REPLCompletions.jl index 967340b779bf3..fe09ce3ef5625 100644 --- a/base/REPLCompletions.jl +++ b/base/REPLCompletions.jl @@ -95,10 +95,10 @@ end function complete_keyword(s::ByteString) const sorted_keywords = [ "abstract", "baremodule", "begin", "bitstype", "break", "catch", "ccall", - "const", "continue", "do", "else", "elseif", "end", "export", "finally", - "for", "function", "global", "if", "immutable", "import", "importall", - "let", "local", "macro", "module", "quote", "return", "try", "type", - "typealias", "using", "while"] + "const", "continue", "do", "else", "elseif", "end", "export", "false", + "finally", "for", "function", "global", "if", "immutable", "import", + "importall", "let", "local", "macro", "module", "quote", "return", + "true", "try", "type", "typealias", "using", "while"] r = searchsorted(sorted_keywords, s) i = first(r) n = length(sorted_keywords) @@ -109,7 +109,7 @@ function complete_keyword(s::ByteString) sorted_keywords[r] end -function complete_path(path::AbstractString, pos) +function complete_path(path::AbstractString, pos; use_envpath=false) if Base.is_unix(OS_NAME) && ismatch(r"^~(?:/|$)", path) # if the path is just "~", don't consider the expanded username as a prefix if path == "~" @@ -141,6 +141,49 @@ function complete_path(path::AbstractString, pos) push!(matches, id ? file * (@windows? "\\\\" : "/") : file) end end + + if use_envpath && length(dir) == 0 + # Look for files in PATH as well + local pathdirs = split(ENV["PATH"], @unix? ":" : ";") + + for pathdir in pathdirs + local actualpath + try + actualpath = realpath(pathdir) + catch + # Bash doesn't expect every folder in PATH to exist, so neither shall we + continue + end + + if actualpath != pathdir && in(actualpath,pathdirs) + # Remove paths which (after resolving links) are in the env path twice. + # Many distros eg. point /bin to /usr/bin but have both in the env path. + continue + end + + local filesinpath + try + filesinpath = readdir(pathdir) + catch e + # Bash allows dirs in PATH that can't be read, so we should as well. + if isa(e, SystemError) + continue + else + # We only handle SystemErrors here + rethrow(e) + end + end + + for file in filesinpath + # In a perfect world, we would filter on whether the file is executable + # here, or even on whether the current user can execute the file in question. + if startswith(file, prefix) && isfile(joinpath(pathdir, file)) + push!(matches, file) + end + end + end + end + matches = UTF8String[replace(s, r"\s", "\\ ") for s in matches] startpos = pos - endof(prefix) + 1 - length(matchall(r" ", prefix)) # The pos - endof(prefix) + 1 is correct due to `endof(prefix)-endof(prefix)==0`, @@ -222,14 +265,64 @@ get_value(sym::Symbol, fn) = isdefined(fn, sym) ? (fn.(sym), true) : (nothing, f get_value(sym::QuoteNode, fn) = isdefined(fn, sym.value) ? (fn.(sym.value), true) : (nothing, false) get_value(sym, fn) = sym, true +# Return the value of a getfield call expression +function get_value_getfield(ex::Expr, fn) + # Example :((top(getfield))(Base,:max)) + val, found = get_value_getfield(ex.args[2],fn) #Look up Base in Main and returns the module + found || return (nothing, false) + get_value_getfield(ex.args[3],val) #Look up max in Base and returns the function if found. +end +get_value_getfield(sym, fn) = get_value(sym, fn) +# Determines the return type with Base.return_types of a function call using the type information of the arguments. +function get_type_call(expr::Expr) + f_name = expr.args[1] + # The if statement should find the f function. How f is found depends on how f is referenced + if isa(f_name, TopNode) + f = Base.(f_name.name) + found = true + elseif isa(f_name, Expr) && f_name.args[1] === TopNode(:getfield) + f, found = get_value_getfield(f_name, Main) + else + f, found = get_value(f_name, Main) + end + found || return (Any, false) # If the function f is not found return Any. + args = Any[] + for ex in expr.args[2:end] # Find the type of the function arguments + typ, found = get_type(ex, Main) + found ? push!(args, typ) : push!(args, Any) + end + return_types = Base.return_types(f,Tuple{args...}) + length(return_types) == 1 || return (Any, false) + return (return_types[1], true) +end +# Returns the return type. example: get_type(:(Base.strip("",' ')),Main) returns (ASCIIString,true) +function get_type(sym::Expr, fn) + sym=expand(sym) + val, found = get_value(sym, fn) + found && return Base.typesof(val).parameters[1], found + if sym.head === :call + # getfield call is special cased as the evaluation of getfield provides good type information, + # is inexpensive and it is also performed in the complete_symbol function. + if sym.args[1] === TopNode(:getfield) + val, found = get_value_getfield(sym, Main) + return found ? Base.typesof(val).parameters[1] : Any, found + end + return get_type_call(sym) + end + (Any, false) +end +function get_type(sym, fn) + val, found = get_value(sym, fn) + return found ? Base.typesof(val).parameters[1] : Any, found +end # Method completion on function call expression that look like :(max(1)) function complete_methods(ex_org::Expr) args_ex = DataType[] func, found = get_value(ex_org.args[1], Main) (!found || (found && !isgeneric(func))) && return UTF8String[] for ex in ex_org.args[2:end] - val, found = get_value(ex, Main) - found ? push!(args_ex, Base.typesof(val).parameters[1]) : push!(args_ex, Any) + val, found = get_type(ex, Main) + push!(args_ex, val) end out = UTF8String[] t_in = Tuple{args_ex...} # Input types @@ -390,8 +483,18 @@ function shell_completions(string, pos) isempty(args.args[end].args) && return UTF8String[], 0:-1, false arg = args.args[end].args[end] if all(s -> isa(s, AbstractString), args.args[end].args) - # Treat this as a path (perhaps give a list of commands in the future as well?) - return complete_path(join(args.args[end].args), pos) + # Treat this as a path + + # As Base.shell_parse throws away trailing spaces (unless they are escaped), + # we need to special case here. + # If the last char was a space, but shell_parse ignored it search on "". + ignore_last_word = arg != " " && scs[end] == ' ' + prefix = ignore_last_word ? "" : join(args.args[end].args) + + # Also try looking into the env path if the user wants to complete the first argument + use_envpath = !ignore_last_word && length(args.args) < 2 + + return complete_path(prefix, pos, use_envpath=use_envpath) elseif isexpr(arg, :escape) && (isexpr(arg.args[1], :incomplete) || isexpr(arg.args[1], :error)) r = first(last_parse):prevind(last_parse, last(last_parse)) partial = scs[r] diff --git a/base/abstractarray.jl b/base/abstractarray.jl index ba7721dc5ce8c..5ca8f10fe44fe 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -408,9 +408,6 @@ start(A::AbstractArray) = (@_inline_meta(); itr = eachindex(A); (itr, start(itr) next(A::AbstractArray,i) = (@_inline_meta(); (idx, s) = next(i[1], i[2]); (A[idx], (i[1], s))) done(A::AbstractArray,i) = done(i[1], i[2]) -iterstate(i) = i -iterstate(i::Tuple{UnitRange{Int},Int}) = i[2] - # eachindex iterates over all indices. LinearSlow definitions are later. eachindex(A::AbstractArray) = (@_inline_meta(); eachindex(linearindexing(A), A)) diff --git a/base/array.jl b/base/array.jl index 51ffb9e055735..308a9e5ad091b 100644 --- a/base/array.jl +++ b/base/array.jl @@ -429,9 +429,9 @@ end function push!{T}(a::Array{T,1}, item) # convert first so we don't grow the array if the assignment won't work - item = convert(T, item) + itemT = convert(T, item) ccall(:jl_array_grow_end, Void, (Any, UInt), a, 1) - a[end] = item + a[end] = itemT return a end @@ -830,36 +830,36 @@ function findmax(a) if isempty(a) throw(ArgumentError("collection must be non-empty")) end - i = start(a) - mi = i - m, i = next(a, i) - while !done(a, i) - iold = i - ai, i = next(a, i) + s = start(a) + mi = i = 1 + m, s = next(a, s) + while !done(a, s) + ai, s = next(a, s) + i += 1 if ai > m || m!=m m = ai - mi = iold + mi = i end end - return (m, iterstate(mi)) + return (m, mi) end function findmin(a) if isempty(a) throw(ArgumentError("collection must be non-empty")) end - i = start(a) - mi = i - m, i = next(a, i) - while !done(a, i) - iold = i - ai, i = next(a, i) + s = start(a) + mi = i = 1 + m, s = next(a, s) + while !done(a, s) + ai, s = next(a, s) + i += 1 if ai < m || m!=m m = ai - mi = iold + mi = i end end - return (m, iterstate(mi)) + return (m, mi) end indmax(a) = findmax(a)[2] diff --git a/base/bitarray.jl b/base/bitarray.jl index 68cccfa435f17..9e4c8b0c38ea8 100644 --- a/base/bitarray.jl +++ b/base/bitarray.jl @@ -310,21 +310,17 @@ function convert{T,N}(::Type{BitArray{N}}, A::AbstractArray{T,N}) ind = 1 @inbounds begin for i = 1:length(Bc)-1 - u = UInt64(1) c = UInt64(0) for j = 0:63 - A[ind]!=0 && (c |= u) + c |= (UInt64(A[ind] != 0) << j) ind += 1 - u <<= 1 end Bc[i] = c end - u = UInt64(1) c = UInt64(0) for j = 0:_mod64(l-1) - A[ind]!=0 && (c |= u) + c |= (UInt64(A[ind] != 0) << j) ind += 1 - u <<= 1 end Bc[end] = c end @@ -359,15 +355,12 @@ end i1, i2 = get_chunks_id(i) u = UInt64(1) << i2 @inbounds begin - if x - Bc[i1] |= u - else - Bc[i1] &= ~u - end + c = Bc[i1] + Bc[i1] = ifelse(x, c | u, c & ~u) end end -setindex!(B::BitArray, x, i::Int) = (checkbounds(B, i); unsafe_setindex!(B, x, i)) +@inline setindex!(B::BitArray, x, i::Int) = (checkbounds(B, i); unsafe_setindex!(B, x, i)) @inline function unsafe_setindex!(B::BitArray, x, i::Int) unsafe_bitsetindex!(B.chunks, convert(Bool, x), i) return B @@ -417,11 +410,7 @@ function unsafe_setindex!(B::BitArray, X::AbstractArray, I::BitArray) if Imsk & u != 0 lx < c && throw_setindex_mismatch(X, c) x = convert(Bool, unsafe_getindex(X, c)) - if x - C |= u - else - C &= ~u - end + C = ifelse(x, C | u, C & ~u) c += 1 end u <<= 1 diff --git a/base/broadcast.jl b/base/broadcast.jl index de593d46e0c47..c590f6a38b149 100644 --- a/base/broadcast.jl +++ b/base/broadcast.jl @@ -104,16 +104,34 @@ end const bitcache_chunks = 64 # this can be changed const bitcache_size = 64 * bitcache_chunks # do not change this +function bpack(z::UInt64) + z |= z >>> 7 + z |= z >>> 14 + z |= z >>> 28 + z &= 0xFF + return z +end + function dumpbitcache(Bc::Vector{UInt64}, bind::Int, C::Vector{Bool}) ind = 1 nc = min(bitcache_chunks, length(Bc)-bind+1) - for i = 1:nc - u = UInt64(1) + C8 = reinterpret(UInt64, C) + nc8 = (nc >>> 3) << 3 + @inbounds for i = 1:nc8 + c = UInt64(0) + for j = 0:8:63 + c |= (bpack(C8[ind]) << j) + ind += 1 + end + Bc[bind] = c + bind += 1 + end + ind = (ind-1) << 3 + 1 + @inbounds for i = (nc8+1):nc c = UInt64(0) - for j = 1:64 - C[ind] && (c |= u) + for j = 0:63 + c |= (UInt64(C[ind]) << j) ind += 1 - u <<= 1 end Bc[bind] = c bind += 1 diff --git a/base/channels.jl b/base/channels.jl index a4ef643afae12..b7d071f1a37a5 100644 --- a/base/channels.jl +++ b/base/channels.jl @@ -2,27 +2,22 @@ abstract AbstractChannel +const DEF_CHANNEL_SZ=32 + type Channel{T} <: AbstractChannel cond_take::Condition # waiting for data to become available cond_put::Condition # waiting for a writeable slot state::Symbol data::Array{T,1} - szp1::Int # current channel size plus one sz_max::Int # maximum size of channel - take_pos::Int # read position - put_pos::Int # write position function Channel(sz) sz_max = sz == typemax(Int) ? typemax(Int) - 1 : sz - szp1 = sz > 32 ? 33 : sz+1 - new(Condition(), Condition(), :open, - Array(T, szp1), szp1, sz_max, 1, 1) + new(Condition(), Condition(), :open, Array(T, 0), sz_max) end end -const DEF_CHANNEL_SZ=32 - Channel(sz::Int = DEF_CHANNEL_SZ) = Channel{Any}(sz) closed_exception() = InvalidStateException("Channel is closed.", :closed) @@ -40,53 +35,28 @@ end function put!(c::Channel, v) !isopen(c) && throw(closed_exception()) - d = c.take_pos - c.put_pos - if (d == 1) || (d == -(c.szp1-1)) - # grow the channel if possible - if (c.szp1 - 1) < c.sz_max - if ((c.szp1-1) * 2) > c.sz_max - c.szp1 = c.sz_max + 1 - else - c.szp1 = ((c.szp1-1) * 2) + 1 - end - newdata = Array(eltype(c), c.szp1) - if c.put_pos > c.take_pos - copy!(newdata, 1, c.data, c.take_pos, (c.put_pos - c.take_pos)) - c.put_pos = c.put_pos - c.take_pos + 1 - else - len_first_part = length(c.data) - c.take_pos + 1 - copy!(newdata, 1, c.data, c.take_pos, len_first_part) - copy!(newdata, len_first_part+1, c.data, 1, c.put_pos-1) - c.put_pos = len_first_part + c.put_pos - end - c.take_pos = 1 - c.data = newdata - else - wait(c.cond_put) - end + while length(c.data) == c.sz_max + wait(c.cond_put) end - - c.data[c.put_pos] = v - c.put_pos = (c.put_pos == c.szp1 ? 1 : c.put_pos + 1) + push!(c.data, v) notify(c.cond_take, nothing, true, false) # notify all, since some of the waiters may be on a "fetch" call. v end function fetch(c::Channel) wait(c) - c.data[c.take_pos] + c.data[1] end function take!(c::Channel) !isopen(c) && !isready(c) && throw(closed_exception()) wait(c) - v = c.data[c.take_pos] - c.take_pos = (c.take_pos == c.szp1 ? 1 : c.take_pos + 1) + v = shift!(c.data) notify(c.cond_put, nothing, false, false) # notify only one, since only one slot has become available for a put!. v end -isready(c::Channel) = (c.take_pos == c.put_pos ? false : true) +isready(c::Channel) = n_avail(c) > 0 function wait(c::Channel) while !isready(c) @@ -102,13 +72,7 @@ end eltype{T}(::Type{Channel{T}}) = T -function n_avail(c::Channel) - if c.put_pos >= c.take_pos - return c.put_pos - c.take_pos - else - return c.szp1 - c.take_pos + c.put_pos - end -end +n_avail(c::Channel) = length(c.data) show(io::IO, c::Channel) = print(io, "$(typeof(c))(sz_max:$(c.sz_max),sz_curr:$(n_avail(c)))") diff --git a/base/deepcopy.jl b/base/deepcopy.jl index d705f0f74c00d..b28520de04a45 100644 --- a/base/deepcopy.jl +++ b/base/deepcopy.jl @@ -34,18 +34,15 @@ end function _deepcopy_t(x, T::DataType, stackdict::ObjectIdDict) nf = nfields(T) (isbits(T) || nf == 0) && return x + y = ccall(:jl_new_struct_uninit, Any, (Any,), T) if T.mutable - y = ccall(:jl_new_struct_uninit, Any, (Any,), T) stackdict[x] = y - for i in 1:nf - if isdefined(x,i) - y.(i) = deepcopy_internal(x.(i), stackdict) - end + end + for i in 1:nf + if isdefined(x,i) + ccall(:jl_set_nth_field, Void, (Any, Csize_t, Any), y, i-1, + deepcopy_internal(getfield(x,i), stackdict)) end - else - fields = Any[deepcopy_internal(x.(i), stackdict) for i in 1:nf] - y = ccall(:jl_new_structv, Any, (Any, Ptr{Void}, UInt32), - T, pointer(fields), length(fields)) end return y::T end diff --git a/base/deprecated.jl b/base/deprecated.jl index 92010f064fc80..b5f59f0287291 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -444,7 +444,7 @@ end to_index_nodep(i::Real) = convert(Int,i)::Int @noinline function to_index(i::Real) - depwarn("indexing with non Integer Reals is deprecated", :to_index) + depwarn("Indexing with non-Integer Reals is deprecated. It may be that your index arose from an integer division of the form i/j, in which case you should consider using i÷j or div(i,j) instead.", :to_index) to_index_nodep(i) end diff --git a/base/hashing2.jl b/base/hashing2.jl index c90639fde2332..2be68504399e5 100644 --- a/base/hashing2.jl +++ b/base/hashing2.jl @@ -135,8 +135,8 @@ function decompose(x::BigFloat) x == 0 && return big(0), 0, Int(x.sign) s = BigInt() ccall((:__gmpz_realloc2, :libgmp), Void, (Ptr{BigInt}, Culong), &s, x.prec) - s.size = -fld(-x.prec,(sizeof(Culong)<<3)) - ccall(:memcpy, Ptr{Void}, (Ptr{Void}, Ptr{Void}, Csize_t), s.d, x.d, s.size*sizeof(Culong)) + s.size = -fld(-x.prec,(sizeof(GMP.Limb)<<3)) + ccall(:memcpy, Ptr{Void}, (Ptr{Void}, Ptr{Void}, Csize_t), s.d, x.d, s.size*sizeof(GMP.Limb)) s, Int(x.exp - x.prec), Int(x.sign) end diff --git a/base/interactiveutil.jl b/base/interactiveutil.jl index bee7db3be6ebf..ca835b461d34c 100644 --- a/base/interactiveutil.jl +++ b/base/interactiveutil.jl @@ -440,8 +440,6 @@ function whos(io::IO=STDOUT, m::Module=current_module(), pattern::Regex=r"") @printf(head, "%6d KB ", bytes ÷ (1024)) end print(head, summary(value)) - print(head, " : ") - show(head, value) catch e print(head, "#=ERROR: unable to show value=#") end diff --git a/base/iterator.jl b/base/iterator.jl index d3f76c1e83f0c..9e00c94ec2a9d 100644 --- a/base/iterator.jl +++ b/base/iterator.jl @@ -23,11 +23,23 @@ eltype{I}(::Type{Enumerate{I}}) = Tuple{Int, eltype(I)} abstract AbstractZipIterator +immutable Zip1{I} <: AbstractZipIterator + a::I +end +zip(a) = Zip1(a) +length(z::Zip1) = length(z.a) +eltype{I}(::Type{Zip1{I}}) = Tuple{eltype(I)} +start(z::Zip1) = (start(z.a),) +function next(z::Zip1, st) + n = next(z.a,st[1]) + return ((n[1],), (n[2],)) +end +done(z::Zip1, st) = done(z.a,st[1]) + immutable Zip2{I1, I2} <: AbstractZipIterator a::I1 b::I2 end -zip(a) = a zip(a, b) = Zip2(a, b) length(z::Zip2) = min(length(z.a), length(z.b)) eltype{I1,I2}(::Type{Zip2{I1,I2}}) = Tuple{eltype(I1), eltype(I2)} diff --git a/base/methodshow.jl b/base/methodshow.jl index 815d1510cff9e..a9d303534a16f 100644 --- a/base/methodshow.jl +++ b/base/methodshow.jl @@ -97,7 +97,12 @@ function url(m::Method) line = m.func.code.line line <= 0 || ismatch(r"In\[[0-9]+\]", file) && return "" if inbase(M) - return "https://github.com/JuliaLang/julia/tree/$(Base.GIT_VERSION_INFO.commit)/base/$file#L$line" + if isempty(Base.GIT_VERSION_INFO.commit) + # this url will only work if we're on a tagged release + return "https://github.com/JuliaLang/julia/tree/v$VERSION/base/$file#L$line" + else + return "https://github.com/JuliaLang/julia/tree/$(Base.GIT_VERSION_INFO.commit)/base/$file#L$line" + end else try d = dirname(file) diff --git a/base/multi.jl b/base/multi.jl index b56ab8ce6a09f..d19ede35d0ac2 100644 --- a/base/multi.jl +++ b/base/multi.jl @@ -336,30 +336,35 @@ function rmprocs(args...; waitfor = 0.0) error("only process 1 can add and remove processes") end - rmprocset = [] - for i in vcat(args...) - if i == 1 - warn("rmprocs: process 1 not removed") - else - if haskey(map_pid_wrkr, i) - w = map_pid_wrkr[i] - set_worker_state(w, W_TERMINATING) - kill(w.manager, i, w.config) - push!(rmprocset, w) + lock(worker_lock) + try + rmprocset = [] + for i in vcat(args...) + if i == 1 + warn("rmprocs: process 1 not removed") + else + if haskey(map_pid_wrkr, i) + w = map_pid_wrkr[i] + set_worker_state(w, W_TERMINATING) + kill(w.manager, i, w.config) + push!(rmprocset, w) + end end end - end - start = time() - while (time() - start) < waitfor - if all(w -> w.state == W_TERMINATED, rmprocset) - break; - else - sleep(0.1) + start = time() + while (time() - start) < waitfor + if all(w -> w.state == W_TERMINATED, rmprocset) + break; + else + sleep(0.1) + end end - end - ((waitfor > 0) && any(w -> w.state != W_TERMINATED, rmprocset)) ? :timed_out : :ok + ((waitfor > 0) && any(w -> w.state != W_TERMINATED, rmprocset)) ? :timed_out : :ok + finally + unlock(worker_lock) + end end @@ -554,11 +559,7 @@ end function send_del_client(rr::RemoteRef) if rr.where == myid() del_client(rr2id(rr), myid()) - else - if in(rr.where, map_del_wrkr) - # for a removed worker, don't bother - return - end + elseif rr.where in procs() # process only if a valid worker w = worker_from_id(rr.where) push!(w.del_msgs, (rr2id(rr), myid())) w.gcflag = true @@ -567,7 +568,6 @@ function send_del_client(rr::RemoteRef) end function add_client(id, client) - #println("$(myid()) adding client $client to $id") rv = lookup_ref(id) push!(rv.clientset, client) nothing @@ -582,12 +582,11 @@ end function send_add_client(rr::RemoteRef, i) if rr.where == myid() add_client(rr2id(rr), i) - elseif i != rr.where + elseif (i != rr.where) && (rr.where in procs()) # don't need to send add_client if the message is already going # to the processor that owns the remote ref. it will add_client # itself inside deserialize(). w = worker_from_id(rr.where) - #println("$(myid()) adding $((rr2id(rr), i)) for $(rr.where)") push!(w.add_msgs, (rr2id(rr), i)) w.gcflag = true notify(any_gc_flag) @@ -1091,7 +1090,20 @@ end # `manager` is of type ClusterManager. The respective managers are responsible # for launching the workers. All keyword arguments (plus a few default values) # are available as a dictionary to the `launch` methods +# +# Only one addprocs can be in progress at any time +# +const worker_lock = ReentrantLock() function addprocs(manager::ClusterManager; kwargs...) + lock(worker_lock) + try + addprocs_locked(manager::ClusterManager; kwargs...) + finally + unlock(worker_lock) + end +end + +function addprocs_locked(manager::ClusterManager; kwargs...) params = merge(default_addprocs_params(), AnyDict(kwargs)) topology(symbol(params[:topology])) diff --git a/base/multidimensional.jl b/base/multidimensional.jl index 5ff800c1cd492..eadcfe991f1de 100644 --- a/base/multidimensional.jl +++ b/base/multidimensional.jl @@ -3,7 +3,7 @@ ### Multidimensional iterators module IteratorsMD -import Base: eltype, length, start, done, next, last, getindex, setindex!, linearindexing, min, max, eachindex, ndims, iterstate +import Base: eltype, length, start, done, next, last, getindex, setindex!, linearindexing, min, max, eachindex, ndims importall ..Base.Operators import Base: simd_outer_range, simd_inner_length, simd_index, @generated import Base: @nref, @ncall, @nif, @nexprs, LinearFast, LinearSlow, to_index @@ -59,8 +59,6 @@ immutable CartesianRange{I<:CartesianIndex} stop::I end -iterstate{CR<:CartesianRange,CI<:CartesianIndex}(i::Tuple{CR,CI}) = Base._sub2ind(i[1].stop.I, i[2].I) - @generated function CartesianRange{N}(I::CartesianIndex{N}) startargs = fill(1, N) :(CartesianRange($I($(startargs...)), I)) diff --git a/base/pkg/cache.jl b/base/pkg/cache.jl index 842d3ba9eba52..3916405635476 100644 --- a/base/pkg/cache.jl +++ b/base/pkg/cache.jl @@ -13,19 +13,13 @@ function mkcachedir() return end - @windows_only if Base.windows_version() < Base.WINDOWS_VISTA_VER - mkdir(cache) - return - end - if Dir.isversioned(pwd()) + @unix_only if Dir.isversioned(pwd()) rootcache = joinpath(realpath(".."), ".cache") if !isdir(rootcache) mkdir(rootcache) end - try - symlink(rootcache, cache) - return - end + symlink(rootcache, cache) + return end mkdir(cache) end diff --git a/base/pkg/dir.jl b/base/pkg/dir.jl index 0e253001fe96f..09bd1dfbd685e 100644 --- a/base/pkg/dir.jl +++ b/base/pkg/dir.jl @@ -43,7 +43,7 @@ function init(meta::AbstractString=DEFAULT_META, branch::AbstractString=META_BRA Git.set_remote_url(meta, dir=metadata_dir) return end - local temp_dir + local temp_dir = "" try mkpath(dir) temp_dir = mktempdir(dir) diff --git a/base/pkg/generate.jl b/base/pkg/generate.jl index 0c41174bf8768..af6c74376f2f2 100644 --- a/base/pkg/generate.jl +++ b/base/pkg/generate.jl @@ -4,7 +4,7 @@ module Generate import ..Git, ..Read -copyright_year() = readchomp(`date +%Y`) +copyright_year() = string(Dates.year(Dates.today())) copyright_name(dir::AbstractString) = readchomp(Git.cmd(`config --get user.name`, dir=dir)) github_user() = readchomp(ignorestatus(`git config --global --get github.user`)) diff --git a/base/pkg/write.jl b/base/pkg/write.jl index 4f9c68c7bd16a..77f3bfe2a7564 100644 --- a/base/pkg/write.jl +++ b/base/pkg/write.jl @@ -32,7 +32,8 @@ function install(pkg::AbstractString, sha1::AbstractString) if isdir(".trash/$pkg") mv(".trash/$pkg", "./$pkg") else - Git.run(`clone -q $(Cache.path(pkg)) $pkg`) + fileprefix = @windows? "file://" : "" + Git.run(`clone -q $(fileprefix * Cache.path(pkg)) $pkg`) end fetch(pkg, sha1) checkout(pkg, sha1) diff --git a/base/process.jl b/base/process.jl index c9af9053f9d7d..63af2e30cc4dc 100644 --- a/base/process.jl +++ b/base/process.jl @@ -91,8 +91,8 @@ const DevNull = DevNullStream() isreadable(::DevNullStream) = false iswritable(::DevNullStream) = true isopen(::DevNullStream) = true -read{T<:DevNullStream}(::T, args...) = throw(EOFErorr()) -write{T<:DevNullStream}(::T, args...) = 0 +read(::DevNullStream, ::Type{UInt8}) = throw(EOFError()) +write(::DevNullStream, ::UInt8) = 1 close(::DevNullStream) = nothing flush(::DevNullStream) = nothing copy(::DevNullStream) = DevNull diff --git a/base/replutil.jl b/base/replutil.jl index fa9eacda8d615..3fc381fcbbc34 100644 --- a/base/replutil.jl +++ b/base/replutil.jl @@ -188,7 +188,11 @@ function showerror(io::IO, ex::MethodError) print(io, "This may have arisen from a call to the constructor $construct_type(...),", "\nsince type constructors fall back to convert methods.") end - show_method_candidates(io, ex) + try + show_method_candidates(io, ex) + catch + warn(io, "Error showing method candidates, aborted") + end end #Show an error by directly calling jl_printf. diff --git a/base/sparse/cholmod.jl b/base/sparse/cholmod.jl index ec7e980a7ed49..fde43db12838b 100644 --- a/base/sparse/cholmod.jl +++ b/base/sparse/cholmod.jl @@ -853,7 +853,8 @@ function convert{Tv<:VTypes}(::Type{Sparse}, A::SparseMatrixCSC{Tv,SuiteSparse_l return o end -function convert{Tv<:VTypes}(::Type{Sparse}, A::SparseMatrixCSC{Tv,SuiteSparse_long}) + +function convert{Tv<:VTypes,Ti<:ITypes}(::Type{Sparse}, A::SparseMatrixCSC{Tv,Ti}) o = Sparse(A, 0) # check if array is symmetric and change stype if it is if ishermitian(o) @@ -861,13 +862,14 @@ function convert{Tv<:VTypes}(::Type{Sparse}, A::SparseMatrixCSC{Tv,SuiteSparse_l end o end - +convert{Ti<:ITypes}(::Type{Sparse}, A::SparseMatrixCSC{Float32,Ti}) = convert(Sparse, convert(SparseMatrixCSC{Float64,SuiteSparse_long}, A)) +convert{Ti<:ITypes}(::Type{Sparse}, A::SparseMatrixCSC{Complex{Float32},Ti}) = convert(Sparse, convert(SparseMatrixCSC{Complex{Float64},SuiteSparse_long}, A)) convert(::Type{Sparse}, A::Symmetric{Float64,SparseMatrixCSC{Float64,SuiteSparse_long}}) = Sparse(A.data, A.uplo == 'L' ? -1 : 1) convert{Tv<:VTypes}(::Type{Sparse}, A::Hermitian{Tv,SparseMatrixCSC{Tv,SuiteSparse_long}}) = Sparse(A.data, A.uplo == 'L' ? -1 : 1) -function convert{T}(::Type{Sparse}, - A::Union{SparseMatrixCSC{T,SuiteSparse_long}, - Symmetric{T,SparseMatrixCSC{T,SuiteSparse_long}}, - Hermitian{T,SparseMatrixCSC{T,SuiteSparse_long}}}, +function convert{T,Ti<:ITypes}(::Type{Sparse}, + A::Union{SparseMatrixCSC{T,Ti}, + Symmetric{T,SparseMatrixCSC{T,Ti}}, + Hermitian{T,SparseMatrixCSC{T,Ti}}}, args...) return Sparse(float(A), args...) end diff --git a/base/sysinfo.jl b/base/sysinfo.jl index f02950920c46b..030abfbacca8c 100644 --- a/base/sysinfo.jl +++ b/base/sysinfo.jl @@ -146,4 +146,6 @@ function set_process_title(title::AbstractString) uv_error("set_process_title", err) end +maxrss() = ccall(:jl_maxrss, Csize_t, ()) + end # module Sys diff --git a/contrib/add_license_to_files.jl b/contrib/add_license_to_files.jl index 53a7e94ddfe38..97f755b8c7c4a 100644 --- a/contrib/add_license_to_files.jl +++ b/contrib/add_license_to_files.jl @@ -30,26 +30,27 @@ const excludedirs = [ const skipfiles = [ "../contrib/add_license_to_files.jl", + "../contrib/windows/juliarc.jl", # files to check - already copyright - # see: https://github.com/JuliaLang/julia/pull/11073#issuecomment-98099389 - "../base/special/trig.jl", - "../base/sparse/csparse.jl", - "../base/linalg/givens.jl", - # - "../src/abi_llvm.cpp", - "../src/abi_win32.cpp", - "../src/abi_win64.cpp", - "../src/abi_x86.cpp", - "../src/abi_x86_64.cpp", - "../src/disasm.cpp", - "../src/support/END.h", - "../src/support/ENTRY.amd64.h", - "../src/support/ENTRY.i387.h", - "../src/support/MurmurHash3.c", - "../src/support/MurmurHash3.h", - "../src/support/asprintf.c", - "../src/support/strtod.c", - "../src/support/utf8.c", + # see: https://github.com/JuliaLang/julia/pull/11073#issuecomment-98099389 + "../base/special/trig.jl", + "../base/sparse/csparse.jl", + "../base/linalg/givens.jl", + # + "../src/abi_llvm.cpp", + "../src/abi_win32.cpp", + "../src/abi_win64.cpp", + "../src/abi_x86.cpp", + "../src/abi_x86_64.cpp", + "../src/disasm.cpp", + "../src/support/END.h", + "../src/support/ENTRY.amd64.h", + "../src/support/ENTRY.i387.h", + "../src/support/MurmurHash3.c", + "../src/support/MurmurHash3.h", + "../src/support/asprintf.c", + "../src/support/strtod.c", + "../src/support/utf8.c", ] const ext_prefix = Dict([ diff --git a/contrib/build_sysimg.jl b/contrib/build_sysimg.jl index 3aad063b88516..7021031affdc4 100644 --- a/contrib/build_sysimg.jl +++ b/contrib/build_sysimg.jl @@ -26,7 +26,7 @@ function build_sysimg(sysimg_path=default_sysimg_path, cpu_target="native", user base_dir = dirname(Base.find_source_file("sysimg.jl")) cd(base_dir) do julia = joinpath(JULIA_HOME, "julia") - ld = find_system_linker() + cc = find_system_compiler() # Ensure we have write-permissions to wherever we're trying to write to try @@ -65,8 +65,8 @@ function build_sysimg(sysimg_path=default_sysimg_path, cpu_target="native", user println("$julia -C $cpu_target --output-ji $sysimg_path.ji --output-o $sysimg_path.o -J $inference_path.ji --startup-file=no sysimg.jl") run(`$julia -C $cpu_target --output-ji $sysimg_path.ji --output-o $sysimg_path.o -J $inference_path.ji --startup-file=no sysimg.jl`) - if ld != nothing - link_sysimg(sysimg_path, ld) + if cc != nothing + link_sysimg(sysimg_path, cc) else info("System image successfully built at $sysimg_path.ji") end @@ -89,18 +89,18 @@ function build_sysimg(sysimg_path=default_sysimg_path, cpu_target="native", user end end -# Search for a linker to link sys.o into sys.dl_ext. Honor LD environment variable. -function find_system_linker() - if haskey( ENV, "LD" ) - if !success(`$(ENV["LD"]) -v`) - warn("Using linker override $(ENV["LD"]), but unable to run `$(ENV["LD"]) -v`") +# Search for a compiler to link sys.o into sys.dl_ext. Honor LD environment variable. +function find_system_compiler() + if haskey( ENV, "CC" ) + if !success(`$(ENV["CC"]) -v`) + warn("Using compiler override $(ENV["CC"]), but unable to run `$(ENV["CC"]) -v`") end - return ENV["LD"] + return ENV["CC"] end # On Windows, check to see if WinRPM is installed, and if so, see if gcc is installed @windows_only try - require("WinRPM") + eval(Main, :(using WinRPM)) winrpmgcc = joinpath(WinRPM.installdir,"usr","$(Sys.ARCH)-w64-mingw32", "sys-root","mingw","bin","gcc.exe") if success(`$winrpmgcc --version`) @@ -113,33 +113,28 @@ function find_system_linker() end - # See if `ld` exists + # See if `cc` exists try - if success(`ld -v`) - return "ld" + if success(`cc -v`) + return "cc" end end - warn( "No supported linker found; startup times will be longer" ) + warn( "No supported compiler found; startup times will be longer" ) end # Link sys.o into sys.$(dlext) -function link_sysimg(sysimg_path=default_sysimg_path, ld=find_system_linker()) +function link_sysimg(sysimg_path=default_sysimg_path, cc=find_system_compiler()) julia_libdir = dirname(Libdl.dlpath("libjulia")) FLAGS = ["-L$julia_libdir"] - if OS_NAME == :Darwin - push!(FLAGS, "-dylib") - push!(FLAGS, "-macosx_version_min") - push!(FLAGS, "10.7") - else - push!(FLAGS, "-shared") - end + + push!(FLAGS, "-shared") push!(FLAGS, "-ljulia") @windows_only push!(FLAGS, "-lssp") info("Linking sys.$(Libdl.dlext)") - run(`$ld $FLAGS -o $sysimg_path.$(Libdl.dlext) $sysimg_path.o`) + run(`$cc $FLAGS -o $sysimg_path.$(Libdl.dlext) $sysimg_path.o`) info("System image successfully built at $sysimg_path.$(Libdl.dlext)") @windows_only begin diff --git a/contrib/prepare_release.sh b/contrib/prepare_release.sh new file mode 100755 index 0000000000000..0d564ab163f07 --- /dev/null +++ b/contrib/prepare_release.sh @@ -0,0 +1,62 @@ +#!/bin/sh +# This file is a part of Julia. License is MIT: http://julialang.org/license + +# script to prepare binaries and source tarballs for a Julia release +# aka "bucket dance" julianightlies -> julialang +set -e # stop on failure +cd "$(dirname "$0")"/.. # run in top-level directory + +shashort=$(git rev-parse --short=10 HEAD) +tag=$(git tag --points-at $shashort) +if [ -z "$tag" ]; then + echo "error: this script must be run with a tagged commit checked out" >&2 + exit 1 +fi +version=$(cat VERSION) +majmin=$(cut -d. -f1-2 VERSION) +if [ "$tag" != "v$version" ]; then + echo "error: tagged commit does not match content of VERSION file" >&2 + exit 1 +fi + +# create full-source-dist and light-source-dist tarballs from a separate +# clone to ensure the directory name in them is julia-$version +git clone https://github.com/JuliaLang/julia -b $tag julia-$version +cd julia-$version +make full-source-dist +make light-source-dist +mv julia-${version}_$shashort-full.tar.gz ../julia-$version-full.tar.gz +mv julia-${version}_$shashort.tar.gz ../julia-$version.tar.gz +cd .. +rm -rf julia-$version + +# download and rename binaries, with -latest copies +julianightlies="https://s3.amazonaws.com/julianightlies/bin" +curl -L -o julia-$version-linux-x86_64.tar.gz \ + $julianightlies/linux/x64/$majmin/julia-$version-$shashort-linux64.tar.gz +cp julia-$version-linux-x86_64.tar.gz julia-$majmin-latest-linux-x86_64.tar.gz +curl -L -o julia-$version-linux-i686.tar.gz \ + $julianightlies/linux/x86/$majmin/julia-$version-$shashort-linux32.tar.gz +cp julia-$version-linux-i686.tar.gz julia-$majmin-latest-linux-i686.tar.gz +curl -L -o "julia-$version-osx10.7 .dmg" \ + $julianightlies/osx/x64/$majmin/julia-$version-$shashort-osx.dmg +cp "julia-$version-osx10.7 .dmg" "julia-$majmin-latest-osx10.7 .dmg" +curl -L -o julia-$version-win64.exe \ + $julianightlies/winnt/x64/$majmin/julia-$version-$shashort-win64.exe +cp julia-$version-win64.exe julia-$majmin-latest-win64.exe +curl -L -o julia-$version-win32.exe \ + $julianightlies/winnt/x86/$majmin/julia-$version-$shashort-win32.exe +cp julia-$version-win32.exe julia-$majmin-latest-win32.exe + +shasum -a 256 julia-$version* | grep -v sha256 | grep -v md5 > julia-$version.sha256 +md5sum julia-$version* | grep -v sha256 | grep -v md5 > julia-$version.md5 + +gpg -u julia --armor --detach-sig julia-$version-full.tar.gz +gpg -u julia --armor --detach-sig julia-$version.tar.gz +gpg -u julia --armor --detach-sig julia-$version-linux-x86_64.tar.gz +gpg -u julia --armor --detach-sig julia-$version-linux-i686.tar.gz + +echo "All files prepared. Attach julia-$version.tar.gz and julia-$version-full.tar.gz" +echo "to github releases, upload all binaries and checksums to julialang S3. Be sure" +echo "to set all S3 uploads to publicly readable, and replace $majmin-latest binaries." +# TODO: also automate uploads via aws cli and github api? diff --git a/contrib/travis_fastfail.sh b/contrib/travis_fastfail.sh index 8d692c430653b..6eb349a49b1ff 100755 --- a/contrib/travis_fastfail.sh +++ b/contrib/travis_fastfail.sh @@ -5,9 +5,10 @@ curlhdr="Accept: application/vnd.travis-ci.2+json" endpoint="https://api.travis-ci.org/repos/$TRAVIS_REPO_SLUG" # Fail fast for superseded builds to PR's -if ! [ "$TRAVIS_PULL_REQUEST" = "false" ]; then - if ! [ \"$TRAVIS_BUILD_NUMBER\" = $(curl -H "$curlhdr" $endpoint/builds?event_type=pull_request | \ - jq ".builds | map(select(.pull_request_number == $TRAVIS_PULL_REQUEST))[0].number") ]; then +if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then + newestbuildforthisPR=$(curl -H "$curlhdr" $endpoint/builds?event_type=pull_request | \ + jq ".builds | map(select(.pull_request_number == $TRAVIS_PULL_REQUEST))[0].number") + if [ $newestbuildforthisPR != null -a $newestbuildforthisPR != \"$TRAVIS_BUILD_NUMBER\" ]; then echo "There are newer queued builds for this pull request, failing early." exit 1 fi @@ -17,7 +18,7 @@ else master | release*) ;; *) - if ! [ \"$TRAVIS_BUILD_NUMBER\" = $(curl -H "$curlhdr" \ + if [ \"$TRAVIS_BUILD_NUMBER\" != $(curl -H "$curlhdr" \ $endpoint/branches/$TRAVIS_BRANCH | jq ".branch.number") ]; then echo "There are newer queued builds for this branch, failing early." exit 1 diff --git a/contrib/windows/juliarc.jl b/contrib/windows/juliarc.jl index db0e0ea61a36f..f2eca8637eed9 100644 --- a/contrib/windows/juliarc.jl +++ b/contrib/windows/juliarc.jl @@ -1,4 +1,4 @@ -# This file is a part of Julia. License is MIT: http://julialang.org/license +# This file should contain site-specific commands to be executed on Julia startup +# Users should store their own personal commands in homedir(), in a file named .juliarc.jl -ENV["PATH"] = JULIA_HOME*";"*joinpath(JULIA_HOME,"..","Git","bin")*";"* - joinpath(JULIA_HOME,"..","Git","usr","bin")*";"*ENV["PATH"] +ENV["PATH"] = JULIA_HOME*";"*joinpath(JULIA_HOME,"..","Git","bin")*";"*ENV["PATH"] diff --git a/contrib/windows/msys_build.sh b/contrib/windows/msys_build.sh index cecae2581a369..4feba26649c2e 100755 --- a/contrib/windows/msys_build.sh +++ b/contrib/windows/msys_build.sh @@ -197,6 +197,7 @@ else make VERBOSE=1 -C base version_git.jl.phony echo 'NO_GIT = 1' >> Make.user fi +echo 'FORCE_ASSERTIONS = 1' >> Make.user cat Make.user make VERBOSE=1 diff --git a/deps/Makefile b/deps/Makefile index b7f301fa2af1e..051b23d2ea82b 100644 --- a/deps/Makefile +++ b/deps/Makefile @@ -1080,6 +1080,9 @@ OPENBLAS_BUILD_OPTS += NO_AVX2=1 endif $(OPENBLAS_SRC_DIR)/config.status: $(OPENBLAS_SRC_DIR)/Makefile +ifeq ($(OS),WINNT) + cd $(dir $@) && patch -p1 < ../openblas-win64.patch +endif perl -i -ple 's/^\s*(EXTRALIB\s*\+=\s*-lSystemStubs)\s*$$/# $$1/g' $(OPENBLAS_SRC_DIR)/Makefile.system touch $@ $(OPENBLAS_OBJ_SOURCE): $(OPENBLAS_SRC_DIR)/config.status diff --git a/deps/checksums/libuv-9ab431a88fe255dd21e19a11f7fa2dd95774abf4.tar.gz/md5 b/deps/checksums/libuv-9ab431a88fe255dd21e19a11f7fa2dd95774abf4.tar.gz/md5 new file mode 100644 index 0000000000000..65f8ed01d20e2 --- /dev/null +++ b/deps/checksums/libuv-9ab431a88fe255dd21e19a11f7fa2dd95774abf4.tar.gz/md5 @@ -0,0 +1 @@ +68b47fd2886292f14feb3a8de7446e1d diff --git a/deps/checksums/libuv-9ab431a88fe255dd21e19a11f7fa2dd95774abf4.tar.gz/sha512 b/deps/checksums/libuv-9ab431a88fe255dd21e19a11f7fa2dd95774abf4.tar.gz/sha512 new file mode 100644 index 0000000000000..5df5ca3746492 --- /dev/null +++ b/deps/checksums/libuv-9ab431a88fe255dd21e19a11f7fa2dd95774abf4.tar.gz/sha512 @@ -0,0 +1 @@ +27bb21f3a7a663623245d50b9def2e73bc8ee47f13a74fcd83693a57651814790b5e8e0667e00ef45bfb4d41bb80943f6e143eec4a503f6a9a099e3804999e76 diff --git a/deps/checksums/libuv-b99045f254789f2407e4cd0d9163b421bfedfb3f.tar.gz/md5 b/deps/checksums/libuv-b99045f254789f2407e4cd0d9163b421bfedfb3f.tar.gz/md5 deleted file mode 100644 index 18e53611c426f..0000000000000 --- a/deps/checksums/libuv-b99045f254789f2407e4cd0d9163b421bfedfb3f.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -2bf6a037e8998d9ed1081591ff9dc483 diff --git a/deps/checksums/libuv-b99045f254789f2407e4cd0d9163b421bfedfb3f.tar.gz/sha512 b/deps/checksums/libuv-b99045f254789f2407e4cd0d9163b421bfedfb3f.tar.gz/sha512 deleted file mode 100644 index 0adafbd4e1946..0000000000000 --- a/deps/checksums/libuv-b99045f254789f2407e4cd0d9163b421bfedfb3f.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -8646c2ffa3ff90a3edb6bbd8197fc2918225acb4cdf8c9c1029f1f9e56fa72e4bcbea9ce6a3ab387fbf8b3eabfaae7329b83f7e7b9f5e4afa04e0b3a82f0209c diff --git a/deps/libuv.version b/deps/libuv.version index c41aae1aa6da1..7cc090bad451d 100644 --- a/deps/libuv.version +++ b/deps/libuv.version @@ -1,2 +1,2 @@ LIBUV_BRANCH=julia0.4-uv0.11.26 -LIBUV_SHA1=b99045f254789f2407e4cd0d9163b421bfedfb3f +LIBUV_SHA1=9ab431a88fe255dd21e19a11f7fa2dd95774abf4 diff --git a/deps/openblas-win64.patch b/deps/openblas-win64.patch new file mode 100644 index 0000000000000..a93e874499f7c --- /dev/null +++ b/deps/openblas-win64.patch @@ -0,0 +1,75 @@ +commit 640cccc2b159dba6d99b9eea11b7f23f98d6c85d +Author: Zhang Xianyi +Date: Mon Nov 30 15:19:45 2015 -0600 + + Refs #697. Fixed gemv bug for Windows. + + Thank matzeri's patch. + +diff --git a/interface/gemv.c b/interface/gemv.c +index 0a222a6..97c68bf 100644 +--- a/interface/gemv.c ++++ b/interface/gemv.c +@@ -37,6 +37,7 @@ + /*********************************************************************/ + + #include ++#include + #include "common.h" + #include "l1param.h" + #ifdef FUNCTION_PROFILE +@@ -224,13 +225,17 @@ void CNAME(enum CBLAS_ORDER order, + #ifdef ALIGNED_ACCESS + stack_alloc_size += 3; + #endif +- if(stack_alloc_size < 128) ++// if(stack_alloc_size < 128) + //dgemv_n.S require a 128 bytes buffer +- stack_alloc_size = 128; ++// increasing instead of capping 128 ++// ABI STACK for windows 288 bytes ++ stack_alloc_size += 288 / sizeof(FLOAT) ; + + if(stack_alloc_size > MAX_STACK_ALLOC / sizeof(FLOAT)) + stack_alloc_size = 0; + ++// stack overflow check ++ volatile double stack_check = 3.14159265358979323846; + FLOAT stack_buffer[stack_alloc_size]; + buffer = stack_alloc_size ? stack_buffer : (FLOAT *)blas_memory_alloc(1); + // printf("stack_alloc_size=%d\n", stack_alloc_size); +@@ -265,6 +270,8 @@ void CNAME(enum CBLAS_ORDER order, + + } + #endif ++// stack overflow check ++assert(stack_check==3.14159265358979323846); + + #ifdef MAX_STACK_ALLOC + if(!stack_alloc_size){ +diff --git a/exports/Makefile b/exports/Makefile +index 177e975..29a9ca8 100644 +--- a/exports/Makefile ++++ b/exports/Makefile +@@ -96,9 +96,9 @@ libgoto_hpl.def : gensymbol + ifeq (, $(SYMBOLPREFIX)$(SYMBOLSUFFIX)) + $(LIBDYNNAME) : ../$(LIBNAME) osx.def + else +-../$(LIBNAME).renamed : ../$(LIBNAME) objconv.def +- $(OBJCONV) @objconv.def ../$(LIBNAME) ../$(LIBNAME).renamed +-$(LIBDYNNAME) : ../$(LIBNAME).renamed osx.def ++../$(LIBNAME).osx.renamed : ../$(LIBNAME) objconv.def ++ $(OBJCONV) @objconv.def ../$(LIBNAME) ../$(LIBNAME).osx.renamed ++$(LIBDYNNAME) : ../$(LIBNAME).osx.renamed osx.def + endif + ifeq ($(NOFORTRAN), $(filter $(NOFORTRAN),1 2)) + #only build without Fortran +@@ -220,7 +220,7 @@ linktest.c : gensymbol ../Makefile.system ../getarch.c + perl ./gensymbol linktest $(ARCH) $(BU) $(EXPRECISION) $(NO_CBLAS) $(NO_LAPACK) $(NO_LAPACKE) $(NEED2UNDERSCORES) $(ONLY_CBLAS) "$(SYMBOLPREFIX)" "$(SYMBOLSUFFIX)" > linktest.c + + clean :: +- @rm -f *.def *.dylib __.SYMDEF* ++ @rm -f *.def *.dylib __.SYMDEF* *.renamed + + include ../Makefile.tail + diff --git a/doc/genstdlib.jl b/doc/genstdlib.jl index 1bf108b9f3020..30759b85cc83f 100644 --- a/doc/genstdlib.jl +++ b/doc/genstdlib.jl @@ -133,7 +133,7 @@ function tryrst(md, remove_decl) return Markdown.rst(md) catch e warn("Error converting docstring:") -# display(md) + # display(md) println(e) return end @@ -141,7 +141,7 @@ end torst(md,remove_decl) = isrst(md) ? flatten(md).content[1].code : tryrst(md, remove_decl) -function split_decl_rst(md, decl) +function split_decl_rst(md) if isrst(md) rst_text = flatten(md).content[1].code ls = split(rst_text, "\n") @@ -158,14 +158,17 @@ function split_decl_rst(md, decl) end return decl, join(ls[body_start:end], "\n") end - return decl, rst_text else if isa(md.content[1], Markdown.Code) && md.content[1].language == "" - decl = ".. function:: " * replace(shift!(md.content).code, "\n", + sigs = shift!(md.content) + decl = ".. function:: " * replace(sigs.code, "\n", "\n ") + body = Markdown.rst(md) + unshift!(md.content, sigs) + return decl, body end - return decl, Markdown.rst(md) end + return "", "" end # Disable by default since it is hard to eliminate false positives @@ -239,17 +242,19 @@ function translate(file) end end doc = nothing + decl = nothing + body = nothing for mdoc in getdoc(mod, funcname) - trst = tryrst(mdoc, false) - trst !== nothing || continue - if contains(replace(trst, r"[\n ][\n ]+", " "), - " " * replace(full, r"[\n ][\n ]+", " ")) + mdecl, mbody = split_decl_rst(mdoc) + if contains(replace(mdecl, r"[\n ][\n ]+", " "), + replace(".. function:: " * full, + r"[\n ][\n ]+", " ")) if doc != nothing error("duplicate $full $l") end doc = mdoc - else - #@show trst full + decl = mdecl + body = mbody end end if doc == nothing || torst(doc, false) == nothing @@ -262,7 +267,6 @@ function translate(file) delete!(all_docs, doc) doccing = true start_func_doc(full) - decl, body = split_decl_rst(doc, l) println(io, decl) println(io) println(io, " .. Docstring generated from Julia source\n") @@ -282,9 +286,11 @@ function translate(file) end for folder in ["stdlib", "manual", "devdocs"] - println("\nConverting $folder/\n") + println("\nConverting rst files in $folder/\n") for file in readdir("$folder") - translate("$folder/$file") + if endswith(file, ".rst") + translate("$folder/$file") + end end end diff --git a/doc/manual/conversion-and-promotion.rst b/doc/manual/conversion-and-promotion.rst index 89412562dc5d1..c975e4ebc9198 100644 --- a/doc/manual/conversion-and-promotion.rst +++ b/doc/manual/conversion-and-promotion.rst @@ -159,6 +159,8 @@ This is the actual implementation in julia:: convert{T<:Real}(::Type{T}, z::Complex) = (imag(z)==0 ? convert(T,real(z)) : throw(InexactError())) +.. doctest:: + julia> convert(Bool, 1im) ERROR: InexactError() in convert at complex.jl:18 diff --git a/doc/manual/faq.rst b/doc/manual/faq.rst index a2e6c4880cd3f..dce33384c7523 100644 --- a/doc/manual/faq.rst +++ b/doc/manual/faq.rst @@ -871,37 +871,35 @@ is fully asynchronous. The following:: @sync for i in 1:3 - @async print(i, " Foo ", " Bar ") + @async write(STDOUT, string(i), " Foo ", " Bar ") end results in:: 123 Foo Foo Foo Bar Bar Bar -This is happening because, while ``print(i, " Foo ", " Bar ")`` is synchronous, -internally, the writing of each argument yields to other tasks while waiting for -that part of the I/O to complete. +This is happening because, while the ``write`` call is synchronous, the writing of +each argument yields to other tasks while waiting for that part of the I/O to complete. -``println`` to asynchronous streams like STDOUT, TCPSockets, "locks" the stream -during a call. Consequently changing ``print`` to ``println`` in the above example -results in:: +``print`` and ``println`` "lock" the stream during a call. Consequently changing ``write`` to +``println`` in the above example results in:: 1 Foo Bar 2 Foo Bar 3 Foo Bar -For other functions and streams, etc, you could lock your writes with a ``ReentrantLock`` -like this:: +You can lock your writes with a ``ReentrantLock`` like this:: l = ReentrantLock() @sync for i in 1:3 @async begin lock(l) try - print(i, " Foo ", " Bar ") + write(STDOUT, string(i), " Foo ", " Bar ") finally unlock(l) end + end end diff --git a/doc/manual/mathematical-operations.rst b/doc/manual/mathematical-operations.rst index 64e6f1e223b37..5bdefaa4208b2 100644 --- a/doc/manual/mathematical-operations.rst +++ b/doc/manual/mathematical-operations.rst @@ -347,6 +347,69 @@ Control flow ``&&`` followed by ``||`` followed by ``?`` Assignments ``= += -= *= /= //= \= ^= ÷= %= |= &= $= <<= >>= >>>=`` and ``.+= .-= .*= ./= .//= .\= .^= .÷= .%=`` ================= ============================================================================================= +.. _man-numerical-conversions: + +Numerical Conversions +--------------------- + +Julia supports three forms of numerical conversion, which differ in their +handling of inexact conversions. + +- The notation ``T(x)`` or ``convert(T,x)`` converts ``x`` to a value of type ``T``. + + - If ``T`` is a floating-point type, the result is the nearest representable + value, which could be positive or negative infinity. + + - If ``T`` is an integer type, an ``InexactError`` is raised if ``x`` + is not representable by ``T``. + + +- ``x % T`` converts an integer ``x`` to a value of integer type ``T`` + congruent to ``x`` modulo ``2^n``, where ``n`` is the number of bits in ``T``. + In other words, the binary representation is truncated to fit. + +- The :ref:`man-rounding-functions` take a type ``T`` as an optional argument. + For example, ``round(Int,x)`` is a shorthand for ``Int(round(x))``. + +The following examples show the different forms. + +.. doctest:: + + julia> Int8(127) + 127 + + julia> Int8(128) + ERROR: InexactError() + in call at essentials.jl:56 + + julia> Int8(127.0) + 127 + + julia> Int8(3.14) + ERROR: InexactError() + in call at essentials.jl:56 + + julia> Int8(128.0) + ERROR: InexactError() + in call at essentials.jl:56 + + julia> 127 % Int8 + 127 + + julia> 128 % Int8 + -128 + + julia> round(Int8,127.4) + 127 + + julia> round(Int8,127.6) + ERROR: InexactError() + in trunc at float.jl:357 + in round at float.jl:177 + +See :ref:`man-conversion-and-promotion` for how to define your own +conversions and promotions. + .. _man-elementary-functions: Elementary Functions @@ -358,6 +421,8 @@ class of numerical values as permit sensible definitions, including integers, floating-point numbers, rationals, and complexes, wherever such definitions make sense. +.. _man-rounding-functions: + Rounding functions ~~~~~~~~~~~~~~~~~~ diff --git a/doc/manual/metaprogramming.rst b/doc/manual/metaprogramming.rst index 6cbeaa42c0c8d..0b193eb099d27 100644 --- a/doc/manual/metaprogramming.rst +++ b/doc/manual/metaprogramming.rst @@ -535,7 +535,7 @@ Building an advanced macro Here is a simplified definition of Julia's :obj:`@assert` macro:: macro assert(ex) - return :($ex ? nothing : error("Assertion failed: ", $(string(ex)))) + return :( $ex ? nothing : throw(AssertionError($(string(ex)))) ) end This macro can be used like this: @@ -550,8 +550,8 @@ This macro can be used like this: In place of the written syntax, the macro call is expanded at parse time to its returned result. This is equivalent to writing:: - 1==1.0 ? nothing : error("Assertion failed: ", "1==1.0") - 1==0 ? nothing : error("Assertion failed: ", "1==0") + 1==1.0 ? nothing : throw(AssertionError("1==1.0")) + 1==0 ? nothing : throw(AssertionError("1==0")) That is, in the first call, the expression ``:(1==1.0)`` is spliced into the test condition slot, while the value of ``string(:(1==1.0))`` is @@ -572,8 +572,8 @@ ellipses following the last argument:: macro assert(ex, msgs...) msg_body = isempty(msgs) ? ex : msgs[1] - msg = string("assertion failed: ", msg_body) - return :($ex ? nothing : error($msg)) + msg = string(msg_body) + return :($ex ? nothing : throw(AssertionError($msg))) end Now :obj:`@assert` has two modes of operation, depending upon the number of @@ -1036,7 +1036,7 @@ indices - in other words, to calculate the index ``i`` that can be used to index into an array ``A`` using ``A[i]``, instead of ``A[x,y,z,...]``. One possible implementation is the following:: - function sub2ind_loop(dims::NTuple{N}, I::Integer...) + function sub2ind_loop{N}(dims::NTuple{N}, I::Integer...) ind = I[N] - 1 for i = N-1:-1:1 ind = I[i]-1 + dims[i]*ind diff --git a/doc/manual/methods.rst b/doc/manual/methods.rst index ac7898ee55305..f4feff7692676 100644 --- a/doc/manual/methods.rst +++ b/doc/manual/methods.rst @@ -54,7 +54,7 @@ for structuring and organizing programs. .. [#] In C++ or Java, for example, in a method call like ``obj.meth(arg1,arg2)``, the object obj "receives" the method call and is - implicitly passed to the method via the ``this`` keyword, rather then as an + implicitly passed to the method via the ``this`` keyword, rather than as an explicit method argument. When the current ``this`` object is the receiver of a method call, it can be omitted altogether, writing just ``meth(arg1,arg2)``, with ``this`` implied as the receiving object. diff --git a/doc/manual/packages.rst b/doc/manual/packages.rst index 6b652024ba70c..4cea4040ba90d 100644 --- a/doc/manual/packages.rst +++ b/doc/manual/packages.rst @@ -370,9 +370,35 @@ want to make (this is your *commit message*), and then hit "Propose file change." Your changes will be submitted for consideration by the package owner(s) and collaborators. +For larger documentation changes---and especially ones that you expect +to have to update in response to feedback---you might find it easier +to use the procedure for code changes described below. + Code changes ~~~~~~~~~~~~ +Executive summary +^^^^^^^^^^^^^^^^^ + +Here we assume you've already set up git on your local machine and +have a GitHub account (see above). Let's imagine you're fixing a bug +in the Images package:: + + Pkg.checkout("Images") # check out the master branch + + cd(Pkg.dir("Images")) + ;git checkout -b myfixes # create a branch for your changes + # be sure to add a test for your bug + Pkg.test("Images") # make sure everything works now + ;git commit -a -m "Fix foo by calling bar" # write a descriptive message + Pkg.submit("Images") + +The last line will present you with a link to submit a pull request +to incorporate your changes. + +Detailed description +^^^^^^^^^^^^^^^^^^^^ + If you want to fix a bug or add new functionality, you want to be able to test your changes before you submit them for consideration. You also need to have an easy way to update your proposal in response to @@ -863,8 +889,8 @@ if they haven't already been, and then opens a pull request to ``METADATA``:: .. _man-manual-publish: -Publishing METADATA manually -============================ +Publishing METADATA Manually +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ If :func:`Pkg.publish` fails you can follow these instructions to manually publish your package. diff --git a/doc/manual/strings.rst b/doc/manual/strings.rst index 48844b93dd290..f7de1ca0d3ac3 100644 --- a/doc/manual/strings.rst +++ b/doc/manual/strings.rst @@ -439,8 +439,8 @@ backslash: julia> print("I have \$100 in my account.\n") I have $100 in my account. -Triple-Quoted Strings Literals ------------------------------- +Triple-Quoted String Literals +----------------------------- When strings are created using triple-quotes (``"""..."""``) they have some special behavior that can be useful for creating longer blocks of text. First, @@ -482,6 +482,12 @@ defining strings within code that is indented. For example: In this case the final (empty) line before the closing ``"""`` sets the indentation level. +Note that line breaks in literal strings, whether single- or triple-quoted, +result in a newline (LF) character ``\n`` in the string, even if your +editor uses a carriage return ``\r`` (CR) or CRLF combination to end lines. +To include a CR in a string, use an explicit escape ``\r``; for example, +you can enter the literal string ``"a CRLF line ending\r\n"``. + Common Operations ----------------- @@ -690,7 +696,7 @@ You can extract the following info from a :obj:`RegexMatch` object: For when a capture doesn't match, instead of a substring, ``m.captures`` contains ``nothing`` in that position, and ``m.offsets`` has a zero offset (recall that indices in Julia are 1-based, so a zero offset into -a string is invalid). Here's is a pair of somewhat contrived examples:: +a string is invalid). Here is a pair of somewhat contrived examples:: julia> m = match(r"(a|b)(c)?(d)", "acd") RegexMatch("acd", 1="a", 2="c", 3="d") diff --git a/doc/manual/types.rst b/doc/manual/types.rst index 549b052eac272..ac1e76f2fd5a2 100644 --- a/doc/manual/types.rst +++ b/doc/manual/types.rst @@ -1298,7 +1298,7 @@ minimal interface designed to ensure that interactions with missing values are safe. At present, the interface consists of four possible interactions: - Construct a :obj:`Nullable` object. -- Check if an :obj:`Nullable` object has a missing value. +- Check if a :obj:`Nullable` object has a missing value. - Access the value of a :obj:`Nullable` object with a guarantee that a :exc:`NullException` will be thrown if the object's value is missing. - Access the value of a :obj:`Nullable` object with a guarantee that a default @@ -1339,8 +1339,8 @@ Note the core distinction between these two ways of constructing a :obj:`Nullabl object: in one style, you provide a type, ``T``, as a function parameter; in the other style, you provide a single value of type ``T`` as an argument. -Checking if an :obj:`Nullable` object has a value -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Checking if a :obj:`Nullable` object has a value +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ You can check if a :obj:`Nullable` object has any value using :func:`isnull`: @@ -1352,10 +1352,10 @@ You can check if a :obj:`Nullable` object has any value using :func:`isnull`: julia> isnull(Nullable(0.0)) false -Safely accessing the value of an :obj:`Nullable` object -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Safely accessing the value of a :obj:`Nullable` object +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -You can safely access the value of an :obj:`Nullable` object using :func:`get`: +You can safely access the value of a :obj:`Nullable` object using :func:`get`: .. doctest:: diff --git a/doc/stdlib/dates.rst b/doc/stdlib/dates.rst index 8074d8b074e68..6a01349afb0dd 100644 --- a/doc/stdlib/dates.rst +++ b/doc/stdlib/dates.rst @@ -43,8 +43,8 @@ Dates Functions --------------- All Dates functions are defined in the ``Dates`` module; note that only the ``Date``, ``DateTime``, and ``now`` functions are exported; -to use all other ``Dates`` functions, you'll need to prefix each function call with an explicit ``Dates.``, e.g. ``Dates.dayofweek(dt)``; -alternatively, you could call ``using Dates`` to bring all exported functions into ``Main`` to be used without the ``Dates.`` prefix. +to use all other ``Dates`` functions, you'll need to prefix each function call with an explicit ``Dates.``, e.g. ``Dates.dayofweek(dt)``. +Alternatively, you can write ``using Base.Dates`` to bring all exported functions into ``Main`` to be used without the ``Dates.`` prefix. .. function:: DateTime(y, [m, d, h, mi, s, ms]) -> DateTime diff --git a/src/ccall.cpp b/src/ccall.cpp index 22015ff6f6a52..7a4a9867327aa 100644 --- a/src/ccall.cpp +++ b/src/ccall.cpp @@ -1414,8 +1414,13 @@ static Value *emit_ccall(jl_value_t **args, size_t nargs, jl_codectx_t *ctx) if (needStackRestore) { stacksave = CallInst::Create(Intrinsic::getDeclaration(jl_Module, Intrinsic::stacksave)); - if (savespot) - instList.insertAfter((Instruction*)savespot, (Instruction*)stacksave); + if (savespot) { +#ifdef LLVM38 + instList.insertAfter(savespot->getIterator(), (Instruction*)stacksave); +#else + instList.insertAfter((Instruction*)savespot, (Instruction*)stacksave); +#endif + } else instList.push_front((Instruction*)stacksave); } diff --git a/src/cgutils.cpp b/src/cgutils.cpp index a04b992f075fd..e9225a1527245 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -190,7 +190,7 @@ class FunctionMover : public ValueMaterializer Function::arg_iterator DestI = NewF->arg_begin(); for (Function::const_arg_iterator I = F->arg_begin(), E = F->arg_end(); I != E; ++I) { DestI->setName(I->getName()); // Copy the name over... - VMap[I] = DestI++; // Add mapping to VMap + VMap[&*I] = &*(DestI++); // Add mapping to VMap } #ifdef LLVM36 @@ -1205,7 +1205,7 @@ static Value *emit_bounds_check(Value *a, jl_value_t *ty, Value *i, Value *len, // --- loading and storing --- static AllocaInst *emit_static_alloca(Type *lty, jl_codectx_t *ctx) { - return new AllocaInst(lty, "", /*InsertBefore=*/ctx->gc.gcframe); + return new AllocaInst(lty, "", /*InsertBefore=*/&*ctx->gc.gcframe); } static Value *emit_reg2mem(Value *v, jl_codectx_t *ctx) { diff --git a/src/codegen.cpp b/src/codegen.cpp index efcac59c47309..cafe14806d5e4 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -1008,7 +1008,7 @@ Function* CloneFunctionToModule(Function *F, Module *destModule) Function::arg_iterator DestI = NewF->arg_begin(); for (Function::const_arg_iterator I = F->arg_begin(), E = F->arg_end(); I != E; ++I) { DestI->setName(I->getName()); // Copy the name over... - VMap[I] = DestI++; // Add mapping to VMap + VMap[&*I] = &*DestI++; // Add mapping to VMap } SmallVector Returns; @@ -1043,7 +1043,7 @@ const jl_value_t *jl_dump_function_ir(void *f, bool strip_ir_metadata, bool dump BasicBlock::InstListType::iterator f2_il = (*f2_bb).getInstList().begin(); // iterate over instructions in basic block for (; f2_il != (*f2_bb).getInstList().end(); ) { - Instruction *inst = f2_il++; + Instruction *inst = &*f2_il++; // remove dbg.declare and dbg.value calls if (isa(inst) || isa(inst)) { inst->eraseFromParent(); @@ -3071,7 +3071,7 @@ static void emit_assignment(jl_value_t *l, jl_value_t *r, jl_codectx_t *ctx) !is_stable_expr(r, ctx)) { Instruction *newroot = cast(emit_local_slot(ctx->gc.argSpaceSize++, ctx)); newroot->removeFromParent(); // move it to the gc frame basic block so it can be reused as needed - newroot->insertAfter(ctx->gc.last_gcframe_inst); + newroot->insertAfter(&*ctx->gc.last_gcframe_inst); vi.memvalue = bp = newroot; vi.hasGCRoot = true; // this has been discovered to need a gc root, add it now //TODO: move this logic after the emit_expr @@ -3554,7 +3554,7 @@ static void finalize_gc_frame(jl_codectx_t *ctx) } BasicBlock::iterator bbi(gc->gcframe); AllocaInst *newgcframe = gc->gcframe; - builder.SetInsertPoint(++gc->last_gcframe_inst); // set insert *before* point, e.g. after the gcframe + builder.SetInsertPoint(&*++gc->last_gcframe_inst); // set insert *before* point, e.g. after the gcframe // Allocate the real GC frame // n_frames++; newgcframe->setOperand(0, ConstantInt::get(T_int32, 2 + gc->argSpaceSize + gc->maxDepth)); // fix up the size of the gc frame @@ -3692,7 +3692,7 @@ static Function *gen_cfun_wrapper(jl_function_t *ff, jl_value_t *jlrettype, jl_t Function::arg_iterator AI = cw->arg_begin(); Value *sretPtr = NULL; if (sret) - sretPtr = AI++; + sretPtr = &*AI++; Value *result; size_t FParamIndex = 0; @@ -3706,7 +3706,7 @@ static Function *gen_cfun_wrapper(jl_function_t *ff, jl_value_t *jlrettype, jl_t } for (size_t i = 0; i < nargs; i++) { - Value *val = AI++; + Value *val = &*AI++; jl_value_t *jargty = jl_nth_slot_type(lam->specTypes, i); // figure out how to unpack this type @@ -3878,7 +3878,7 @@ static Function *gen_jlcall_wrapper(jl_lambda_info_t *lam, jl_expr_t *ast, Funct addComdat(w); Function::arg_iterator AI = w->arg_begin(); /* const Argument &fArg = */ *AI++; - Value *argArray = AI++; + Value *argArray = &*AI++; /* const Argument &argCount = *AI++; */ BasicBlock *b0 = BasicBlock::Create(jl_LLVMContext, "top", w); @@ -4271,9 +4271,16 @@ static Function *emit_function(jl_lambda_info_t *lam) 0, // ScopeLine 0, // Flags true, // isOptimized - f); // Fn + #ifdef LLVM38 + nullptr); // Template Parameters + #else + f); // Function + #endif // set initial line number builder.SetCurrentDebugLocation(DebugLoc::get(lno, 0, (MDNode*)SP, NULL)); + #ifdef LLVM38 + f->setSubprogram(SP); + #endif #ifndef LLVM37 assert(SP.Verify() && SP.describes(f) && SP.getFunction() == f); #endif @@ -4387,9 +4394,9 @@ static Function *emit_function(jl_lambda_info_t *lam) unsigned argIdx = 0; if (!specsig) { Function::arg_iterator AI = f->arg_begin(); - fArg = AI++; - argArray = AI++; - argCount = AI++; + fArg = &*AI++; + argArray = &*AI++; + argCount = &*AI++; ctx.argArray = argArray; ctx.argCount = argCount; @@ -4566,7 +4573,7 @@ static Function *emit_function(jl_lambda_info_t *lam) if (specsig) { argType = jl_nth_slot_type(lam->specTypes,i); if (!vi.isGhost) { - argPtr = AI++; + argPtr = &*AI++; argPtr = mark_julia_type(argPtr, argType); } } @@ -4797,7 +4804,7 @@ static Function *emit_function(jl_lambda_info_t *lam) mallocVisitLine(filename, lno); if (ctx.sret) - builder.CreateStore(retval, ctx.f->arg_begin()); + builder.CreateStore(retval, &*ctx.f->arg_begin()); if (type_is_ghost(retty) || ctx.sret) builder.CreateRetVoid(); else diff --git a/src/gc.c b/src/gc.c index c02bbbd20a2c3..32b6b704649c4 100644 --- a/src/gc.c +++ b/src/gc.c @@ -333,26 +333,26 @@ static void run_finalizer(jl_value_t *o, jl_value_t *ff) } } -static int finalize_object(jl_value_t *o) +static void finalize_object(arraylist_t *list, jl_value_t *o) { - int success = 0; + /* int success = 0; */ jl_value_t *f = NULL; JL_GC_PUSH1(&f); - for(int i = 0; i < finalizer_list.len; i+=2) { - if (o == (jl_value_t*)finalizer_list.items[i]) { - f = (jl_value_t*)finalizer_list.items[i+1]; - if (i < finalizer_list.len - 2) { - finalizer_list.items[i] = finalizer_list.items[finalizer_list.len-2]; - finalizer_list.items[i+1] = finalizer_list.items[finalizer_list.len-1]; + for(int i = 0; i < list->len; i+=2) { + if (o == (jl_value_t*)list->items[i]) { + f = (jl_value_t*)list->items[i+1]; + if (i < list->len - 2) { + list->items[i] = list->items[list->len-2]; + list->items[i+1] = list->items[list->len-1]; i -= 2; } - finalizer_list.len -= 2; + list->len -= 2; run_finalizer(o, f); - success = 1; + /* success = 1; */ } } JL_GC_POP(); - return success; + /* return success; */ } static void run_finalizers(void) @@ -404,7 +404,10 @@ DLLEXPORT void jl_gc_add_finalizer(jl_value_t *v, jl_function_t *f) void jl_finalize(jl_value_t *o) { - (void)finalize_object(o); + // No need to check the to_finalize list since the user is apparently + // still holding a reference to the object + finalize_object(&finalizer_list, o); + finalize_object(&finalizer_list_marked, o); } static region_t *find_region(void *ptr, int maybe) diff --git a/src/jltypes.c b/src/jltypes.c index 3591bddf2f719..e518cf097a5ed 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -833,7 +833,7 @@ static jl_value_t *jl_type_intersect(jl_value_t *a, jl_value_t *b, if (b == (jl_value_t*)jl_any_type || b == jl_ANY_flag) return a; // tuple if (jl_is_tuple_type(a)) { - long alen = (long)jl_nparams(a); + size_t alen = jl_nparams(a); jl_value_t *temp=NULL; JL_GC_PUSH2(&b, &temp); if (jl_is_ntuple_type(b)) { @@ -845,7 +845,7 @@ static jl_value_t *jl_type_intersect(jl_value_t *a, jl_value_t *b, if (eqc->data[i] == lenvar) { jl_value_t *v = eqc->data[i+1]; // N is already known in NTuple{N,...} - if (jl_get_size(v, (size_t *)&alen)) break; + if (jl_get_size(v, &alen)) break; } } b = (jl_value_t*)jl_tupletype_fill(alen, elty); diff --git a/src/julia-parser.scm b/src/julia-parser.scm index 0c43298b0da8d..f91e9176d88bf 100644 --- a/src/julia-parser.scm +++ b/src/julia-parser.scm @@ -1831,6 +1831,14 @@ (list* ex (tostr custom b) e) 0))) + ; convert literal \r and \r\n in strings to \n (issue #11988) + ((eqv? c #\return) ; \r + (begin + (if (eqv? (peek-char p) #\linefeed) ; \r\n + (read-char p)) + (write-char #\newline b) + (loop (read-char p) b e 0))) + (else (write-char (not-eof-3 c) b) (loop (read-char p) b e 0))))) diff --git a/src/sys.c b/src/sys.c index 4d88301b8aed3..d59ae7c29d300 100644 --- a/src/sys.c +++ b/src/sys.c @@ -10,7 +10,9 @@ #include #include #include -#ifndef _OS_WINDOWS_ +#ifdef _OS_WINDOWS_ +#include +#else #include #include #include @@ -22,6 +24,13 @@ #include #include +#ifndef _OS_WINDOWS_ +// for getrusage +#include +#include +#include +#endif + #ifdef __APPLE__ #include #include @@ -301,9 +310,13 @@ static void NORETURN throw_eof_error(void) DLLEXPORT uint64_t jl_ios_get_nbyte_int(ios_t *s, const size_t n) { assert(n <= 8); - size_t ret = ios_readprep(s, n); - if (ret < n) - throw_eof_error(); + size_t space, ret; + do { + space = s->size - s->bpos; + ret = ios_readprep(s, n); + if (space == ret && ret < n) + throw_eof_error(); + } while(ret < n); uint64_t x = 0; uint8_t *buf = (uint8_t*)&s->buf[s->bpos]; if (n == 8) { @@ -742,6 +755,30 @@ DLLEXPORT jl_sym_t* jl_get_ARCH() return ARCH; } +DLLEXPORT size_t jl_maxrss() +{ +#if defined(_OS_WINDOWS_) + PROCESS_MEMORY_COUNTERS counter; + GetProcessMemoryInfo( GetCurrentProcess( ), &counter, sizeof(counter) ); + return (size_t)counter.PeakWorkingSetSize; + +#elif defined(_OS_LINUX_) || defined(_OS_DARWIN_) || defined (_OS_FREEBSD_) + struct rusage rusage; + getrusage( RUSAGE_SELF, &rusage ); + +#if defined(_OS_LINUX_) + return (size_t)(rusage.ru_maxrss * 1024); +#else + return (size_t)rusage.ru_maxrss; +#endif + +#else + return (size_t)0; +#endif +} + + + #ifdef __cplusplus } #endif diff --git a/src/task.c b/src/task.c index 31c3dc8b91085..99e8ac9647191 100644 --- a/src/task.c +++ b/src/task.c @@ -232,12 +232,26 @@ static void throw_if_exception_set(jl_task_t *t) } } -static void NOINLINE NORETURN start_task() +static void record_backtrace(void); +static void NOINLINE NORETURN start_task(void) { // this runs the first time we switch to a task jl_task_t *t = jl_current_task; - throw_if_exception_set(t); - jl_value_t *res = jl_apply(t->start, NULL, 0); + jl_value_t *res; + if (t->exception != NULL && t->exception != jl_nothing) { + record_backtrace(); + res = t->exception; + } + else { + JL_TRY { + res = jl_apply(t->start, NULL, 0); + } + JL_CATCH { + res = jl_exception_in_transit; + t->exception = res; + jl_gc_wb(t, res); + } + } finish_task(t, res); abort(); } @@ -795,17 +809,11 @@ void NORETURN throw_internal(jl_value_t *e) jl_longjmp(jl_current_task->eh->eh_ctx, 1); } else { - if (jl_current_task == jl_root_task) { - jl_printf(JL_STDERR, "fatal: error thrown and no exception handler available.\n"); - jl_static_show(JL_STDERR, e); - jl_printf(JL_STDERR, "\n"); - jlbacktrace(); - jl_exit(1); - } - jl_current_task->exception = e; - jl_gc_wb(jl_current_task, e); - finish_task(jl_current_task, e); - assert(0); + jl_printf(JL_STDERR, "fatal: error thrown and no exception handler available.\n"); + jl_static_show(JL_STDERR, e); + jl_printf(JL_STDERR, "\n"); + jlbacktrace(); + jl_exit(1); } assert(0); } diff --git a/test/arrayops.jl b/test/arrayops.jl index ccccea049ee6c..16b5f882709b1 100644 --- a/test/arrayops.jl +++ b/test/arrayops.jl @@ -306,7 +306,7 @@ for i = 1:3 end @test isequal(a,findn(z)) -#argmin argmax +#findmin findmax indmin indmax @test indmax([10,12,9,11]) == 2 @test indmin([10,12,9,11]) == 3 @test findmin([NaN,3.2,1.8]) == (1.8,3) @@ -316,6 +316,16 @@ end @test findmin([3.2,1.8,NaN,2.0]) == (1.8,2) @test findmax([3.2,1.8,NaN,2.0]) == (3.2,1) +# #14085 +@test findmax(4:9) == (9,6) +@test indmax(4:9) == 6 +@test findmin(4:9) == (4,1) +@test indmin(4:9) == 1 +@test findmax(5:-2:1) == (5,1) +@test indmax(5:-2:1) == 1 +@test findmin(5:-2:1) == (1,3) +@test indmin(5:-2:1) == 3 + ## permutedims ## #keeps the num of dim diff --git a/test/choosetests.jl b/test/choosetests.jl index 2a2eecfcbe248..ad3027f08d9a8 100644 --- a/test/choosetests.jl +++ b/test/choosetests.jl @@ -32,7 +32,7 @@ function choosetests(choices = []) "nullable", "meta", "profile", "libgit2", "docs", "markdown", "base64", "serialize", "functors", "misc", "enums", "cmdlineargs", "i18n", "workspace", "libdl", "int", - "intset", "floatfuncs", "compile" + "intset", "floatfuncs", "compile", "parallel" ] if Base.USE_GPL_LIBS @@ -43,9 +43,6 @@ function choosetests(choices = []) push!(testnames, "examples") end - # parallel tests depend on other workers - do them last - push!(testnames, "parallel") - tests = [] skip_tests = [] diff --git a/test/cmdlineargs.jl b/test/cmdlineargs.jl index 9a8e9396aed1a..66c0e76c2a7b5 100644 --- a/test/cmdlineargs.jl +++ b/test/cmdlineargs.jl @@ -228,8 +228,11 @@ let exename = `$(joinpath(JULIA_HOME, Base.julia_exename())) --precompiled=yes` @test readchomp(`$exename -e 'println(ARGS);' ''`) == "UTF8String[\"\"]" # issue #12679 - @test readchomp(pipeline(ignorestatus(`$exename -f --compile=yes -foo`),stderr=`cat`)) == "ERROR: unknown option `-o`" - @test readchomp(pipeline(ignorestatus(`$exename -f -p`),stderr=`cat`)) == "ERROR: option `-p/--procs` is missing an argument" - @test readchomp(pipeline(ignorestatus(`$exename -f --inline`),stderr=`cat`)) == "ERROR: option `--inline` is missing an argument" - @test readchomp(pipeline(ignorestatus(`$exename -f -e "@show ARGS" -now -- julia RUN.jl`),stderr=`cat`)) == "ERROR: unknown option `-n`" + extrapath = @windows? joinpath(JULIA_HOME,"..","Git","usr","bin")*";" : "" + withenv("PATH" => extrapath * ENV["PATH"]) do + @test readchomp(pipeline(ignorestatus(`$exename -f --compile=yes -foo`),stderr=`cat`)) == "ERROR: unknown option `-o`" + @test readchomp(pipeline(ignorestatus(`$exename -f -p`),stderr=`cat`)) == "ERROR: option `-p/--procs` is missing an argument" + @test readchomp(pipeline(ignorestatus(`$exename -f --inline`),stderr=`cat`)) == "ERROR: option `--inline` is missing an argument" + @test readchomp(pipeline(ignorestatus(`$exename -f -e "@show ARGS" -now -- julia RUN.jl`),stderr=`cat`)) == "ERROR: unknown option `-n`" + end end diff --git a/test/copy.jl b/test/copy.jl index cd5ca21cb3567..f452af8365e32 100644 --- a/test/copy.jl +++ b/test/copy.jl @@ -57,3 +57,6 @@ let a = Any[[1]], q = QuoteNode([1]) @test dca[1] !== a[1] @test deepcopy(q).value !== q.value end + +# issue #14027 +@test isnull(deepcopy(Nullable{Array}())) diff --git a/test/core.jl b/test/core.jl index eaaf75a998fa5..ecae3a3a8f1ca 100644 --- a/test/core.jl +++ b/test/core.jl @@ -3415,3 +3415,29 @@ f11327{T}(::Type{T},x::T) = x let T=TypeVar(:T,true) @test typeintersect(Tuple{Type{T},T}, Tuple{Type{Type{Float64}},Type{Int}}) === Union{} end + +# issue 13855 +@eval @noinline function foo13855(x) + $(Expr(:localize, :(() -> () -> x))) +end +@test foo13855(Base.AddFun())() == Base.AddFun() +@test foo13855(Base.MulFun())() == Base.MulFun() + +# check if finalizers for the old gen can be triggered manually +# issue #13986 +let + obj = Ref(1) + finalized = 0 + finalizer(obj, (obj) -> (finalized = 1)) + # obj should be marked for promotion after the second gc and be promoted + # after the third GC + # GC_CLEAN; age = 0 + gc(false) + # GC_CLEAN; age = 1 + gc(false) + # GC_QUEUED; age = 1 + gc(false) + # GC_MARKED; age = 1 + finalize(obj) + @test finalized == 1 +end diff --git a/test/examples.jl b/test/examples.jl index fb64ce6b418ad..ca363b31e6c86 100644 --- a/test/examples.jl +++ b/test/examples.jl @@ -51,11 +51,10 @@ end dc_path = joinpath(dir, "dictchannel.jl") include(dc_path) -w_set=filter!(x->x != myid(), workers()) -pid = length(w_set) > 0 ? w_set[1] : myid() - -remotecall_fetch(pid, f->(include(f); nothing), dc_path) -dc=RemoteRef(()->DictChannel(), pid) +# Run the remote on pid 1, since runtests may terminate workers +# at any time depending on memory usage +remotecall_fetch(1, f->(include(f); nothing), dc_path) +dc=RemoteRef(()->DictChannel(), 1) @test typeof(dc) == RemoteRef{DictChannel} @test isready(dc) == false diff --git a/test/file.jl b/test/file.jl index ed3a385229bbd..4fc1880b136b6 100644 --- a/test/file.jl +++ b/test/file.jl @@ -922,3 +922,84 @@ test_12992() test2_12992() test2_12992() test2_12992() + +# issue 13559 + +function test_13559() + fn = tempname() + run(`mkfifo $fn`) + # use subprocess to write 127 bytes to FIFO + writer_cmds = "x=open(\"$fn\", \"w\"); for i=1:127 write(x,0xaa); flush(x); sleep(0.1) end; close(x); quit()" + open(`$(Base.julia_cmd()) -e $writer_cmds`) + #quickly read FIFO, draining it and blocking but not failing with EOFError yet + r = open(fn, "r") + # 15 proper reads + for i=1:15 + @test read(r, Int64) == -6148914691236517206 + end + # last read should throw EOFError when FIFO closes, since there are only 7 bytes available. + @test_throws EOFError read(r, Int64) + close(r) + rm(fn) +end +@unix_only test_13559() + +function test_read_nbyte() + fn = tempname() + # Write one byte. One byte read should work once + # but 2-byte read should throw EOFError. + f = open(fn, "w+") do f + write(f, 0x55) + flush(f) + seek(f, 0) + @test read(f, UInt8) == 0x55 + @test_throws EOFError read(f, UInt8) + seek(f, 0) + @test_throws EOFError read(f, UInt16) + end + # Write 2 more bytes. Now 2-byte read should work once + # but 4-byte read should fail with EOFError. + open(fn, "a+") do f + write(f, 0x4444) + flush(f) + seek(f, 0) + @test read(f, UInt16) == 0x4455 + @test_throws EOFError read(f, UInt16) + seek(f,0) + @test_throws EOFError read(f, UInt32) + end + # Write 4 more bytes. Now 4-byte read should work once + # but 8-byte read should fail with EOFError. + open(fn, "a+") do f + write(f, 0x33333333) + flush(f) + seek(f, 0) + @test read(f, UInt32) == 0x33444455 + @test_throws EOFError read(f, UInt32) + seek(f,0) + @test_throws EOFError read(f, UInt64) + end + # Writing one more byte should allow an 8-byte + # read to proceed. + open(fn, "a+") do f + write(f, 0x22) + flush(f) + seek(f, 0) + @test read(f, UInt64) == 0x2233333333444455 + end + rm(fn) +end +test_read_nbyte() + +# DevNull +@test !isreadable(DevNull) +@test iswritable(DevNull) +@test isopen(DevNull) +@test write(DevNull, 0xff) === 1 +@test write(DevNull, Int32(1234)) === 4 +@test_throws EOFError read(DevNull, UInt8) +@test close(DevNull) === nothing +@test flush(DevNull) === nothing +@test copy(DevNull) === DevNull +@test eof(DevNull) +@test print(DevNull, "go to /dev/null") === nothing diff --git a/test/float16.jl b/test/float16.jl index 2cdd1b4a98f99..54bd3461759e8 100644 --- a/test/float16.jl +++ b/test/float16.jl @@ -23,6 +23,16 @@ g = Float16(1.) @test convert(Int128,Float16(-2.0)) == Int128(-2) @test convert(UInt128,Float16(2.0)) == UInt128(2) +# convert(::Type{Int128}, x::Float16) +@test convert(Int128, Float16(1.0)) === Int128(1.0) +@test convert(Int128, Float16(-1.0)) === Int128(-1.0) +@test_throws InexactError convert(Int128, Float16(3.5)) + +# convert(::Type{UInt128}, x::Float16) +@test convert(UInt128, Float16(1.0)) === UInt128(1.0) +@test_throws InexactError convert(UInt128, Float16(3.5)) +@test_throws InexactError convert(UInt128, Float16(-1)) + x = Float32(rand()) y = Float32(rand()) z = Float32(rand()) diff --git a/test/functional.jl b/test/functional.jl index 9df37f2d4c52f..5c80224004123 100644 --- a/test/functional.jl +++ b/test/functional.jl @@ -42,6 +42,22 @@ end # issue #4718 @test collect(filter(x->x[1], zip([true, false, true, false],"abcd"))) == [(true,'a'),(true,'c')] +let z = zip(1:2) + @test collect(z) == [(1,), (2,)] + # Issue #13979 + @test eltype(z) == Tuple{Int} +end + +let z = zip(1:2, 3:4) + @test collect(z) == [(1,3), (2,4)] + @test eltype(z) == Tuple{Int,Int} +end + +let z = zip(1:2, 3:4, 5:6) + @test collect(z) == [(1,3,5), (2,4,6)] + @test eltype(z) == Tuple{Int,Int,Int} +end + # enumerate (issue #6284) let b = IOBuffer("1\n2\n3\n"), a = [] for (i,x) in enumerate(eachline(b)) diff --git a/test/gitutils.jl b/test/gitutils.jl index 9485719ebf880..d42d02a15640d 100644 --- a/test/gitutils.jl +++ b/test/gitutils.jl @@ -72,9 +72,12 @@ function verify_work(d::Dict) end end # check for anything that's not in d - for line in eachline(`ls -A`) - name = chomp(line) - @test name == ".git" || haskey(d,name) + extrapath = @windows? joinpath(JULIA_HOME,"..","Git","usr","bin")*";" : "" + withenv("PATH" => extrapath * ENV["PATH"]) do + for line in eachline(`ls -A`) + name = chomp(line) + @test name == ".git" || haskey(d,name) + end end end @@ -91,9 +94,12 @@ function git_setup(h::Dict, i::Dict, w::Dict, parents::AbstractString...) work = mktree(w) # clear the repo - for line in eachline(`ls -A`) - name = chomp(line) - name == ".git" || rm(name, recursive=true) + extrapath = @windows? joinpath(JULIA_HOME,"..","Git","usr","bin")*";" : "" + withenv("PATH" => extrapath * ENV["PATH"]) do + for line in eachline(`ls -A`) + name = chomp(line) + name == ".git" || rm(name, recursive=true) + end end # create the head commit diff --git a/test/iobuffer.jl b/test/iobuffer.jl index 36add0105c485..991d244676e22 100644 --- a/test/iobuffer.jl +++ b/test/iobuffer.jl @@ -6,6 +6,9 @@ let io = IOBuffer() @test eof(io) @test_throws EOFError read(io,UInt8) @test write(io,"abc") == 3 +@test isreadable(io) +@test iswritable(io) +@test isopen(io) @test ioslength(io) == 3 @test position(io) == 3 @test eof(io) @@ -184,3 +187,21 @@ end let io = IOBuffer(0) write(io, ones(UInt8, 1048577)) end + +let bstream = BufferStream() + @test isopen(bstream) + @test isreadable(bstream) + @test iswritable(bstream) + @test sprint(io -> show(io,bstream)) == "BufferStream() bytes waiting:$(nb_available(bstream.buffer)), isopen:true" + a = rand(UInt8,10) + write(bstream,a) + flush(bstream) + b = read(bstream,UInt8) + @test a[1] == b + b = read(bstream,UInt8) + @test a[2] == b + c = zeros(UInt8,8) + read!(bstream,c) + @test c == a[3:10] + close(bstream) +end diff --git a/test/math.jl b/test/math.jl index c7781daa89e25..14adc4f9f658d 100644 --- a/test/math.jl +++ b/test/math.jl @@ -14,6 +14,16 @@ @test clamp([0, 1, 2, 3, 4], 1.0, 3.0) == [1.0, 1.0, 2.0, 3.0, 3.0] +@test !(pi == e) +@test !(e == 1//2) +@test 1//2 <= e +@test big(1//2) < e +@test e < big(20//6) +@test e^pi == exp(pi) +@test e^2 == exp(2) +@test e^2.4 == exp(2.4) +@test e^(2//3) == exp(2//3) + begin x = [0.0, 1.0, 2.0, 3.0, 4.0] clamp!(x, 1, 3) @@ -108,6 +118,7 @@ for T in (Float32, Float64) @test_approx_eq_eps expm1(T(1)) T(e)-1 10*eps(T) @test isequal(hypot(T(3),T(4)), T(5)) @test isequal(log(T(1)), T(0)) + @test isequal(log(e,T(1)), T(0)) @test_approx_eq_eps log(T(e)) T(1) eps(T) @test isequal(log10(T(1)), T(0)) @test isequal(log10(T(10)), T(1)) @@ -166,12 +177,16 @@ for T in (Float32, Float64) @test isnan(log1p(convert(T,NaN))) @test_throws DomainError log1p(convert(T,-2.0)) end +@test_approx_eq exp10(5) exp10(5.0) +@test log(e) == 1 for T in (Int, Float64, BigFloat) @test_approx_eq deg2rad(T(180)) 1pi @test_approx_eq deg2rad(T[45, 60]) [pi/T(4), pi/T(3)] @test_approx_eq rad2deg([pi/T(4), pi/T(3)]) [45, 60] @test_approx_eq rad2deg(T(1)*pi) 180 + @test_approx_eq rad2deg(T(1)) rad2deg(true) + @test_approx_eq deg2rad(T(1)) deg2rad(true) end # degree-based trig functions @@ -229,6 +244,13 @@ end @test cospi(1) == -1 @test cospi(2) == 1 +@test sinc(1) == 0 +@test sinc(complex(1,0)) == 0 +@test sinc(0) == 1 +@test cosc(1) == -1 +@test cosc(0) == 0 +@test cosc(complex(1,0)) == -1 + # check type stability for T = (Float32,Float64,BigFloat) for f = (sind,cosd,sinpi,cospi) @@ -236,7 +258,6 @@ for T = (Float32,Float64,BigFloat) end end - # error functions @test_approx_eq erf(1) 0.84270079294971486934 @test_approx_eq erfc(1) 0.15729920705028513066 @@ -448,6 +469,8 @@ end for x in (3.2, 2+1im, 3//2, 3.2+0.1im) @test factorial(x) == gamma(1+x) end +@test lfact(1) == 0 +@test lfact(2) == lgamma(3) # digamma for elty in (Float32, Float64) @@ -492,14 +515,18 @@ for elty in (Float32, Float64) @test abs(invdigamma(digamma(convert(elty, val))) - convert(elty, val)) < 1e-8 end end +@test abs(invdigamma(2)) == abs(invdigamma(2.)) @test_approx_eq polygamma(20, 7.) -4.644616027240543262561198814998587152547 # eta, zeta @test_approx_eq eta(1) log(2) @test_approx_eq eta(2) pi^2/12 +@test_approx_eq eta(Float32(2)) eta(2) +@test_approx_eq eta(Complex64(2)) eta(2) @test_approx_eq zeta(0) -0.5 @test_approx_eq zeta(2) pi^2/6 +@test_approx_eq zeta(Complex64(2)) zeta(2) @test_approx_eq zeta(4) pi^4/90 @test_approx_eq zeta(one(Float32)) Float32(zeta(one(Float64))) @test isnan(zeta(NaN)) @@ -559,6 +586,9 @@ end @test polygamma(4, -0.0) == Inf == -polygamma(4, +0.0) @test zeta(4, +0.0) == Inf == zeta(4, -0.0) @test zeta(5, +0.0) == Inf == -zeta(5, -0.0) +@test zeta(Inf, 1.) == 1 +@test zeta(Inf, 2.) == 0 +@test isnan(zeta(NaN, 1.)) @test isa([digamma(x) for x in [1.0]], Vector{Float64}) @test isa([trigamma(x) for x in [1.0]], Vector{Float64}) @test isa([polygamma(3,x) for x in [1.0]], Vector{Float64}) diff --git a/test/misc.jl b/test/misc.jl index 2c76e387ace96..80a84e703f420 100644 --- a/test/misc.jl +++ b/test/misc.jl @@ -181,7 +181,7 @@ x = 1.0 end @test sprint(whos, Main, r"^$") == "" let v = sprint(whos, _test_whos_) - @test contains(v, "x 8 bytes Float64 : 1.0") + @test contains(v, "x 8 bytes Float64") end # issue #13021 @@ -193,3 +193,17 @@ catch ex end @test isa(ex, ErrorException) && ex.msg == "cannot assign variables in other modules" end + +@test Base.is_unix(:Darwin) +@test Base.is_unix(:FreeBSD) +@test_throws ArgumentError Base.is_unix(:BeOS) +@unix_only @test Base.windows_version() == (0,0) + +# Issue 14173 +module Tmp14173 + export A + A = randn(2000, 2000) +end +whos(IOBuffer(), Tmp14173) # warm up +@test @allocated(whos(IOBuffer(), Tmp14173)) < 7000 + diff --git a/test/mmap.jl b/test/mmap.jl index 2a7b493bd1b68..35f2024a8c525 100644 --- a/test/mmap.jl +++ b/test/mmap.jl @@ -89,11 +89,10 @@ m = Mmap.mmap(file, Vector{UInt8}, 1, sz+1) @test m[1] == 0x00 finalize(m); m=nothing; gc() -# Uncomment out once #11351 is resolved -# s = open(file, "r") -# m = Mmap.mmap(s) -# @test_throws ReadOnlyMemoryError m[5] = Vector{UInt8}('x') # tries to setindex! on read-only array -# finalize(m); m=nothing; gc() +s = open(file, "r") +m = Mmap.mmap(s) +@test_throws ReadOnlyMemoryError m[5] = UInt8('x') # tries to setindex! on read-only array +finalize(m); m=nothing; gc() s = open(file, "w") do f write(f, "Hello World\n") @@ -284,4 +283,4 @@ n = similar(m, (2,2)) n = similar(m, 12) @test length(n) == 12 @test size(n) == (12,) -finalize(m); m = nothing; gc() \ No newline at end of file +finalize(m); m = nothing; gc() diff --git a/test/numbers.jl b/test/numbers.jl index 907157acf52c4..3e334fb0e9b72 100644 --- a/test/numbers.jl +++ b/test/numbers.jl @@ -473,6 +473,118 @@ end @test copysign(big(1.0),big(-2.0)) == big(-1.0) +#copysign +@test copysign(-1,1) == 1 +@test copysign(1,-1) == -1 + +@test copysign(-1,1.0) == 1 +@test copysign(1,-1.0) == -1 + +@test copysign(-1,1//2) == 1 +@test copysign(1,-1//2) == -1 + +@test copysign(1.0,-1) == -1.0 +@test copysign(-1.0,1) == 1.0 + +@test copysign(1.0,-1.0) == -1.0 +@test copysign(-1.0,1.0) == 1.0 + +@test copysign(1.0,-1//2) == -1.0 +@test copysign(-1.0,1//2) == 1.0 + +@test copysign(1//2,-1) == -1//2 +@test copysign(-1//2,1) == 1//2 + +@test copysign(1//2,-1//2) == -1//2 +@test copysign(-1//2,1//2) == 1//2 + +@test copysign(1//2,-1.0) == -1//2 +@test copysign(-1//2,1.0) == 1//2 + +# verify type stability with integer (x is negative) +@test eltype(copysign(-1,1)) <: Integer +@test eltype(copysign(-1,BigInt(1))) <: Integer +@test eltype(copysign(-1,1.0)) <: Integer +@test eltype(copysign(-1,1//2)) <: Integer +@test eltype(copysign(-BigInt(1),1)) <: Integer +@test eltype(copysign(-BigInt(1),1.0)) <: Integer +@test eltype(copysign(-BigInt(1),1//2)) <: Integer +@test eltype(copysign(-BigInt(1),BigInt(1))) <: Integer +@test eltype(copysign(-1,-1)) <: Integer +@test eltype(copysign(-1,-BigInt(1))) <: Integer +@test eltype(copysign(-1,-1.0)) <: Integer +@test eltype(copysign(-1,-1//2)) <: Integer +@test eltype(copysign(-BigInt(1),-1)) <: Integer +@test eltype(copysign(-BigInt(1),-1.0)) <: Integer +@test eltype(copysign(-BigInt(1),-1//2)) <: Integer +@test eltype(copysign(-BigInt(1),-BigInt(1))) <: Integer + +# verify type stability with integer (x is positive) +@test eltype(copysign(1,1)) <: Integer +@test eltype(copysign(1,BigInt(1))) <: Integer +@test eltype(copysign(1,1.0)) <: Integer +@test eltype(copysign(1,1//2)) <: Integer +@test eltype(copysign(BigInt(1),1)) <: Integer +@test eltype(copysign(BigInt(1),1.0)) <: Integer +@test eltype(copysign(BigInt(1),1//2)) <: Integer +@test eltype(copysign(BigInt(1),BigInt(1))) <: Integer +@test eltype(copysign(1,-1)) <: Integer +@test eltype(copysign(1,-BigInt(1))) <: Integer +@test eltype(copysign(1,-1.0)) <: Integer +@test eltype(copysign(1,-1//2)) <: Integer +@test eltype(copysign(BigInt(1),-1)) <: Integer +@test eltype(copysign(BigInt(1),-1.0)) <: Integer +@test eltype(copysign(BigInt(1),-1//2)) <: Integer +@test eltype(copysign(BigInt(1),-BigInt(1))) <: Integer + +# verify type stability with real (x is negative) +@test eltype(copysign(-1.0,1)) <: Real +@test eltype(copysign(-1.0,BigInt(1))) <: Real +@test eltype(copysign(-1.0,1.0)) <: Real +@test eltype(copysign(-1.0,1//2)) <: Real +@test eltype(copysign(-1.0,-1)) <: Real +@test eltype(copysign(-1.0,-BigInt(1))) <: Real +@test eltype(copysign(-1.0,-1.0)) <: Real +@test eltype(copysign(-1.0,-1//2)) <: Real + +# Verify type stability with real (x is positive) +@test eltype(copysign(1.0,1)) <: Real +@test eltype(copysign(1.0,BigInt(1))) <: Real +@test eltype(copysign(1.0,1.0)) <: Real +@test eltype(copysign(1.0,1//2)) <: Real +@test eltype(copysign(1.0,-1)) <: Real +@test eltype(copysign(1.0,-BigInt(1))) <: Real +@test eltype(copysign(1.0,-1.0)) <: Real +@test eltype(copysign(1.0,-1//2)) <: Real + +# Verify type stability with rational (x is negative) +@test eltype(copysign(-1//2,1)) <: Rational +@test eltype(copysign(-1//2,BigInt(1))) <: Rational +@test eltype(copysign(-1//2,1.0)) <: Rational +@test eltype(copysign(-1//2,1//2)) <: Rational +@test eltype(copysign(-1//2,-1)) <: Rational +@test eltype(copysign(-1//2,-BigInt(1))) <: Rational +@test eltype(copysign(-1//2,-1.0)) <: Rational +@test eltype(copysign(-1//2,-1//2)) <: Rational + +# Verify type stability with rational (x is positive) +@test eltype(copysign(-1//2,1)) <: Rational +@test eltype(copysign(-1//2,BigInt(1))) <: Rational +@test eltype(copysign(-1//2,1.0)) <: Rational +@test eltype(copysign(-1//2,1//2)) <: Rational +@test eltype(copysign(-1//2,-1)) <: Rational +@test eltype(copysign(-1//2,-BigInt(1))) <: Rational +@test eltype(copysign(-1//2,-1.0)) <: Rational +@test eltype(copysign(-1//2,-1//2)) <: Rational + +# test x = NaN +@test isnan(copysign(0/0,1)) +@test isnan(copysign(0/0,-1)) + +# test x = Inf +@test isinf(copysign(1/0,1)) +@test isinf(copysign(1/0,-1)) + @test isnan(1) == false @test isnan(1.0) == false @test isnan(-1.0) == false @@ -2126,6 +2238,11 @@ end @test !isprime(0xffffffffffffffc7) @test !isprime(0xffffffffffffffc9) +for T in [Int8,UInt8,Int16,UInt16,Int128,UInt128] + @test isprime(T(2)) + @test !isprime(T(4)) +end + # issue #5210 @test prod([ k^v for (k,v) in factor(typemax(UInt32)) ]) == typemax(UInt32) @test prod([ k^v for (k,v) in factor(typemax(Int8)) ]) == typemax(Int8) @@ -2444,6 +2561,21 @@ for x in [1.23, 7, e, 4//5] #[FP, Int, Irrational, Rat] @test getindex(x) == x end +#getindex(x::Number,-1) throws BoundsError +#getindex(x::Number,0) throws BoundsError +#getindex(x::Number,2) throws BoundsError +#getindex(x::Array,-1) throws BoundsError +#getindex(x::Array,0 throws BoundsError +#getindex(x::Array,length(x::Array)+1) throws BoundsError +for x in [1.23, 7, e, 4//5] #[FP, Int, Irrational, Rat] + @test_throws BoundsError getindex(x,-1) + @test_throws BoundsError getindex(x,0) + @test_throws BoundsError getindex(x,2) + @test_throws BoundsError getindex([x x],-1) + @test_throws BoundsError getindex([x x],0) + @test_throws BoundsError getindex([x x],length([x,x])+1) +end + #copysign(x::Real, y::Real) = ifelse(signbit(x)!=signbit(y), -x, x) #same sign for x in [1.23, 7, e, 4//5] diff --git a/test/parallel.jl b/test/parallel.jl index 77e58b7461086..777cec5bfc300 100644 --- a/test/parallel.jl +++ b/test/parallel.jl @@ -1,544 +1,14 @@ # This file is a part of Julia. License is MIT: http://julialang.org/license -# NOTE: worker processes cannot add more workers, only the client process can. -using Base.Test +# Run the parallel test outside of the main driver, since it runs off its own +# set of workers. -if nworkers() < 3 - remotecall_fetch(1, () -> addprocs(3 - nworkers())) -end - -id_me = myid() -id_other = filter(x -> x != id_me, procs())[rand(1:(nprocs()-1))] - -@test fetch(@spawnat id_other myid()) == id_other -@test @fetchfrom id_other begin myid() end == id_other -@fetch begin myid() end - -rr=RemoteRef() -@test typeof(rr) == RemoteRef{Channel{Any}} -a = rand(5,5) -put!(rr, a) -@test rr[2,3] == a[2,3] -@test rr[] == a - -rr=RemoteRef(workers()[1]) -@test typeof(rr) == RemoteRef{Channel{Any}} -a = rand(5,5) -put!(rr, a) -@test rr[1,5] == a[1,5] -@test rr[] == a - -dims = (20,20,20) - -@linux_only begin - S = SharedArray(Int64, dims) - @test startswith(S.segname, "/jl") - @test !ispath("/dev/shm" * S.segname) - - S = SharedArray(Int64, dims; pids=[id_other]) - @test startswith(S.segname, "/jl") - @test !ispath("/dev/shm" * S.segname) -end - -# TODO : Need a similar test of shmem cleanup for OSX - -##### SharedArray tests - -function check_pids_all(S::SharedArray) - pidtested = falses(size(S)) - for p in procs(S) - idxes_in_p = remotecall_fetch(p, D -> parentindexes(D.loc_subarr_1d)[1], S) - @test all(sdata(S)[idxes_in_p] .== p) - pidtested[idxes_in_p] = true - end - @test all(pidtested) -end - -d = Base.shmem_rand(1:100, dims) -a = convert(Array, d) - -partsums = Array(Int, length(procs(d))) -@sync begin - for (i, p) in enumerate(procs(d)) - @async partsums[i] = remotecall_fetch(p, D->sum(D.loc_subarr_1d), d) - end -end -@test sum(a) == sum(partsums) - -d = Base.shmem_rand(dims) -for p in procs(d) - idxes_in_p = remotecall_fetch(p, D -> parentindexes(D.loc_subarr_1d)[1], d) - idxf = first(idxes_in_p) - idxl = last(idxes_in_p) - d[idxf] = Float64(idxf) - rv = remotecall_fetch(p, (D,idxf,idxl) -> begin assert(D[idxf] == Float64(idxf)); D[idxl] = Float64(idxl); D[idxl]; end, d,idxf,idxl) - @test d[idxl] == rv -end - -@test ones(10, 10, 10) == Base.shmem_fill(1.0, (10,10,10)) -@test zeros(Int32, 10, 10, 10) == Base.shmem_fill(0, (10,10,10)) +cmd = `$(Base.julia_cmd()) --check-bounds=yes --depwarn=error parallel_exec.jl` -d = Base.shmem_rand(dims) -s = Base.shmem_rand(dims) -copy!(s, d) -@test s == d -s = Base.shmem_rand(dims) -copy!(s, sdata(d)) -@test s == d -a = rand(dims) -@test sdata(a) == a - -d = SharedArray(Int, dims; init = D->fill!(D.loc_subarr_1d, myid())) -for p in procs(d) - idxes_in_p = remotecall_fetch(p, D -> parentindexes(D.loc_subarr_1d)[1], d) - idxf = first(idxes_in_p) - idxl = last(idxes_in_p) - @test d[idxf] == p - @test d[idxl] == p +(strm, proc) = open(cmd) +errors = readall(strm) +wait(proc) +if !success(proc) && ccall(:jl_running_on_valgrind,Cint,()) == 0 + println(errors); + error("Parallel test failed, cmd : $cmd") end - -d = SharedArray(Float64, (2,3)) -@test isa(d[:,2], Vector{Float64}) - -### SharedArrays from a file - -# Mapping an existing file -fn = tempname() -open(fn, "w") do io - write(io, 1:30) -end -sz = (6,5) -Atrue = reshape(1:30, sz) - -S = SharedArray(fn, Int, sz) -@test S == Atrue -@test length(procs(S)) > 1 -@sync begin - for p in procs(S) - @async remotecall_wait(p, D->fill!(D.loc_subarr_1d, myid()), S) - end -end -check_pids_all(S) - -filedata = similar(Atrue) -open(fn, "r") do io - read!(io, filedata) -end -@test filedata == sdata(S) - -# Error for write-only files -@test_throws ArgumentError SharedArray(fn, Int, sz, mode="w") - -# Error for file doesn't exist, but not allowed to create -@test_throws ArgumentError SharedArray(tempname(), Int, sz, mode="r") - -# Creating a new file -fn2 = tempname() -S = SharedArray(fn2, Int, sz, init=D->D[localindexes(D)] = myid()) -@test S == filedata -filedata2 = similar(Atrue) -open(fn2, "r") do io - read!(io, filedata2) -end -@test filedata == filedata2 - -# Appending to a file -fn3 = tempname() -open(fn3, "w") do io - write(io, ones(UInt8, 4)) -end -S = SharedArray(fn3, UInt8, sz, 4, mode="a+", init=D->D[localindexes(D)]=0x02) -len = prod(sz)+4 -@test filesize(fn3) == len -filedata = Array(UInt8, len) -open(fn3, "r") do io - read!(io, filedata) -end -@test all(filedata[1:4] .== 0x01) -@test all(filedata[5:end] .== 0x02) - -@unix_only begin # these give unlink: operation not permitted (EPERM) on Windows - rm(fn); rm(fn2); rm(fn3) -end - -### Utility functions - -# reshape - -d = Base.shmem_fill(1.0, (10,10,10)) -@test ones(100, 10) == reshape(d,(100,10)) -d = Base.shmem_fill(1.0, (10,10,10)) -@test_throws DimensionMismatch reshape(d,(50,)) - -# rand, randn -d = Base.shmem_rand(dims) -@test size(rand!(d)) == dims -d = Base.shmem_fill(1.0, dims) -@test size(randn!(d)) == dims - -# similar -d = Base.shmem_rand(dims) -@test size(similar(d, Complex128)) == dims -@test size(similar(d, dims)) == dims - -# issue #6362 -d = Base.shmem_rand(dims) -s = copy(sdata(d)) -ds = deepcopy(d) -@test ds == d -pids_d = procs(d) -remotecall_fetch(pids_d[findfirst(id->(id != myid()), pids_d)], setindex!, d, 1.0, 1:10) -@test ds != d -@test s != d - - -# SharedArray as an array -# Since the data in d will depend on the nprocs, just test that these operations work -a = d[1:5] -@test_throws BoundsError d[-1:5] -a = d[1,1,1:3:end] -d[2:4] = 7 -d[5,1:2:4,8] = 19 - -AA = rand(4,2) -A = convert(SharedArray, AA) -B = convert(SharedArray, AA') -@test B*A == ctranspose(AA)*AA - -d=SharedArray(Int64, (10,10); init = D->fill!(D.loc_subarr_1d, myid()), pids=[id_me, id_other]) -d2 = map(x->1, d) -@test reduce(+, d2) == 100 - -@test reduce(+, d) == ((50*id_me) + (50*id_other)) -map!(x->1, d) -@test reduce(+, d) == 100 - -@test fill!(d, 1) == ones(10, 10) -@test fill!(d, 2.) == fill(2, 10, 10) -@test d[:] == fill(2, 100) -@test d[:,1] == fill(2, 10) -@test d[1,:] == fill(2, 1, 10) - -# Boundary cases where length(S) <= length(pids) -@test 2.0 == remotecall_fetch(id_other, D->D[2], Base.shmem_fill(2.0, 2; pids=[id_me, id_other])) -@test 3.0 == remotecall_fetch(id_other, D->D[1], Base.shmem_fill(3.0, 1; pids=[id_me, id_other])) - -# Test @parallel load balancing - all processors should get either M or M+1 -# iterations out of the loop range for some M. -workloads = hist(@parallel((a,b)->[a;b], for i=1:7; myid(); end), nprocs())[2] -@test maximum(workloads) - minimum(workloads) <= 1 - -# @parallel reduction should work even with very short ranges -@test @parallel(+, for i=1:2; i; end) == 3 - -# Testing timedwait on multiple channels -@sync begin - rr1 = Channel() - rr2 = Channel() - rr3 = Channel() - - @async begin sleep(0.5); put!(rr1, :ok) end - @async begin sleep(1.0); put!(rr2, :ok) end - @async begin sleep(2.0); put!(rr3, :ok) end - - tic() - timedwait(1.0) do - all(map(isready, [rr1, rr2, rr3])) - end - et=toq() - # assuming that 0.5 seconds is a good enough buffer on a typical modern CPU - try - @test (et >= 1.0) && (et <= 1.5) - @test !isready(rr3) - catch - warn("timedwait tests delayed. et=$et, isready(rr3)=$(isready(rr3))") - end - @test isready(rr1) -end - -@test_throws ArgumentError sleep(-1) -@test_throws ArgumentError timedwait(()->false, 0.1, pollint=-0.5) - -# specify pids for pmap -@test sort(workers()[1:2]) == sort(unique(pmap(x->(sleep(0.1);myid()), 1:10, pids = workers()[1:2]))) - -# Testing buffered and unbuffered reads -# This large array should write directly to the socket -a = ones(10^6) -@test a == remotecall_fetch(id_other, (x)->x, a) - -# Not a bitstype, should be buffered -s = [randstring() for x in 1:10^5] -@test s == remotecall_fetch(id_other, (x)->x, s) - -#large number of small requests -num_small_requests = 10000 -@test fill(id_other, num_small_requests) == [remotecall_fetch(id_other, myid) for i in 1:num_small_requests] - -# test parallel sends of large arrays from multiple tasks to the same remote worker -ntasks = 10 -rr_list = [Channel() for x in 1:ntasks] -a=ones(2*10^5); -for rr in rr_list - @async let rr=rr - try - for i in 1:10 - @test a == remotecall_fetch(id_other, (x)->x, a) - yield() - end - put!(rr, :OK) - catch - put!(rr, :ERROR) - end - end -end - -@test [fetch(rr) for rr in rr_list] == [:OK for x in 1:ntasks] - -function test_channel(c) - put!(c, 1) - put!(c, "Hello") - put!(c, 5.0) - - @test isready(c) == true - @test fetch(c) == 1 - @test fetch(c) == 1 # Should not have been popped previously - @test take!(c) == 1 - @test take!(c) == "Hello" - @test fetch(c) == 5.0 - @test take!(c) == 5.0 - @test isready(c) == false - close(c) -end - -test_channel(Channel(10)) -test_channel(RemoteRef(()->Channel(10))) - -c=Channel{Int}(1) -@test_throws MethodError put!(c, "Hello") - -c=Channel(256) -# Test growth of channel -@test c.szp1 <= 33 -for x in 1:40 - put!(c, x) -end -@test c.szp1 <= 65 -for x in 1:39 - take!(c) -end -for x in 1:64 - put!(c, x) -end -@test (c.szp1 > 65) && (c.szp1 <= 129) -for x in 1:39 - take!(c) -end -@test fetch(c) == 39 -for x in 1:26 - take!(c) -end -@test isready(c) == false - -# test channel iterations -function test_iteration(in_c, out_c) - t=@schedule for v in in_c - put!(out_c, v) - end - - isa(in_c, Channel) && @test isopen(in_c) == true - put!(in_c, 1) - @test take!(out_c) == 1 - put!(in_c, "Hello") - close(in_c) - @test take!(out_c) == "Hello" - isa(in_c, Channel) && @test isopen(in_c) == false - @test_throws InvalidStateException put!(in_c, :foo) - yield() - @test istaskdone(t) == true -end - -test_iteration(Channel(10), Channel(10)) -# make sure exceptions propagate when waiting on Tasks -@test_throws CompositeException (@sync (@async error("oops"))) -try - @sync begin - for i in 1:5 - @async error(i) - end - end - error("unexpected") -catch ex - @test typeof(ex) == CompositeException - @test length(ex) == 5 - @test typeof(ex.exceptions[1]) == CapturedException - @test typeof(ex.exceptions[1].ex) == ErrorException - errors = map(x->x.ex.msg, ex.exceptions) - @test collect(1:5) == sort(map(x->parse(Int, x), errors)) -end - -macro test_remoteexception_thrown(expr) - quote - try - $(esc(expr)) - error("unexpected") - catch ex - @test typeof(ex) == RemoteException - @test typeof(ex.captured) == CapturedException - @test typeof(ex.captured.ex) == ErrorException - @test ex.captured.ex.msg == "foobar" - end - end -end - -for id in [id_other, id_me] - @test_remoteexception_thrown remotecall_fetch(id, ()->throw(ErrorException("foobar"))) - @test_remoteexception_thrown remotecall_wait(id, ()->throw(ErrorException("foobar"))) - @test_remoteexception_thrown wait(remotecall(id, ()->throw(ErrorException("foobar")))) -end - -# The below block of tests are usually run only on local development systems, since: -# - tests which print errors -# - addprocs tests are memory intensive -# - ssh addprocs requires sshd to be running locally with passwordless login enabled. -# The test block is enabled by defining env JULIA_TESTFULL=1 - -DoFullTest = Bool(parse(Int,(get(ENV, "JULIA_TESTFULL", "0")))) - -if DoFullTest - # pmap tests - # needs at least 4 processors dedicated to the below tests - ppids = remotecall_fetch(1, ()->addprocs(4)) - s = "abcdefghijklmnopqrstuvwxyz"; - ups = uppercase(s); - @test ups == bytestring(UInt8[UInt8(c) for c in pmap(x->uppercase(x), s)]) - @test ups == bytestring(UInt8[UInt8(c) for c in pmap(x->uppercase(Char(x)), s.data)]) - - # retry, on error exit - res = pmap(x->(x=='a') ? error("EXPECTED TEST ERROR. TO BE IGNORED.") : (sleep(0.1);uppercase(x)), s; err_retry=true, err_stop=true, pids=ppids); - @test (length(res) < length(ups)) - @test isa(res[1], Exception) - - # no retry, on error exit - res = pmap(x->(x=='a') ? error("EXPECTED TEST ERROR. TO BE IGNORED.") : (sleep(0.1);uppercase(x)), s; err_retry=false, err_stop=true, pids=ppids); - @test (length(res) < length(ups)) - @test isa(res[1], Exception) - - # retry, on error continue - res = pmap(x->iseven(myid()) ? error("EXPECTED TEST ERROR. TO BE IGNORED.") : (sleep(0.1);uppercase(x)), s; err_retry=true, err_stop=false, pids=ppids); - @test length(res) == length(ups) - @test ups == bytestring(UInt8[UInt8(c) for c in res]) - - # no retry, on error continue - res = pmap(x->(x=='a') ? error("EXPECTED TEST ERROR. TO BE IGNORED.") : (sleep(0.1);uppercase(x)), s; err_retry=false, err_stop=false, pids=ppids); - @test length(res) == length(ups) - @test isa(res[1], Exception) - - # Topology tests need to run externally since a given cluster at any - # time can only support a single topology and the current session - # is already running in parallel under the default topology. - script = joinpath(dirname(@__FILE__), "topology.jl") - cmd = `$(joinpath(JULIA_HOME,Base.julia_exename())) $script` - - (strm, proc) = open(cmd) - wait(proc) - if !success(proc) && ccall(:jl_running_on_valgrind,Cint,()) == 0 - println(readall(strm)) - error("Topology tests failed : $cmd") - end - - println("Testing exception printing on remote worker from a `remote_do` call") - println("Please ensure the remote error and backtrace is displayed on screen") - - Base.remote_do(id_other, ()->throw(ErrorException("TESTING EXCEPTION ON REMOTE DO. PLEASE IGNORE"))) - sleep(0.5) # Give some time for the above error to be printed - -@unix_only begin - function test_n_remove_pids(new_pids) - for p in new_pids - w_in_remote = sort(remotecall_fetch(p, workers)) - try - @test intersect(new_pids, w_in_remote) == new_pids - catch e - print("p : $p\n") - print("newpids : $new_pids\n") - print("w_in_remote : $w_in_remote\n") - print("intersect : $(intersect(new_pids, w_in_remote))\n\n\n") - rethrow(e) - end - end - - @test :ok == remotecall_fetch(1, (p)->rmprocs(p; waitfor=5.0), new_pids) - end - - print("\n\nTesting SSHManager. A minimum of 4GB of RAM is recommended.\n") - print("Please ensure sshd is running locally with passwordless login enabled.\n") - - sshflags = `-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o LogLevel=ERROR ` - #Issue #9951 - hosts=[] - localhost_aliases = ["localhost", string(getipaddr()), "127.0.0.1"] - num_workers = parse(Int,(get(ENV, "JULIA_ADDPROCS_NUM", "9"))) - - for i in 1:(num_workers/length(localhost_aliases)) - append!(hosts, localhost_aliases) - end - - print("\nTesting SSH addprocs with $(length(hosts)) workers...\n") - new_pids = remotecall_fetch(1, (h, sf) -> addprocs(h; sshflags=sf), hosts, sshflags) - @test length(new_pids) == length(hosts) - test_n_remove_pids(new_pids) - - print("\nMixed ssh addprocs with :auto\n") - new_pids = sort(remotecall_fetch(1, (h, sf) -> addprocs(h; sshflags=sf), ["localhost", ("127.0.0.1", :auto), "localhost"], sshflags)) - @test length(new_pids) == (2 + Sys.CPU_CORES) - test_n_remove_pids(new_pids) - - print("\nMixed ssh addprocs with numeric counts\n") - new_pids = sort(remotecall_fetch(1, (h, sf) -> addprocs(h; sshflags=sf), [("localhost", 2), ("127.0.0.1", 2), "localhost"], sshflags)) - @test length(new_pids) == 5 - test_n_remove_pids(new_pids) - - print("\nssh addprocs with tunnel\n") - new_pids = sort(remotecall_fetch(1, (h, sf) -> addprocs(h; tunnel=true, sshflags=sf), [("localhost", num_workers)], sshflags)) - @test length(new_pids) == num_workers - test_n_remove_pids(new_pids) - -end -end - -# issue #7727 -let A = [], B = [] - t = @task produce(11) - @sync begin - @async for x in t; push!(A,x); end - @async for x in t; push!(B,x); end - end - @test (A == [11]) != (B == [11]) -end - -let t = @task 42 - schedule(t, ErrorException(""), error=true) - @test_throws ErrorException wait(t) -end - -# issue #8207 -let A = Any[] - @parallel (+) for i in (push!(A,1); 1:2) - i - end - @test length(A) == 1 -end - -# issue #13168 -function f13168(n) - val = 0 - for i=1:n val+=sum(rand(n,n)^2) end - val -end -let t = schedule(@task f13168(100)) - @test schedule(t) === t -end - -# issue #13122 -@test remotecall_fetch(workers()[1], identity, C_NULL) === C_NULL diff --git a/test/parallel_exec.jl b/test/parallel_exec.jl new file mode 100644 index 0000000000000..f48ef394cca15 --- /dev/null +++ b/test/parallel_exec.jl @@ -0,0 +1,532 @@ +# This file is a part of Julia. License is MIT: http://julialang.org/license + +using Base.Test + +addprocs(3; exeflags=`--check-bounds=yes --depwarn=error`) + +id_me = myid() +id_other = filter(x -> x != id_me, procs())[rand(1:(nprocs()-1))] + +@test fetch(@spawnat id_other myid()) == id_other +@test @fetchfrom id_other begin myid() end == id_other +@fetch begin myid() end + +rr=RemoteRef() +@test typeof(rr) == RemoteRef{Channel{Any}} +a = rand(5,5) +put!(rr, a) +@test rr[2,3] == a[2,3] +@test rr[] == a + +rr=RemoteRef(workers()[1]) +@test typeof(rr) == RemoteRef{Channel{Any}} +a = rand(5,5) +put!(rr, a) +@test rr[1,5] == a[1,5] +@test rr[] == a + +dims = (20,20,20) + +@linux_only begin + S = SharedArray(Int64, dims) + @test startswith(S.segname, "/jl") + @test !ispath("/dev/shm" * S.segname) + + S = SharedArray(Int64, dims; pids=[id_other]) + @test startswith(S.segname, "/jl") + @test !ispath("/dev/shm" * S.segname) +end + +# TODO : Need a similar test of shmem cleanup for OSX + +##### SharedArray tests + +function check_pids_all(S::SharedArray) + pidtested = falses(size(S)) + for p in procs(S) + idxes_in_p = remotecall_fetch(p, D -> parentindexes(D.loc_subarr_1d)[1], S) + @test all(sdata(S)[idxes_in_p] .== p) + pidtested[idxes_in_p] = true + end + @test all(pidtested) +end + +d = Base.shmem_rand(1:100, dims) +a = convert(Array, d) + +partsums = Array(Int, length(procs(d))) +@sync begin + for (i, p) in enumerate(procs(d)) + @async partsums[i] = remotecall_fetch(p, D->sum(D.loc_subarr_1d), d) + end +end +@test sum(a) == sum(partsums) + +d = Base.shmem_rand(dims) +for p in procs(d) + idxes_in_p = remotecall_fetch(p, D -> parentindexes(D.loc_subarr_1d)[1], d) + idxf = first(idxes_in_p) + idxl = last(idxes_in_p) + d[idxf] = Float64(idxf) + rv = remotecall_fetch(p, (D,idxf,idxl) -> begin assert(D[idxf] == Float64(idxf)); D[idxl] = Float64(idxl); D[idxl]; end, d,idxf,idxl) + @test d[idxl] == rv +end + +@test ones(10, 10, 10) == Base.shmem_fill(1.0, (10,10,10)) +@test zeros(Int32, 10, 10, 10) == Base.shmem_fill(0, (10,10,10)) + +d = Base.shmem_rand(dims) +s = Base.shmem_rand(dims) +copy!(s, d) +@test s == d +s = Base.shmem_rand(dims) +copy!(s, sdata(d)) +@test s == d +a = rand(dims) +@test sdata(a) == a + +d = SharedArray(Int, dims; init = D->fill!(D.loc_subarr_1d, myid())) +for p in procs(d) + idxes_in_p = remotecall_fetch(p, D -> parentindexes(D.loc_subarr_1d)[1], d) + idxf = first(idxes_in_p) + idxl = last(idxes_in_p) + @test d[idxf] == p + @test d[idxl] == p +end + +d = SharedArray(Float64, (2,3)) +@test isa(d[:,2], Vector{Float64}) + +### SharedArrays from a file + +# Mapping an existing file +fn = tempname() +open(fn, "w") do io + write(io, 1:30) +end +sz = (6,5) +Atrue = reshape(1:30, sz) + +S = SharedArray(fn, Int, sz) +@test S == Atrue +@test length(procs(S)) > 1 +@sync begin + for p in procs(S) + @async remotecall_wait(p, D->fill!(D.loc_subarr_1d, myid()), S) + end +end +check_pids_all(S) + +filedata = similar(Atrue) +open(fn, "r") do io + read!(io, filedata) +end +@test filedata == sdata(S) + +# Error for write-only files +@test_throws ArgumentError SharedArray(fn, Int, sz, mode="w") + +# Error for file doesn't exist, but not allowed to create +@test_throws ArgumentError SharedArray(tempname(), Int, sz, mode="r") + +# Creating a new file +fn2 = tempname() +S = SharedArray(fn2, Int, sz, init=D->D[localindexes(D)] = myid()) +@test S == filedata +filedata2 = similar(Atrue) +open(fn2, "r") do io + read!(io, filedata2) +end +@test filedata == filedata2 + +# Appending to a file +fn3 = tempname() +open(fn3, "w") do io + write(io, ones(UInt8, 4)) +end +S = SharedArray(fn3, UInt8, sz, 4, mode="a+", init=D->D[localindexes(D)]=0x02) +len = prod(sz)+4 +@test filesize(fn3) == len +filedata = Array(UInt8, len) +open(fn3, "r") do io + read!(io, filedata) +end +@test all(filedata[1:4] .== 0x01) +@test all(filedata[5:end] .== 0x02) + +@unix_only begin # these give unlink: operation not permitted (EPERM) on Windows + rm(fn); rm(fn2); rm(fn3) +end + +### Utility functions + +# reshape + +d = Base.shmem_fill(1.0, (10,10,10)) +@test ones(100, 10) == reshape(d,(100,10)) +d = Base.shmem_fill(1.0, (10,10,10)) +@test_throws DimensionMismatch reshape(d,(50,)) + +# rand, randn +d = Base.shmem_rand(dims) +@test size(rand!(d)) == dims +d = Base.shmem_fill(1.0, dims) +@test size(randn!(d)) == dims + +# similar +d = Base.shmem_rand(dims) +@test size(similar(d, Complex128)) == dims +@test size(similar(d, dims)) == dims + +# issue #6362 +d = Base.shmem_rand(dims) +s = copy(sdata(d)) +ds = deepcopy(d) +@test ds == d +pids_d = procs(d) +remotecall_fetch(pids_d[findfirst(id->(id != myid()), pids_d)], setindex!, d, 1.0, 1:10) +@test ds != d +@test s != d + + +# SharedArray as an array +# Since the data in d will depend on the nprocs, just test that these operations work +a = d[1:5] +@test_throws BoundsError d[-1:5] +a = d[1,1,1:3:end] +d[2:4] = 7 +d[5,1:2:4,8] = 19 + +AA = rand(4,2) +A = convert(SharedArray, AA) +B = convert(SharedArray, AA') +@test B*A == ctranspose(AA)*AA + +d=SharedArray(Int64, (10,10); init = D->fill!(D.loc_subarr_1d, myid()), pids=[id_me, id_other]) +d2 = map(x->1, d) +@test reduce(+, d2) == 100 + +@test reduce(+, d) == ((50*id_me) + (50*id_other)) +map!(x->1, d) +@test reduce(+, d) == 100 + +@test fill!(d, 1) == ones(10, 10) +@test fill!(d, 2.) == fill(2, 10, 10) +@test d[:] == fill(2, 100) +@test d[:,1] == fill(2, 10) +@test d[1,:] == fill(2, 1, 10) + +# Boundary cases where length(S) <= length(pids) +@test 2.0 == remotecall_fetch(id_other, D->D[2], Base.shmem_fill(2.0, 2; pids=[id_me, id_other])) +@test 3.0 == remotecall_fetch(id_other, D->D[1], Base.shmem_fill(3.0, 1; pids=[id_me, id_other])) + +# Test @parallel load balancing - all processors should get either M or M+1 +# iterations out of the loop range for some M. +workloads = hist(@parallel((a,b)->[a;b], for i=1:7; myid(); end), nprocs())[2] +@test maximum(workloads) - minimum(workloads) <= 1 + +# @parallel reduction should work even with very short ranges +@test @parallel(+, for i=1:2; i; end) == 3 + +# Testing timedwait on multiple channels +@sync begin + rr1 = Channel() + rr2 = Channel() + rr3 = Channel() + + @async begin sleep(0.5); put!(rr1, :ok) end + @async begin sleep(1.0); put!(rr2, :ok) end + @async begin sleep(2.0); put!(rr3, :ok) end + + tic() + timedwait(1.0) do + all(map(isready, [rr1, rr2, rr3])) + end + et=toq() + # assuming that 0.5 seconds is a good enough buffer on a typical modern CPU + try + @test (et >= 1.0) && (et <= 1.5) + @test !isready(rr3) + catch + warn("timedwait tests delayed. et=$et, isready(rr3)=$(isready(rr3))") + end + @test isready(rr1) +end + +# Test multiple concurrent put!/take! on a channel +function testcpt() + c = Channel() + size = 0 + inc() = size += 1 + dec() = size -= 1 + @sync for i = 1:10^4 + @async (sleep(rand()); put!(c, i); inc()) + @async (sleep(rand()); take!(c); dec()) + end + @test size == 0 +end +testcpt() + +@test_throws ArgumentError sleep(-1) +@test_throws ArgumentError timedwait(()->false, 0.1, pollint=-0.5) + +# specify pids for pmap +@test sort(workers()[1:2]) == sort(unique(pmap(x->(sleep(0.1);myid()), 1:10, pids = workers()[1:2]))) + +# Testing buffered and unbuffered reads +# This large array should write directly to the socket +a = ones(10^6) +@test a == remotecall_fetch(id_other, (x)->x, a) + +# Not a bitstype, should be buffered +s = [randstring() for x in 1:10^5] +@test s == remotecall_fetch(id_other, (x)->x, s) + +#large number of small requests +num_small_requests = 10000 +@test fill(id_other, num_small_requests) == [remotecall_fetch(id_other, myid) for i in 1:num_small_requests] + +# test parallel sends of large arrays from multiple tasks to the same remote worker +ntasks = 10 +rr_list = [Channel() for x in 1:ntasks] +a=ones(2*10^5); +for rr in rr_list + @async let rr=rr + try + for i in 1:10 + @test a == remotecall_fetch(id_other, (x)->x, a) + yield() + end + put!(rr, :OK) + catch + put!(rr, :ERROR) + end + end +end + +@test [fetch(rr) for rr in rr_list] == [:OK for x in 1:ntasks] + +function test_channel(c) + put!(c, 1) + put!(c, "Hello") + put!(c, 5.0) + + @test isready(c) == true + @test fetch(c) == 1 + @test fetch(c) == 1 # Should not have been popped previously + @test take!(c) == 1 + @test take!(c) == "Hello" + @test fetch(c) == 5.0 + @test take!(c) == 5.0 + @test isready(c) == false + close(c) +end + +test_channel(Channel(10)) +test_channel(RemoteRef(()->Channel(10))) + +c=Channel{Int}(1) +@test_throws MethodError put!(c, "Hello") + +# test channel iterations +function test_iteration(in_c, out_c) + t=@schedule for v in in_c + put!(out_c, v) + end + + isa(in_c, Channel) && @test isopen(in_c) == true + put!(in_c, 1) + @test take!(out_c) == 1 + put!(in_c, "Hello") + close(in_c) + @test take!(out_c) == "Hello" + isa(in_c, Channel) && @test isopen(in_c) == false + @test_throws InvalidStateException put!(in_c, :foo) + yield() + @test istaskdone(t) == true +end + +test_iteration(Channel(10), Channel(10)) +# make sure exceptions propagate when waiting on Tasks +@test_throws CompositeException (@sync (@async error("oops"))) +try + @sync begin + for i in 1:5 + @async error(i) + end + end + error("unexpected") +catch ex + @test typeof(ex) == CompositeException + @test length(ex) == 5 + @test typeof(ex.exceptions[1]) == CapturedException + @test typeof(ex.exceptions[1].ex) == ErrorException + errors = map(x->x.ex.msg, ex.exceptions) + @test collect(1:5) == sort(map(x->parse(Int, x), errors)) +end + +macro test_remoteexception_thrown(expr) + quote + try + $(esc(expr)) + error("unexpected") + catch ex + @test typeof(ex) == RemoteException + @test typeof(ex.captured) == CapturedException + @test typeof(ex.captured.ex) == ErrorException + @test ex.captured.ex.msg == "foobar" + end + end +end + +for id in [id_other, id_me] + @test_remoteexception_thrown remotecall_fetch(id, ()->throw(ErrorException("foobar"))) + @test_remoteexception_thrown remotecall_wait(id, ()->throw(ErrorException("foobar"))) + @test_remoteexception_thrown wait(remotecall(id, ()->throw(ErrorException("foobar")))) +end + +# The below block of tests are usually run only on local development systems, since: +# - tests which print errors +# - addprocs tests are memory intensive +# - ssh addprocs requires sshd to be running locally with passwordless login enabled. +# The test block is enabled by defining env JULIA_TESTFULL=1 + +DoFullTest = Bool(parse(Int,(get(ENV, "JULIA_TESTFULL", "0")))) + +if DoFullTest + # pmap tests + # needs at least 4 processors dedicated to the below tests + ppids = remotecall_fetch(1, ()->addprocs(4)) + s = "abcdefghijklmnopqrstuvwxyz"; + ups = uppercase(s); + @test ups == bytestring(UInt8[UInt8(c) for c in pmap(x->uppercase(x), s)]) + @test ups == bytestring(UInt8[UInt8(c) for c in pmap(x->uppercase(Char(x)), s.data)]) + + # retry, on error exit + res = pmap(x->(x=='a') ? error("EXPECTED TEST ERROR. TO BE IGNORED.") : (sleep(0.1);uppercase(x)), s; err_retry=true, err_stop=true, pids=ppids); + @test (length(res) < length(ups)) + @test isa(res[1], Exception) + + # no retry, on error exit + res = pmap(x->(x=='a') ? error("EXPECTED TEST ERROR. TO BE IGNORED.") : (sleep(0.1);uppercase(x)), s; err_retry=false, err_stop=true, pids=ppids); + @test (length(res) < length(ups)) + @test isa(res[1], Exception) + + # retry, on error continue + res = pmap(x->iseven(myid()) ? error("EXPECTED TEST ERROR. TO BE IGNORED.") : (sleep(0.1);uppercase(x)), s; err_retry=true, err_stop=false, pids=ppids); + @test length(res) == length(ups) + @test ups == bytestring(UInt8[UInt8(c) for c in res]) + + # no retry, on error continue + res = pmap(x->(x=='a') ? error("EXPECTED TEST ERROR. TO BE IGNORED.") : (sleep(0.1);uppercase(x)), s; err_retry=false, err_stop=false, pids=ppids); + @test length(res) == length(ups) + @test isa(res[1], Exception) + + # Topology tests need to run externally since a given cluster at any + # time can only support a single topology and the current session + # is already running in parallel under the default topology. + script = joinpath(dirname(@__FILE__), "topology.jl") + cmd = `$(joinpath(JULIA_HOME,Base.julia_exename())) $script` + + (strm, proc) = open(cmd) + wait(proc) + if !success(proc) && ccall(:jl_running_on_valgrind,Cint,()) == 0 + println(readall(strm)) + error("Topology tests failed : $cmd") + end + + println("Testing exception printing on remote worker from a `remote_do` call") + println("Please ensure the remote error and backtrace is displayed on screen") + + Base.remote_do(id_other, ()->throw(ErrorException("TESTING EXCEPTION ON REMOTE DO. PLEASE IGNORE"))) + sleep(0.5) # Give some time for the above error to be printed + +@unix_only begin + function test_n_remove_pids(new_pids) + for p in new_pids + w_in_remote = sort(remotecall_fetch(p, workers)) + try + @test intersect(new_pids, w_in_remote) == new_pids + catch e + print("p : $p\n") + print("newpids : $new_pids\n") + print("w_in_remote : $w_in_remote\n") + print("intersect : $(intersect(new_pids, w_in_remote))\n\n\n") + rethrow(e) + end + end + + @test :ok == remotecall_fetch(1, (p)->rmprocs(p; waitfor=5.0), new_pids) + end + + print("\n\nTesting SSHManager. A minimum of 4GB of RAM is recommended.\n") + print("Please ensure sshd is running locally with passwordless login enabled.\n") + + sshflags = `-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o LogLevel=ERROR ` + #Issue #9951 + hosts=[] + localhost_aliases = ["localhost", string(getipaddr()), "127.0.0.1"] + num_workers = parse(Int,(get(ENV, "JULIA_ADDPROCS_NUM", "9"))) + + for i in 1:(num_workers/length(localhost_aliases)) + append!(hosts, localhost_aliases) + end + + print("\nTesting SSH addprocs with $(length(hosts)) workers...\n") + new_pids = remotecall_fetch(1, (h, sf) -> addprocs(h; sshflags=sf), hosts, sshflags) + @test length(new_pids) == length(hosts) + test_n_remove_pids(new_pids) + + print("\nMixed ssh addprocs with :auto\n") + new_pids = sort(remotecall_fetch(1, (h, sf) -> addprocs(h; sshflags=sf), ["localhost", ("127.0.0.1", :auto), "localhost"], sshflags)) + @test length(new_pids) == (2 + Sys.CPU_CORES) + test_n_remove_pids(new_pids) + + print("\nMixed ssh addprocs with numeric counts\n") + new_pids = sort(remotecall_fetch(1, (h, sf) -> addprocs(h; sshflags=sf), [("localhost", 2), ("127.0.0.1", 2), "localhost"], sshflags)) + @test length(new_pids) == 5 + test_n_remove_pids(new_pids) + + print("\nssh addprocs with tunnel\n") + new_pids = sort(remotecall_fetch(1, (h, sf) -> addprocs(h; tunnel=true, sshflags=sf), [("localhost", num_workers)], sshflags)) + @test length(new_pids) == num_workers + test_n_remove_pids(new_pids) + +end +end + +# issue #7727 +let A = [], B = [] + t = @task produce(11) + @sync begin + @async for x in t; push!(A,x); end + @async for x in t; push!(B,x); end + end + @test (A == [11]) != (B == [11]) +end + +let t = @task 42 + schedule(t, ErrorException(""), error=true) + @test_throws ErrorException wait(t) +end + +# issue #8207 +let A = Any[] + @parallel (+) for i in (push!(A,1); 1:2) + i + end + @test length(A) == 1 +end + +# issue #13168 +function f13168(n) + val = 0 + for i=1:n val+=sum(rand(n,n)^2) end + val +end +let t = schedule(@task f13168(100)) + @test schedule(t) === t +end + +# issue #13122 +@test remotecall_fetch(workers()[1], identity, C_NULL) === C_NULL diff --git a/test/parse.jl b/test/parse.jl index 333bdc4dc2e15..1de89d9c5b071 100644 --- a/test/parse.jl +++ b/test/parse.jl @@ -304,3 +304,7 @@ let p = parse("try @test p.args[2] === false @test p.args[3].args[end] == parse("b,c = t") end + +# issue #11988 -- normalize \r and \r\n in literal strings to \n +@test "foo\nbar" == parse("\"\"\"\r\nfoo\r\nbar\"\"\"") == parse("\"\"\"\nfoo\nbar\"\"\"") == parse("\"\"\"\rfoo\rbar\"\"\"") == parse("\"foo\r\nbar\"") == parse("\"foo\rbar\"") == parse("\"foo\nbar\"") +@test '\r' == first("\r") == first("\r\n") # still allow explicit \r diff --git a/test/pkg.jl b/test/pkg.jl index 1f97090568920..7473c4923342e 100644 --- a/test/pkg.jl +++ b/test/pkg.jl @@ -16,11 +16,14 @@ function temp_pkg_dir(fn::Function) end # Test basic operations: adding or removing a package, status, free -#Also test for the existence of REQUIRE and META_Branch +# Also test for the existence of REQUIRE and META_Branch temp_pkg_dir() do @test isfile(joinpath(Pkg.dir(),"REQUIRE")) @test isfile(joinpath(Pkg.dir(),"META_BRANCH")) @test isempty(Pkg.installed()) + @test sprint(io -> Pkg.status(io)) == "No packages installed\n" + @test !isempty(Pkg.available()) + Pkg.add("Example") @test [keys(Pkg.installed())...] == ["Example"] iob = IOBuffer() @@ -57,6 +60,7 @@ temp_pkg_dir() do Pkg.status("Example", iob) str = chomp(takebuf_string(iob)) @test endswith(str, string(Pkg.installed("Example"))) + @test isempty(Pkg.dependents("Example")) # adding a package with unsatisfiable julia version requirements (REPL.jl) errors try diff --git a/test/pollfd.jl b/test/pollfd.jl index 2044663f7df2b..1c1a71b88232d 100644 --- a/test/pollfd.jl +++ b/test/pollfd.jl @@ -33,8 +33,8 @@ function pfd_tst_reads(idx, intvl) @test @windows ? evt.writable : !evt.writable # println("Expected ", intvl, ", actual ", t_elapsed, ", diff ", t_elapsed - intvl) - # Assuming that a 2 second buffer is good enough on a modern system - @test t_elapsed <= (intvl + 1) + # Disabled since this assertion fails randomly, notably on build VMs (issue #12824) + # @test t_elapsed <= (intvl + 1) dout = Array(UInt8, 1) @windows ? ( @@ -56,8 +56,9 @@ function pfd_tst_timeout(idx, intvl) @test !evt.writable t_elapsed = toq() - @unix_only @test (intvl <= t_elapsed) # TODO: enable this test on windows when the libuv version is bumped - @test (t_elapsed <= (intvl + 1)) + # Disabled since these assertions fail randomly, notably on build VMs (issue #12824) + # @unix_only @test (intvl <= t_elapsed) # TODO: enable this test on windows when the libuv version is bumped + # @test (t_elapsed <= (intvl + 1)) end diff --git a/test/random.jl b/test/random.jl index dc78c9368239d..5ed896248319f 100644 --- a/test/random.jl +++ b/test/random.jl @@ -286,6 +286,11 @@ let mt = MersenneTwister() end end +# make sure reading 128-bit ints from RandomDevice works +let a = [rand(RandomDevice(), UInt128) for i=1:10] + @test reduce(|, a)>>>64 != 0 +end + # test all rand APIs for rng in ([], [MersenneTwister()], [RandomDevice()]) for f in [rand, randn, randexp] diff --git a/test/ranges.jl b/test/ranges.jl index a4a53ed49cc02..e0cdd346ffdb3 100644 --- a/test/ranges.jl +++ b/test/ranges.jl @@ -108,6 +108,12 @@ end @test intersect(reverse(typemin(Int):2:typemax(Int)),typemin(Int):2:typemax(Int)) == reverse(typemin(Int):2:typemax(Int)) @test intersect(typemin(Int):2:typemax(Int),reverse(typemin(Int):2:typemax(Int))) == typemin(Int):2:typemax(Int) +@test intersect(UnitRange(1,2),3) == UnitRange(3,2) +@test intersect(UnitRange(1,2), UnitRange(1,5), UnitRange(3,7), UnitRange(4,6)) == UnitRange(4,3) + +@test sort(UnitRange(1,2)) == UnitRange(1,2) +@test sort!(UnitRange(1,2)) == UnitRange(1,2) + @test 0 in UInt(0):100:typemax(UInt) @test last(UInt(0):100:typemax(UInt)) in UInt(0):100:typemax(UInt) @test -9223372036854775790 in -9223372036854775790:100:9223372036854775710 @@ -237,6 +243,7 @@ end @test (1:2:6) + 0.3 == 1+0.3:2:5+0.3 @test (1:2:6) - 1 == 0:2:4 @test (1:2:6) - 0.3 == 1-0.3:2:5-0.3 +@test 2 .- (1:3) == 1:-1:-1 # operations between ranges and arrays @test all(([1:5;] + (5:-1:1)) .== 6) @@ -517,6 +524,8 @@ end @test convert(StepRange, 0:5) === 0:1:5 @test convert(StepRange{Int128,Int128}, 0.:5) === Int128(0):Int128(1):Int128(5) +@test_throws ArgumentError StepRange(1.1,1,5.1) + @test promote(0f0:inv(3f0):1f0, 0.:2.:5.) === (0:1/3:1, 0.:2.:5.) @test convert(FloatRange{Float64}, 0:1/3:1) === 0:1/3:1 @test convert(FloatRange{Float64}, 0f0:inv(3f0):1f0) === 0:1/3:1 @@ -544,6 +553,9 @@ for x in r end @test i == 7 +@test sprint(io -> show(io,UnitRange(1,2))) == "1:2" +@test sprint(io -> show(io,StepRange(1,2,5))) == "1:2:5" + # Issue 11049 and related @test promote(linspace(0f0, 1f0, 3), linspace(0., 5., 2)) === (linspace(0., 1., 3), linspace(0., 5., 2)) diff --git a/test/replcompletions.jl b/test/replcompletions.jl index 7e9f55e7ae305..3c3a6fc5d8390 100644 --- a/test/replcompletions.jl +++ b/test/replcompletions.jl @@ -33,7 +33,15 @@ module CompletionFoo test3(x::AbstractArray{Int}, y::Int) = pass test3(x::AbstractArray{Float64}, y::Float64) = pass + test4(x::AbstractString, y::AbstractString) = pass + test4(x::AbstractString, y::Regex) = pass + + test5(x::Array{Bool,1}) = pass + test5(x::BitArray{1}) = pass + test5(x::Float64) = pass + array = [1, 1] + varfloat = 0.1 end function temp_pkg_dir(fn::Function) @@ -186,7 +194,7 @@ c, r, res = test_complete(s) @test r == 1:3 @test s[r] == "max" -# Test completion of methods with input args +# Test completion of methods with input concrete args and args where typeinference determine their type s = "CompletionFoo.test(1,1, " c, r, res = test_complete(s) @test !res @@ -239,24 +247,54 @@ for (T, arg) in [(ASCIIString,"\")\""),(Char, "')'")] @test s[r] == "CompletionFoo.test2" end -# This cannot find the correct method due to the backticks expands to a macro in the parser. -# Then the function argument is an expression which is not handled by current method completion logic. s = "(1, CompletionFoo.test2(`)`," c, r, res = test_complete(s) -@test length(c) == 3 +@test c[1] == string(methods(CompletionFoo.test2, Tuple{Cmd})[1]) +@test length(c) == 1 -s = "CompletionFoo.test3([1.,2.]," +s = "CompletionFoo.test3([1, 2] + CompletionFoo.varfloat," c, r, res = test_complete(s) @test !res -@test length(c) == 2 +@test c[1] == string(methods(CompletionFoo.test3, Tuple{Array{Float64, 1}, Float64})[1]) +@test length(c) == 1 s = "CompletionFoo.test3([1.,2.], 1.," c, r, res = test_complete(s) @test !res @test c[1] == string(methods(CompletionFoo.test3, Tuple{Array{Float64, 1}, Float64})[1]) @test r == 1:19 +@test length(c) == 1 @test s[r] == "CompletionFoo.test3" +s = "CompletionFoo.test4(\"e\",r\" \"," +c, r, res = test_complete(s) +@test !res +@test c[1] == string(methods(CompletionFoo.test4, Tuple{ASCIIString, Regex})[1]) +@test r == 1:19 +@test length(c) == 1 +@test s[r] == "CompletionFoo.test4" + +s = "CompletionFoo.test5(push!(Base.split(\"\",' '),\"\",\"\").==\"\"," +c, r, res = test_complete(s) +@test !res +@test length(c) == 1 +@test c[1] == string(methods(CompletionFoo.test5, Tuple{BitArray{1}})[1]) + +########## Test where the current inference logic fails ######## +# Fails due to inferrence fails to determine a concrete type from the map +# But it returns AbstractArray{T,N} and hence is able to remove test5(x::Float64) from the suggestions +s = "CompletionFoo.test5(map(x-> x==\"\",push!(Base.split(\"\",' '),\"\",\"\"))," +c, r, res = test_complete(s) +@test !res +@test length(c) == 2 + +# equivalent to above but due to the time macro the completion fails to find the concrete type +s = "CompletionFoo.test3(@time([1, 2] + CompletionFoo.varfloat)," +c, r, res = test_complete(s) +@test !res +@test length(c) == 2 +################################################################# + # Test completion in multi-line comments s = "#=\n\\alpha" c, r, res = test_complete(s) @@ -393,6 +431,28 @@ c, r, res = test_scomplete(s) @test r == 6:7 @test s[r] == "Pk" + # Pressing tab after having entered "/tmp " should not + # attempt to complete "/tmp" but rather work on the current + # working directory again. + + let + file = joinpath(path, "repl completions") + s = "/tmp " + c,r = test_scomplete(s) + @test r == 6:5 + end + + # Test completing paths with an escaped trailing space + let + file = joinpath(tempdir(), "repl completions") + touch(file) + s = string(tempdir(), "/repl\\ ") + c,r = test_scomplete(s) + @test ["repl\\ completions"] == c + @test s[r] == "repl\\ " + rm(file) + end + # Tests homedir expansion let path = homedir() @@ -408,6 +468,32 @@ c, r, res = test_scomplete(s) c,r = test_complete(s) rm(dir) end + + # Tests detecting of files in the env path (in shell mode) + let + oldpath = ENV["PATH"] + path = tempdir() + # PATH can also contain folders which we aren't actually allowed to read. + unreadable = joinpath(tempdir(), "replcompletion-unreadable") + ENV["PATH"] = string(path, ":", unreadable) + + file = joinpath(path, "tmp-executable") + touch(file) + chmod(file, 0o755) + mkdir(unreadable) + chmod(unreadable, 0o000) + + s = "tmp-execu" + c,r = test_scomplete(s) + @test "tmp-executable" in c + @test r == 1:9 + @test s[r] == "tmp-execu" + + rm(file) + rm(unreadable) + ENV["PATH"] = oldpath + end + end let #test that it can auto complete with spaces in file/path @@ -469,3 +555,11 @@ c,r = test_complete("cd(\"folder_do_not_exist_77/file") end rm(tmp) end + +# auto completions of true and false... issue #14101 +s = "tru" +c, r, res = test_complete(s) +@test "true" in c +s = "fals" +c, r, res = test_complete(s) +@test "false" in c diff --git a/test/runtests.jl b/test/runtests.jl index 349faf18c64f9..b4aae903a9a6a 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -20,7 +20,49 @@ cd(dirname(@__FILE__)) do @everywhere include("testdefs.jl") - reduce(propagate_errors, nothing, pmap(runtests, tests; err_retry=false, err_stop=true)) + results=[] + if haskey(ENV, "JULIA_TEST_MAXRSS_MB") + max_worker_rss = parse(Int, ENV["JULIA_TEST_MAXRSS_MB"]) * 2^20 + else + max_worker_rss = typemax(Csize_t) + end + @sync begin + for p in workers() + @async begin + while length(tests) > 0 + test = shift!(tests) + local resp + try + resp = remotecall_fetch(p, t -> runtests(t), test) + catch e + resp = e + end + push!(results, (test, resp)) + + if (isa(resp, Integer) && (resp > max_worker_rss)) || isa(resp, Exception) + if n > 1 + rmprocs(p, waitfor=0.5) + p = addprocs(1; exeflags=`--check-bounds=yes --depwarn=error`)[1] + remotecall_fetch(p, ()->include("testdefs.jl")) + else + # single process testing, bail if mem limit reached, or, on an exception. + isa(resp, Exception) ? rethrow(resp) : error("Halting tests. Memory limit reached : $resp > $max_worker_rss") + end + end + end + end + end + end + + errors = filter(x->isa(x[2], Exception), results) + if length(errors) > 0 + for err in errors + println("Exception running test $(err[1]) :") + showerror(STDERR, err[2]) + println() + end + error("Some tests exited with errors.") + end if compile_test n > 1 && print("\tFrom worker 1:\t") diff --git a/test/show.jl b/test/show.jl index 0fb185acbc365..80f473abdd6d1 100644 --- a/test/show.jl +++ b/test/show.jl @@ -302,3 +302,23 @@ function f13127() takebuf_string(buf) end @test f13127() == "f" + +let a = Pair(1.0,2.0) + @test sprint(show,a) == "1.0=>2.0" +end +let a = Pair(Pair(1,2),Pair(3,4)) + @test sprint(show,a) == "(1=>2)=>(3=>4)" +end + +#test methodshow.jl functions +@test Base.inbase(Base) +@test Base.inbase(LinAlg) + +@test contains(sprint(io -> writemime(io,"text/plain",methods(Base.inbase))),"inbase(m::Module)") +@test contains(sprint(io -> writemime(io,"text/html",methods(Base.inbase))),"inbase(m::Module)") + +if isempty(Base.GIT_VERSION_INFO.commit) + @test contains(Base.url(methods(eigs).defs),"https://github.com/JuliaLang/julia/tree/v$VERSION/base/linalg/arnoldi.jl#L") +else + @test contains(Base.url(methods(eigs).defs),"https://github.com/JuliaLang/julia/tree/$(Base.GIT_VERSION_INFO.commit)/base/linalg/arnoldi.jl#L") +end diff --git a/test/socket.jl b/test/socket.jl index 1810dfac1f1d7..a07022aea43fd 100644 --- a/test/socket.jl +++ b/test/socket.jl @@ -86,22 +86,22 @@ wait(port) @test readall(connect(fetch(port))) == "Hello World\n" * ("a1\n"^100) wait(tsk) -socketname = (@windows ? "\\\\.\\pipe\\uv-test" : "testsocket") * "-" * randstring(6) -@unix_only isfile(socketname) && Base.FS.unlink(socketname) - -c=Base.Condition() -for T in (ASCIIString, UTF8String, UTF16String) # test for issue #9435 - tsk = @async begin - s = listen(T(socketname)) - Base.notify(c) - sock = accept(s) - write(sock,"Hello World\n") - close(s) - close(sock) +mktempdir() do tmpdir + socketname = @windows ? ("\\\\.\\pipe\\uv-test-" * randstring(6)) : joinpath(tmpdir, "socket") + c = Base.Condition() + for T in (ASCIIString, UTF8String, UTF16String) # test for issue #9435 + tsk = @async begin + s = listen(T(socketname)) + Base.notify(c) + sock = accept(s) + write(sock,"Hello World\n") + close(s) + close(sock) + end + wait(c) + @test readall(connect(socketname)) == "Hello World\n" + wait(tsk) end - wait(c) - @test readall(connect(socketname)) == "Hello World\n" - wait(tsk) end @test_throws Base.UVError getaddrinfo(".invalid") diff --git a/test/sparsedir/cholmod.jl b/test/sparsedir/cholmod.jl index 8026c0039bf9e..a5d53f8e3c19f 100644 --- a/test/sparsedir/cholmod.jl +++ b/test/sparsedir/cholmod.jl @@ -597,3 +597,7 @@ Base.writemime(IOBuffer(), MIME"text/plain"(), cholfact(sparse(Float64[ 10 1 1 1 # Element promotion and type inference @inferred cholfact(As)\ones(Int, size(As, 1)) @inferred ldltfact(As)\ones(Int, size(As, 1)) + +# Issue 14076 +@test cholfact(sparse([1,2,3,4], [1,2,3,4], Float32[1,4,16,64]))\[1,4,16,64] == ones(4) + diff --git a/test/sparsedir/sparse.jl b/test/sparsedir/sparse.jl index 4cbba2adfc14c..8483fefcbdb51 100644 --- a/test/sparsedir/sparse.jl +++ b/test/sparsedir/sparse.jl @@ -2,6 +2,10 @@ using Base.Test +@test issparse(sparse(ones(5,5))) +@test !issparse(ones(5,5)) +@test Base.SparseMatrix.indtype(sparse(ones(Int8,2),ones(Int8,2),rand(2))) == Int8 + # check sparse matrix construction @test isequal(full(sparse(complex(ones(5,5),ones(5,5)))), complex(ones(5,5),ones(5,5))) diff --git a/test/spawn.jl b/test/spawn.jl index 5707e28a917f1..e4d87d73b8ffe 100644 --- a/test/spawn.jl +++ b/test/spawn.jl @@ -11,6 +11,8 @@ #TODO: # - Windows: # - Add a test whether coreutils are available and skip tests if not +@windows_only oldpath = ENV["PATH"] +@windows_only ENV["PATH"] = joinpath(JULIA_HOME,"..","Git","usr","bin")*";"*oldpath valgrind_off = ccall(:jl_running_on_valgrind,Cint,()) == 0 @@ -23,8 +25,8 @@ yes = `perl -le 'while (1) {print STDOUT "y"}'` @test length(spawn(pipeline(`echo hello`, `sort`)).processes) == 2 out = readall(`echo hello` & `echo world`) -@test search(out,"world") != (0,0) -@test search(out,"hello") != (0,0) +@test search(out,"world") != 0:-1 +@test search(out,"hello") != 0:-1 @test readall(pipeline(`echo hello` & `echo world`, `sort`)) == "hello\nworld\n" @test (run(`printf " \033[34m[stdio passthrough ok]\033[0m\n"`); true) @@ -329,3 +331,5 @@ let cmd = AbstractString[] cmd = ["foo bar", "baz"] @test string(`$cmd`) == "`'foo bar' baz`" end + +@windows_only ENV["PATH"] = oldpath diff --git a/test/strings/io.jl b/test/strings/io.jl index dc315fb3bb62e..405444afbdf78 100644 --- a/test/strings/io.jl +++ b/test/strings/io.jl @@ -134,6 +134,8 @@ end @test "\x0f" == unescape_string("\\x0f") @test "\x0F" == unescape_string("\\x0F") +extrapath = @windows? joinpath(JULIA_HOME,"..","Git","usr","bin")*";" : "" +withenv("PATH" => extrapath * ENV["PATH"]) do if !success(`iconv --version`) warn("iconv not found, skipping unicode tests!") @windows_only warn("Use WinRPM.install(\"win_iconv\") to run these tests") @@ -201,6 +203,7 @@ else end rm(unicodedir) end +end # Tests of join() @test join([]) == "" diff --git a/test/testdefs.jl b/test/testdefs.jl index 944b86d16c781..0c9a7cb4f002b 100644 --- a/test/testdefs.jl +++ b/test/testdefs.jl @@ -5,18 +5,9 @@ using Base.Test function runtests(name) @printf(" \033[1m*\033[0m \033[31m%-21s\033[0m", name) tt = @elapsed include("$name.jl") - @printf(" in %6.2f seconds\n", tt) - nothing -end - -function propagate_errors(a,b) - if isa(a,Exception) - rethrow(a) - end - if isa(b,Exception) - rethrow(b) - end - nothing + rss = Sys.maxrss() + @printf(" in %6.2f seconds, maxrss %7.2f MB\n", tt, rss / 2^20) + rss end # looking in . messes things up badly