simplify locking

This commit is contained in:
Dmitriy Ryajov 2023-09-18 13:18:51 -06:00
parent 52d6a857ac
commit 7306a0bfd4
No known key found for this signature in database
GPG Key ID: DA8C680CE7C657A4
4 changed files with 58 additions and 73 deletions

2
.gitignore vendored
View File

@ -10,3 +10,5 @@ datastore.nims
nimcache nimcache
TODO TODO
nim.cfg nim.cfg
nimble.develop
nimble.paths

View File

@ -10,3 +10,7 @@ when (NimMajor, NimMinor) == (1, 2):
when (NimMajor, NimMinor) > (1, 2): when (NimMajor, NimMinor) > (1, 2):
switch("hint", "XCannotRaiseY:off") switch("hint", "XCannotRaiseY:off")
# begin Nimble config (version 2)
when withDir(thisDir(), system.fileExists("nimble.paths")):
include "nimble.paths"
# end Nimble config

View File

@ -46,13 +46,10 @@ type
ds: Datastore ds: Datastore
semaphore: AsyncSemaphore # semaphore is used for backpressure \ semaphore: AsyncSemaphore # semaphore is used for backpressure \
# to avoid exhausting file descriptors # to avoid exhausting file descriptors
case withLocks: bool withLocks: bool
of true:
tasks: Table[Key, Future[void]] tasks: Table[Key, Future[void]]
queryLock: AsyncLock # global query lock, this is only really \ queryLock: AsyncLock # global query lock, this is only really \
# needed for the fsds, but it is expensive! # needed for the fsds, but it is expensive!
else:
futs: seq[Future[void]] # keep a list of the futures to the signals around
template withLocks( template withLocks(
self: ThreadDatastore, self: ThreadDatastore,
@ -61,27 +58,21 @@ template withLocks(
fut: Future[void], fut: Future[void],
body: untyped) = body: untyped) =
try: try:
case self.withLocks: if key.isSome and key.get in self.tasks:
of true: if self.withLocks:
if key.isSome and
key.get in self.tasks:
await self.tasks[key.get] await self.tasks[key.get]
await self.queryLock.acquire() # lock query or wait to finish self.tasks[key.get] = fut # we alway want to store the future, but only await if we're using locks
self.tasks[key.get] = fut if self.withLocks:
else: await self.queryLock.acquire() # only lock if it's required (fsds)
self.futs.add(fut)
body body
finally: finally:
case self.withLocks: if self.withLocks:
of true: if key.isSome and key.get in self.tasks:
if key.isSome:
self.tasks.del(key.get) self.tasks.del(key.get)
if self.queryLock.locked: if self.queryLock.locked:
self.queryLock.release() self.queryLock.release()
else:
self.futs.keepItIf(it != fut)
template dispatchTask( template dispatchTask(
self: ThreadDatastore, self: ThreadDatastore,
@ -359,13 +350,8 @@ method get*(
return success(@(res.get())) return success(@(res.get()))
method close*(self: ThreadDatastore): Future[?!void] {.async.} = method close*(self: ThreadDatastore): Future[?!void] {.async.} =
var futs = if self.withLocks: for fut in self.tasks.values.toSeq:
self.tasks.values.toSeq # toSeq(...) doesn't work here??? await fut.cancelAndWait() # probably want to store the signal, instead of the future (or both?)
else:
self.futs
for fut in futs:
await fut.cancelAndWait()
await self.ds.close() await self.ds.close()
@ -418,18 +404,9 @@ method query*(
defer: defer:
locked = false locked = false
self.semaphore.release() self.semaphore.release()
case self.withLocks:
of true:
if self.queryLock.locked:
self.queryLock.release()
else:
discard
trace "About to query" trace "About to query"
await self.semaphore.acquire() await self.semaphore.acquire()
if self.withLocks:
await self.queryLock.acquire()
if locked: if locked:
return failure (ref DatastoreError)(msg: "Should always await query features") return failure (ref DatastoreError)(msg: "Should always await query features")

View File

@ -21,47 +21,49 @@ import pkg/datastore/threads/threadproxyds {.all.}
import ./dscommontests import ./dscommontests
import ./querycommontests import ./querycommontests
suite "Test Basic ThreadDatastore with SQLite": const NumThreads = 200 # IO threads aren't attached to CPU count
var # suite "Test Basic ThreadDatastore with SQLite":
sqlStore: Datastore
ds: ThreadDatastore
taskPool: Taskpool
key = Key.init("/a/b").tryGet()
bytes = "some bytes".toBytes
otherBytes = "some other bytes".toBytes
setupAll: # var
sqlStore = SQLiteDatastore.new(Memory).tryGet() # sqlStore: Datastore
taskPool = Taskpool.new(countProcessors() * 2) # ds: ThreadDatastore
ds = ThreadDatastore.new(sqlStore, tp = taskPool).tryGet() # taskPool: Taskpool
# key = Key.init("/a/b").tryGet()
# bytes = "some bytes".toBytes
# otherBytes = "some other bytes".toBytes
teardownAll: # setupAll:
(await ds.close()).tryGet() # sqlStore = SQLiteDatastore.new(Memory).tryGet()
taskPool.shutdown() # taskPool = Taskpool.new(NumThreads)
# ds = ThreadDatastore.new(sqlStore, tp = taskPool).tryGet()
basicStoreTests(ds, key, bytes, otherBytes) # teardownAll:
# (await ds.close()).tryGet()
# taskPool.shutdown()
suite "Test Query ThreadDatastore with SQLite": # basicStoreTests(ds, key, bytes, otherBytes)
var # suite "Test Query ThreadDatastore with SQLite":
sqlStore: Datastore
ds: ThreadDatastore
taskPool: Taskpool
key = Key.init("/a/b").tryGet()
bytes = "some bytes".toBytes
otherBytes = "some other bytes".toBytes
setup: # var
sqlStore = SQLiteDatastore.new(Memory).tryGet() # sqlStore: Datastore
taskPool = Taskpool.new(countProcessors() * 2) # ds: ThreadDatastore
ds = ThreadDatastore.new(sqlStore, tp = taskPool).tryGet() # taskPool: Taskpool
# key = Key.init("/a/b").tryGet()
# bytes = "some bytes".toBytes
# otherBytes = "some other bytes".toBytes
teardown: # setup:
(await ds.close()).tryGet() # sqlStore = SQLiteDatastore.new(Memory).tryGet()
taskPool.shutdown() # taskPool = Taskpool.new(NumThreads)
# ds = ThreadDatastore.new(sqlStore, tp = taskPool).tryGet()
queryTests(ds, true) # teardown:
# (await ds.close()).tryGet()
# taskPool.shutdown()
# queryTests(ds, true)
suite "Test Basic ThreadDatastore with fsds": suite "Test Basic ThreadDatastore with fsds":
let let
@ -83,7 +85,7 @@ suite "Test Basic ThreadDatastore with fsds":
createDir(basePathAbs) createDir(basePathAbs)
fsStore = FSDatastore.new(root = basePathAbs, depth = 3).tryGet() fsStore = FSDatastore.new(root = basePathAbs, depth = 3).tryGet()
taskPool = Taskpool.new(countProcessors() * 2) taskPool = Taskpool.new(NumThreads)
ds = ThreadDatastore.new(fsStore, withLocks = true, tp = taskPool).tryGet() ds = ThreadDatastore.new(fsStore, withLocks = true, tp = taskPool).tryGet()
teardownAll: teardownAll:
@ -112,7 +114,7 @@ suite "Test Query ThreadDatastore with fsds":
createDir(basePathAbs) createDir(basePathAbs)
fsStore = FSDatastore.new(root = basePathAbs, depth = 5).tryGet() fsStore = FSDatastore.new(root = basePathAbs, depth = 5).tryGet()
taskPool = Taskpool.new(countProcessors() * 2) taskPool = Taskpool.new(NumThreads)
ds = ThreadDatastore.new(fsStore, withLocks = true, tp = taskPool).tryGet() ds = ThreadDatastore.new(fsStore, withLocks = true, tp = taskPool).tryGet()
teardown: teardown:
@ -138,7 +140,7 @@ suite "Test ThreadDatastore cancelations":
setupAll: setupAll:
sqlStore = SQLiteDatastore.new(Memory).tryGet() sqlStore = SQLiteDatastore.new(Memory).tryGet()
taskPool = Taskpool.new(countProcessors() * 2) taskPool = Taskpool.new(NumThreads)
ds = ThreadDatastore.new(sqlStore, tp = taskPool).tryGet() ds = ThreadDatastore.new(sqlStore, tp = taskPool).tryGet()
test "Should monitor signal and cancel": test "Should monitor signal and cancel":