summary refs log tree commit diff stats
path: root/tests
diff options
context:
space:
mode:
authorAndreas Rumpf <rumpf_a@web.de>2020-07-04 07:37:24 +0200
committerGitHub <noreply@github.com>2020-07-04 07:37:24 +0200
commit1854d29781aff913ca6892cbf73df91b0399397e (patch)
tree4a71bc7988c753ef1acab28776edec691c6d27bd /tests
parent695154970d839add2fbbe9754e9e638511120729 (diff)
downloadNim-1854d29781aff913ca6892cbf73df91b0399397e.tar.gz
scoped memory management (#14790)
* fixes the regressions
* closes #13936
* scope based memory management implemented
* enabled tcontrolflow.nim test case
* final cleanups
Diffstat (limited to 'tests')
-rw-r--r--tests/arc/tarcmisc.nim36
-rw-r--r--tests/arc/tcontrolflow.nim21
-rw-r--r--tests/arc/tmovebug.nim6
-rw-r--r--tests/arc/tthread.nim10
-rw-r--r--tests/arc/tweave.nim154
-rw-r--r--tests/async/testmanyasyncevents.nim3
-rw-r--r--tests/casestmt/t12785.nim8
-rw-r--r--tests/destructor/tdestructor.nim38
8 files changed, 240 insertions, 36 deletions
diff --git a/tests/arc/tarcmisc.nim b/tests/arc/tarcmisc.nim
index 898efd138..e66d9a75f 100644
--- a/tests/arc/tarcmisc.nim
+++ b/tests/arc/tarcmisc.nim
@@ -173,3 +173,39 @@ proc bug14495 =
     echo o[]
 
 bug14495()
+
+when false:
+  # bug #14396
+  type
+    Spinny = ref object
+      t: ref int
+      text: string
+
+  proc newSpinny*(): Spinny =
+    Spinny(t: new(int), text: "hello")
+
+  proc spinnyLoop(x: ref int, spinny: sink Spinny) =
+    echo x[]
+
+  proc start*(spinny: sink Spinny) =
+    spinnyLoop(spinny.t, spinny)
+
+  var spinner1 = newSpinny()
+  spinner1.start()
+
+  # bug #14345
+
+  type
+    SimpleLoopB = ref object
+      children: seq[SimpleLoopB]
+      parent: SimpleLoopB
+
+  proc addChildLoop(self: SimpleLoopB, loop: SimpleLoopB) =
+    self.children.add loop
+
+  proc setParent(self: SimpleLoopB, parent: SimpleLoopB) =
+    self.parent = parent
+    self.parent.addChildLoop(self)
+
+  var l = SimpleLoopB()
+  l.setParent(l)
diff --git a/tests/arc/tcontrolflow.nim b/tests/arc/tcontrolflow.nim
index efe51cc32..478806567 100644
--- a/tests/arc/tcontrolflow.nim
+++ b/tests/arc/tcontrolflow.nim
@@ -11,15 +11,13 @@ begin true
 if
 end true
 7
+##index 2 not in 0 .. 1##
 '''
   cmd: "nim c --gc:arc -d:danger $file"
-  disabled: "true"
 """
 # we use the -d:danger switch to detect uninitialized stack
 # slots more reliably (there shouldn't be any, of course).
 
-# XXX Enable once scope based destruction works!
-
 type
   Foo = object
     id: int
@@ -68,12 +66,25 @@ proc run(data: Control) =
   evt.control = data
   if evt.button == 1:
     discard
-  else: 
+  else:
     return
-  
+
   echo data.x
 
 var c = Control(x: 7)
 
 run(c)
 
+proc sysFatal(exceptn: typedesc, message: string) {.inline, noreturn.} =
+  var buf = newStringOfCap(200)
+  add(buf, "##")
+  add(buf, message)
+  add(buf, "##")
+  echo buf
+
+proc ifexpr(i, a, b: int) {.compilerproc, noinline.} =
+  sysFatal(IndexDefect,
+    if b < a: "index out of bounds, the container is empty"
+    else: "index " & $i & " not in " & $a & " .. " & $b)
+
+ifexpr(2, 0, 1)
diff --git a/tests/arc/tmovebug.nim b/tests/arc/tmovebug.nim
index 951979a46..ec0fce9a8 100644
--- a/tests/arc/tmovebug.nim
+++ b/tests/arc/tmovebug.nim
@@ -247,7 +247,7 @@ iterator combinations[T](s: openarray[T], k: int): seq[T] =
       break
 
 type
-  UndefEx = object of Exception
+  UndefEx = object of ValueError
 
 proc main2 =
   var delayedSyms = @[1, 2, 3]
@@ -318,10 +318,10 @@ proc `=sink`(dest: var O2, src: O2) =
 
 var testSeq: O2
 
-proc Update(): void =
+proc update() =
   # testSeq.add(0) # uncommenting this line fixes the leak
   testSeq = O2(s: @[])
   testSeq.s.add(0)
 
 for i in 1..3:
-  Update()
+  update()
diff --git a/tests/arc/tthread.nim b/tests/arc/tthread.nim
index c653c753f..8a55a666e 100644
--- a/tests/arc/tthread.nim
+++ b/tests/arc/tthread.nim
@@ -14,7 +14,7 @@ type
     p: int
   MyObjRef = ref MyObj
 
-proc `=destroy`(x: var MyObj) = 
+proc `=destroy`(x: var MyObj) =
   if x.p != 0:
     echo "destroyed"
 
@@ -48,16 +48,16 @@ proc thread5(x: sink MyObjRef): MyObjRef =
   os.sleep(1000)
   result = x
 
-proc ref_forwarding_test = 
+proc ref_forwarding_test =
   var x = new(MyObj)
   x[].p = 2
   var y = spawn thread4(x)
- 
-proc ref_sink_forwarding_test = 
+
+proc ref_sink_forwarding_test =
   var x = new(MyObj)
   x[].p = 2
   var y = spawn thread5(x)
 
 ref_forwarding_test()
-ref_sink_forwarding_test()  
+ref_sink_forwarding_test()
 sync()
diff --git a/tests/arc/tweave.nim b/tests/arc/tweave.nim
new file mode 100644
index 000000000..220d65f97
--- /dev/null
+++ b/tests/arc/tweave.nim
@@ -0,0 +1,154 @@
+discard """
+  outputsub: '''Success'''
+  cmd: '''nim c --gc:arc --threads:on $file'''
+  disabled: "bsd"
+"""
+
+# bug #13936
+
+import std/atomics
+
+const MemBlockSize = 256
+
+type
+  ChannelSPSCSingle* = object
+    full{.align: 128.}: Atomic[bool]
+    itemSize*: uint8
+    buffer*{.align: 8.}: UncheckedArray[byte]
+
+proc `=`(
+    dest: var ChannelSPSCSingle,
+    source: ChannelSPSCSingle
+  ) {.error: "A channel cannot be copied".}
+
+proc initialize*(chan: var ChannelSPSCSingle, itemsize: SomeInteger) {.inline.} =
+  ## If ChannelSPSCSingle is used intrusive another data structure
+  ## be aware that it should be the last part due to ending by UncheckedArray
+  ## Also due to 128 bytes padding, it automatically takes half
+  ## of the default MemBlockSize
+  assert itemsize.int in 0 .. int high(uint8)
+  assert itemSize.int +
+          sizeof(chan.itemsize) +
+          sizeof(chan.full) < MemBlockSize
+
+  chan.itemSize = uint8 itemsize
+  chan.full.store(false, moRelaxed)
+
+func isEmpty*(chan: var ChannelSPSCSingle): bool {.inline.} =
+  not chan.full.load(moAcquire)
+
+func tryRecv*[T](chan: var ChannelSPSCSingle, dst: var T): bool {.inline.} =
+  ## Try receiving the item buffered in the channel
+  ## Returns true if successful (channel was not empty)
+  ##
+  ## ⚠ Use only in the consumer thread that reads from the channel.
+  assert (sizeof(T) == chan.itemsize.int) or
+          # Support dummy object
+          (sizeof(T) == 0 and chan.itemsize == 1)
+
+  let full = chan.full.load(moAcquire)
+  if not full:
+    return false
+  dst = cast[ptr T](chan.buffer.addr)[]
+  chan.full.store(false, moRelease)
+  return true
+
+func trySend*[T](chan: var ChannelSPSCSingle, src: sink T): bool {.inline.} =
+  ## Try sending an item into the channel
+  ## Reurns true if successful (channel was empty)
+  ##
+  ## ⚠ Use only in the producer thread that writes from the channel.
+  assert (sizeof(T) == chan.itemsize.int) or
+          # Support dummy object
+          (sizeof(T) == 0 and chan.itemsize == 1)
+
+  let full = chan.full.load(moAcquire)
+  if full:
+    return false
+  cast[ptr T](chan.buffer.addr)[] = src
+  chan.full.store(true, moRelease)
+  return true
+
+# Sanity checks
+# ------------------------------------------------------------------------------
+when isMainModule:
+
+  when not compileOption("threads"):
+    {.error: "This requires --threads:on compilation flag".}
+
+  template sendLoop[T](chan: var ChannelSPSCSingle,
+                       data: sink T,
+                       body: untyped): untyped =
+    while not chan.trySend(data):
+      body
+
+  template recvLoop[T](chan: var ChannelSPSCSingle,
+                       data: var T,
+                       body: untyped): untyped =
+    while not chan.tryRecv(data):
+      body
+
+  type
+    ThreadArgs = object
+      ID: WorkerKind
+      chan: ptr ChannelSPSCSingle
+
+    WorkerKind = enum
+      Sender
+      Receiver
+
+  template Worker(id: WorkerKind, body: untyped): untyped {.dirty.} =
+    if args.ID == id:
+      body
+
+  proc thread_func(args: ThreadArgs) =
+
+    # Worker RECEIVER:
+    # ---------
+    # <- chan
+    # <- chan
+    # <- chan
+    #
+    # Worker SENDER:
+    # ---------
+    # chan <- 42
+    # chan <- 53
+    # chan <- 64
+    Worker(Receiver):
+      var val: int
+      for j in 0 ..< 10:
+        args.chan[].recvLoop(val):
+          # Busy loop, in prod we might want to yield the core/thread timeslice
+          discard
+        echo "                  Receiver got: ", val
+        doAssert val == 42 + j*11
+
+    Worker(Sender):
+      doAssert args.chan.full.load(moRelaxed) == false
+      for j in 0 ..< 10:
+        let val = 42 + j*11
+        args.chan[].sendLoop(val):
+          # Busy loop, in prod we might want to yield the core/thread timeslice
+          discard
+        echo "Sender sent: ", val
+
+  proc main() =
+    echo "Testing if 2 threads can send data"
+    echo "-----------------------------------"
+    var threads: array[2, Thread[ThreadArgs]]
+
+    var chan = cast[ptr ChannelSPSCSingle](allocShared(MemBlockSize))
+    chan[].initialize(itemSize = sizeof(int))
+
+    createThread(threads[0], thread_func, ThreadArgs(ID: Receiver, chan: chan))
+    createThread(threads[1], thread_func, ThreadArgs(ID: Sender, chan: chan))
+
+    joinThread(threads[0])
+    joinThread(threads[1])
+
+    freeShared(chan)
+
+    echo "-----------------------------------"
+    echo "Success"
+
+  main()
diff --git a/tests/async/testmanyasyncevents.nim b/tests/async/testmanyasyncevents.nim
index 5a103736f..9fdd01b4f 100644
--- a/tests/async/testmanyasyncevents.nim
+++ b/tests/async/testmanyasyncevents.nim
@@ -1,8 +1,9 @@
 discard """
 output: '''
 hasPendingOperations: false
-triggerCount: 100 
+triggerCount: 100
 '''
+disabled: "windows"
 """
 
 import asyncDispatch
diff --git a/tests/casestmt/t12785.nim b/tests/casestmt/t12785.nim
index bf0f30d42..7177fb9c2 100644
--- a/tests/casestmt/t12785.nim
+++ b/tests/casestmt/t12785.nim
@@ -3,6 +3,8 @@ discard """
   output: '''copied
 copied
 2
+destroyed
+destroyed
 copied
 copied
 2
@@ -37,9 +39,9 @@ proc test(a: range[0..1], arg: ObjWithDestructor) =
     inc iteration
 
     case a
-    of 0:  
-        assert false
-    of 1:      
+    of 0:
+      assert false
+    of 1:
       echo b
       if iteration == 2:
         break
diff --git a/tests/destructor/tdestructor.nim b/tests/destructor/tdestructor.nim
index 9fd47fe00..e081eb251 100644
--- a/tests/destructor/tdestructor.nim
+++ b/tests/destructor/tdestructor.nim
@@ -1,27 +1,27 @@
 discard """
-  output: '''----
+  output: '''----1
 myobj constructed
 myobj destroyed
-----
+----2
 mygeneric1 constructed
 mygeneric1 destroyed
-----
+----3
 mygeneric2 constructed
 mygeneric2 destroyed
 myobj destroyed
-----
+----4
 mygeneric3 constructed
 mygeneric1 destroyed
-----
+----5
 mydistinctObj constructed
 myobj destroyed
 mygeneric2 destroyed
-------------------
-----
-----
-myobj destroyed
+------------------8
 mygeneric1 destroyed
----
+----6
+myobj destroyed
+----7
+---9
 myobj destroyed
 myobj destroyed
 '''
@@ -114,19 +114,19 @@ proc mydistinctObj =
 
   echo "mydistinctObj constructed"
 
-echo "----"
+echo "----1"
 myobj()
 
-echo "----"
+echo "----2"
 mygeneric1()
 
-echo "----"
+echo "----3"
 mygeneric2[int](10)
 
-echo "----"
+echo "----4"
 mygeneric3()
 
-echo "----"
+echo "----5"
 mydistinctObj()
 
 proc caseobj =
@@ -134,16 +134,16 @@ proc caseobj =
     var o1 = TCaseObj(kind: A, x: TMyGeneric1[int](x: 10))
 
   block:
-    echo "----"
+    echo "----6"
     var o2 = TCaseObj(kind: B, y: open())
 
   block:
-    echo "----"
+    echo "----7"
     var o3 = TCaseObj(kind: D, innerKind: B, r: "test",
                       p: TMyGeneric3[int, float, string](x: 10, y: 1.0, z: "test"))
 
 
-echo "------------------"
+echo "------------------8"
 caseobj()
 
 proc caseobj_test_sink: TCaseObj =
@@ -153,7 +153,7 @@ proc caseobj_test_sink: TCaseObj =
   result = TCaseObj(kind: B, y: open())
 
 
-echo "---"
+echo "---9"
 discard caseobj_test_sink()
 
 # issue #14315