Skip to content

Commit 85df349

Browse files
committed
Use st_table with num hash for gcguard table
PTR2NUM may allocate a new object. This makes the process abort by object allocation during GC. Using st_table instead of a Hash can avoid this problem.
1 parent 6412d21 commit 85df349

File tree

1 file changed

+81
-5
lines changed

1 file changed

+81
-5
lines changed

ext/pycall/gc.c

Lines changed: 81 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,71 @@
11
#include "pycall_internal.h"
22

3+
struct gcguard {
4+
st_table *guarded_objects;
5+
};
6+
7+
static int
8+
gcguard_mark_i(st_data_t key, st_data_t val, st_data_t arg)
9+
{
10+
VALUE obj = (VALUE)val;
11+
rb_gc_mark(obj);
12+
return ST_CONTINUE;
13+
}
14+
15+
static void
16+
gcguard_mark(void* ptr)
17+
{
18+
struct gcguard *gg = (struct gcguard *)ptr;
19+
st_foreach(gg->guarded_objects, gcguard_mark_i, 0);
20+
}
21+
22+
static void
23+
gcguard_free(void* ptr)
24+
{
25+
struct gcguard *gg = (struct gcguard *)ptr;
26+
st_free_table(gg->guarded_objects);
27+
}
28+
29+
static size_t
30+
gcguard_memsize(const void* ptr)
31+
{
32+
const struct gcguard *gg = (const struct gcguard *)ptr;
33+
return st_memsize(gg->guarded_objects);
34+
}
35+
36+
static rb_data_type_t gcguard_data_type = {
37+
"PyCall::gcguard",
38+
{
39+
gcguard_mark,
40+
gcguard_free,
41+
gcguard_memsize,
42+
},
43+
#ifdef RUBY_TYPED_FREE_IMMEDIATELY
44+
0, 0, RUBY_TYPED_FREE_IMMEDIATELY
45+
#endif
46+
};
47+
48+
static void
49+
gcguard_aset(VALUE gcguard, PyObject *pyptr, VALUE rbobj)
50+
{
51+
struct gcguard *gg;
52+
TypedData_Get_Struct(gcguard, struct gcguard, &gcguard_data_type, gg);
53+
54+
st_insert(gg->guarded_objects, (st_data_t)pyptr, (st_data_t)rbobj);
55+
}
56+
57+
static void
58+
gcguard_delete(VALUE gcguard, PyObject *pyptr)
59+
{
60+
struct gcguard *gg;
61+
st_data_t key, val;
62+
63+
TypedData_Get_Struct(gcguard, struct gcguard, &gcguard_data_type, gg);
64+
65+
key = (st_data_t)pyptr;
66+
st_delete(gg->guarded_objects, &key, &val);
67+
}
68+
369
static ID id_gcguard_table;
470
static PyObject *weakref_callback_pyobj;
571
static PyObject *gcguard_weakref_destroyed(PyObject *self, PyObject *weakref);
@@ -21,15 +87,15 @@ gcguard_weakref_destroyed(PyObject *self, PyObject *weakref)
2187
void
2288
pycall_gcguard_aset(PyObject *pyobj, VALUE rbobj)
2389
{
24-
VALUE table = rb_ivar_get(mPyCall, id_gcguard_table);
25-
rb_hash_aset(table, PTR2NUM(pyobj), rbobj);
90+
VALUE gcguard = rb_ivar_get(mPyCall, id_gcguard_table);
91+
gcguard_aset(gcguard, pyobj, rbobj);
2692
}
2793

2894
void
2995
pycall_gcguard_delete(PyObject *pyobj)
3096
{
31-
VALUE table = rb_ivar_get(mPyCall, id_gcguard_table);
32-
rb_hash_delete(table, PTR2NUM(pyobj));
97+
VALUE gcguard = rb_ivar_get(mPyCall, id_gcguard_table);
98+
gcguard_delete(gcguard, pyobj);
3399
}
34100

35101
void
@@ -64,11 +130,21 @@ pycall_gcguard_register(PyObject *pyobj, VALUE obj)
64130
pycall_gcguard_aset(wref, obj);
65131
}
66132

133+
static VALUE
134+
gcguard_new(void)
135+
{
136+
struct gcguard *gg;
137+
VALUE obj = TypedData_Make_Struct(0, struct gcguard, &gcguard_data_type, gg);
138+
gg->guarded_objects = st_init_numtable();
139+
140+
return obj;
141+
}
142+
67143
void
68144
pycall_init_gcguard(void)
69145
{
70146
id_gcguard_table = rb_intern("gcguard_table");
71-
rb_ivar_set(mPyCall, id_gcguard_table, rb_hash_new());
147+
rb_ivar_set(mPyCall, id_gcguard_table, gcguard_new());
72148

73149
weakref_callback_pyobj = Py_API(PyCFunction_NewEx)(&gcguard_weakref_callback_def, NULL, NULL);
74150
}

0 commit comments

Comments
 (0)