1 /*===========================================================================
2  *  Filename : test-gc-protect.c
3  *  About    : garbage collector protection test
4  *
5  *  Copyright (c) 2007-2008 SigScheme Project <uim-en AT googlegroups.com>
6  *
7  *  All rights reserved.
8  *
9  *  Redistribution and use in source and binary forms, with or without
10  *  modification, are permitted provided that the following conditions
11  *  are met:
12  *
13  *  1. Redistributions of source code must retain the above copyright
14  *     notice, this list of conditions and the following disclaimer.
15  *  2. Redistributions in binary form must reproduce the above copyright
16  *     notice, this list of conditions and the following disclaimer in the
17  *     documentation and/or other materials provided with the distribution.
18  *  3. Neither the name of authors nor the names of its contributors
19  *     may be used to endorse or promote products derived from this software
20  *     without specific prior written permission.
21  *
22  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS
23  *  IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
24  *  THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
25  *  PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
26  *  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
27  *  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
28  *  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
29  *  OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
30  *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
31  *  OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
32  *  ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 ===========================================================================*/
34 
35 #include <sigscheme/config.h>
36 
37 #include "sscm-test.h"
38 #include "sigscheme.h"
39 #include "sigschemeinternal.h"
40 
41 /* Due to the conservative GC algorithm, an object cannot be detected as "this
42  * object is NOT protected", although "IS protected" can. But since testing
43  * such uncertain unprotected objects helps GC debugging, this file try such
44  * tests iff --enable-debug is specified although they may fail.
45  *   -- YamaKen 2008-04-27 */
46 #define TRY_TESTS_THAT_PASS_IN_MOST_CASES SCM_DEBUG
47 
48 static ScmObj make_obj(void);
49 static void *make_obj_internal(void *dummy);
50 static void *protected_func(void *arg);
51 static void *var_in_protected_func(void *arg);
52 static void *vars_in_protected_func(void *arg);
53 static void *test_implicit_protection(void *dummy);
54 
55 /* To disable GC stack protection, remove scm_call_with_gc_ready_stack() */
56 #undef TST_RUN
57 #define TST_RUN(fn, s, c) (fn(s, c))
58 
59 #define N_OBJS 128
60 static ScmObj static_objs[N_OBJS];
61 static ScmObj protected_lst, unprotected_lst;
62 
63 static ScmObj
make_obj(void)64 make_obj(void)
65 {
66     return (ScmObj)scm_call_with_gc_ready_stack(make_obj_internal, NULL);
67 }
68 
69 static void *
make_obj_internal(void * dummy)70 make_obj_internal(void *dummy)
71 {
72     return (void *)CONS(SCM_FALSE, SCM_FALSE);
73 }
74 
75 TST_CASE("scm_gc_protected_contextp()")
76 {
77     TST_TN_FALSE(scm_gc_protected_contextp());
78 
79     TST_TN_FALSE(protected_func(NULL));
80     TST_TN_TRUE (scm_call_with_gc_ready_stack(protected_func, NULL));
81     TST_TN_FALSE(protected_func(NULL));
82 }
83 
84 static void *
protected_func(void * arg)85 protected_func(void *arg)
86 {
87     return (void *)scm_gc_protected_contextp();
88 }
89 
90 TST_CASE("GC stack protection")
91 {
92     TST_TN_FALSE(scm_gc_protected_contextp());
93 
94 #if TRY_TESTS_THAT_PASS_IN_MOST_CASES
95     TST_TN_FALSE(var_in_protected_func(NULL));
96 #endif
97     TST_TN_TRUE (scm_call_with_gc_ready_stack(var_in_protected_func, NULL));
98 #if TRY_TESTS_THAT_PASS_IN_MOST_CASES
99     TST_TN_FALSE(var_in_protected_func(NULL));
100 #endif
101 }
102 
103 static void *
var_in_protected_func(void * arg)104 var_in_protected_func(void *arg)
105 {
106     ScmObj obj;
107 
108     obj = make_obj();
109     return (void *)scm_gc_protectedp(obj);
110 }
111 
112 TST_CASE("GC stack protection for long array")
113 {
114     TST_TN_FALSE(scm_gc_protected_contextp());
115 
116 #if TRY_TESTS_THAT_PASS_IN_MOST_CASES
117     TST_TN_FALSE(vars_in_protected_func(NULL));
118 #endif
119     TST_TN_TRUE (scm_call_with_gc_ready_stack(vars_in_protected_func, NULL));
120 #if TRY_TESTS_THAT_PASS_IN_MOST_CASES
121     TST_TN_FALSE(vars_in_protected_func(NULL));
122 #endif
123 }
124 
125 static void *
vars_in_protected_func(void * arg)126 vars_in_protected_func(void *arg)
127 {
128     ScmObj objs[N_OBJS];
129     int i;
130     scm_bool protectedp;
131 
132     for (i = 0; i < N_OBJS; i++)
133         objs[i] = make_obj();
134     for (i = 0, protectedp = scm_true; i < N_OBJS; i++)
135         protectedp = protectedp && scm_gc_protectedp(objs[i]);
136 
137     return (void *)protectedp;
138 }
139 
140 TST_CASE("GC static variable protection")
141 {
142     int i;
143 
144     TST_TN_FALSE(scm_gc_protected_contextp());
145 
146 #if TRY_TESTS_THAT_PASS_IN_MOST_CASES
147     /* unprotected */
148     for (i = 0; i < N_OBJS; i++)
149         static_objs[i] = make_obj();
150     for (i = 0; i < N_OBJS; i++)
151         TST_TN_FALSE(scm_gc_protectedp(static_objs[i]));
152 #endif
153 
154     /* protected */
155     for (i = 0; i < N_OBJS; i++) {
156         scm_gc_protect(&static_objs[i]);
157         static_objs[i] = make_obj();
158     }
159     for (i = 0; i < N_OBJS; i++)
160         TST_TN_TRUE(scm_gc_protectedp(static_objs[i]));
161 
162 #if TRY_TESTS_THAT_PASS_IN_MOST_CASES
163     /* unprotect again */
164     for (i = 0; i < N_OBJS; i++)
165         scm_gc_unprotect(&static_objs[i]);
166     for (i = 0; i < N_OBJS; i++)
167         TST_TN_FALSE(scm_gc_protectedp(static_objs[i]));
168 #endif
169 }
170 
171 TST_CASE("GC auto variable protection with scm_gc_protect()")
172 {
173     ScmObj auto_objs[N_OBJS];
174     int i;
175 
176     TST_TN_FALSE(scm_gc_protected_contextp());
177 
178 #if TRY_TESTS_THAT_PASS_IN_MOST_CASES
179     /* unprotected */
180     for (i = 0; i < N_OBJS; i++)
181         auto_objs[i] = make_obj();
182     for (i = 0; i < N_OBJS; i++)
183         TST_TN_FALSE(scm_gc_protectedp(auto_objs[i]));
184 #endif
185 
186     /* protected */
187     for (i = 0; i < N_OBJS; i++) {
188         scm_gc_protect(&auto_objs[i]);
189         auto_objs[i] = make_obj();
190     }
191     for (i = 0; i < N_OBJS; i++)
192         TST_TN_TRUE(scm_gc_protectedp(auto_objs[i]));
193 
194 #if TRY_TESTS_THAT_PASS_IN_MOST_CASES
195     /* unprotect again */
196     for (i = 0; i < N_OBJS; i++)
197         scm_gc_unprotect(&auto_objs[i]);
198     for (i = 0; i < N_OBJS; i++)
199         TST_TN_FALSE(scm_gc_protectedp(auto_objs[i]));
200 #endif
201 }
202 
203 static void *
test_implicit_protection(void * dummy)204 test_implicit_protection(void *dummy)
205 {
206     scm_bool result;
207     ScmObj lst;
208 
209     lst = LIST_2(SCM_FALSE, SCM_FALSE);
210     unprotected_lst = CDR(lst);
211 
212     result = scm_gc_protectedp(lst);
213     /* the cdr is implicitly protected since indirectly referred from the lst */
214     result = result && scm_gc_protectedp(unprotected_lst);
215     /* unlink the indirect reference */
216     lst = SCM_FALSE;
217     /* it makes the variable unprotected */
218 #if 0
219     /* This condition may not be met since the values of unprotected_lst or
220      * lst may be remained in registers */
221     result = result && !scm_gc_protectedp(unprotected_lst);
222 #endif
223 
224     return (void *)result;
225 }
226 
227 TST_CASE("GC indirect protection via on-heap object reference")
228 {
229     ScmObj lst;  /* unprotected */
230 
231     TST_TN_FALSE(scm_gc_protected_contextp());
232 
233     TST_TN_TRUE (scm_call_with_gc_ready_stack(test_implicit_protection, NULL));
234 
235 #if TRY_TESTS_THAT_PASS_IN_MOST_CASES
236     /* unprotected lst */
237     lst = LIST_2(SCM_FALSE, SCM_FALSE);
238     unprotected_lst = CDR(lst);
239 
240     TST_TN_FALSE(scm_gc_protectedp(lst));
241     TST_TN_FALSE(scm_gc_protectedp(unprotected_lst));
242     lst = SCM_FALSE;
243     TST_TN_FALSE(scm_gc_protectedp(unprotected_lst));
244 
245     /* unprotected static lst */
246     protected_lst = LIST_2(SCM_FALSE, SCM_FALSE);
247     unprotected_lst = CDR(protected_lst);
248 
249     TST_TN_FALSE(scm_gc_protectedp(protected_lst));
250     TST_TN_FALSE(scm_gc_protectedp(unprotected_lst));
251     lst = SCM_FALSE;
252     TST_TN_FALSE(scm_gc_protectedp(unprotected_lst));
253 #endif
254 
255     /* protected static lst */
256     scm_gc_protect(&protected_lst);
257     protected_lst = LIST_2(SCM_FALSE, SCM_FALSE);
258     unprotected_lst = CDR(protected_lst);
259 
260     TST_TN_TRUE (scm_gc_protectedp(protected_lst));
261     TST_TN_TRUE (scm_gc_protectedp(unprotected_lst));
262     protected_lst = SCM_FALSE;
263 #if TRY_TESTS_THAT_PASS_IN_MOST_CASES
264     TST_TN_FALSE(scm_gc_protectedp(unprotected_lst));
265 #endif
266 }
267