Change Future identifier type from `int` to `uint`. (#228)
* Change Future identifier type from `int` to `uint64`. * Fix compilation error and tests. * Add integer overflow test, to avoid confusion with next Nim versions, we expect that unsigned integers do not raise any exceptions if overflow happens. * Switch from `uint64` to `uint` type. * Add `uint` overflow tests.
This commit is contained in:
parent
2e57ddb455
commit
3bc0bc36f7
|
@ -9,6 +9,7 @@
|
||||||
# MIT license (LICENSE-MIT)
|
# MIT license (LICENSE-MIT)
|
||||||
|
|
||||||
import std/[os, tables, strutils, heapqueue, options, deques, sequtils]
|
import std/[os, tables, strutils, heapqueue, options, deques, sequtils]
|
||||||
|
import stew/base10
|
||||||
import ./srcloc
|
import ./srcloc
|
||||||
export srcloc
|
export srcloc
|
||||||
|
|
||||||
|
@ -38,7 +39,7 @@ type
|
||||||
state*: FutureState
|
state*: FutureState
|
||||||
error*: ref CatchableError ## Stored exception
|
error*: ref CatchableError ## Stored exception
|
||||||
mustCancel*: bool
|
mustCancel*: bool
|
||||||
id*: int
|
id*: uint64
|
||||||
|
|
||||||
when defined(chronosStackTrace):
|
when defined(chronosStackTrace):
|
||||||
errorStackTrace*: StackTrace
|
errorStackTrace*: StackTrace
|
||||||
|
@ -75,10 +76,10 @@ type
|
||||||
FutureList* = object
|
FutureList* = object
|
||||||
head*: FutureBase
|
head*: FutureBase
|
||||||
tail*: FutureBase
|
tail*: FutureBase
|
||||||
count*: int
|
count*: uint
|
||||||
|
|
||||||
var currentID* {.threadvar.}: int
|
var currentID* {.threadvar.}: uint64
|
||||||
currentID = 0
|
currentID = 0'u64
|
||||||
|
|
||||||
when defined(chronosFutureTracking):
|
when defined(chronosFutureTracking):
|
||||||
var futureList* {.threadvar.}: FutureList
|
var futureList* {.threadvar.}: FutureList
|
||||||
|
@ -86,12 +87,12 @@ when defined(chronosFutureTracking):
|
||||||
|
|
||||||
template setupFutureBase(loc: ptr SrcLoc) =
|
template setupFutureBase(loc: ptr SrcLoc) =
|
||||||
new(result)
|
new(result)
|
||||||
|
currentID.inc()
|
||||||
result.state = FutureState.Pending
|
result.state = FutureState.Pending
|
||||||
when defined(chronosStackTrace):
|
when defined(chronosStackTrace):
|
||||||
result.stackTrace = getStackTrace()
|
result.stackTrace = getStackTrace()
|
||||||
result.id = currentID
|
result.id = currentID
|
||||||
result.location[LocCreateIndex] = loc
|
result.location[LocCreateIndex] = loc
|
||||||
currentID.inc()
|
|
||||||
|
|
||||||
when defined(chronosFutureTracking):
|
when defined(chronosFutureTracking):
|
||||||
result.next = nil
|
result.next = nil
|
||||||
|
@ -198,7 +199,7 @@ proc checkFinished(future: FutureBase, loc: ptr SrcLoc) =
|
||||||
var msg = ""
|
var msg = ""
|
||||||
msg.add("An attempt was made to complete a Future more than once. ")
|
msg.add("An attempt was made to complete a Future more than once. ")
|
||||||
msg.add("Details:")
|
msg.add("Details:")
|
||||||
msg.add("\n Future ID: " & $future.id)
|
msg.add("\n Future ID: " & Base10.toString(future.id))
|
||||||
msg.add("\n Creation location:")
|
msg.add("\n Creation location:")
|
||||||
msg.add("\n " & $future.location[LocCreateIndex])
|
msg.add("\n " & $future.location[LocCreateIndex])
|
||||||
msg.add("\n First completion location:")
|
msg.add("\n First completion location:")
|
||||||
|
|
|
@ -1112,7 +1112,7 @@ when defined(chronosFutureTracking):
|
||||||
yield slider
|
yield slider
|
||||||
slider = slider.next
|
slider = slider.next
|
||||||
|
|
||||||
proc pendingFuturesCount*(): int =
|
proc pendingFuturesCount*(): uint =
|
||||||
## Returns number of pending Futures (Future[T] objects which not yet
|
## Returns number of pending Futures (Future[T] objects which not yet
|
||||||
## completed, cancelled or failed).
|
## completed, cancelled or failed).
|
||||||
futureList.count
|
futureList.count
|
||||||
|
|
|
@ -12,6 +12,9 @@
|
||||||
import ./asyncloop
|
import ./asyncloop
|
||||||
export asyncloop
|
export asyncloop
|
||||||
|
|
||||||
|
when defined(chronosFutureTracking):
|
||||||
|
import stew/base10
|
||||||
|
|
||||||
const
|
const
|
||||||
AllFutureStates* = {FutureState.Pending, FutureState.Cancelled,
|
AllFutureStates* = {FutureState.Pending, FutureState.Cancelled,
|
||||||
FutureState.Finished, FutureState.Failed}
|
FutureState.Finished, FutureState.Failed}
|
||||||
|
@ -28,7 +31,7 @@ proc dumpPendingFutures*(filter = AllFutureStates): string =
|
||||||
## not yet finished).
|
## not yet finished).
|
||||||
## 2. Future[T] objects with ``FutureState.Finished/Cancelled/Failed`` state
|
## 2. Future[T] objects with ``FutureState.Finished/Cancelled/Failed`` state
|
||||||
## which callbacks are scheduled, but not yet fully processed.
|
## which callbacks are scheduled, but not yet fully processed.
|
||||||
var count = 0
|
var count = 0'u
|
||||||
var res = ""
|
var res = ""
|
||||||
when defined(chronosFutureTracking):
|
when defined(chronosFutureTracking):
|
||||||
for item in pendingFutures():
|
for item in pendingFutures():
|
||||||
|
@ -41,13 +44,16 @@ proc dumpPendingFutures*(filter = AllFutureStates): string =
|
||||||
"\"unspecified\""
|
"\"unspecified\""
|
||||||
else:
|
else:
|
||||||
"\"" & procedure & "\""
|
"\"" & procedure & "\""
|
||||||
let item = "Future[" & $item.id & "] with name " & $procname &
|
let item = "Future[" & Base10.toString(item.id) & "] with name " &
|
||||||
" created at " & "<" & filename & ":" & $loc.line & ">" &
|
$procname & " created at " & "<" & filename & ":" &
|
||||||
|
Base10.toString(uint(loc.line)) & ">" &
|
||||||
" and state = " & $item.state & "\n"
|
" and state = " & $item.state & "\n"
|
||||||
res.add(item)
|
res.add(item)
|
||||||
result = $count & " pending Future[T] objects found:\n" & $res
|
Base10.toString(count) & " pending Future[T] objects found:\n" & $res
|
||||||
|
else:
|
||||||
|
"0 pending Future[T] objects found\n"
|
||||||
|
|
||||||
proc pendingFuturesCount*(filter: set[FutureState]): int =
|
proc pendingFuturesCount*(filter: set[FutureState]): uint =
|
||||||
## Returns number of `pending` Future[T] objects which satisfy the ``filter``
|
## Returns number of `pending` Future[T] objects which satisfy the ``filter``
|
||||||
## condition.
|
## condition.
|
||||||
##
|
##
|
||||||
|
@ -57,10 +63,10 @@ proc pendingFuturesCount*(filter: set[FutureState]): int =
|
||||||
if filter == AllFutureStates:
|
if filter == AllFutureStates:
|
||||||
pendingFuturesCount()
|
pendingFuturesCount()
|
||||||
else:
|
else:
|
||||||
var res = 0
|
var res = 0'u
|
||||||
for item in pendingFutures():
|
for item in pendingFutures():
|
||||||
if item.state in filter:
|
if item.state in filter:
|
||||||
inc(res)
|
inc(res)
|
||||||
res
|
res
|
||||||
else:
|
else:
|
||||||
0
|
0'u
|
||||||
|
|
|
@ -1315,4 +1315,39 @@ suite "Future[T] behavior test suite":
|
||||||
f2.finished()
|
f2.finished()
|
||||||
f3.finished()
|
f3.finished()
|
||||||
|
|
||||||
|
test "Unsigned integer overflow test":
|
||||||
|
check:
|
||||||
|
0xFFFF_FFFF_FFFF_FFFF'u64 + 1'u64 == 0'u64
|
||||||
|
0xFFFF_FFFF'u32 + 1'u32 == 0'u32
|
||||||
|
|
||||||
|
when sizeof(uint) == 8:
|
||||||
|
check 0xFFFF_FFFF_FFFF_FFFF'u + 1'u == 0'u
|
||||||
|
else:
|
||||||
|
check 0xFFFF_FFFF'u + 1'u == 0'u
|
||||||
|
|
||||||
|
var v1_64 = 0xFFFF_FFFF_FFFF_FFFF'u64
|
||||||
|
var v2_64 = 0xFFFF_FFFF_FFFF_FFFF'u64
|
||||||
|
var v1_32 = 0xFFFF_FFFF'u32
|
||||||
|
var v2_32 = 0xFFFF_FFFF'u32
|
||||||
|
inc(v1_64)
|
||||||
|
inc(v1_32)
|
||||||
|
check:
|
||||||
|
v1_64 == 0'u64
|
||||||
|
v2_64 + 1'u64 == 0'u64
|
||||||
|
v1_32 == 0'u32
|
||||||
|
v2_32 + 1'u32 == 0'u32
|
||||||
|
|
||||||
|
when sizeof(uint) == 8:
|
||||||
|
var v1_u = 0xFFFF_FFFF_FFFF_FFFF'u
|
||||||
|
var v2_u = 0xFFFF_FFFF_FFFF_FFFF'u
|
||||||
|
inc(v1_u)
|
||||||
|
check:
|
||||||
|
v1_u == 0'u
|
||||||
|
v2_u + 1'u == 0'u
|
||||||
|
else:
|
||||||
|
var v1_u = 0xFFFF_FFFF'u
|
||||||
|
var v2_u = 0xFFFF_FFFF'u
|
||||||
|
inc(v1_u)
|
||||||
|
check:
|
||||||
|
v1_u == 0'u
|
||||||
|
v2_u + 1'u == 0'u
|
||||||
|
|
|
@ -12,19 +12,20 @@ when defined(nimHasUsed): {.used.}
|
||||||
|
|
||||||
suite "Asynchronous utilities test suite":
|
suite "Asynchronous utilities test suite":
|
||||||
when defined(chronosFutureTracking):
|
when defined(chronosFutureTracking):
|
||||||
proc getCount(): int =
|
proc getCount(): uint =
|
||||||
# This procedure counts number of Future[T] in double-linked list via list
|
# This procedure counts number of Future[T] in double-linked list via list
|
||||||
# iteration.
|
# iteration.
|
||||||
result = 0
|
var res = 0'u
|
||||||
for item in pendingFutures():
|
for item in pendingFutures():
|
||||||
inc(result)
|
inc(res)
|
||||||
|
res
|
||||||
|
|
||||||
test "Future clean and leaks test":
|
test "Future clean and leaks test":
|
||||||
when defined(chronosFutureTracking):
|
when defined(chronosFutureTracking):
|
||||||
if pendingFuturesCount(WithoutFinished) == 0:
|
if pendingFuturesCount(WithoutFinished) == 0'u:
|
||||||
if pendingFuturesCount(OnlyFinished) > 0:
|
if pendingFuturesCount(OnlyFinished) > 0'u:
|
||||||
poll()
|
poll()
|
||||||
check pendingFuturesCount() == 0
|
check pendingFuturesCount() == 0'u
|
||||||
else:
|
else:
|
||||||
echo dumpPendingFutures()
|
echo dumpPendingFutures()
|
||||||
check false
|
check false
|
||||||
|
@ -35,31 +36,31 @@ suite "Asynchronous utilities test suite":
|
||||||
when defined(chronosFutureTracking):
|
when defined(chronosFutureTracking):
|
||||||
var fut1 = newFuture[void]()
|
var fut1 = newFuture[void]()
|
||||||
check:
|
check:
|
||||||
getCount() == 1
|
getCount() == 1'u
|
||||||
pendingFuturesCount() == 1
|
pendingFuturesCount() == 1'u
|
||||||
var fut2 = newFuture[void]()
|
var fut2 = newFuture[void]()
|
||||||
check:
|
check:
|
||||||
getCount() == 2
|
getCount() == 2'u
|
||||||
pendingFuturesCount() == 2
|
pendingFuturesCount() == 2'u
|
||||||
var fut3 = newFuture[void]()
|
var fut3 = newFuture[void]()
|
||||||
check:
|
check:
|
||||||
getCount() == 3
|
getCount() == 3'u
|
||||||
pendingFuturesCount() == 3
|
pendingFuturesCount() == 3'u
|
||||||
fut1.complete()
|
fut1.complete()
|
||||||
poll()
|
poll()
|
||||||
check:
|
check:
|
||||||
getCount() == 2
|
getCount() == 2'u
|
||||||
pendingFuturesCount() == 2
|
pendingFuturesCount() == 2'u
|
||||||
fut2.fail(newException(ValueError, ""))
|
fut2.fail(newException(ValueError, ""))
|
||||||
poll()
|
poll()
|
||||||
check:
|
check:
|
||||||
getCount() == 1
|
getCount() == 1'u
|
||||||
pendingFuturesCount() == 1
|
pendingFuturesCount() == 1'u
|
||||||
fut3.cancel()
|
fut3.cancel()
|
||||||
poll()
|
poll()
|
||||||
check:
|
check:
|
||||||
getCount() == 0
|
getCount() == 0'u64
|
||||||
pendingFuturesCount() == 0
|
pendingFuturesCount() == 0'u64
|
||||||
else:
|
else:
|
||||||
skip()
|
skip()
|
||||||
|
|
||||||
|
@ -70,17 +71,17 @@ suite "Asynchronous utilities test suite":
|
||||||
|
|
||||||
var fut = simpleProc()
|
var fut = simpleProc()
|
||||||
check:
|
check:
|
||||||
getCount() == 2
|
getCount() == 2'u64
|
||||||
pendingFuturesCount() == 2
|
pendingFuturesCount() == 2'u64
|
||||||
|
|
||||||
waitFor fut
|
waitFor fut
|
||||||
check:
|
check:
|
||||||
getCount() == 1
|
getCount() == 1'u64
|
||||||
pendingFuturesCount() == 1
|
pendingFuturesCount() == 1'u64
|
||||||
|
|
||||||
poll()
|
poll()
|
||||||
check:
|
check:
|
||||||
getCount() == 0
|
getCount() == 0'u64
|
||||||
pendingFuturesCount() == 0
|
pendingFuturesCount() == 0'u64
|
||||||
else:
|
else:
|
||||||
skip()
|
skip()
|
||||||
|
|
Loading…
Reference in New Issue