During ios app development I've been encountering weird GC crashes, but I always dismissed as "I must be doing something wrong, let's debug that later". After fixing everything I could see I still get them for stuff which doesn't have any sense. I'm implementing the logic in Nimrod and letting the UI make calls to exported C functions. At the moment I load objects form the database into a big global array and have them as ref object. All exported C API works on these references.
To be consistent with objc memory management, I've implemented setters/getters which retain/release Nimrod objects so they don't disappear while something else is going. This is their implementation:
proc retain_weight*(w: PWeight) {.exportc.} =
if not w.isNil:
#echo cast[int](w), " plus"
GC_ref(w)
proc release_weight*(w: var PWeight) {.exportc.} =
if not w.isNil:
#echo cast[int](w), " minus"
GC_unref(w)
w = nil
From the plus/minus you can see I've log debugged allocations, all the calls are paired correctly. Still, between these calls I call other nimrod procs which format strings for the UI and return a cstring which is then duplicated into an NSString for the UI.
So when I scroll the list of objects in the app, the only Nimrod procs being called are these retain/release wrappers (objects never really get released as they are still referenced by Nimrod's global array) and two formatting procs which return C strings. But something crashes when trying to retain one object after some some time:
(lldb) bt
* thread #1: tid = 0x23da56, 0x0003e91a Weight diary`canbecycleroot_42257(c=0x08b94160) + 122 at seohyun_seohyun_types.m:224, queue = 'com.apple.main-thread, stop reason = EXC_BAD_ACCESS (code=2, address=0x5)
frame #0: 0x0003e91a Weight diary`canbecycleroot_42257(c=0x08b94160) + 122 at seohyun_seohyun_types.m:224
frame #1: 0x0003eaf2 Weight diary`incref_44620(c=0x08b94160) + 146 at seohyun_seohyun_types.m:254
frame #2: 0x0003db4f Weight diary`nimGCref(p=0x08b94168) + 127 at seohyun_seohyun_types.m:278
frame #3: 0x0003da89 Weight diary`retain_weight(w=0x08b94168) + 153 at seohyun_seohyun_types.m:288
frame #4: 0x0004034e Weight diary`-[EHWeight_cell setWeight:](self=0x08e6fd00, _cmd=0x000416bb, w=0x08b94168) + 46 at EHWeight_cell.m:38
frame #5: 0x00004dde Weight diary`-[EHDB_vc tableView:cellForRowAtIndexPath:](self=0x08ab3070, _cmd=0x007f2adf, tableView=0x099b5a00, indexPath=0x08e6c3c0) + 254 at EHDB_vc.m:122
frame #6: 0x0017561f UIKit`-[UITableView _createPreparedCellForGlobalRow:withIndexPath:] + 412
...more iOS framework calls...
(lldb) f
frame #0: 0x0003e91a Weight diary`canbecycleroot_42257(c=0x08b94160) + 122 at seohyun_seohyun_types.m:224
221 nimln(119, "gc.nim");
222 nimln(731, "system.nim");
223 nimln(731, "system.nim");
-> 224 result = !((((*(*c).Typ).flags &(1<<((((NU8) 1))&7)))!=0));
225 popFrame();
226 return result;
227 }
(lldb)
The retain/release + format calls don't give problems when unit tested inside nimrod. The problems disappear if I avoid the retain/release calls. Any ideas on how to debug the issue or what the cause of the problem is?So when I scroll the list of objects in the app, the only Nimrod procs being called are these retain/release wrappers (objects never really get released as they are still referenced by Nimrod's global array) and two formatting procs which return C strings.
Returning C strings sounds troublesome. More code would help to tell you what's wrong.
I did update the csources and tried the devel branch. Now NimMain crashes. It's enough to modify backend.nim to contain a global variable like this:
# Backend for the different user interfaces.
var
TEST = ""
proc myAdd*(x, y: int): int {.cdecl, exportc.} =
result = x + y
Then the crash is:
(lldb) bt
* thread #1: tid = 0x24bed6, 0x0000ead2 cross-calculator`markstackandregisters_63636(gch=0x0002a088) + 290 at root_system.m:3937, queue = 'com.apple.main-thread, stop reason = EXC_BAD_ACCESS (code=1, address=0xc0000000)
frame #0: 0x0000ead2 cross-calculator`markstackandregisters_63636(gch=0x0002a088) + 290 at root_system.m:3937
frame #1: 0x0001056e cross-calculator`collectctbody_66007(gch=0x0002a088) + 238 at root_system.m:4438
frame #2: 0x00010aee cross-calculator`collectct_44008(gch=0x0002a088) + 430 at root_system.m:4533
frame #3: 0x00011501 cross-calculator`newObjRC1(typ=0x0002ad28, size=16) + 113 at root_system.m:4751
frame #4: 0x00011681 cross-calculator`copyStringRC1(src=0x000254b0) + 129 at root_system.m:4785
frame #5: 0x00002bfe cross-calculator`backendInit + 110 at nimrod_backend_backend.m:276
frame #6: 0x00002b8b cross-calculator`NimMain + 11 at nimrod_backend_backend.m:269
frame #7: 0x00002919 cross-calculator`main(argc=1, argv=0xbfffe90c) + 89 at main.m:9
frame #8: 0x00002425 cross-calculator`start + 53
(lldb) f
frame #0: 0x0000ead2 cross-calculator`markstackandregisters_63636(gch=0x0002a088) + 290 at root_system.m:3937
3934 nimln(860, "gc.nim");
3935 if (!((NU32)(sp) < (NU32)((NI32)(max - 32)))) goto LA6;
3936 nimln(861, "gc.nim");
-> 3937 gcmark_62016(gch, ((void**) (sp))[(0)- 0]);
3938 nimln(862, "gc.nim");
3939 gcmark_62016(gch, ((void**) (sp))[(1)- 0]);
3940 nimln(863, "gc.nim");
(lldb) f 5
frame #5: 0x00002bfe cross-calculator`backendInit + 110 at nimrod_backend_backend.m:276
273 nimfr("backend", "backend.nim")
274 nimln(4, "backend.nim");
275 LOC1 = 0;
-> 276 LOC1 = test_80003; test_80003 = copyStringRC1(((NimStringDesc*) &TMP126));
277 if (LOC1) nimGCunrefNoCycle(LOC1);
278 popFrame();
279 }
(lldb)
I think I'm to blame here, due to the PreMain proc that I introduced recently in the devel branch. I'll fix this tonight, but in the meantime make sure to call PreMain(); as well.
EDIT: Actually, are you compiling this as dynamic library (that's what I first thought, but your stack trace suggests otherwise)? Turns out that PreMain is correctly called by the dynamic library's __attribute__ ((__constructor__)) function.
I managed to replicate the situation in a small test not depending on iOS. You can see at http://pastebin.com/x50b2b8d the output of me downloading that gist and running the script, which first compiles the mini.nim file to later compile the C test including the generated nimcache files.
The pastebin output shows on line 148 that the test_mini() proc automatically called from NimMain() runs without problems. After that the C version runs its own version which crashes on the second outer loop.
Essentially, the nimrod code works, and the C code attempting to mimic the nimrod code fails, and I can't see why. The C code works if I remove the call to format_memdate(), but previously Araq mentioned returning a C string from a proc should be ok since the GC doesn't run after the proc finishes. Maybe it's not ok after all?