From 96b385efc50c0ab85e9be08474872e1c257e9e64 Mon Sep 17 00:00:00 2001 From: flywind Date: Mon, 30 Nov 2020 17:55:08 +0800 Subject: [PATCH 1/8] add std/once --- lib/std/once.nim | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 lib/std/once.nim diff --git a/lib/std/once.nim b/lib/std/once.nim new file mode 100644 index 0000000000000..f246c286d5bd9 --- /dev/null +++ b/lib/std/once.nim @@ -0,0 +1,31 @@ +import atomics +import locks + + +type + Once = object + finished: Atomic[bool] + lock: Lock + +proc initOnce*(once: var Once) = + once.finished.store(false) + initLock(once.lock) + +template once*(alreadyExecuted: Once, body: untyped) = + runnableExamples: + var block1: Once + var count = 0 + initOnce(block1) + + + for i in 1 .. 10: + onlyOnce(block1): + inc count + + doAssert count == 1 + if not alreadyExecuted.finished.load: + withLock alreadyExecuted.lock: + try: + body + finally: + alreadyExecuted.finished.store(true) From 6ed6c7252915f44aebedb8963873425763d50f65 Mon Sep 17 00:00:00 2001 From: flywind Date: Mon, 30 Nov 2020 17:58:53 +0800 Subject: [PATCH 2/8] minor --- lib/std/once.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/once.nim b/lib/std/once.nim index f246c286d5bd9..50ef3a352ffd6 100644 --- a/lib/std/once.nim +++ b/lib/std/once.nim @@ -19,7 +19,7 @@ template once*(alreadyExecuted: Once, body: untyped) = for i in 1 .. 10: - onlyOnce(block1): + once(block1): inc count doAssert count == 1 From 06ff965bf169b047c82e3d8b94744eb881a0e84e Mon Sep 17 00:00:00 2001 From: flywind Date: Mon, 30 Nov 2020 18:00:49 +0800 Subject: [PATCH 3/8] small --- lib/std/once.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/once.nim b/lib/std/once.nim index 50ef3a352ffd6..252296123a88d 100644 --- a/lib/std/once.nim +++ b/lib/std/once.nim @@ -3,7 +3,7 @@ import locks type - Once = object + Once* = object finished: Atomic[bool] lock: Lock From e26d200b1563c6e714d8adcef5e53477c4f303ba Mon Sep 17 00:00:00 2001 From: flywind Date: Tue, 1 Dec 2020 11:08:16 +0800 Subject: [PATCH 4/8] fix --- lib/std/once.nim | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/std/once.nim b/lib/std/once.nim index 252296123a88d..07d5456800421 100644 --- a/lib/std/once.nim +++ b/lib/std/once.nim @@ -23,9 +23,11 @@ template once*(alreadyExecuted: Once, body: untyped) = inc count doAssert count == 1 + if not alreadyExecuted.finished.load: withLock alreadyExecuted.lock: - try: - body - finally: - alreadyExecuted.finished.store(true) + if not alreadyExecuted.finished.load: + try: + body + finally: + alreadyExecuted.finished.store(true) From ca1d4f7b2eda3b662fc2cfe4dfdde9362525f76d Mon Sep 17 00:00:00 2001 From: flywind Date: Tue, 1 Dec 2020 11:39:34 +0800 Subject: [PATCH 5/8] add tests --- tests/stdlib/tonce.nim | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 tests/stdlib/tonce.nim diff --git a/tests/stdlib/tonce.nim b/tests/stdlib/tonce.nim new file mode 100644 index 0000000000000..afc9330666d15 --- /dev/null +++ b/tests/stdlib/tonce.nim @@ -0,0 +1,34 @@ +discard """ + cmd: "nim c -r --threads:on $options $file" + matrix: "-d:caseA; -d:caseB" +""" +import std/once + +when defined(caseA): + block: + var thr: array[0..4, Thread[void]] + var block1: Once + var count = 0 + initOnce(block1) + proc threadFunc() {.thread.} = + for i in 1 .. 10: + once(block1): + inc count + for i in 0..high(thr): + createThread(thr[i], threadFunc) + joinThreads(thr) + doAssert count == 1 +elif defined(caseB): + block: + var thr: array[0..4, Thread[void]] + var count = 0 + proc threadFunc() {.thread.} = + var block1: Once + initOnce(block1) + for i in 1 .. 10: + once(block1): + inc count + for i in 0..high(thr): + createThread(thr[i], threadFunc) + joinThreads(thr) + doAssert count == 5 From 5922d716f829b0a4fcb897bede951fb5b1f2bb5f Mon Sep 17 00:00:00 2001 From: flywind Date: Thu, 10 Dec 2020 21:20:04 +0800 Subject: [PATCH 6/8] changes --- lib/std/once.nim | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/lib/std/once.nim b/lib/std/once.nim index 07d5456800421..ce35816a69409 100644 --- a/lib/std/once.nim +++ b/lib/std/once.nim @@ -1,33 +1,39 @@ -import atomics -import locks +import std/[atomics, locks] type - Once* = object + Once* = object ## A object that ensures a block of code is executed once. finished: Atomic[bool] lock: Lock proc initOnce*(once: var Once) = + ## Initializes a `Once` object. once.finished.store(false) initLock(once.lock) -template once*(alreadyExecuted: Once, body: untyped) = +template once*(cond: Once, body: untyped) = + ## Executes a block of code only once (the first time the block is reached). + ## It is thread-safe. runnableExamples: var block1: Once var count = 0 initOnce(block1) - for i in 1 .. 10: once(block1): inc count + # only the first `block1` is executed + once(block1): + count = 888 + doAssert count == 1 - if not alreadyExecuted.finished.load: - withLock alreadyExecuted.lock: - if not alreadyExecuted.finished.load: + if not cond.finished.load(moAcquire): + withLock cond.lock: + # TODO load a value without atomic + if not cond.finished.load(moRelaxed): try: body finally: - alreadyExecuted.finished.store(true) + cond.finished.store(true, moRelease) From e3019dc76e8086589681922aef7b13e89913b211 Mon Sep 17 00:00:00 2001 From: flywind Date: Sun, 18 Apr 2021 21:08:13 +0800 Subject: [PATCH 7/8] Update once.nim --- lib/std/once.nim | 84 +++++++++++++++++++++++++++++++++++++----------- 1 file changed, 66 insertions(+), 18 deletions(-) diff --git a/lib/std/once.nim b/lib/std/once.nim index ce35816a69409..b2853f82c3bda 100644 --- a/lib/std/once.nim +++ b/lib/std/once.nim @@ -1,23 +1,32 @@ -import std/[atomics, locks] +when compileOption("threads"): + import std/[atomics, locks] type Once* = object ## A object that ensures a block of code is executed once. - finished: Atomic[bool] - lock: Lock + when compileOption("threads"): + finished: Atomic[bool] + lock: Lock + else: + finished: bool -proc initOnce*(once: var Once) = +proc `=destroy`*(once: var Once) {.inline.} = + when compileOption("threads"): + deinitLock(once.lock) + +proc init*(once: var Once) = ## Initializes a `Once` object. - once.finished.store(false) - initLock(once.lock) + when compileOption("threads"): + once.finished.store(false, moRelaxed) + initLock(once.lock) -template once*(cond: Once, body: untyped) = +template once*(cond: var Once, body: untyped) = ## Executes a block of code only once (the first time the block is reached). ## It is thread-safe. - runnableExamples: + runnableExamples("--gc:orc --threads:on"): var block1: Once var count = 0 - initOnce(block1) + init(block1) for i in 1 .. 10: once(block1): @@ -27,13 +36,52 @@ template once*(cond: Once, body: untyped) = once(block1): count = 888 - doAssert count == 1 + assert count == 1 + + when compileOption("threads"): + if not cond.finished.load(moAcquire): + withLock cond.lock: + if not cond.finished.load(moRelaxed): + try: + body + finally: + cond.finished.store(true, moRelease) + else: + if not cond.finished: + try: + body + finally: + cond.finished = true - if not cond.finished.load(moAcquire): - withLock cond.lock: - # TODO load a value without atomic - if not cond.finished.load(moRelaxed): - try: - body - finally: - cond.finished.store(true, moRelease) + +## The code block is executed only once among threads. +runnableExamples("--gc:orc --threads:on"): + block: + var thr: array[0..4, Thread[void]] + var block1: Once + var count = 0 + init(block1) + proc threadFunc() {.thread.} = + for i in 1 .. 10: + once(block1): + inc count + for i in 0..high(thr): + createThread(thr[i], threadFunc) + joinThreads(thr) + assert count == 1 + +## The code blocks is executed per thread. +runnableExamples("--gc:orc --threads:on"): + block: + var thr: array[0..4, Thread[void]] + var count = 0 + proc threadFunc() {.thread.} = + var block1: Once + init(block1) + for i in 1 .. 10: + once(block1): + inc count + for i in 0..high(thr): + createThread(thr[i], threadFunc) + joinThreads(thr) + assert count == thr.len From b648d031d3e0d801ac74c3c63485d598e5872e2b Mon Sep 17 00:00:00 2001 From: flywind Date: Sun, 18 Apr 2021 21:09:08 +0800 Subject: [PATCH 8/8] Update tonce.nim --- tests/stdlib/tonce.nim | 56 +++++++++++++++++++++--------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/tests/stdlib/tonce.nim b/tests/stdlib/tonce.nim index afc9330666d15..4e35cc06c3cb7 100644 --- a/tests/stdlib/tonce.nim +++ b/tests/stdlib/tonce.nim @@ -1,34 +1,34 @@ discard """ - cmd: "nim c -r --threads:on $options $file" - matrix: "-d:caseA; -d:caseB" + matrix: "--gc:orc --threads:on" """ import std/once -when defined(caseA): - block: - var thr: array[0..4, Thread[void]] + +block: + var thr: array[0..4, Thread[void]] + var block1: Once + var count = 0 + initOnce(block1) + proc threadFunc() {.thread.} = + for i in 1 .. 10: + once(block1): + inc count + for i in 0..high(thr): + createThread(thr[i], threadFunc) + joinThreads(thr) + doAssert count == 1 + + +block: + var thr: array[0..4, Thread[void]] + var count = 0 + proc threadFunc() {.thread.} = var block1: Once - var count = 0 initOnce(block1) - proc threadFunc() {.thread.} = - for i in 1 .. 10: - once(block1): - inc count - for i in 0..high(thr): - createThread(thr[i], threadFunc) - joinThreads(thr) - doAssert count == 1 -elif defined(caseB): - block: - var thr: array[0..4, Thread[void]] - var count = 0 - proc threadFunc() {.thread.} = - var block1: Once - initOnce(block1) - for i in 1 .. 10: - once(block1): - inc count - for i in 0..high(thr): - createThread(thr[i], threadFunc) - joinThreads(thr) - doAssert count == 5 + for i in 1 .. 10: + once(block1): + inc count + for i in 0..high(thr): + createThread(thr[i], threadFunc) + joinThreads(thr) + doAssert count == 5