diff --git a/src/gc.c b/src/gc.c index 0526b40304a9773a4b1a6cd933d7dae918ee6b5a..89c306edb0b8a9ee0c7e3acf89a8dbe2c673bc55 100644 --- a/src/gc.c +++ b/src/gc.c @@ -6119,11 +6119,14 @@ void identify_loop_visit_ref(void *dst, int UNUSED(ref_type), ref_to = my_make_mc_marker(dst, visit_dst, extra); if (type != PIKE_T_UNKNOWN) { + /* NB: low_mapping_insert() for object indices may throw errors + * if eg lfun::`==() throws an error. We therefore instead + * use the raw pointers as indices instead. + */ struct svalue s; - SET_SVAL(s, type, 0, refs, dst); - low_mapping_insert(identify_loop_reverse, &s, Pike_sp-1, 0); - + SET_SVAL(s, PIKE_T_INT, NUMBER_NUMBER, integer, (INT_TYPE)(ptrdiff_t)dst); mc_wq_enqueue(ref_to); + low_mapping_insert(identify_loop_reverse, &s, Pike_sp-1, 0); } else { /* Not a valid svalue type. * @@ -6258,14 +6261,22 @@ void f_identify_cycle(INT32 args) } #endif + /* NB: low_mapping_lookup() for object indices may throw errors + * if eg lfun::`==() throws an error. We therefore instead + * use the raw pointers as indices instead. + */ + push_int((INT_TYPE)(ptrdiff_t)s->u.refs); while ((k = low_mapping_lookup(identify_loop_reverse, Pike_sp-1))) { /* NB: Since we entered this loop, we know that there's a * reference loop involving s, as s otherwise wouldn't * have been in the mapping. */ + pop_stack(); push_svalue(k); + push_int((INT_TYPE)(ptrdiff_t)k->u.refs); if (k->u.refs == s->u.refs) { /* Found! */ + pop_stack(); break; } } diff --git a/src/testsuite.in b/src/testsuite.in index 91dfd718ed7053d037b69d794d0805c826f616aa..19cc1c78aebb4cebbaad5617faaff71caee0097f 100644 --- a/src/testsuite.in +++ b/src/testsuite.in @@ -7294,6 +7294,33 @@ test_program([[ } ]]) +// Pike.identify_cycle +test_any_equal([[ + class Foo { Foo next; int counter; }; + array(Foo) foos = allocate(10, Foo)(); + for (int i = 0; i < 10; i++) { + foos[i]->next = foos[(i+1)%10]; + foos[i]->counter = i; + } + return Pike.identify_cycle(foos[0])->counter; +]], ({ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 })) +test_any([[ + // PIKE-106: Mutex not unlocked properly on error in identify_cycle(). + class Foo { + int cnt; + int __hash() { return 0; } + int `==(mixed x) { if (!cnt++) error("Comparison failure.\n"); } + }; + catch { + // This triggered an error. + Pike.identify_cycle(allocate(2, Foo)()); + }; + catch { + // This hanged due to the mc_mutex not having been released. + Pike.identify_cycle(allocate(2, Foo)()); + }; +]], 0) + // Numerical limits. test_true([[Int.NATIVE_MIN <= -2147483648]]) test_true([[Int.NATIVE_MAX >= 2147483647]])