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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
|
#include <gtest/gtest.h>
#if HAVE_BOEHMGC
#include <gc/gc.h>
#include "eval.hh"
#include "serialise.hh"
#endif
namespace nix {
#if HAVE_BOEHMGC
static void finalizer(void *obj, void *data) {
*((bool*)data) = true;
}
static bool* make_witness(volatile void* obj) {
/* We can't store the witnesses on the stack,
since they might be collected long afterwards */
bool* res = (bool*)GC_MALLOC_UNCOLLECTABLE(1);
*res = false;
GC_register_finalizer((void*)obj, finalizer, res, nullptr, nullptr);
return res;
}
// Generate 2 objects, discard one, run gc,
// see if one got collected and the other didn't
// GC is disabled inside coroutines on __APPLE__
static void testFinalizerCalls() {
volatile void* do_collect = GC_MALLOC_ATOMIC(128);
volatile void* dont_collect = GC_MALLOC_ATOMIC(128);
bool* do_collect_witness = make_witness(do_collect);
bool* dont_collect_witness = make_witness(dont_collect);
GC_gcollect();
GC_invoke_finalizers();
ASSERT_TRUE(GC_is_disabled() || *do_collect_witness);
ASSERT_FALSE(*dont_collect_witness);
ASSERT_NE(nullptr, dont_collect);
}
TEST(CoroGC, BasicFinalizers) {
initGC();
testFinalizerCalls();
}
// Run testFinalizerCalls inside a coroutine
// this tests that GC works as expected inside a coroutine
TEST(CoroGC, CoroFinalizers) {
initGC();
auto source = sinkToSource([&](Sink& sink) {
testFinalizerCalls();
// pass control to main
writeString("foo", sink);
});
// pass control to coroutine
std::string foo = readString(*source);
ASSERT_EQ(foo, "foo");
}
#if __APPLE__
// This test tests that GC is disabled on darwin
// to work around the patch not being sufficient there,
// causing crashes whenever gc is invoked inside a coroutine
TEST(CoroGC, AppleCoroDisablesGC) {
initGC();
auto source = sinkToSource([&](Sink& sink) {
ASSERT_TRUE(GC_is_disabled());
// pass control to main
writeString("foo", sink);
ASSERT_TRUE(GC_is_disabled());
// pass control to main
writeString("bar", sink);
});
// pass control to coroutine
std::string foo = readString(*source);
ASSERT_EQ(foo, "foo");
ASSERT_FALSE(GC_is_disabled());
// pass control to coroutine
std::string bar = readString(*source);
ASSERT_EQ(bar, "bar");
ASSERT_FALSE(GC_is_disabled());
}
#endif
// This test tests that boehm handles coroutine stacks correctly
// This test tests that coroutine stacks are registered to the GC,
// even when the coroutine is not running. It also tests that
// the main stack is still registered to the GC when the coroutine is running.
TEST(CoroGC, CoroutineStackNotGCd) {
initGC();
volatile void* do_collect = GC_MALLOC_ATOMIC(128);
volatile void* dont_collect = GC_MALLOC_ATOMIC(128);
bool* do_collect_witness = make_witness(do_collect);
bool* dont_collect_witness = make_witness(dont_collect);
do_collect = nullptr;
auto source = sinkToSource([&](Sink& sink) {
volatile void* dont_collect_inner = GC_MALLOC_ATOMIC(128);
volatile void* do_collect_inner = GC_MALLOC_ATOMIC(128);
bool* do_collect_inner_witness = make_witness(do_collect_inner);
bool* dont_collect_inner_witness = make_witness(dont_collect_inner);
do_collect_inner = nullptr;
// pass control to main
writeString("foo", sink);
ASSERT_FALSE(*dont_collect_inner_witness);
ASSERT_TRUE(*do_collect_inner_witness);
ASSERT_NE(nullptr, dont_collect_inner);
// pass control to main
writeString("bar", sink);
});
// pass control to coroutine
std::string foo = readString(*source);
ASSERT_EQ(foo, "foo");
ASSERT_FALSE(GC_is_disabled());
GC_gcollect();
GC_invoke_finalizers();
// pass control to coroutine
std::string bar = readString(*source);
ASSERT_EQ(bar, "bar");
ASSERT_FALSE(*dont_collect_witness);
ASSERT_TRUE(*do_collect_witness);
ASSERT_NE(nullptr, dont_collect);
}
#endif
}
|