From abe03ab04d1a3920f75ac68878951a5943379840 Mon Sep 17 00:00:00 2001 From: Nathan Daly Date: Sat, 26 Jan 2019 19:01:39 -0500 Subject: [PATCH 1/6] Add Channel{T}(f::Function) constructors. The Channel constructors that do and don't create a Task are currently written in different styles: - The non-Task constructor takes a Type {T} as a type parameter, and the size as a positional argument. - `Channel{Int}(Inf)` - `Channel(0)` - The Task constructor takes a Function, but the size and type are keyword arguments: - `Channel(c->put!(c,0), ctype=Int, csize=0)` - `Channel(ctype=Int, csize=0, taskref=t) do c; put!(c,0); end` This commit adds convenience methods to the Task-creating constructor, allowing you to specify the Type and/or size as in the non-Task constructor: - `Channel{Char}(1) do c; put!(c, 'a'); end` - `Channel{Char}(csize=2, taskref=t) do c; put!(c, 'a'); end` --- Also adds tests for the existing Task-creating constructors, which weren't explicitly tested before. --- base/channels.jl | 22 ++++++++++++++++++++++ test/channels.jl | 26 ++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/base/channels.jl b/base/channels.jl index 1cb6b5386f960..c29c2e29e017d 100644 --- a/base/channels.jl +++ b/base/channels.jl @@ -95,6 +95,22 @@ Hello julia> istaskdone(taskref[]) true ``` + +Other constructors: +* `Channel{T}(func::Function, sz=0)` +* `Channel{T}(func::Function; csize=0, taskref=nothing)` + +```jldoctest +julia> chnl = Channel{Char}(1) do ch + for c in "hello world" + put!(ch, c) + end + end +>> Channel{Char}(sz_max:1,sz_curr:1) + +julia> String(collect(chnl)) +>> "hello world" +``` """ function Channel(func::Function; ctype=Any, csize=0, taskref=nothing) chnl = Channel{ctype}(csize) @@ -105,6 +121,12 @@ function Channel(func::Function; ctype=Any, csize=0, taskref=nothing) isa(taskref, Ref{Task}) && (taskref[] = task) return chnl end +function Channel{T}(f::Function, sz=0) where T + return Channel(f, csize=sz, ctype=T) +end +function Channel{T}(f::Function; csize=0, taskref=nothing) where T + return Channel(f, csize=csize, ctype=T, taskref=taskref) +end closed_exception() = InvalidStateException("Channel is closed.", :closed) diff --git a/test/channels.jl b/test/channels.jl index 2583e2f8e4e39..cdbc341d26663 100644 --- a/test/channels.jl +++ b/test/channels.jl @@ -36,6 +36,32 @@ end @test_throws InexactError Channel(1.5) end +@testset "Task constructors" begin + c = Channel(ctype=Float32, csize=2) do c; map(i->put!(c,i), 1:100); end + @test eltype(c) == Float32 + @test c.sz_max == 2 + @test isopen(c) + @test collect(c) == 1:100 + + c = Channel{Int}() do c; map(i->put!(c,i), 1:100); end + @test eltype(c) == Int + @test c.sz_max == 0 + @test collect(c) == 1:100 + + c = Channel{Int}(Inf) do c; put!(c,1); end + @test eltype(c) == Int + @test c.sz_max == typemax(Int) + + taskref = Ref{Task}() + c = Channel{Int}(csize=0, taskref=taskref) do c; put!(c, 0); end + @test eltype(c) == Int + @test c.sz_max == 0 + @test istaskstarted(taskref[]) + @test !istaskdone(taskref[]) + take!(c); yield() + @test istaskdone(taskref[]) +end + @testset "multiple concurrent put!/take! on a channel for different sizes" begin function testcpt(sz) c = Channel{Int}(sz) From eb37c0e7876aca134d19940a183d14bb54820690 Mon Sep 17 00:00:00 2001 From: Nathan Daly Date: Sat, 26 Jan 2019 19:39:12 -0500 Subject: [PATCH 2/6] Fix test: switch `yield()` to `wait(taskref[])` --- test/channels.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/channels.jl b/test/channels.jl index cdbc341d26663..52ea021393747 100644 --- a/test/channels.jl +++ b/test/channels.jl @@ -58,7 +58,7 @@ end @test c.sz_max == 0 @test istaskstarted(taskref[]) @test !istaskdone(taskref[]) - take!(c); yield() + take!(c); wait(taskref[]) @test istaskdone(taskref[]) end From e0481cb9448db2bec29c1de2157f0ace48a094df Mon Sep 17 00:00:00 2001 From: Nathan Daly Date: Mon, 1 Jul 2019 16:42:23 -0400 Subject: [PATCH 3/6] Add !!! compat "Julia 1.3" message --- base/channels.jl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/base/channels.jl b/base/channels.jl index c29c2e29e017d..4aac83b3893df 100644 --- a/base/channels.jl +++ b/base/channels.jl @@ -96,6 +96,9 @@ julia> istaskdone(taskref[]) true ``` +!!! compat "Julia 1.3" + The following constructors were added in Julia 1.3. + Other constructors: * `Channel{T}(func::Function, sz=0)` * `Channel{T}(func::Function; csize=0, taskref=nothing)` From d5cad7464b19636f856a6e386acd356bdd7cb8ec Mon Sep 17 00:00:00 2001 From: Nathan Daly Date: Mon, 1 Jul 2019 16:44:46 -0400 Subject: [PATCH 4/6] Add `Channel()` constr, defaults to unbufferred channel Add unit tests for default constructor --- base/channels.jl | 7 ++++++- test/channels.jl | 8 +++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/base/channels.jl b/base/channels.jl index 4aac83b3893df..0c6ce4bfd8e43 100644 --- a/base/channels.jl +++ b/base/channels.jl @@ -8,7 +8,7 @@ Representation of a channel passing objects of type `T`. abstract type AbstractChannel{T} end """ - Channel{T}(sz::Int) + Channel{T}(sz::Int=0) Constructs a `Channel` with an internal buffer that can hold a maximum of `sz` objects of type `T`. @@ -19,8 +19,12 @@ And vice-versa. Other constructors: +* `Channel()`: default constructor, equivalent to `Channel{Any}(0)` * `Channel(Inf)`: equivalent to `Channel{Any}(typemax(Int))` * `Channel(sz)`: equivalent to `Channel{Any}(sz)` + +!!! compat "Julia 1.3" + The default constructor `Channel()` was added in Julia 1.3. """ mutable struct Channel{T} <: AbstractChannel{T} cond_take::Threads.Condition # waiting for data to become available @@ -48,6 +52,7 @@ function Channel{T}(sz::Float64) where T return Channel{T}(sz) end Channel(sz) = Channel{Any}(sz) +Channel() = Channel{Any}(0) # special constructors """ diff --git a/test/channels.jl b/test/channels.jl index 52ea021393747..5aa6fc83a6134 100644 --- a/test/channels.jl +++ b/test/channels.jl @@ -13,6 +13,9 @@ using Random end @testset "various constructors" begin + c = Channel() + @test eltype(c) == Any + c = Channel(1) @test eltype(c) == Any @test put!(c, 1) == 1 @@ -31,7 +34,6 @@ end tvals = Int[take!(c) for i in 1:10^6] @test pvals == tvals - @test_throws MethodError Channel() @test_throws ArgumentError Channel(-1) @test_throws InexactError Channel(1.5) end @@ -48,6 +50,10 @@ end @test c.sz_max == 0 @test collect(c) == 1:100 + c = Channel() do c; put!(c, 1); put!(c, "hi") end + @test c.sz_max == 0 + @test collect(c) == [1, "hi"] + c = Channel{Int}(Inf) do c; put!(c,1); end @test eltype(c) == Int @test c.sz_max == typemax(Int) From 18a80ae7060138c0f30d795f2a8ba1376feda2e9 Mon Sep 17 00:00:00 2001 From: Nathan Daly Date: Mon, 1 Jul 2019 17:33:24 -0400 Subject: [PATCH 5/6] Add `Channel{Int}()` constructor, default to sz=0 --- base/channels.jl | 4 ++-- test/channels.jl | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/base/channels.jl b/base/channels.jl index 0c6ce4bfd8e43..2f4ebe9a9c7d7 100644 --- a/base/channels.jl +++ b/base/channels.jl @@ -24,7 +24,7 @@ Other constructors: * `Channel(sz)`: equivalent to `Channel{Any}(sz)` !!! compat "Julia 1.3" - The default constructor `Channel()` was added in Julia 1.3. + The default constructor `Channel()` and default `sz=0` were added in Julia 1.3. """ mutable struct Channel{T} <: AbstractChannel{T} cond_take::Threads.Condition # waiting for data to become available @@ -36,7 +36,7 @@ mutable struct Channel{T} <: AbstractChannel{T} data::Vector{T} sz_max::Int # maximum size of channel - function Channel{T}(sz::Integer) where T + function Channel{T}(sz::Integer = 0) where T if sz < 0 throw(ArgumentError("Channel size must be either 0, a positive integer or Inf")) end diff --git a/test/channels.jl b/test/channels.jl index 5aa6fc83a6134..29a0ab52631da 100644 --- a/test/channels.jl +++ b/test/channels.jl @@ -15,6 +15,7 @@ end @testset "various constructors" begin c = Channel() @test eltype(c) == Any + @test c.sz_max == 0 c = Channel(1) @test eltype(c) == Any @@ -28,6 +29,10 @@ end @test eltype(c) == Int @test_throws MethodError put!(c, "Hello") + c = Channel{Int}() + @test eltype(c) == Int + @test c.sz_max == 0 + c = Channel{Int}(Inf) @test eltype(c) == Int pvals = map(i->put!(c,i), 1:10^6) From 64109f4c371d830af6b0d590572d1e4a9a664a50 Mon Sep 17 00:00:00 2001 From: Nathan Daly Date: Tue, 2 Jul 2019 00:29:37 -0400 Subject: [PATCH 6/6] Oops: removed custom output prompt from jldoctest I copy/pasted from my terminal, and I had a custom output prompt set via OhMyREPL that I didn't notice. --- base/channels.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/base/channels.jl b/base/channels.jl index 2f4ebe9a9c7d7..67899f97237db 100644 --- a/base/channels.jl +++ b/base/channels.jl @@ -114,10 +114,10 @@ julia> chnl = Channel{Char}(1) do ch put!(ch, c) end end ->> Channel{Char}(sz_max:1,sz_curr:1) +Channel{Char}(sz_max:1,sz_curr:1) julia> String(collect(chnl)) ->> "hello world" +"hello world" ``` """ function Channel(func::Function; ctype=Any, csize=0, taskref=nothing)