Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix once #68

Merged
merged 6 commits into from
Jul 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading