-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Calling =destroy
on non-var fields with stack based collections (Table, HashSet, etc)
#24579
Comments
Don't manually call the proc |
When you provide a custom |
The docs are correct. |
It should say these fields for manual memory management such as objects allocated by alloc() , file descriptors etc, but not nim traced objects. |
Why should it say something that is wrong? If you override the destructor you need to destroy everything inside the destructor. This design choice allows us to avoid recursions more easily for linked data structures. |
I believe you may misunderstand the details of how the destructors work with ARC/ORC. The documentation only shows resources created by alloc, file descriptors, etc. However all objects need to have their destructors called. Containers like Overall Nim with ARC doesn't trace objects. ORC only traces cycles of refs. Otherwise all memory is mamanged in the RAII style based on the destructors being called. |
Really? |
@elcritch Here is a simple demo The mm arc test>%nim20_home%\bin\nim c -d:release --mm:arc destroytest.nim
Hint: used config file '..\nim-2.0.14\config\nim.cfg' [Conf]
Hint: used config file '..\nim-2.0.14\config\config.nims' [Conf]
......................................................................
CC: ../../../Programs/nim-2.0.14/lib/system.nim
CC: destroytest.nim
Hint: [Link]
Hint: mm: arc; threads: on; opt: speed; options: -d:release
27515 lines; 2.750s; 31.238MiB peakmem; proj: ..destroytest.nim; out: ..\destroytest.exe [SuccessX]
>destroytest
Bar2#4 destroyed.
Foo#3 destroyed.
Bar#2 destroyed.
Foo#1 destroyed. The orc test>%nim20_home%\bin\nim c -d:release --mm:orc destroytest.nim
Hint: used config file '..\nim-2.0.14\config\nim.cfg' [Conf]
Hint: used config file '..\nim-2.0.14\config\config.nims' [Conf]
........................................................................
CC: ../../../Programs/nim-2.0.14/lib/system/exceptions.nim
CC: ../../../Programs/nim-2.0.14/lib/std/private/digitsutils.nim
CC: ../../../Programs/nim-2.0.14/lib/system/dollars.nim
CC: ../../../Programs/nim-2.0.14/lib/std/typedthreads.nim
CC: ../../../Programs/nim-2.0.14/lib/std/exitprocs.nim
CC: ../../../Programs/nim-2.0.14/lib/std/syncio.nim
CC: ../../../Programs/nim-2.0.14/lib/system.nim
CC: destroytest.nim
Hint: [Link]
Hint: mm: orc; threads: on; opt: speed; options: -d:release
28092 lines; 3.500s; 31.254MiB peakmem; proj: ..\destroytest.nim; out: ..\destroytest.exe [SuccessX]
>destroytest
Bar2#4 destroyed.
Foo#3 destroyed.
Bar#2 destroyed.
Foo#1 destroyed. The test source# destroytest.nim
type
Foo = object
id: int
Bar = object
id: int
foo: Foo
Bar2 = object
id: int
fref: ref Foo
proc `=destroy`(foo: Foo) =
echo "Foo#" & $(foo.id) & " destroyed."
proc `=destroy`(bar: Bar) =
echo "Bar#" & $(bar.id) & " destroyed."
proc `=destroy`(bar2: Bar2) =
echo "Bar2#" & $(bar2.id) & " destroyed."
proc main() =
discard Bar(id: 2, foo: Foo(id: 1))
discard Bar2(id: 4, fref: (ref Foo)(id: 3))
main() |
Heh seems to be a bug that was introduced between 2.0.0 and 2.0.2. It very much should not call the builtin destructor. |
Yeah I just filed a bug. Appears to be something with the |
Also could relate to the lowered performance in Nim 2.x on some of those Fibonnaci benchmarks? If a bunch of extra copies/destroys or whatnot are being added. |
It appears you found a nice bug with discarded variables. What happens with your example is that the 2.x compiler is doing some funky extra creations/destruction which shouldn't be there. Run this version with variables and you'll see that if you don't call # destroytest.nim
# run with nim c -d:debug -d:useMalloc --mm:arc -d:traceArc -d:nimArcDebug -r "destroytest.nim"
import std/strutils
type
Foo = object
id: int
Bar = object
id: int
foo: Foo
Baz = object
id: int
fref: ref Foo
var depth = 1
template printDestroying(x) =
depth.inc
echo "\t".repeat(depth), "Destroying: ", x.repr, " addr: 0x", addr(x).pointer.repr
template printDestroyed(x) =
echo "\t".repeat(depth), "Destroyed: ", x.repr, " addr: 0x", addr(x).pointer.repr
depth.dec
proc `=destroy`*(x: var Foo) =
printDestroying(x)
addr(x)[].id = -1
printDestroyed(x)
proc `=destroy`*(x: var Bar) =
printDestroying(x)
`=destroy`(x.foo)
addr(x)[].id = -1
printDestroyed(x)
proc `=destroy`*(x: var Baz) =
printDestroying(x)
if not x.fref.isNil:
`=destroy`(x.fref)
addr(x)[].id = -1
printDestroyed(x)
proc test2() =
echo "\n=== Test2 ==="
let x1 = Foo(id: 1)
let x2 = Bar(id: 2, foo: x1)
let x3 = (ref Foo)(id: 3)
let x4 = Baz(id: 4, fref: x3)
echo "addr x1: ", $(x1), " ", x1.unsafeAddr.pointer.repr
echo "addr x2: ", $(x2), " ", x2.unsafeAddr.pointer.repr
echo "addr x2.foo: ", $(x2.foo), " ", x2.foo.unsafeAddr.pointer.repr
echo "addr x3: ref Foo: ", $(x3[]), " ", cast[pointer](x3).repr
echo "addr x4: Baz #", $(x4), " ", x4.unsafeAddr.pointer.repr
echo "addr x4.fref: ", $(x4.fref[]), " ", cast[pointer](x4.fref).repr
echo ""
test2() |
I think that impllicitly calling |
It doesn't matter what you think, it matters what the spec says. |
The spec says that variables are destroyed via this hook when they go out of scope or when the routine they were declared in is about to return. So the behavior is same as C++ RAII or Rust. |
About the original issue, the generic non-var overload of when defined(nimPreviewNonVarDestructor) and defined(gcDestructors):
proc `=destroy`*(agent: AgentObj) =
`=destroy`(agent.subscribers)
else:
proc `=destroy`*(agent: var AgentObj) =
`=destroy`(agent.subscribers) |
@Araq But nim 2.x(2.0.8, 2.0.14, 2.2.0 etc) also proved my this thinking. |
@forchid I believe you are misunderstanding something with "tracing" and how destructors work with RAII. Yes when an object goes out of scope their However, if an object isn't on the current stack, but exists inside another object What you're seeing in you example with block:
let foo'gensym1 = Foo(id: 1)
let bar'gensym2 = Bar(id: 2, foo'gensym1)
`=destroy`(tmp'gensym1)
`=destroy`(tmp'gensym2) However, it's up to |
Nice! Though in Nim 2.x the Though it'd be great if the |
Description
Nim Version
Since the
var
variant of destroy was added, but currently:Current Output
Known Workarounds
Maybe:
Additional Information
No response
The text was updated successfully, but these errors were encountered: