Skip to content

Commit

Permalink
Merge pull request #8 from tkf/dev
Browse files Browse the repository at this point in the history
Add a few more collections
  • Loading branch information
tkf authored Jun 14, 2021
2 parents 76815e7 + a1b70d7 commit 5cf504c
Show file tree
Hide file tree
Showing 27 changed files with 910 additions and 4 deletions.
40 changes: 40 additions & 0 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: Documentation

on:
push:
branches:
- master
- actions/trigger/docs
tags: '*'
pull_request:

jobs:
Documenter:
runs-on: ubuntu-latest
env:
GKSwstype: "nul"
steps:
- uses: actions/checkout@v2
- uses: julia-actions/setup-julia@latest
with:
version: nightly
# Use `JULIA_PKG_SERVER` mitigation implemented in julia-buildpkg:
- uses: julia-actions/julia-buildpkg@v1
- name: Install Run.jl
run: julia -e 'using Pkg; pkg"add [email protected]"'
- name: Install dependencies
run: julia -e 'using Run; Run.prepare_docs()'
- name: Build and deploy
id: build-and-deploy
if: |
github.event_name == 'push' || (
github.event_name == 'pull_request' &&
!contains(github.head_ref, 'create-pull-request/')
)
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
DOCUMENTER_KEY: ${{ secrets.SSH_KEY }}
run: julia -e 'using Run; Run.docs()'
- name: Just build
if: steps.build-and-deploy.outcome == 'skipped'
run: julia -e 'using Run; Run.docs()'
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
julia-version: ['1.6', 'nightly']
julia-version: ['nightly']
fail-fast: false
name: Test Julia ${{ matrix.julia-version }}
steps:
Expand Down
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ authors = ["Takafumi Arakaki <[email protected]> and contributors"]
version = "0.1.0"

[compat]
julia = "1.6"
julia = "1.8"
2 changes: 2 additions & 0 deletions docs/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
build/
site/
2 changes: 2 additions & 0 deletions docs/Project.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[deps]
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
26 changes: 26 additions & 0 deletions docs/make.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using Documenter
using ConcurrentCollections

makedocs(
sitename = "ConcurrentCollections",
format = Documenter.HTML(),
modules = [ConcurrentCollections],
)

for (root, dirs, files) in walkdir(joinpath(@__DIR__, "build"))
for name in files
path = joinpath(root, name)
if endswith(name, ".html") || name == "search_index.js"
html = replace(
read(path, String),
"ConcurrentCollections.Implementations" => "ConcurrentCollections",
)
write(path, html)
end
end
end

deploydocs(
repo = "github.com/tkf/ConcurrentCollections.jl",
# push_preview = true,
)
29 changes: 29 additions & 0 deletions docs/src/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# ConcurrentCollections.jl

```@index
```

## Collections

```@docs
ConcurrentQueue
ConcurrentStack
WorkStealingDeque
```

## Functions

```@docs
trypop!
trypopfirst!
```

## Experimental

```@docs
ConcurrentDict
modify!
tryget
Keep
Delete
```
17 changes: 15 additions & 2 deletions src/ConcurrentCollections.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,17 @@ baremodule ConcurrentCollections
export
#
ConcurrentDict,
ConcurrentQueue,
ConcurrentStack,
Delete,
Keep,
WorkStealingDeque,
length_upper_bound,
length_upper_bound,
modify!,
tryget,
trypop!
trypop!,
trypopfirst!

import Base

Expand All @@ -25,6 +29,7 @@ abstract type ConcurrentDict{Key,Value} <: Base.AbstractDict{Key,Value} end

function modify! end
function trypop! end
function trypopfirst! end
function tryget end
function length_lower_bound end
function length_upper_bound end
Expand All @@ -47,15 +52,23 @@ using ..ConcurrentCollections:
length_upper_bound,
modify!,
tryget,
trypop!
trypop!,
trypopfirst!

include("UnsafeAtomics.jl")
using .UnsafeAtomics: acq_rel, acquire, monotonic, release, seq_cst, unordered

include("utils.jl")
include("atomicsutils.jl")
include("dict.jl")
include("workstealing.jl")
include("msqueue.jl")
include("stack.jl")

end # module Implementations

using .Implementations: ConcurrentQueue, ConcurrentStack, WorkStealingDeque

Implementations.define_docstrings()

end # baremodule ConcurrentCollections
26 changes: 26 additions & 0 deletions src/docs/ConcurrentDict.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
ConcurrentDict{K,V}()

Concurrent dictionary. All operations are lock-free except when the dictionary
is resized.

!!! warning
`ConcurrentDict` is experimental because it cannot be implemented with the
builtin atomics.

!!! note
Although tasks `wait` on concurrent modifications (e.g., `setindex!`) during
resize, the worker threads participate in the resize to avoid wasting CPU
resources.

# Examples

```julia
julia> using ConcurrentCollections

julia> dict = ConcurrentDict{String,Int}();

julia> dict["hello"] = 1;

julia> dict["hello"]
1
```
28 changes: 28 additions & 0 deletions src/docs/ConcurrentQueue.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
ConcurrentQueue{T}()

Concurrent queue of objects of type `T`.

Use `push!` to insert an element at the tail and [`trypopfirst!`](@ref) to
retrieve and remove an element at the head.

Implementation detail: It implements the Michael and Scott queue.

# Examples

```julia
julia> using ConcurrentCollections

julia> queue = ConcurrentQueue{Int}();

julia> push!(queue, 1);

julia> push!(queue, 2);

julia> popfirst!(queue)
1

julia> trypopfirst!(queue)
Some(2)

julia> trypopfirst!(queue) # returns nothing
```
28 changes: 28 additions & 0 deletions src/docs/ConcurrentStack.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
ConcurrentStack{T}()

Concurrent stack of objects of type `T`.

Use `push!` to insert an element and [`trypop!`](@ref) to retrieve and remove an
element.

It implements the Treiber stack.

# Examples

```julia
julia> using ConcurrentCollections

julia> stack = ConcurrentStack{Int}();

julia> push!(stack, 1);

julia> push!(stack, 2);

julia> pop!(stack)
2

julia> trypop!(stack)
Some(1)

julia> trypop!(stack) # returns nothing
```
24 changes: 24 additions & 0 deletions src/docs/Delete.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
Delete(ans)

A special type used in [`modify!`](@ref) to indicate that a slot should be
removed.

That is to say

```Julia
y = modify!(dict, key) do value
Delete(f(something(value)))
end
y[]
```

is an optimization of

```Julia
r = Ref{Any}()
modify!(dict, key) do value
r[] = f(something(value))
nothing
end
r[]
```
25 changes: 25 additions & 0 deletions src/docs/Keep.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
Keep(ans)

A special type used in [`modify!`](@ref) to indicate that a slot should be
remain unchanged while propagating the result `ans` of some computation to
the caller.

That is to say,

```Julia
y = modify!(dict, key) do value
Keep(f(something(value)))
end
y[]
```

is an optimization of

```Julia
r = Ref{Any}()
modify!(dict, key) do value
r[] = f(something(value))
Some(value)
end
r[]
```
38 changes: 38 additions & 0 deletions src/docs/WorkStealingDeque.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
WorkStealingDeque{T}()

Concurrent work-stealing "deque" of objects of type `T`.

This is not a full deque in the sense that:

* `push!` and [`trypop!`](@ref) operating at the tail of the collection can
only be executed by a single task.
* [`trypopfirst!`](@ref) (aka steal) for retrieving and removing an element at
the head can be invoked from any tasks. However, there is no `pushfirst!`.

Implementation detail: It implements the dynamic circular work-stealing deque by
Chase and Lev (2005).

# Examples

```julia
julia> using ConcurrentCollections

julia> deque = WorkStealingDeque{Int}();

julia> push!(deque, 1);

julia> push!(deque, 2);

julia> push!(deque, 3);

julia> trypop!(deque)
Some(3)

julia> fetch(Threads.@spawn trypopfirst!(deque))
Some(1)

julia> fetch(Threads.@spawn popfirst!(deque))
2

julia> trypopfirst!(deque) # returns nothing
```
35 changes: 35 additions & 0 deletions src/docs/modify!.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
modify!(f, dict::ConcurrentDict{K,V}, key::K) -> y

Atomically update `key` slot of `dict` using a function `f`.

If `key` does not exist, `f` is called with `nothing`. The call `f(nothing)`
must return either (1) `nothing` to keep the slot unoccupied or (2)
`Some(value::V)` to insert `value`.

If `key` exist, `f` is called with a `ref` such that `ref[]` retrieves the
`value` corresponding to the `key`. The call `f(ref)` must return either (1)
`nothing` to delete the slot, (2) `Some(value′::V)` to insert `value`, (3)
[`Keep(ans)`](@ref ConcurrentCollection.Keep) to return `y = Keep(ans)` from
`modify!`, or (4) [`Delete(ans)`](@ref ConcurrentCollection.Delete) to delete
slot and return a value `y = Delete(ans)` from `modify!`.

The function `f` may be called more than once if multiple tasks try to modify
the dictionary.

# Examples

```julia
julia> using ConcurrentCollections

julia> dict = ConcurrentDict{String,Int}();

julia> modify!(dict, "hello") do _
Some(1)
end
Some(1)

julia> modify!(dict, "hello") do ref
Some(something(ref[]) + 1)
end
Some(2)
```
2 changes: 2 additions & 0 deletions src/docs/tryget.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
tryget(dict::ConcurrentDict{K,V}, key) -> Some(value::T) or nothing

Loading

0 comments on commit 5cf504c

Please sign in to comment.