Optimize asyncsync primitives

This commit is contained in:
cheatfate 2018-05-22 11:51:11 +03:00
parent c1159286e6
commit c220b05a4c
3 changed files with 123 additions and 87 deletions

View File

@ -10,7 +10,12 @@ skipDirs = @["tests", "Nim", "nim"]
requires "nim > 0.18.0"
task test, "Run all tests":
exec "nim c -r -d:useSysAssert -d:useGcAssert tests/testsync"
exec "nim c -r tests/testsync"
exec "nim c -r -d:release tests/testsync"
exec "nim c -r -d:useSysAssert -d:useGcAssert tests/testdatagram"
exec "nim c -r -d:useSysAssert -d:useGcAssert tests/teststream"
exec "nim c -r -d:release tests/testdatagram"
exec "nim c -r -d:release tests/teststream"

View File

@ -110,14 +110,14 @@ proc release*(lock: AsyncLock) =
## other coroutines are blocked waiting for the lock to become unlocked,
## allow exactly one of them to proceed.
var w: Future[void]
proc wakeup(udata: pointer) {.gcsafe.} = w.complete()
# proc wakeup(udata: pointer) {.gcsafe.} = w.complete()
if lock.locked:
lock.locked = false
while len(lock.waiters) > 0:
w = lock.waiters.popFirst()
if not w.finished:
callSoon(wakeup)
w.complete()
break
else:
raise newException(AsyncLockError, "AsyncLock is not acquired!")
@ -153,16 +153,13 @@ proc fire*(event: AsyncEvent) =
## Set the internal flag of ``event`` to `true`. All tasks waiting for it
## to become `true` are awakened. Task that call `wait()` once the flag is
## `true` will not block at all.
proc wakeupAll(udata: pointer) {.gcsafe.} =
if len(event.waiters) > 0:
var w = event.waiters.popFirst()
if not w.finished:
w.complete()
callSoon(wakeupAll)
var w: Future[void]
if not event.flag:
event.flag = true
callSoon(wakeupAll)
while len(event.waiters) > 0:
w = event.waiters.popFirst()
if not w.finished:
w.complete()
proc clear*(event: AsyncEvent) =
## Reset the internal flag of ``event`` to `false`. Subsequently, tasks
@ -205,23 +202,19 @@ proc putNoWait*[T](aq: AsyncQueue[T], item: T) =
##
## If queue ``aq`` is full, then ``AsyncQueueFullError`` exception raised
var w: Future[void]
proc wakeup(udata: pointer) {.gcsafe.} = w.complete()
if aq.full():
raise newException(AsyncQueueFullError, "AsyncQueue is full!")
aq.queue.addLast(item)
while len(aq.getters) > 0:
w = aq.getters.popFirst()
if not w.finished:
callSoon(wakeup)
w.complete()
proc getNoWait*[T](aq: AsyncQueue[T]): T =
## Remove and return ``item`` from the queue immediately.
##
## If queue ``aq`` is empty, then ``AsyncQueueEmptyError`` exception raised.
var w: Future[void]
proc wakeup(udata: pointer) {.gcsafe.} = w.complete()
if aq.empty():
raise newException(AsyncQueueEmptyError, "AsyncQueue is empty!")
@ -229,7 +222,7 @@ proc getNoWait*[T](aq: AsyncQueue[T]): T =
while len(aq.putters) > 0:
w = aq.putters.popFirst()
if not w.finished:
callSoon(wakeup)
w.complete()
proc put*[T](aq: AsyncQueue[T], item: T) {.async.} =
## Put an ``item`` into the queue ``aq``. If the queue is full, wait until
@ -257,74 +250,3 @@ proc len*[T](aq: AsyncQueue[T]): int {.inline.} =
proc size*[T](aq: AsyncQueue[T]): int {.inline.} =
## Return the maximum number of elements in ``aq``.
result = len(aq.maxsize)
when isMainModule:
# Locks test
block:
var test = ""
var lock = newAsyncLock()
proc testLock(n: int, lock: AsyncLock) {.async.} =
await lock.acquire()
test = test & $n
lock.release()
lock.own()
asyncCheck testLock(0, lock)
asyncCheck testLock(1, lock)
asyncCheck testLock(2, lock)
asyncCheck testLock(3, lock)
asyncCheck testLock(4, lock)
asyncCheck testLock(5, lock)
asyncCheck testLock(6, lock)
asyncCheck testLock(7, lock)
asyncCheck testLock(8, lock)
asyncCheck testLock(9, lock)
lock.release()
poll()
doAssert(test == "0123456789")
# Events test
block:
var test = ""
var event = newAsyncEvent()
proc testEvent(n: int, ev: AsyncEvent) {.async.} =
await ev.wait()
test = test & $n
event.clear()
asyncCheck testEvent(0, event)
asyncCheck testEvent(1, event)
asyncCheck testEvent(2, event)
asyncCheck testEvent(3, event)
asyncCheck testEvent(4, event)
asyncCheck testEvent(5, event)
asyncCheck testEvent(6, event)
asyncCheck testEvent(7, event)
asyncCheck testEvent(8, event)
asyncCheck testEvent(9, event)
event.fire()
poll()
doAssert(test == "0123456789")
# Queues test
block:
const queueSize = 10
const testsCount = 1000
var test = 0
proc task1(aq: AsyncQueue[int]) {.async.} =
for i in 1..(testsCount - 1):
var item = await aq.get()
test -= item
proc task2(aq: AsyncQueue[int]) {.async.} =
for i in 1..testsCount:
await aq.put(i)
test += i
var queue = newAsyncQueue[int](queueSize)
discard task1(queue) or task2(queue)
poll()
doAssert(test == testsCount)

