closureleak.nim 1.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354
  1. discard """
  2. outputsub: "true"
  3. disabled: "32bit"
  4. retries: 2
  5. """
  6. type
  7. TFoo* = object
  8. id: int
  9. fn: proc() {.closure.}
  10. var foo_counter = 0
  11. var alive_foos = newseq[int](0)
  12. when defined(gcDestructors):
  13. proc `=destroy`(some: TFoo) =
  14. alive_foos.del alive_foos.find(some.id)
  15. # TODO: fixme: investigate why `=destroy` requires `some.fn` to be `gcsafe`
  16. # the debugging info below came from `symPrototype` in the liftdestructors
  17. # proc (){.closure, gcsafe.}, {tfThread, tfHasAsgn, tfCheckedForDestructor, tfExplicitCallConv}
  18. # var proc (){.closure, gcsafe.}, {tfHasGCedMem}
  19. # it worked by accident with var T destructors because in the sempass2
  20. #
  21. # let argtype = skipTypes(a.typ, abstractInst) # !!! it does't skip `tyVar`
  22. # if argtype.kind == tyProc and notGcSafe(argtype) and not tracked.inEnforcedGcSafe:
  23. # localError(tracked.config, n.info, $n & " is not GC safe")
  24. {.cast(gcsafe).}:
  25. `=destroy`(some.fn)
  26. else:
  27. proc free*(some: ref TFoo) =
  28. #echo "Tfoo #", some.id, " freed"
  29. alive_foos.del alive_foos.find(some.id)
  30. proc newFoo*(): ref TFoo =
  31. when defined(gcDestructors):
  32. new result
  33. else:
  34. new result, free
  35. result.id = foo_counter
  36. alive_foos.add result.id
  37. inc foo_counter
  38. for i in 0 ..< 10:
  39. discard newFoo()
  40. for i in 0 ..< 10:
  41. let f = newFoo()
  42. f.fn = proc =
  43. echo f.id
  44. GC_fullcollect()
  45. echo alive_foos.len <= 3