Skip to content

Commit 044baee

Browse files
authored
simplify and revert optional memory ordering (#36)
* simplify and revert optional memory ordering * modernize ci * slap the compiler aroun' * run docs against mainline
1 parent 9a10711 commit 044baee

File tree

5 files changed

+115
-86
lines changed

5 files changed

+115
-86
lines changed

.github/workflows/ci.yml

Lines changed: 69 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,14 @@ on:
55

66
push:
77
branches:
8-
- master
8+
- main
99
pull_request:
1010
branches:
1111
- '*'
1212

13+
concurrency:
14+
group: ci-${{ github.ref }}
15+
cancel-in-progress: true
1316

1417
jobs:
1518
changes:
@@ -23,10 +26,10 @@ jobs:
2326
# For PRs the path filter check with Github API, so no need to checkout
2427
# for them.
2528
- if: github.event_name != 'pull_request'
26-
name: Checkout (if not PR)
27-
uses: actions/checkout@v2
29+
name: Checkout
30+
uses: actions/checkout@v4
2831

29-
- uses: dorny/paths-filter@v2
32+
- uses: dorny/paths-filter@v3
3033
id: filter
3134
with:
3235
filters: |
@@ -51,41 +54,78 @@ jobs:
5154
strategy:
5255
fail-fast: false
5356
matrix:
54-
os: ['macos-latest', 'ubuntu-latest']
55-
nim: ['devel']
56-
name: '${{ matrix.os }} (${{ matrix.nim }})'
57+
os: [ubuntu-latest]
58+
compiler:
59+
- name: nim
60+
version: version-2-0
61+
- name: nimskull
62+
version: "*"
63+
64+
name: '${{ matrix.os }} (${{ matrix.compiler.name }} ${{ matrix.compiler.version }})'
5765
runs-on: ${{ matrix.os }}
66+
67+
defaults:
68+
run:
69+
shell: bash
70+
working-directory: project
71+
5872
steps:
5973
- name: Checkout
6074
uses: actions/checkout@v4
6175
with:
62-
path: ci
76+
path: project
6377

64-
- name: Setup Nim
78+
- name: Nim
79+
if: matrix.compiler.name == 'nim'
6580
uses: alaviss/[email protected]
6681
with:
6782
path: nim
68-
version: ${{ matrix.nim }}
83+
version: ${{ matrix.compiler.version }}
84+
85+
- name: Nimskull
86+
id: nimskull
87+
if: matrix.compiler.name == 'nimskull'
88+
uses: nim-works/[email protected]
89+
with:
90+
nimskull-version: ${{ matrix.compiler.version }}
91+
92+
- name: Fetch Nimble
93+
if: matrix.compiler.name == 'nimskull'
94+
uses: actions/[email protected]
95+
with:
96+
path: nimble
97+
repository: alaviss/nimble
98+
ref: nimskull
99+
100+
- name: Build Nimble
101+
if: matrix.compiler.name == 'nimskull'
102+
run: |
103+
nim c -d:release -o:"$NIMSKULL_BIN/nimble" src/nimble.nim
104+
# Add nimble binary folder to PATH
105+
echo "$HOME/.nimble/bin" >> "$GITHUB_PATH"
106+
working-directory: nimble
107+
env:
108+
NIMSKULL_BIN: ${{ steps.nimskull.outputs.bin-path }}
69109

70110
- name: Dependencies
71-
shell: bash
72111
run: |
73-
cd ci
74112
nimble --accept install "https://github.com/nim-works/cps"
75113
nimble --accept install "https://github.com/disruptek/balls"
76114
nimble --accept develop
77115
78-
- name: Tests
79-
shell: bash
116+
- name: Tests (gcc)
80117
run: |
81-
cd ci
82-
balls --path="." --gc:arc --backend:c --define:danger --define:release
118+
balls --path="." --cc:gcc --gc:arc --backend:c --define:danger --define:release
119+
120+
- name: Tests (clang)
121+
run: |
122+
balls --path="." --cc:clang --gc:arc --backend:c --define:danger --define:release
83123
84124
- name: Build docs
85-
if: ${{ matrix.docs }} == 'true'
86-
shell: bash
125+
if: >
126+
github.event_name == 'push' && github.ref == 'refs/heads/main' &&
127+
matrix.os == 'ubuntu-latest' && matrix.compiler.version == 'version-2-0'
87128
run: |
88-
cd ci
89129
branch=${{ github.ref }}
90130
branch=${branch##*/}
91131
nimble doc --project --outdir:docs --path="." \
@@ -102,19 +142,23 @@ jobs:
102142
cp docs/{the,}index.html || true
103143
- name: Publish docs
104144
if: >
105-
github.event_name == 'push' && github.ref == 'refs/heads/master' &&
106-
matrix.os == 'ubuntu-latest' && matrix.nim == 'devel'
107-
uses: crazy-max/ghaction-github-pages@v2.5.0
145+
github.event_name == 'push' && github.ref == 'refs/heads/main' &&
146+
matrix.os == 'ubuntu-latest' && matrix.compiler.version == 'version-2-0'
147+
uses: crazy-max/ghaction-github-pages@v3.1.0
108148
with:
109-
build_dir: ci/docs
149+
build_dir: project/docs
110150
env:
111151
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
112152

113153
# Set check-required on this
114154
success:
115155
needs: build
156+
if: always()
116157
runs-on: ubuntu-latest
117158
name: 'All check passes'
118159
steps:
119-
- run: |
120-
echo "This is a workaround for Github's broken software"
160+
- if: contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled')
161+
name: 'Fail when previous jobs fails'
162+
run: |
163+
echo "::error::One of the previous jobs failed"
164+
exit 1

loony.nim

Lines changed: 23 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -57,20 +57,13 @@ proc toStrTuple*(tag: TagPtr): string =
5757
var res = (nptr:tag.nptr, idx:tag.idx)
5858
return $res
5959
60-
proc fetchAddSlot(tag: TagPtr; w: uint): uint =
61-
## A convenience to fetchAdd the node's slot.
62-
if tag == 0:
63-
raise AssertionDefect.newException "tagptr was empty"
64-
else:
65-
result = fetchAddSlot(tag.node, idx tag, w)
66-
67-
proc fetchTail(queue: LoonyQueue, moorder: MemoryOrder = moRelaxed): TagPtr =
60+
proc fetchTail(queue: LoonyQueue): TagPtr =
6861
## get the TagPtr of the tail (nptr: NodePtr, idx: uint16)
69-
TagPtr load(queue.tail, order = moorder)
62+
TagPtr load(queue.tail, order = moRelaxed)
7063
71-
proc fetchHead(queue: LoonyQueue, moorder: MemoryOrder = moRelaxed): TagPtr =
64+
proc fetchHead(queue: LoonyQueue): TagPtr =
7265
## get the TagPtr of the head (nptr: NodePtr, idx: uint16)
73-
TagPtr load(queue.head, order = moorder)
66+
TagPtr load(queue.head, order = moRelaxed)
7467
7568
proc fetchCurrTail(queue: LoonyQueue): NodePtr {.used.} =
7669
# get the NodePtr of the current tail
@@ -80,20 +73,20 @@ proc fetchCurrTail(queue: LoonyQueue): NodePtr {.used.} =
8073
# imported std/atomics or we export atomics.
8174
# For the sake of not polluting the users namespace I have changed these into procs.
8275
# Atomic inc of idx in (nptr: NodePtr, idx: uint16)
83-
proc fetchIncTail(queue: LoonyQueue, moorder: MemoryOrder = moAcquire): TagPtr =
84-
cast[TagPtr](queue.tail.fetchAdd(1, order = moorder))
85-
proc fetchIncHead(queue: LoonyQueue, moorder: MemoryOrder = moAcquire): TagPtr =
86-
cast[TagPtr](queue.head.fetchAdd(1, order = moorder))
76+
proc fetchIncTail(queue: LoonyQueue): TagPtr =
77+
cast[TagPtr](queue.tail.fetchAdd(1, order = moAcquire))
78+
proc fetchIncHead(queue: LoonyQueue): TagPtr =
79+
cast[TagPtr](queue.head.fetchAdd(1, order = moAcquire))
8780
88-
proc compareAndSwapTail(queue: LoonyQueue, expect: var uint, swap: uint | TagPtr): bool =
89-
queue.tail.compareExchange(expect, swap)
81+
proc compareAndSwapTail(queue: LoonyQueue; expect: var uint; swap: uint | TagPtr): bool =
82+
queue.tail.compareExchange(expect, swap, moRelease, moRelaxed)
9083
91-
proc compareAndSwapHead(queue: LoonyQueue, expect: var uint, swap: uint | TagPtr): bool =
92-
queue.head.compareExchange(expect, swap)
84+
proc compareAndSwapHead(queue: LoonyQueue; expect: var uint; swap: uint | TagPtr): bool =
85+
queue.head.compareExchange(expect, swap, moRelease, moRelaxed)
9386
94-
proc compareAndSwapCurrTail(queue: LoonyQueue, expect: var uint,
87+
proc compareAndSwapCurrTail(queue: LoonyQueue; expect: var uint;
9588
swap: uint | TagPtr): bool {.used.} =
96-
queue.currTail.compareExchange(expect, swap)
89+
queue.currTail.compareExchange(expect, swap, moRelease, moRelaxed)
9790
9891
proc `=destroy`*[T](x: var LoonyQueueImpl[T]) =
9992
## Destroy is completely operated on the basis that no other threads are
@@ -191,7 +184,7 @@ proc advTail[T](queue: LoonyQueue[T]; pel: uint; tag: TagPtr): AdvTail =
191184
result = AdvOnly
192185
break done
193186
# Get current tails next node
194-
var next = origTail.fetchNext()
187+
var next = fetchNext(origTail, moRelaxed)
195188
if cast[ptr Node](next).isNil():
196189
# Prepare the new node with our element in it
197190
var node = allocNode pel
@@ -242,7 +235,7 @@ proc advHead(queue: LoonyQueue; curr, h, t: var TagPtr): AdvHead =
242235
incrDeqCount h.node
243236
QueueEmpty
244237
else:
245-
var next = fetchNext h.nptr
238+
var next = fetchNext(h.nptr, moAcquire)
246239
# Equivalent to (nptr: NodePtr, idx: idx+=1)
247240
curr += 1
248241
block done:
@@ -297,12 +290,12 @@ proc pushImpl[T](queue: LoonyQueue[T], el: sink T,
297290
atomicThreadFence(ATOMIC_RELEASE)
298291
while true:
299292
# Enq proc begins with incr the index of node in TagPtr
300-
var tag = fetchIncTail(queue)
293+
var tag = queue.fetchIncTail()
301294
if likely(tag.idx < N):
302295
# FAST PATH OPERATION - 99% of push will enter here; we want the minimal
303296
# amount of necessary operations in this path.
304297
# Perform a FAA on our reserved slot which should be 0'd.
305-
let prev = tag.fetchAddSlot pel
298+
let prev = fetchAddSlot(tag.node, tag.idx, pel, moAcquire)
306299
case prev
307300
of 0, RESUME:
308301
break # the slot was empty; we're good to go
@@ -353,17 +346,17 @@ proc isEmptyImpl(head, tail: TagPtr): bool =
353346
proc isEmpty*(queue: LoonyQueue): bool =
354347
## This operation should only be used by internal code. The response for this
355348
## operation is not precise.
356-
let head = fetchHead queue
357-
let tail = fetchTail queue
349+
let head = queue.fetchHead()
350+
let tail = queue.fetchTail()
358351
isEmptyImpl(head, tail)
359352
360353
proc popImpl[T](queue: LoonyQueue[T]; forcedCoherence: static bool = false): T =
361354
assert not queue.isNil(), "The queue has not been initialised"
362355
while true:
363356
# Before incr the deq index, init check performed to determine if queue is empty.
364357
# Ensure head is loaded last to keep mem hot
365-
var tail = fetchTail queue
366-
var curr = fetchHead queue
358+
var tail = queue.fetchTail()
359+
var curr = queue.fetchHead()
367360
if isEmptyImpl(curr, tail):
368361
# Queue was empty; nil can be caught in cps w/ "while cont.running"
369362
when T is ref or T is ptr:
@@ -374,7 +367,7 @@ proc popImpl[T](queue: LoonyQueue[T]; forcedCoherence: static bool = false): T =
374367
var head = queue.fetchIncHead()
375368
if likely(head.idx < N):
376369
# FAST PATH OPS
377-
var prev = head.fetchAddSlot READER
370+
var prev = fetchAddSlot(head.node, head.idx, READER, moRelease)
378371
# Last slot in a node - init reclaim proc; if WRITER bit set then upper bits
379372
# contain a valid pointer to an enqd el that can be returned (see enqueue)
380373
if not unlikely((prev and SLOTMASK) == 0):

loony/node.nim

Lines changed: 8 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -94,14 +94,11 @@ proc prepareElement*[T](el: var T): uint =
9494
result = cast[uint](el) or WRITER
9595
wasMoved el
9696
97-
proc fetchNext*(node: var Node, moorder: MemoryOrder = moAcquireRelease): NodePtr =
98-
load(node.next, order = moorder)
99-
100-
proc fetchNext*(node: NodePtr, moorder: MemoryOrder = moAcquireRelease): NodePtr =
97+
proc fetchNext*(node: NodePtr; moorder: MemoryOrder): NodePtr =
10198
# get the NodePtr to the next Node, can be converted to a TagPtr of (nptr: NodePtr, idx: 0'u16)
102-
fetchNext(node.toNode)
99+
load(node.toNode.next, order = moorder)
103100
104-
proc fetchAddSlot*(t: var Node, idx: uint16, w: uint, moorder: MemoryOrder = moAcquireRelease): uint =
101+
proc fetchAddSlot*(t: var Node, idx: uint16, w: uint, moorder: MemoryOrder): uint =
105102
## Fetches the pointer to the object in the slot while atomically
106103
## increasing the value by `w`.
107104
##
@@ -111,11 +108,10 @@ proc fetchAddSlot*(t: var Node, idx: uint16, w: uint, moorder: MemoryOrder = moA
111108
t.slots[idx].fetchAdd(w, order = moorder)
112109
113110
proc compareAndSwapNext*(t: var Node, expect: var uint, swap: uint): bool =
114-
t.next.compareExchange(expect, swap, moRelaxed) # MO as per cpp impl
111+
t.next.compareExchange(expect, swap, moRelease, moRelaxed)
115112
116113
proc compareAndSwapNext*(t: NodePtr, expect: var uint, swap: uint): bool =
117-
# cpp impl is Relaxed; we use Release here to remove tsan warning
118-
(toNode t).next.compareExchange(expect, swap, moRelease)
114+
(toNode t).next.compareExchange(expect, swap, moRelease, moRelaxed)
119115
120116
proc `=destroy`*(n: var Node) =
121117
decDebugCounter()
@@ -137,8 +133,7 @@ proc tryReclaim*(node: var Node; start: uint16) =
137133
for i in start..<N:
138134
template s: Atomic[uint] = node.slots[i]
139135
if (s.load(order = moAcquire) and CONSUMED) != CONSUMED:
140-
# cpp impl is Relaxed; we use Release here to remove tsan warning
141-
var prev = s.fetchAdd(RESUME, order = moRelease) and CONSUMED
136+
var prev = s.fetchAdd(RESUME, order = moRelaxed) and CONSUMED
142137
if prev != CONSUMED:
143138
incRecPathCounter()
144139
break done
@@ -148,9 +143,7 @@ proc tryReclaim*(node: var Node; start: uint16) =
148143
incReclaimCounter()
149144

150145
proc incrEnqCount*(node: var Node; final: uint16 = 0) =
151-
var mask =
152-
node.ctrl.fetchAddTail:
153-
(final.uint32 shl 16) + 1
146+
var mask = node.ctrl.fetchAddTail((final.uint32 shl 16) + 1)
154147
template finalCount: uint16 =
155148
if final == 0:
156149
getHigh mask
@@ -165,9 +158,7 @@ proc incrEnqCount*(node: var Node; final: uint16 = 0) =
165158

166159
proc incrDeqCount*(node: var Node; final: uint16 = 0) =
167160
incDeqPathCounter()
168-
var mask =
169-
node.ctrl.fetchAddHead:
170-
(final.uint32 shl 16) + 1
161+
var mask = node.ctrl.fetchAddHead((final.uint32 shl 16) + 1)
171162
template finalCount: uint16 =
172163
if final == 0:
173164
getHigh mask

loony/spec.nim

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -62,14 +62,14 @@ proc getHigh*(mask: ControlMask): uint16 =
6262
proc getLow*(mask: ControlMask): uint16 =
6363
mask.uint16
6464

65-
proc fetchAddTail*(ctrl: var ControlBlock, v: uint32 = 1, moorder: MemoryOrder = moRelease): ControlMask =
66-
ctrl.tailMask.fetchAdd(v, order = moorder)
65+
proc fetchAddTail*(ctrl: var ControlBlock, v: uint32 = 1): ControlMask =
66+
ctrl.tailMask.fetchAdd(v, order = moRelaxed)
6767

68-
proc fetchAddHead*(ctrl: var ControlBlock, v: uint32 = 1, moorder: MemoryOrder = moRelease): ControlMask =
69-
ctrl.headMask.fetchAdd(v, order = moorder)
68+
proc fetchAddHead*(ctrl: var ControlBlock, v: uint32 = 1): ControlMask =
69+
ctrl.headMask.fetchAdd(v, order = moRelaxed)
7070

71-
proc fetchAddReclaim*(ctrl: var ControlBlock, v: uint8 = 1, moorder: MemoryOrder = moAcquireRelease): uint8 =
72-
ctrl.reclaim.fetchAdd(v, order = moorder)
71+
proc fetchAddReclaim*(ctrl: var ControlBlock, v: uint8 = 1): uint8 =
72+
ctrl.reclaim.fetchAdd(v, order = moAcquireRelease)
7373

7474
when defined(loonyDebug):
7575
import std/logging

tests/test.nim

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,14 @@ setLogFilter:
3030
var q: LoonyQueue[Continuation]
3131

3232
proc runThings() {.thread.} =
33-
while true:
34-
var job = pop q
35-
if job.dismissed:
36-
break
37-
else:
38-
while job.running:
39-
job = trampoline job
33+
{.gcsafe.}:
34+
while true:
35+
var job = pop q
36+
if job.dismissed:
37+
break
38+
else:
39+
while job.running:
40+
job = trampoline job
4041

4142
proc enqueue(c: C): C {.cpsMagic.} =
4243
check not q.isNil
@@ -61,7 +62,7 @@ else:
6162
var x = Timespec(tv_sec: 0.Time, tv_nsec: ns)
6263
var y: Timespec
6364
if 0 != nanosleep(x, y):
64-
raise
65+
fail "nanosleep fail"
6566
c
6667

6768
proc doContinualThings() {.cps: C.} =

0 commit comments

Comments
 (0)