Closed PMunch closed 3 months ago
Removing the line which causes the exception makes Valgrind run clean, same for using a variable to store the result and explicitly returning it.
!nim c --gc:arc -d:useMalloc --expandArc:initMyObj
import system/ansi_c
type
MyError = object of CatchableError
MyObj = object
internal: pointer
proc `=destroy`(x: MyObj) =
if x.internal.isNil: return
x.internal.c_free
proc `=copy`(x: var MyObj, y: MyObj) =
if x.internal != y.internal:
x.internal.c_free
x.internal = y.internal
proc initMyObj(x: string): MyObj =
result = MyObj(internal: c_malloc(126))
if x.len < 128:
result.internal.copyMem(x[0].addr, x.len)
else:
raise newException(MyError, "This is an error")
import strutils
try:
echo initMyObj("Hello world")
echo initMyObj("Hello world".repeat(128))
except MyError as e:
echo "Got an error!"
echo e.msg
@juancarlospaco (collaborator)devel :+1: $\color{green}\textbf{\large OK}$
Output
--expandArc: initMyObj
result = MyObj(internal: c_malloc(126'u))
if len(x) < 128:
copyMem(result.internal, addr(x[0]), chckRange(len(x), 0, 9223372036854775807))
else:
raise
(ref MyError)(msg: "This is an error", parent: nil)
-- end of expandArc ------------------------
(internal: ...)
Got an error!
This is an error
2023-09-08T13:29:37
2023-09-08T13:29:40
2 minutes
--expandArc: initMyObj
result = MyObj(internal: c_malloc(126'u))
if len(x) < 128:
copyMem(result.internal, addr(x[0]), chckRange(len(x), 0, 9223372036854775807))
else:
raise
(ref MyError)(msg: "This is an error", parent: nil)
-- end of expandArc ------------------------
(internal: ...)
Got an error!
This is an error
2023-09-08T13:29:40
2023-09-08T13:29:42
2 minutes
--expandArc: initMyObj
result = MyObj(internal: c_malloc(126'u))
if len(x) < 128:
copyMem(result.internal, addr(x[0]), chckRange(len(x), 0, 9223372036854775807))
else:
raise
(ref MyError)(msg: "This is an error", parent: nil)
-- end of expandArc ------------------------
(internal: ...)
Got an error!
This is an error
2023-09-08T13:29:42
2023-09-08T13:29:45
2 minutes
Error: Command failed: nim c --gc:arc -d:useMalloc --expandArc:initMyObj -d:nimArcDebug -d:nimArcIds -d:nimAllocPagesViaMalloc -d:useSysAssert -d:useGcAssert -d:nimLeakDetector --debugger:native --debuginfo:on -d:nimDebugDlOpen -d:ssl -d:nimDisableCertificateValidation --forceBuild:on --colors:off --verbosity:0 --hints:off --warnings:off --lineTrace:off --nimcache:/home/runner/work/Nim/Nim --out:/home/runner/work/Nim/Nim/temp /home/runner/work/Nim/Nim/temp.nim && valgrind /home/runner/work/Nim/Nim/temp
/home/runner/work/Nim/Nim/temp.nim(6, 1) Error: signature for '=destroy' must be proc[T: object](x: var T)
2023-09-08T13:29:48
2023-09-08T13:29:48
353.13 Kb (361,600 bytes)
```cpp
#define NIM_INTBITS 64
#include "nimbase.h"
#include 2023-09-08T13:29:53
2023-09-08T13:29:53
353.13 Kb (361,600 bytes)
```cpp
#define NIM_INTBITS 64
#include "nimbase.h"
#include 2023-09-08T13:30:09
2023-09-08T13:30:09
353.13 Kb (361,600 bytes)
```cpp
#define NIM_INTBITS 64
#include "nimbase.h"
#include 2023-09-08T13:30:20
2023-09-08T13:30:20
353.13 Kb (361,600 bytes)
```cpp
#define NIM_INTBITS 64
#include "nimbase.h"
#include 2023-09-08T13:30:30
2023-09-08T13:30:30
353.13 Kb (361,600 bytes)
```cpp
#define NIM_INTBITS 64
#include "nimbase.h"
#include 11.4.0
2.35
3.18.1
17.1
5.15.0
2023-09-08T13:28:46Z
2
nim c --gc:arc -d:useMalloc --expandArc:initMyObj -d:nimArcDebug -d:nimArcIds -d:nimAllocPagesViaMalloc -d:useSysAssert -d:useGcAssert -d:nimLeakDetector --debugger:native --debuginfo:on -d:nimDebugDlOpen -d:ssl -d:nimDisableCertificateValidation --forceBuild:on --colors:off --verbosity:0 --hints:off --warnings:off --lineTrace:off --nimcache:/home/runner/work/Nim/Nim --out:/home/runner/work/Nim/Nim/temp /home/runner/work/Nim/Nim/temp.nim && valgrind /home/runner/work/Nim/Nim/temp
20 minutes
bisecting 8
commits at 0
commits per second. More info on the logs: https://github.com/nim-lang/Nim/actions/runs/6122353948/job/16617925278#step:5:79
No need for anything special, result just doesn't receive a call to destroy here. This will eat up memory pretty quick
proc leakMemory(): seq[char] =
result = newSeq[char](128)
raise newException(CatchableError, "This is an error")
while true:
try:
discard leakMemory()
except CatchableError:
echo GC_getStatistics()
This also leaks, so not exclusive to result (--exceptions:setjmp
doesn't leak for this one, but I got no clue how to make use of that handler)
proc leakMemory(): seq[char] =
var res = newSeq[char](128)
try:
raise newException(CatchableError, "This is an error")
finally:
return res
while true:
try:
discard leakMemory()
except CatchableError:
echo GC_getStatistics()
Another example: it leaks with ORC
import std/json
let startMemory = getOccupiedMem()
for i in 0 .. 10000:
try:
discard parseJson"""{ invalid"""
except:
discard
doAssert(abs(getOccupiedMem() - startMemory) < 4 * 1024 * 1024)
Just checked to see if this had been fixed in 2.0.4 and it is still present.
Description
Working further on my MAPM bindings I discovered that throwing exceptions while using
result
caused ARC to not create a destroy call forresult
. My smallest repro currently:Nim Version
v2.0.0 and devel
Current Output
From
--expandArc:initMyObj
:Confirmed with
valgrind --leak-check=full
that it does in fact leak:Expected Output
Based on the output of replacing
result
with a variable and returning it at the end of the function:Possible Solution
No response
Additional Information
No response