Skip to content

Commit

Permalink
Merge pull request #68 from planetis-m/fix-once
Browse files Browse the repository at this point in the history
Fix once
  • Loading branch information
Araq authored Jul 25, 2024
2 parents 1a821f8 + 08f36ca commit 6a9557e
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 26 deletions.
10 changes: 5 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,13 @@ jobs:
include:
- target:
os: linux
builder: ubuntu-20.04
builder: ubuntu-latest
- target:
os: macos
builder: macos-11
builder: macos-latest
- target:
os: windows
builder: windows-2019
builder: windows-latest

name: '${{ matrix.target.os }}-${{ matrix.target.cpu }}-nim-${{ matrix.target.nim_branch }} (${{ matrix.branch }})'
runs-on: ${{ matrix.builder }}
Expand Down Expand Up @@ -102,10 +102,10 @@ jobs:
run: |
mkdir -p external
if [[ '${{ matrix.target.cpu }}' == 'amd64' ]]; then
MINGW_URL="https://github.com/brechtsanders/winlibs_mingw/releases/download/11.1.0-12.0.0-9.0.0-r2/winlibs-x86_64-posix-seh-gcc-11.1.0-mingw-w64-9.0.0-r2.7z"
MINGW_URL="https://github.com/brechtsanders/winlibs_mingw/releases/download/14.1.0posix-18.1.8-12.0.0-ucrt-r3/winlibs-x86_64-posix-seh-gcc-14.1.0-mingw-w64ucrt-12.0.0-r3.7z"
ARCH=64
else
MINGW_URL="https://github.com/brechtsanders/winlibs_mingw/releases/download/11.1.0-12.0.0-9.0.0-r2/winlibs-i686-posix-dwarf-gcc-11.1.0-mingw-w64-9.0.0-r2.7z"
MINGW_URL="https://github.com/brechtsanders/winlibs_mingw/releases/download/14.1.0posix-18.1.8-12.0.0-ucrt-r3/winlibs-i686-posix-dwarf-gcc-14.1.0-mingw-w64ucrt-12.0.0-r3.7z"
ARCH=32
fi
curl -L "$MINGW_URL" -o "external/mingw-${{ matrix.target.cpu }}.7z"
Expand Down
58 changes: 44 additions & 14 deletions threading/once.nim
Original file line number Diff line number Diff line change
Expand Up @@ -17,60 +17,90 @@ runnableExamples:
var
counter = 1
instance: ptr Singleton
exceptionOccurred = false
o = createOnce()

proc getInstance(): ptr Singleton =
once(o):
if not exceptionOccurred:
# Simulate an exception on the first call
exceptionOccurred = true
raise newException(ValueError, "Simulated error")
instance = createSharedU(Singleton)
instance.data = counter
inc counter
result = instance

proc worker {.thread.} =
for i in 1..1000:
assert getInstance().data == 1
try:
for i in 1..1000:
let inst = getInstance()
assert inst.data == 1
except ValueError:
echo "Caught expected ValueError"

var threads: array[10, Thread[void]]
for i in 0..<10:
createThread(threads[i], worker)
joinThreads(threads)
deallocShared(instance)
echo "All threads completed"


import std / [locks, atomics]
import std / locks

type
Once* = object
## Once is a type that allows you to execute a block of code exactly once.
## The first call to `once` will execute the block of code and all other
## calls will be ignored.
## calls will be ignored. All concurrent calls to `once` are guaranteed to
## observe any side-effects made by the active call, with no additional
## synchronization.
state: int
L: Lock
finished: Atomic[bool]
c: Cond

const
Unset = 0
Pending = 1
Complete = -1

when defined(nimAllowNonVarDestructor):
proc `=destroy`*(o: Once) {.inline.} =
let x = addr(o)
deinitLock(x.L)
deinitCond(x.c)
else:
proc `=destroy`*(o: var Once) {.inline.} =
deinitLock(o.L)
deinitCond(o.c)

proc `=sink`*(dest: var Once; source: Once) {.error.}
proc `=copy`*(dest: var Once; source: Once) {.error.}

proc createOnce*(): Once =
result = default(Once)
initLock(result.L)
initCond(result.c)

template once*(o: Once, body: untyped) =
## Executes `body` exactly once.
if not o.finished.load(moAcquire):
acquire(o.L)
acquire(o.L)
while o.state == Pending:
wait(o.c, o.L)
if o.state == Unset:
o.state = Pending
release(o.L)
try:
if not o.finished.load(moRelaxed):
try:
body
finally:
o.finished.store(true, moRelease)
finally:
body
acquire(o.L)
o.state = Complete
broadcast(o.c)
release(o.L)
except:
acquire(o.L)
o.state = Unset
broadcast(o.c)
release(o.L)
raise
else:
release(o.L)
14 changes: 7 additions & 7 deletions threading/rwlock.nim
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ type
c: Cond
L: Lock
activeReaders, waitingWriters: int
activeWriter: bool
isWriterActive: bool

when defined(nimAllowNonVarDestructor):
proc `=destroy`*(rw: RwLock) {.inline.} =
Expand All @@ -64,7 +64,7 @@ proc createRwLock*(): RwLock =
proc beginRead*(rw: var RwLock) =
## Acquire a read lock.
acquire(rw.L)
while rw.waitingWriters > 0 or rw.activeWriter:
while rw.waitingWriters > 0 or rw.isWriterActive:
wait(rw.c, rw.L)
inc rw.activeReaders
release(rw.L)
Expand All @@ -73,24 +73,24 @@ proc beginWrite*(rw: var RwLock) =
## Acquire a write lock.
acquire(rw.L)
inc rw.waitingWriters
while rw.activeReaders > 0 or rw.activeWriter:
while rw.activeReaders > 0 or rw.isWriterActive:
wait(rw.c, rw.L)
dec rw.waitingWriters
rw.activeWriter = true
rw.isWriterActive = true
release(rw.L)

proc endRead*(rw: var RwLock) {.inline.} =
## Release a read lock.
acquire(rw.L)
dec rw.activeReaders
rw.c.broadcast()
broadcast(rw.c)
release(rw.L)

proc endWrite*(rw: var RwLock) {.inline.} =
## Release a write lock.
acquire(rw.L)
rw.activeWriter = false
rw.c.broadcast()
rw.isWriterActive = false
broadcast(rw.c)
release(rw.L)

template readWith*(a: RwLock, body: untyped) =
Expand Down

0 comments on commit 6a9557e

Please sign in to comment.