109
tests/testsync.nim Normal file
View File

@ -0,0 +1,109 @@
# Asyncdispatch2 Test Suite
# (c) Copyright 2018
# Status Research & Development GmbH
#
# Licensed under either of
# Apache License, version 2.0, (LICENSE-APACHEv2)
# MIT license (LICENSE-MIT)
import strutils, net, unittest, os
import ../asyncdispatch2
var testLockResult = ""
var testEventResult = ""
var testQueue1Result = 0
var testQueue2Result = 0
proc testLock(n: int, lock: AsyncLock) {.async.} =
await lock.acquire()
testLockResult = testLockResult & $n
lock.release()
proc test1(): string =
var lock = newAsyncLock()
lock.own()
discard testLock(0, lock)
discard testLock(1, lock)
discard testLock(2, lock)
discard testLock(3, lock)
discard testLock(4, lock)
discard testLock(5, lock)
discard testLock(6, lock)
discard testLock(7, lock)
discard testLock(8, lock)
discard testLock(9, lock)
lock.release()
## There must be exactly 20 poll() calls
for i in 0..<20:
poll()
result = testLockResult
proc testEvent(n: int, ev: AsyncEvent) {.async.} =
await ev.wait()
testEventResult = testEventResult & $n
proc test2(): string =
var event = newAsyncEvent()
event.clear()
discard testEvent(0, event)
discard testEvent(1, event)
discard testEvent(2, event)
discard testEvent(3, event)
discard testEvent(4, event)
discard testEvent(5, event)
discard testEvent(6, event)
discard testEvent(7, event)
discard testEvent(8, event)
discard testEvent(9, event)
event.fire()
## There must be exactly 2 poll() calls
poll()
poll()
result = testEventResult
proc task1(aq: AsyncQueue[int]) {.async.} =
var item1 = await aq.get()
var item2 = await aq.get()
testQueue1Result = item1 + item2
proc task2(aq: AsyncQueue[int]) {.async.} =
await aq.put(1000)
await aq.put(2000)
proc test3(): int =
var queue = newAsyncQueue[int](1)
discard task1(queue)
discard task2(queue)
## There must be exactly 2 poll() calls
poll()
poll()
result = testQueue1Result
const testsCount = 1000
const queueSize = 10
proc task3(aq: AsyncQueue[int]) {.async.} =
for i in 1..testsCount:
var item = await aq.get()
testQueue2Result -= item
proc task4(aq: AsyncQueue[int]) {.async.} =
for i in 1..testsCount:
await aq.put(i)
testQueue2Result += i
proc test4(): int =
var queue = newAsyncQueue[int](queueSize)
waitFor(task3(queue) and task4(queue))
result = testQueue2Result
when isMainModule:
suite "Asynchronous sync primitives test suite":
test "AsyncLock() behavior test":
check test1() == "0123456789"
test "AsyncEvent() behavior test":
check test2() == "0123456789"
test "AsyncQueue() behavior test":
check test3() == 3000
test "AsyncQueue() many iterations test":
check test4() == 0