closureleak.nim 1.4 KB

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