-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtest_arc.cpp
More file actions
111 lines (88 loc) · 4.57 KB
/
Copy pathtest_arc.cpp
File metadata and controls
111 lines (88 loc) · 4.57 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
// ARC reclamation engine test — moon fork, Phase 1.
//
// Exercises moonLife_retain / moonLife_release directly on real object graphs, with
// explicit ownership modelling (retain on store, release on drop), since the
// VM/stack is not yet ARC-instrumented. Verifies:
// * an acyclic graph is reclaimed deterministically (whole subtree frees), and
// * a reference cycle leaks (no free) without crashing.
#include <cassert>
#include <cstdio>
#include <cstdlib>
#include "moon.h"
#include "mauxlib.h"
#include "mstate.h"
#include "mobject.h"
#include "mtable.h"
#include "mstring.h"
#include "mlifecycle.h"
static int failures = 0;
static void expect(bool cond, const char* what) {
if (cond) { std::printf(" ok: %s\n", what); }
else { std::printf(" FAIL: %s\n", what); ++failures; }
}
// Store value 'v' into table 't' at integer key 'k'. Table::setInt now performs
// ARC accounting itself (retains the stored value), so no manual retain here.
static void storeInt(moon_State* L, Table* t, moon_Integer k, const TValue* v) {
TValue tmp; tmp = *v;
t->setInt(L, k, &tmp);
}
int main() {
// Deterministic freeing is on by default (MOON_ARC_DRAIN=0 opts out). This
// test verifies reclamation, so pin it on explicitly in case the ambient
// environment carries an opt-out (the flag is latched on first use).
setenv("MOON_ARC_DRAIN", "1", 1);
moon_State* L = moonL_newstate();
if (L == nullptr) { std::printf("could not create state\n"); return 1; }
// ---- Acyclic graph: root -> {childTable, string}; releasing root frees all 3.
{
unsigned long long before = moonLife_deinitcount();
Table* root = Table::create(L); // refcount 1 (birth)
Table* child = Table::create(L); // refcount 1 (birth)
TString* str = TString::create(L, "arc-leaf-unique-xyz"); // refcount 1 (birth)
TValue v;
sethvalue(L, &v, child); storeInt(L, root, 1, &v); // setInt retains child -> rc 2
setsvalue(L, &v, str); storeInt(L, root, 2, &v); // setInt retains str -> rc 2
// Drop the birth references of the children — root is now their sole owner.
moonLife_release(*L, obj2mo(child)); // child rc 1
moonLife_release(*L, obj2mo(str)); // str rc 1
// Drop root's last reference: frees root, then cascades to child and str.
moonLife_release(*L, obj2mo(root));
unsigned long long freed = moonLife_deinitcount() - before;
std::printf("acyclic: reclaimed %llu objects (expected 3)\n", freed);
expect(freed == 3, "acyclic graph fully reclaimed (root + child + leaf)");
}
// ---- Cycle: x <-> y. Dropping both birth refs leaves a self-sustaining
// cycle (each still has refcount 1 from the other) -> leaks, no crash.
{
unsigned long long before = moonLife_deinitcount();
Table* x = Table::create(L); // refcount 1
Table* y = Table::create(L); // refcount 1
TValue v;
sethvalue(L, &v, y); storeInt(L, x, 1, &v); // setInt retains y -> rc 2
sethvalue(L, &v, x); storeInt(L, y, 1, &v); // setInt retains x -> rc 2
// Drop both birth references. Now x and y reference only each other.
moonLife_release(*L, obj2mo(x)); // x rc 1 (held by y)
moonLife_release(*L, obj2mo(y)); // y rc 1 (held by x)
unsigned long long freed = moonLife_deinitcount() - before;
std::printf("cyclic: reclaimed %llu objects (expected 0 — cycle leaks)\n", freed);
expect(freed == 0, "reference cycle leaks (not reclaimed) and does not crash");
}
// ---- Userdata user-values: a full userdata owning a table in user-value 1;
// dropping the userdata must cascade-free the table (exercises the real
// moon_setiuservalue retain + arc_decrefchildren USERDATA release).
{
unsigned long long before = moonLife_deinitcount();
moon_newuserdatauv(L, 8, 1); // stack: [ud] ud rc1 (stack owns)
moon_createtable(L, 0, 0); // stack: [ud, t] t rc1
moon_setiuservalue(L, -2, 1); // ud.uv[0]=t (retain t->2), pop t (release->1)
moon_pop(L, 1); // pop ud: release -> rc0 (queued)
moonLife_drain(*L); // reclaim: frees ud, cascades to t
unsigned long long freed = moonLife_deinitcount() - before;
std::printf("userdata: reclaimed %llu objects (expected 2)\n", freed);
expect(freed == 2, "userdata + its user-value table reclaimed");
}
moon_close(L);
if (failures == 0) std::printf("ARC engine test: ALL OK\n");
else std::printf("ARC engine test: %d FAILURE(S)\n", failures);
return failures == 0 ? 0 : 1;
}