Author: Remi Meier <[email protected]>
Branch:
Changeset: r2005:3af462fd449b
Date: 2017-02-09 09:28 +0100
http://bitbucket.org/pypy/stmgc/changeset/3af462fd449b/
Log: merge new finalizer API
diff --git a/c8/stm/core.c b/c8/stm/core.c
--- a/c8/stm/core.c
+++ b/c8/stm/core.c
@@ -1047,8 +1047,7 @@
assert(tree_is_cleared(STM_PSEGMENT->nursery_objects_shadows));
assert(tree_is_cleared(STM_PSEGMENT->callbacks_on_commit_and_abort[0]));
assert(tree_is_cleared(STM_PSEGMENT->callbacks_on_commit_and_abort[1]));
- assert(list_is_empty(STM_PSEGMENT->young_objects_with_light_finalizers));
- assert(STM_PSEGMENT->finalizers == NULL);
+ assert(list_is_empty(STM_PSEGMENT->young_objects_with_destructors));
assert(STM_PSEGMENT->active_queues == NULL);
#ifndef NDEBUG
/* this should not be used when objects_pointing_to_nursery == NULL */
diff --git a/c8/stm/core.h b/c8/stm/core.h
--- a/c8/stm/core.h
+++ b/c8/stm/core.h
@@ -13,6 +13,8 @@
#include <errno.h>
#include <pthread.h>
#include <signal.h>
+#include <stdbool.h>
+#include "list.h"
/************************************************************/
@@ -144,9 +146,9 @@
pthread_t running_pthread;
#endif
- /* light finalizers */
- struct list_s *young_objects_with_light_finalizers;
- struct list_s *old_objects_with_light_finalizers;
+ /* destructors */
+ struct list_s *young_objects_with_destructors;
+ struct list_s *old_objects_with_destructors;
/* regular finalizers (objs from the current transaction only) */
struct finalizers_s *finalizers;
diff --git a/c8/stm/finalizer.c b/c8/stm/finalizer.c
--- a/c8/stm/finalizer.c
+++ b/c8/stm/finalizer.c
@@ -2,31 +2,65 @@
# error "must be compiled via stmgc.c"
# include "core.h" // silence flymake
#endif
+#include "finalizer.h"
+#include "fprintcolor.h"
+#include "nursery.h"
+#include "gcpage.h"
/* callbacks */
-void (*stmcb_light_finalizer)(object_t *);
+void (*stmcb_destructor)(object_t *);
void (*stmcb_finalizer)(object_t *);
static void init_finalizers(struct finalizers_s *f)
{
f->objects_with_finalizers = list_create();
- f->count_non_young = 0;
- f->run_finalizers = NULL;
- f->running_next = NULL;
+ f->probably_young_objects_with_finalizers = list_create();
+ f->run_finalizers = list_create();
+ f->lock = 0;
+ f->running_trigger_now = NULL;
}
static void setup_finalizer(void)
{
init_finalizers(&g_finalizers);
+
+ for (long j = 1; j < NB_SEGMENTS; j++) {
+ struct stm_priv_segment_info_s *pseg = get_priv_segment(j);
+
+ assert(pseg->finalizers == NULL);
+ struct finalizers_s *f = malloc(sizeof(struct finalizers_s));
+ if (f == NULL)
+ stm_fatalerror("out of memory in create_finalizers"); /* XXX */
+ init_finalizers(f);
+ pseg->finalizers = f;
+ }
}
-static void teardown_finalizer(void)
+void stm_setup_finalizer_queues(int number, stm_finalizer_trigger_fn *triggers)
{
- if (g_finalizers.run_finalizers != NULL)
- list_free(g_finalizers.run_finalizers);
- list_free(g_finalizers.objects_with_finalizers);
+ assert(g_finalizer_triggers.count == 0);
+ assert(g_finalizer_triggers.triggers == NULL);
+
+ g_finalizer_triggers.count = number;
+ g_finalizer_triggers.triggers = (stm_finalizer_trigger_fn *)
+ malloc(number * sizeof(stm_finalizer_trigger_fn));
+
+ for (int qindex = 0; qindex < number; qindex++) {
+ g_finalizer_triggers.triggers[qindex] = triggers[qindex];
+ dprintf(("setup_finalizer_queue(qindex=%d,fun=%p)\n", qindex,
triggers[qindex]));
+ }
+}
+
+static void teardown_finalizer(void) {
+ LIST_FREE(g_finalizers.run_finalizers);
+ LIST_FREE(g_finalizers.objects_with_finalizers);
+ LIST_FREE(g_finalizers.probably_young_objects_with_finalizers);
memset(&g_finalizers, 0, sizeof(g_finalizers));
+
+ if (g_finalizer_triggers.triggers)
+ free(g_finalizer_triggers.triggers);
+ memset(&g_finalizer_triggers, 0, sizeof(g_finalizer_triggers));
}
static void _commit_finalizers(void)
@@ -36,36 +70,31 @@
stm_spin_loop();
}
- if (STM_PSEGMENT->finalizers->run_finalizers != NULL) {
+ struct finalizers_s *local_fs = STM_PSEGMENT->finalizers;
+ if (!list_is_empty(local_fs->run_finalizers)) {
/* copy 'STM_PSEGMENT->finalizers->run_finalizers' into
'g_finalizers.run_finalizers', dropping any initial NULLs
(finalizers already called) */
- struct list_s *src = STM_PSEGMENT->finalizers->run_finalizers;
- uintptr_t frm = 0;
- if (STM_PSEGMENT->finalizers->running_next != NULL) {
- frm = *STM_PSEGMENT->finalizers->running_next;
- assert(frm <= list_count(src));
- *STM_PSEGMENT->finalizers->running_next = (uintptr_t)-1;
- }
- if (frm < list_count(src)) {
- if (g_finalizers.run_finalizers == NULL)
- g_finalizers.run_finalizers = list_create();
+ struct list_s *src = local_fs->run_finalizers;
+ if (list_count(src)) {
g_finalizers.run_finalizers = list_extend(
g_finalizers.run_finalizers,
- src, frm);
+ src, 0);
}
- list_free(src);
}
+ LIST_FREE(local_fs->run_finalizers);
/* copy the whole 'STM_PSEGMENT->finalizers->objects_with_finalizers'
into 'g_finalizers.objects_with_finalizers' */
g_finalizers.objects_with_finalizers = list_extend(
g_finalizers.objects_with_finalizers,
- STM_PSEGMENT->finalizers->objects_with_finalizers, 0);
- list_free(STM_PSEGMENT->finalizers->objects_with_finalizers);
+ local_fs->objects_with_finalizers, 0);
+ LIST_FREE(local_fs->objects_with_finalizers);
+ assert(list_is_empty(local_fs->probably_young_objects_with_finalizers));
+ LIST_FREE(local_fs->probably_young_objects_with_finalizers);
- free(STM_PSEGMENT->finalizers);
- STM_PSEGMENT->finalizers = NULL;
+ // re-init
+ init_finalizers(local_fs);
__sync_lock_release(&g_finalizers.lock);
}
@@ -74,24 +103,22 @@
{
/* like _commit_finalizers(), but forget everything from the
current transaction */
- if (pseg->finalizers != NULL) {
- if (pseg->finalizers->run_finalizers != NULL) {
- if (pseg->finalizers->running_next != NULL) {
- *pseg->finalizers->running_next = (uintptr_t)-1;
- }
- list_free(pseg->finalizers->run_finalizers);
- }
- list_free(pseg->finalizers->objects_with_finalizers);
- free(pseg->finalizers);
- pseg->finalizers = NULL;
- }
+ LIST_FREE(pseg->finalizers->run_finalizers);
+ LIST_FREE(pseg->finalizers->objects_with_finalizers);
+ LIST_FREE(pseg->finalizers->probably_young_objects_with_finalizers);
+ // re-init
+ init_finalizers(pseg->finalizers);
+
+ // if we were running triggers, release the lock:
+ if (g_finalizers.running_trigger_now == pseg)
+ g_finalizers.running_trigger_now = NULL;
/* call the light finalizers for objects that are about to
be forgotten from the current transaction */
char *old_gs_register = STM_SEGMENT->segment_base;
bool must_fix_gs = old_gs_register != pseg->pub.segment_base;
- struct list_s *lst = pseg->young_objects_with_light_finalizers;
+ struct list_s *lst = pseg->young_objects_with_destructors;
long i, count = list_count(lst);
if (lst > 0) {
for (i = 0; i < count; i++) {
@@ -101,15 +128,15 @@
set_gs_register(pseg->pub.segment_base);
must_fix_gs = false;
}
- stmcb_light_finalizer(obj);
+ stmcb_destructor(obj);
}
list_clear(lst);
}
/* also deals with overflow objects: they are at the tail of
- old_objects_with_light_finalizers (this list is kept in order
+ old_objects_with_destructors (this list is kept in order
and we cannot add any already-committed object) */
- lst = pseg->old_objects_with_light_finalizers;
+ lst = pseg->old_objects_with_destructors;
count = list_count(lst);
while (count > 0) {
object_t *obj = (object_t *)list_item(lst, --count);
@@ -120,7 +147,7 @@
set_gs_register(pseg->pub.segment_base);
must_fix_gs = false;
}
- stmcb_light_finalizer(obj);
+ stmcb_destructor(obj);
}
if (STM_SEGMENT->segment_base != old_gs_register)
@@ -128,44 +155,42 @@
}
-void stm_enable_light_finalizer(object_t *obj)
+void stm_enable_destructor(object_t *obj)
{
if (_is_young(obj)) {
- LIST_APPEND(STM_PSEGMENT->young_objects_with_light_finalizers, obj);
+ LIST_APPEND(STM_PSEGMENT->young_objects_with_destructors, obj);
}
else {
assert(_is_from_same_transaction(obj));
- LIST_APPEND(STM_PSEGMENT->old_objects_with_light_finalizers, obj);
+ LIST_APPEND(STM_PSEGMENT->old_objects_with_destructors, obj);
}
}
-object_t *stm_allocate_with_finalizer(ssize_t size_rounded_up)
+
+void stm_enable_finalizer(int queue_index, object_t *obj)
{
- object_t *obj = _stm_allocate_external(size_rounded_up);
-
- if (STM_PSEGMENT->finalizers == NULL) {
- struct finalizers_s *f = malloc(sizeof(struct finalizers_s));
- if (f == NULL)
- stm_fatalerror("out of memory in create_finalizers"); /* XXX */
- init_finalizers(f);
- STM_PSEGMENT->finalizers = f;
+ if (_is_young(obj)) {
+
LIST_APPEND(STM_PSEGMENT->finalizers->probably_young_objects_with_finalizers,
obj);
+
LIST_APPEND(STM_PSEGMENT->finalizers->probably_young_objects_with_finalizers,
queue_index);
}
- assert(STM_PSEGMENT->finalizers->count_non_young
- <= list_count(STM_PSEGMENT->finalizers->objects_with_finalizers));
- LIST_APPEND(STM_PSEGMENT->finalizers->objects_with_finalizers, obj);
- return obj;
+ else {
+ assert(_is_from_same_transaction(obj));
+ LIST_APPEND(STM_PSEGMENT->finalizers->objects_with_finalizers, obj);
+ LIST_APPEND(STM_PSEGMENT->finalizers->objects_with_finalizers,
queue_index);
+ }
}
+
/************************************************************/
-/* Light finalizers
+/* Destructors
*/
-static void deal_with_young_objects_with_finalizers(void)
+static void deal_with_young_objects_with_destructors(void)
{
- /* for light finalizers: executes finalizers for objs that don't survive
+ /* for destructors: executes destructors for objs that don't survive
this minor gc */
- struct list_s *lst = STM_PSEGMENT->young_objects_with_light_finalizers;
+ struct list_s *lst = STM_PSEGMENT->young_objects_with_destructors;
long i, count = list_count(lst);
for (i = 0; i < count; i++) {
object_t *obj = (object_t *)list_item(lst, i);
@@ -174,28 +199,29 @@
object_t *TLPREFIX *pforwarded_array = (object_t *TLPREFIX *)obj;
if (pforwarded_array[0] != GCWORD_MOVED) {
/* not moved: the object dies */
- stmcb_light_finalizer(obj);
+ stmcb_destructor(obj);
}
else {
obj = pforwarded_array[1]; /* moved location */
assert(!_is_young(obj));
- LIST_APPEND(STM_PSEGMENT->old_objects_with_light_finalizers, obj);
+ LIST_APPEND(STM_PSEGMENT->old_objects_with_destructors, obj);
}
}
list_clear(lst);
}
-static void deal_with_old_objects_with_finalizers(void)
+static void deal_with_old_objects_with_destructors(void)
{
- /* for light finalizers */
+ /* for destructors */
int old_gs_register = STM_SEGMENT->segment_num;
int current_gs_register = old_gs_register;
long j;
-
assert(list_is_empty(get_priv_segment(0)->old_objects_with_light_finalizers));
+ assert(list_is_empty(get_priv_segment(0)->old_objects_with_destructors));
for (j = 1; j < NB_SEGMENTS; j++) {
struct stm_priv_segment_info_s *pseg = get_priv_segment(j);
- struct list_s *lst = pseg->old_objects_with_light_finalizers;
+ assert(list_is_empty(pseg->young_objects_with_destructors));
+ struct list_s *lst = pseg->old_objects_with_destructors;
long i, count = list_count(lst);
lst->count = 0;
for (i = 0; i < count; i++) {
@@ -217,7 +243,7 @@
set_gs_register(get_segment_base(j));
current_gs_register = j;
}
- stmcb_light_finalizer(obj);
+ stmcb_destructor(obj);
}
else {
/* object survives */
@@ -230,6 +256,7 @@
}
+
/************************************************************/
/* Algorithm for regular (non-light) finalizers.
Follows closely pypy/doc/discussion/finalizer-order.rst
@@ -328,20 +355,23 @@
struct list_s *marked = list_create();
+ assert(list_is_empty(f->probably_young_objects_with_finalizers));
struct list_s *lst = f->objects_with_finalizers;
long i, count = list_count(lst);
lst->count = 0;
- f->count_non_young = 0;
- for (i = 0; i < count; i++) {
+ for (i = 0; i < count; i += 2) {
object_t *x = (object_t *)list_item(lst, i);
+ uintptr_t qindex = list_item(lst, i + 1);
assert(_finalization_state(x) != 1);
if (_finalization_state(x) >= 2) {
list_set_item(lst, lst->count++, (uintptr_t)x);
+ list_set_item(lst, lst->count++, qindex);
continue;
}
LIST_APPEND(marked, x);
+ LIST_APPEND(marked, qindex);
struct list_s *pending = _finalizer_pending;
LIST_APPEND(pending, x);
@@ -373,27 +403,29 @@
struct list_s *run_finalizers = f->run_finalizers;
long i, count = list_count(marked);
- for (i = 0; i < count; i++) {
+ for (i = 0; i < count; i += 2) {
object_t *x = (object_t *)list_item(marked, i);
+ uintptr_t qindex = list_item(marked, i + 1);
int state = _finalization_state(x);
assert(state >= 2);
if (state == 2) {
- if (run_finalizers == NULL)
- run_finalizers = list_create();
LIST_APPEND(run_finalizers, x);
+ LIST_APPEND(run_finalizers, qindex);
_recursively_bump_finalization_state_from_2_to_3(pseg, x);
}
else {
struct list_s *lst = f->objects_with_finalizers;
list_set_item(lst, lst->count++, (uintptr_t)x);
+ list_set_item(lst, lst->count++, qindex);
}
}
- list_free(marked);
+ LIST_FREE(marked);
f->run_finalizers = run_finalizers;
}
+
static void deal_with_objects_with_finalizers(void)
{
/* for non-light finalizers */
@@ -436,11 +468,10 @@
static void mark_visit_from_finalizer1(
struct stm_priv_segment_info_s *pseg, struct finalizers_s *f)
{
- if (f != NULL && f->run_finalizers != NULL) {
- LIST_FOREACH_R(f->run_finalizers, object_t * /*item*/,
- ({
- mark_visit_possibly_overflow_object(item, pseg);
- }));
+ long i, count = list_count(f->run_finalizers);
+ for (i = 0; i < count; i += 2) {
+ object_t *x = (object_t *)list_item(f->run_finalizers, i);
+ mark_visit_possibly_overflow_object(x, pseg);
}
}
@@ -454,40 +485,6 @@
mark_visit_from_finalizer1(get_priv_segment(0), &g_finalizers);
}
-static void _execute_finalizers(struct finalizers_s *f)
-{
- if (f->run_finalizers == NULL)
- return; /* nothing to do */
-
- restart:
- if (f->running_next != NULL)
- return; /* in a nested invocation of execute_finalizers() */
-
- uintptr_t next = 0, total = list_count(f->run_finalizers);
- f->running_next = &next;
-
- while (next < total) {
- object_t *obj = (object_t *)list_item(f->run_finalizers, next);
- list_set_item(f->run_finalizers, next, 0);
- next++;
-
- stmcb_finalizer(obj);
- }
- if (next == (uintptr_t)-1) {
- /* transaction committed: the whole 'f' was freed */
- return;
- }
- f->running_next = NULL;
-
- if (f->run_finalizers->count > total) {
- memmove(f->run_finalizers->items,
- f->run_finalizers->items + total,
- (f->run_finalizers->count - total) * sizeof(uintptr_t));
- goto restart;
- }
-
- LIST_FREE(f->run_finalizers);
-}
/* XXX: according to translator.backendopt.finalizer, getfield_gc
for primitive types is a safe op in light finalizers.
@@ -495,43 +492,185 @@
getfield on *dying obj*).
*/
+static void _trigger_finalizer_queues(struct finalizers_s *f)
+{
+ /* runs triggers of finalizer queues that have elements in the queue. May
+ NOT run outside of a transaction, but triggers never leave the
+ transactional zone.
+
+ returns true if there are also old-style finalizers to run */
+ assert(in_transaction(STM_PSEGMENT->pub.running_thread));
+
+ bool *to_trigger = (bool*)alloca(g_finalizer_triggers.count *
sizeof(bool));
+ memset(to_trigger, 0, g_finalizer_triggers.count * sizeof(bool));
+
+ while (__sync_lock_test_and_set(&f->lock, 1) != 0) {
+ /* somebody is adding more finalizers (_commit_finalizer()) */
+ stm_spin_loop();
+ }
+
+ int count = list_count(f->run_finalizers);
+ for (int i = 0; i < count; i += 2) {
+ int qindex = (int)list_item(f->run_finalizers, i + 1);
+ dprintf(("qindex=%d\n", qindex));
+ to_trigger[qindex] = true;
+ }
+
+ __sync_lock_release(&f->lock);
+
+ // trigger now:
+ for (int i = 0; i < g_finalizer_triggers.count; i++) {
+ if (to_trigger[i]) {
+ dprintf(("invoke-finalizer-trigger(qindex=%d)\n", i));
+ g_finalizer_triggers.triggers[i]();
+ }
+ }
+}
+
+static bool _has_oldstyle_finalizers(struct finalizers_s *f)
+{
+ int count = list_count(f->run_finalizers);
+ for (int i = 0; i < count; i += 2) {
+ int qindex = (int)list_item(f->run_finalizers, i + 1);
+ if (qindex == -1)
+ return true;
+ }
+ return false;
+}
+
+static void _invoke_local_finalizers()
+{
+ /* called inside a transaction; invoke local triggers, process old-style
+ * local finalizers */
+ dprintf(("invoke_local_finalizers %lu\n",
list_count(STM_PSEGMENT->finalizers->run_finalizers)));
+ if (list_is_empty(STM_PSEGMENT->finalizers->run_finalizers)
+ && list_is_empty(g_finalizers.run_finalizers))
+ return;
+
+ struct stm_priv_segment_info_s *pseg =
get_priv_segment(STM_SEGMENT->segment_num);
+ //try to run local triggers
+ if (STM_PSEGMENT->finalizers->running_trigger_now == NULL) {
+ // we are not recursively running them
+ STM_PSEGMENT->finalizers->running_trigger_now = pseg;
+ _trigger_finalizer_queues(STM_PSEGMENT->finalizers);
+ STM_PSEGMENT->finalizers->running_trigger_now = NULL;
+ }
+
+ // try to run global triggers
+ if (__sync_lock_test_and_set(&g_finalizers.running_trigger_now, pseg) ==
NULL) {
+ // nobody is already running these triggers (recursively)
+ _trigger_finalizer_queues(&g_finalizers);
+ g_finalizers.running_trigger_now = NULL;
+ }
+
+ if (!_has_oldstyle_finalizers(STM_PSEGMENT->finalizers))
+ return; // no oldstyle to run
+
+ object_t *obj;
+ while ((obj = stm_next_to_finalize(-1)) != NULL) {
+ stmcb_finalizer(obj);
+ }
+}
+
static void _invoke_general_finalizers(stm_thread_local_t *tl)
{
- /* called between transactions */
+ /* called between transactions
+ * triggers not called here, since all should have been called already in
_invoke_local_finalizers!
+ * run old-style finalizers (q_index=-1)
+ * queues that are not empty. */
+ dprintf(("invoke_general_finalizers %lu\n",
list_count(g_finalizers.run_finalizers)));
+ if (list_is_empty(g_finalizers.run_finalizers))
+ return;
+
+ if (!_has_oldstyle_finalizers(&g_finalizers))
+ return; // no oldstyle to run
+
+ // run old-style finalizers:
rewind_jmp_buf rjbuf;
stm_rewind_jmp_enterframe(tl, &rjbuf);
_stm_start_transaction(tl);
- /* XXX: become inevitable, bc. otherwise, we would need to keep
- around the original g_finalizers.run_finalizers to restore it
- in case of an abort. */
- _stm_become_inevitable(MSG_INEV_DONT_SLEEP);
- /* did it work? */
- if (STM_PSEGMENT->transaction_state != TS_INEVITABLE) { /* no */
- /* avoid blocking here, waiting for another INEV transaction.
- If we did that, application code could not proceed (start the
- next transaction) and it will not be obvious from the profile
- why we were WAITing. */
- _stm_commit_transaction();
- stm_rewind_jmp_leaveframe(tl, &rjbuf);
- return;
+
+ dprintf(("invoke_oldstyle_finalizers %lu\n",
list_count(g_finalizers.run_finalizers)));
+ object_t *obj;
+ while ((obj = stm_next_to_finalize(-1)) != NULL) {
+ assert(STM_PSEGMENT->transaction_state == TS_INEVITABLE);
+ stmcb_finalizer(obj);
}
+ _stm_commit_transaction();
+ stm_rewind_jmp_leaveframe(tl, &rjbuf);
+}
+
+object_t* stm_next_to_finalize(int queue_index) {
+ assert(STM_PSEGMENT->transaction_state != TS_NONE);
+
+ /* first check local run_finalizers queue, then global */
+ if (!list_is_empty(STM_PSEGMENT->finalizers->run_finalizers)) {
+ struct list_s *lst = STM_PSEGMENT->finalizers->run_finalizers;
+ int count = list_count(lst);
+ for (int i = 0; i < count; i += 2) {
+ int qindex = (int)list_item(lst, i + 1);
+ if (qindex == queue_index) {
+ /* no need to become inevitable for local ones */
+ /* Remove obj from list and return it. */
+ object_t *obj = (object_t*)list_item(lst, i);
+ int remaining = count - i - 2;
+ if (remaining > 0) {
+ memmove(&lst->items[i],
+ &lst->items[i + 2],
+ remaining * sizeof(uintptr_t));
+ }
+ lst->count -= 2;
+ return obj;
+ }
+ }
+ }
+
+ /* no local finalizers found, continue in global list */
+
while (__sync_lock_test_and_set(&g_finalizers.lock, 1) != 0) {
/* somebody is adding more finalizers (_commit_finalizer()) */
stm_spin_loop();
}
- struct finalizers_s copy = g_finalizers;
- assert(copy.running_next == NULL);
- g_finalizers.run_finalizers = NULL;
+
+ struct list_s *lst = g_finalizers.run_finalizers;
+ int count = list_count(lst);
+ for (int i = 0; i < count; i += 2) {
+ int qindex = (int)list_item(lst, i + 1);
+ if (qindex == queue_index) {
+ /* XXX: become inevitable, bc. otherwise, we would need to keep
+ around the original g_finalizers.run_finalizers to restore it
+ in case of an abort. */
+ if (STM_PSEGMENT->transaction_state != TS_INEVITABLE) {
+ _stm_become_inevitable(MSG_INEV_DONT_SLEEP);
+ /* did it work? */
+ if (STM_PSEGMENT->transaction_state != TS_INEVITABLE) { /*
no */
+ /* avoid blocking here, waiting for another INEV
transaction.
+ If we did that, application code could not proceed
(start the
+ next transaction) and it will not be obvious from the
profile
+ why we were WAITing. XXX: still true? */
+ __sync_lock_release(&g_finalizers.lock);
+ return NULL;
+ }
+ }
+
+ /* Remove obj from list and return it. */
+ object_t *obj = (object_t*)list_item(lst, i);
+ int remaining = count - i - 2;
+ if (remaining > 0) {
+ memmove(&lst->items[i],
+ &lst->items[i + 2],
+ remaining * sizeof(uintptr_t));
+ }
+ lst->count -= 2;
+
+ __sync_lock_release(&g_finalizers.lock);
+ return obj;
+ }
+ }
+
/* others may add to g_finalizers again: */
__sync_lock_release(&g_finalizers.lock);
- if (copy.run_finalizers != NULL) {
- _execute_finalizers(©);
- }
-
- _stm_commit_transaction();
- stm_rewind_jmp_leaveframe(tl, &rjbuf);
-
- LIST_FREE(copy.run_finalizers);
+ return NULL;
}
diff --git a/c8/stm/finalizer.h b/c8/stm/finalizer.h
--- a/c8/stm/finalizer.h
+++ b/c8/stm/finalizer.h
@@ -1,16 +1,20 @@
+#ifndef _STM_FINALIZER_H_
+#define _STM_FINALIZER_H_
+
+#include <stdint.h>
/* see deal_with_objects_with_finalizers() for explanation of these fields */
struct finalizers_s {
long lock;
+ struct stm_priv_segment_info_s * running_trigger_now; /* our PSEG, if we
are running triggers */
struct list_s *objects_with_finalizers;
- uintptr_t count_non_young;
+ struct list_s *probably_young_objects_with_finalizers; /* empty on
g_finalizers! */
struct list_s *run_finalizers;
- uintptr_t *running_next;
};
static void mark_visit_from_finalizer_pending(void);
-static void deal_with_young_objects_with_finalizers(void);
-static void deal_with_old_objects_with_finalizers(void);
+static void deal_with_young_objects_with_destructors(void);
+static void deal_with_old_objects_with_destructors(void);
static void deal_with_objects_with_finalizers(void);
static void setup_finalizer(void);
@@ -27,19 +31,22 @@
/* regular finalizers (objs from already-committed transactions) */
static struct finalizers_s g_finalizers;
+static struct {
+ int count;
+ stm_finalizer_trigger_fn *triggers;
+} g_finalizer_triggers;
+
static void _invoke_general_finalizers(stm_thread_local_t *tl);
+static void _invoke_local_finalizers(void);
#define invoke_general_finalizers(tl) do { \
- if (g_finalizers.run_finalizers != NULL) \
- _invoke_general_finalizers(tl); \
+ _invoke_general_finalizers(tl); \
} while (0)
-static void _execute_finalizers(struct finalizers_s *f);
-#define any_local_finalizers() (STM_PSEGMENT->finalizers != NULL && \
- STM_PSEGMENT->finalizers->run_finalizers !=
NULL)
#define exec_local_finalizers() do { \
- if (any_local_finalizers()) \
- _execute_finalizers(STM_PSEGMENT->finalizers); \
+ _invoke_local_finalizers(); \
} while (0)
+
+#endif
diff --git a/c8/stm/fprintcolor.h b/c8/stm/fprintcolor.h
--- a/c8/stm/fprintcolor.h
+++ b/c8/stm/fprintcolor.h
@@ -1,3 +1,7 @@
+#ifndef _FPRINTCOLOR_H
+#define _FPRINTCOLOR_H
+
+
/* ------------------------------------------------------------ */
#ifdef STM_DEBUGPRINT
/* ------------------------------------------------------------ */
@@ -40,3 +44,5 @@
__attribute__((unused))
static void stm_fatalerror(const char *format, ...)
__attribute__((format (printf, 1, 2), noreturn));
+
+#endif
diff --git a/c8/stm/gcpage.c b/c8/stm/gcpage.c
--- a/c8/stm/gcpage.c
+++ b/c8/stm/gcpage.c
@@ -807,9 +807,9 @@
LIST_FREE(marked_objects_to_trace);
- /* weakrefs and execute old light finalizers */
+ /* weakrefs and execute old destructors */
stm_visit_old_weakrefs();
- deal_with_old_objects_with_finalizers();
+ deal_with_old_objects_with_destructors();
/* cleanup */
clean_up_segment_lists();
diff --git a/c8/stm/gcpage.h b/c8/stm/gcpage.h
--- a/c8/stm/gcpage.h
+++ b/c8/stm/gcpage.h
@@ -1,3 +1,7 @@
+#ifndef _STM_GCPAGE_H_
+#define _STM_GCPAGE_H_
+
+#include <stdbool.h>
/* Granularity when grabbing more unused pages: take 20 at a time */
#define GCPAGE_NUM_PAGES 20
@@ -22,3 +26,9 @@
static void major_collection_with_mutex(void);
static bool largemalloc_keep_object_at(char *data); /* for largemalloc.c */
static bool smallmalloc_keep_object_at(char *data); /* for smallmalloc.c */
+
+static inline bool mark_visited_test(object_t *obj);
+static bool is_overflow_obj_safe(struct stm_priv_segment_info_s *pseg,
object_t *obj);
+static void mark_visit_possibly_overflow_object(object_t *obj, struct
stm_priv_segment_info_s *pseg);
+
+#endif
diff --git a/c8/stm/list.h b/c8/stm/list.h
--- a/c8/stm/list.h
+++ b/c8/stm/list.h
@@ -1,5 +1,11 @@
+#ifndef _LIST_H
+#define _LIST_H
+
+
#include <stdlib.h>
#include <stdbool.h>
+#include <stdint.h>
+
/************************************************************/
@@ -11,13 +17,13 @@
static struct list_s *list_create(void) __attribute__((unused));
-static inline void list_free(struct list_s *lst)
+static inline void _list_free(struct list_s *lst)
{
free(lst);
}
#define LIST_CREATE(lst) ((lst) = list_create())
-#define LIST_FREE(lst) (list_free(lst), (lst) = NULL)
+#define LIST_FREE(lst) (_list_free(lst), (lst) = NULL)
static struct list_s *_list_grow(struct list_s *, uintptr_t);
@@ -245,3 +251,5 @@
TREE_FIND(tree, addr, result, return false);
return true;
}
+
+#endif
diff --git a/c8/stm/nursery.c b/c8/stm/nursery.c
--- a/c8/stm/nursery.c
+++ b/c8/stm/nursery.c
@@ -3,6 +3,8 @@
# include "core.h" // silence flymake
#endif
+#include "finalizer.h"
+
/************************************************************/
#define NURSERY_START (FIRST_NURSERY_PAGE * 4096UL)
@@ -56,7 +58,6 @@
/************************************************************/
static object_t *find_existing_shadow(object_t *obj);
-#define GCWORD_MOVED ((object_t *) -1)
#define FLAG_SYNC_LARGE 0x01
@@ -441,23 +442,26 @@
}
}
-static void collect_objs_still_young_but_with_finalizers(void)
+static void collect_young_objects_with_finalizers(void)
{
- struct list_s *lst = STM_PSEGMENT->finalizers->objects_with_finalizers;
- uintptr_t i, total = list_count(lst);
+ /* for real finalizers: in a minor collection, all young objs must
survive! */
- for (i = STM_PSEGMENT->finalizers->count_non_young; i < total; i++) {
+ struct list_s *lst =
STM_PSEGMENT->finalizers->probably_young_objects_with_finalizers;
+ long i, count = list_count(lst);
+ for (i = 0; i < count; i += 2) {
+ object_t *obj = (object_t *)list_item(lst, i);
+ uintptr_t qindex = list_item(lst, i + 1);
- object_t *o = (object_t *)list_item(lst, i);
- minor_trace_if_young(&o);
+ minor_trace_if_young(&obj);
- /* was not actually movable */
- assert(o == (object_t *)list_item(lst, i));
+ LIST_APPEND(STM_PSEGMENT->finalizers->objects_with_finalizers, obj);
+ LIST_APPEND(STM_PSEGMENT->finalizers->objects_with_finalizers, qindex);
}
- STM_PSEGMENT->finalizers->count_non_young = total;
+ list_clear(lst);
}
+
static void throw_away_nursery(struct stm_priv_segment_info_s *pseg)
{
#pragma push_macro("STM_PSEGMENT")
@@ -555,8 +559,7 @@
collect_roots_in_nursery();
- if (STM_PSEGMENT->finalizers != NULL)
- collect_objs_still_young_but_with_finalizers();
+ collect_young_objects_with_finalizers();
if (STM_PSEGMENT->active_queues != NULL)
collect_active_queues();
@@ -568,7 +571,7 @@
acquire_privatization_lock(STM_SEGMENT->segment_num);
stm_move_young_weakrefs();
release_privatization_lock(STM_SEGMENT->segment_num);
- deal_with_young_objects_with_finalizers();
+ deal_with_young_objects_with_destructors();
assert(list_is_empty(STM_PSEGMENT->objects_pointing_to_nursery));
diff --git a/c8/stm/nursery.h b/c8/stm/nursery.h
--- a/c8/stm/nursery.h
+++ b/c8/stm/nursery.h
@@ -1,3 +1,8 @@
+#ifndef _STM_NURSERY_H_
+#define _STM_NURSERY_H_
+
+#include <stdint.h>
+#include <stdbool.h>
#define NSE_SIGPAUSE _STM_NSE_SIGNAL_MAX
#define NSE_SIGABORT _STM_NSE_SIGNAL_ABORT
@@ -44,3 +49,11 @@
#define must_abort() is_abort(STM_SEGMENT->nursery_end)
static object_t *find_shadow(object_t *obj);
+
+
+#define GCWORD_MOVED ((object_t *) -1)
+static inline bool _is_young(object_t *obj);
+static inline struct object_s *mark_loc(object_t *obj);
+static inline bool _is_from_same_transaction(object_t *obj);
+
+#endif
diff --git a/c8/stm/setup.c b/c8/stm/setup.c
--- a/c8/stm/setup.c
+++ b/c8/stm/setup.c
@@ -5,6 +5,8 @@
#include <signal.h>
#include <fcntl.h> /* For O_* constants */
+#include "signal_handler.h"
+#include "fprintcolor.h"
static void setup_mmap(char *reason)
{
@@ -100,8 +102,8 @@
pr->nursery_objects_shadows = tree_create();
pr->callbacks_on_commit_and_abort[0] = tree_create();
pr->callbacks_on_commit_and_abort[1] = tree_create();
- pr->young_objects_with_light_finalizers = list_create();
- pr->old_objects_with_light_finalizers = list_create();
+ pr->young_objects_with_destructors = list_create();
+ pr->old_objects_with_destructors = list_create();
pr->last_commit_log_entry = &commit_log_root;
pr->overflow_number = GCFLAG_OVERFLOW_NUMBER_bit0 * i;
@@ -145,19 +147,19 @@
for (i = 0; i < NB_SEGMENTS; i++) {
struct stm_priv_segment_info_s *pr = get_priv_segment(i);
assert(list_is_empty(pr->objects_pointing_to_nursery));
- list_free(pr->objects_pointing_to_nursery);
- list_free(pr->old_objects_with_cards_set);
- list_free(pr->modified_old_objects);
+ LIST_FREE(pr->objects_pointing_to_nursery);
+ LIST_FREE(pr->old_objects_with_cards_set);
+ LIST_FREE(pr->modified_old_objects);
assert(list_is_empty(pr->large_overflow_objects));
- list_free(pr->large_overflow_objects);
- list_free(pr->young_weakrefs);
- list_free(pr->old_weakrefs);
+ LIST_FREE(pr->large_overflow_objects);
+ LIST_FREE(pr->young_weakrefs);
+ LIST_FREE(pr->old_weakrefs);
tree_free(pr->young_outside_nursery);
tree_free(pr->nursery_objects_shadows);
tree_free(pr->callbacks_on_commit_and_abort[0]);
tree_free(pr->callbacks_on_commit_and_abort[1]);
- list_free(pr->young_objects_with_light_finalizers);
- list_free(pr->old_objects_with_light_finalizers);
+ LIST_FREE(pr->young_objects_with_destructors);
+ LIST_FREE(pr->old_objects_with_destructors);
if (pr->active_queues) tree_free(pr->active_queues);
}
diff --git a/c8/stmgc.h b/c8/stmgc.h
--- a/c8/stmgc.h
+++ b/c8/stmgc.h
@@ -710,13 +710,13 @@
void stm_force_transaction_break(stm_thread_local_t *tl);
-/* Support for light finalizers. This is a simple version of
+/* Support for destructors. This is a simple version of
finalizers that guarantees not to do anything fancy, like not
resurrecting objects. */
-extern void (*stmcb_light_finalizer)(object_t *);
-void stm_enable_light_finalizer(object_t *);
+extern void (*stmcb_destructor)(object_t *);
+void stm_enable_destructor(object_t *);
-/* Support for regular finalizers. Unreachable objects with
+/* XXX: Support for regular finalizers. Unreachable objects with
finalizers are kept alive, as well as everything they point to, and
stmcb_finalizer() is called after the major GC. If there are
several objects with finalizers that reference each other in a
@@ -730,8 +730,14 @@
transaction. For older objects, the finalizer is called from a
random thread between regular transactions, in a new custom
transaction. */
-extern void (*stmcb_finalizer)(object_t *);
-object_t *stm_allocate_with_finalizer(ssize_t size_rounded_up);
+typedef void (*stm_finalizer_trigger_fn)(void);
+void (*stmcb_finalizer)(object_t *);
+void stm_setup_finalizer_queues(int number, stm_finalizer_trigger_fn
*triggers);
+void stm_enable_finalizer(int queue_index, object_t *obj);
+
+/* Returns the next object that supposedly died and should have its finalizer
+ called. XXX: This function turns the transaction inevitable. */
+object_t *stm_next_to_finalize(int queue_index);
/* dummies for now: */
@@ -742,7 +748,7 @@
'object_t *'. Note that the type 'stm_hashtable_t' is not an
object type at all; you need to allocate and free it explicitly.
If you want to embed the hashtable inside an 'object_t' you
- probably need a light finalizer to do the freeing. */
+ probably need a destructor to do the freeing. */
typedef struct stm_hashtable_s stm_hashtable_t;
typedef TLPREFIX struct stm_hashtable_entry_s stm_hashtable_entry_t;
@@ -797,8 +803,8 @@
/* Queues. The items you put() and get() back are in random order.
Like hashtables, the type 'stm_queue_t' is not an object type at
all; you need to allocate and free it explicitly. If you want to
- embed the queue inside an 'object_t' you probably need a light
- finalizer to do the freeing. */
+ embed the queue inside an 'object_t' you probably need a destructor
+ to do the freeing. */
typedef struct stm_queue_s stm_queue_t;
stm_queue_t *stm_queue_create(void);
diff --git a/c8/test/support.py b/c8/test/support.py
--- a/c8/test/support.py
+++ b/c8/test/support.py
@@ -52,7 +52,6 @@
/*void stm_write(object_t *obj); use _checked_stm_write() instead */
object_t *stm_allocate(ssize_t size_rounded_up);
object_t *stm_allocate_weakref(ssize_t size_rounded_up);
-object_t *stm_allocate_with_finalizer(ssize_t size_rounded_up);
object_t *stm_allocate_noconflict(ssize_t size_rounded_up);
/*void stm_write_card(); use _checked_stm_write_card() instead */
@@ -206,11 +205,14 @@
bool (*_stm_smallmalloc_keep)(char *data);
void _stm_smallmalloc_sweep_test(void);
+void (*stmcb_destructor)(object_t *);
+void stm_enable_destructor(object_t *);
-void (*stmcb_light_finalizer)(object_t *);
-void stm_enable_light_finalizer(object_t *);
-
+typedef void (*stm_finalizer_trigger_fn)(void);
void (*stmcb_finalizer)(object_t *);
+void stm_setup_finalizer_queues(int number, stm_finalizer_trigger_fn
*triggers);
+void stm_enable_finalizer(int queue_index, object_t *obj);
+object_t *stm_next_to_finalize(int queue_index);
typedef struct stm_hashtable_s stm_hashtable_t;
typedef ... stm_hashtable_entry_t;
@@ -749,15 +751,19 @@
return o
def stm_allocate_with_finalizer(size):
- o = lib.stm_allocate_with_finalizer(size)
+ # OLD-Style finalizers!
+ o = lib.stm_allocate(size)
tid = 42 + size
lib._set_type_id(o, tid)
+ lib.stm_enable_finalizer(-1, o)
return o
def stm_allocate_with_finalizer_refs(n):
- o = lib.stm_allocate_with_finalizer(HDR + n * WORD)
+ # OLD-Style finalizers!
+ o = lib.stm_allocate(HDR + n * WORD)
tid = 421420 + n
lib._set_type_id(o, tid)
+ lib.stm_enable_finalizer(-1, o)
return o
SIZEOF_HASHTABLE_OBJ = 16 + lib.SIZEOF_MYOBJ
diff --git a/c8/test/test_finalizer.py b/c8/test/test_finalizer.py
--- a/c8/test/test_finalizer.py
+++ b/c8/test/test_finalizer.py
@@ -2,13 +2,22 @@
import py
-class TestLightFinalizer(BaseTest):
+class TestDestructors(BaseTest):
def setup_method(self, meth):
BaseTest.setup_method(self, meth)
#
+ @ffi.callback("stm_finalizer_trigger_fn")
+ def trigger():
+ # triggers not needed for destructors
+ assert False
+ self._trigger_keepalive = trigger
+ triggers = ffi.new("stm_finalizer_trigger_fn*", trigger)
+ lib.stm_setup_finalizer_queues(1, triggers)
+ triggers = None
+ #
@ffi.callback("void(object_t *)")
- def light_finalizer(obj):
+ def destructor(obj):
assert stm_get_obj_size(obj) == 48
segnum = lib.current_segment_num()
tlnum = '?'
@@ -17,21 +26,21 @@
if tl.last_associated_segment_num == segnum:
tlnum = n
break
- self.light_finalizers_called.append((obj, tlnum))
- self.light_finalizers_called = []
- lib.stmcb_light_finalizer = light_finalizer
- self._light_finalizer_keepalive = light_finalizer
+ self.destructors_called.append((obj, tlnum))
+ self.destructors_called = []
+ lib.stmcb_destructor = destructor
+ self._destructor_keepalive = destructor
def teardown_method(self, meth):
- lib.stmcb_light_finalizer = ffi.NULL
+ lib.stmcb_destructor = ffi.NULL
BaseTest.teardown_method(self, meth)
def expect_finalized(self, objs, from_tlnum=None):
- assert [obj for (obj, tlnum) in self.light_finalizers_called] == objs
+ assert [obj for (obj, tlnum) in self.destructors_called] == objs
if from_tlnum is not None:
- for obj, tlnum in self.light_finalizers_called:
+ for obj, tlnum in self.destructors_called:
assert tlnum == from_tlnum
- self.light_finalizers_called = []
+ self.destructors_called = []
def test_no_finalizer(self):
self.start_transaction()
@@ -39,45 +48,45 @@
self.commit_transaction()
self.expect_finalized([])
- def test_young_light_finalizer(self):
+ def test_young_destructor(self):
self.start_transaction()
lp1 = stm_allocate(48)
- lib.stm_enable_light_finalizer(lp1)
+ lib.stm_enable_destructor(lp1)
self.expect_finalized([])
self.commit_transaction()
self.expect_finalized([lp1], from_tlnum=0)
- def test_young_light_finalizer_survives(self):
+ def test_young_destructor_survives(self):
self.start_transaction()
lp1 = stm_allocate(48)
- lib.stm_enable_light_finalizer(lp1)
+ lib.stm_enable_destructor(lp1)
self.push_root(lp1) # stays alive
self.commit_transaction()
self.expect_finalized([])
- def test_young_light_finalizer_aborts(self):
+ def test_young_destructor_aborts(self):
self.start_transaction()
lp1 = stm_allocate(48)
- lib.stm_enable_light_finalizer(lp1)
+ lib.stm_enable_destructor(lp1)
self.expect_finalized([])
self.abort_transaction()
self.start_transaction()
self.expect_finalized([lp1], from_tlnum=0)
- def test_old_light_finalizer(self):
+ def test_old_destructor(self):
self.start_transaction()
lp1 = stm_allocate(48)
self.push_root(lp1)
stm_minor_collect()
lp1 = self.pop_root()
- lib.stm_enable_light_finalizer(lp1)
+ lib.stm_enable_destructor(lp1)
self.commit_transaction()
self.expect_finalized([])
- def test_old_light_finalizer_2(self):
+ def test_old_destructor_2(self):
self.start_transaction()
lp1 = stm_allocate(48)
- lib.stm_enable_light_finalizer(lp1)
+ lib.stm_enable_destructor(lp1)
self.push_root(lp1)
stm_minor_collect()
lp1 = self.pop_root()
@@ -86,10 +95,10 @@
self.expect_finalized([lp1])
self.commit_transaction()
- def test_old_light_finalizer_survives(self):
+ def test_old_destructor_survives(self):
self.start_transaction()
lp1 = stm_allocate(48)
- lib.stm_enable_light_finalizer(lp1)
+ lib.stm_enable_destructor(lp1)
self.push_root(lp1)
stm_minor_collect()
lp1 = self.pop_root()
@@ -98,13 +107,13 @@
self.commit_transaction()
self.expect_finalized([])
- def test_old_light_finalizer_segment(self):
+ def test_old_destructor_segment(self):
self.start_transaction()
#
self.switch(1)
self.start_transaction()
lp1 = stm_allocate(48)
- lib.stm_enable_light_finalizer(lp1)
+ lib.stm_enable_destructor(lp1)
self.push_root(lp1)
stm_minor_collect()
lp1 = self.pop_root()
@@ -114,10 +123,10 @@
stm_major_collect()
self.expect_finalized([lp1], from_tlnum=1)
- def test_old_light_finalizer_aborts(self):
+ def test_old_destructor_aborts(self):
self.start_transaction()
lp1 = stm_allocate(48)
- lib.stm_enable_light_finalizer(lp1)
+ lib.stm_enable_destructor(lp1)
self.push_root(lp1)
self.commit_transaction()
#
@@ -126,10 +135,10 @@
self.abort_transaction()
self.expect_finalized([])
- def test_overflow_light_finalizer_aborts(self):
+ def test_overflow_destructor_aborts(self):
self.start_transaction()
lp1 = stm_allocate(48)
- lib.stm_enable_light_finalizer(lp1)
+ lib.stm_enable_destructor(lp1)
self.push_root(lp1)
stm_minor_collect()
lp1 = self.pop_root()
@@ -139,14 +148,212 @@
self.expect_finalized([lp1], from_tlnum=0)
+class TestRegularFinalizerQueues(BaseTest):
+ expect_content_character = None
+ run_major_collect_in_trigger = False
-class TestRegularFinalizer(BaseTest):
+ def setup_method(self, meth):
+ BaseTest.setup_method(self, meth)
+ #
+ @ffi.callback("stm_finalizer_trigger_fn")
+ def trigger0():
+ self.queues_triggered.append(0)
+ if self.run_major_collect_in_trigger:
+ stm_major_collect()
+ @ffi.callback("stm_finalizer_trigger_fn")
+ def trigger1():
+ self.queues_triggered.append(1)
+ if self.run_major_collect_in_trigger:
+ stm_major_collect()
+ self._trigger_keepalive = [trigger0, trigger1]
+ triggers = ffi.new("stm_finalizer_trigger_fn[]",
self._trigger_keepalive)
+ lib.stm_setup_finalizer_queues(2, triggers)
+ #
+ self.queues_triggered = []
+
+ def get_queues_triggered(self):
+ res = set(self.queues_triggered)
+ self.queues_triggered = []
+ return res
+
+ def get_to_finalize(self, qindex):
+ obj = lib.stm_next_to_finalize(qindex)
+ res = []
+ while obj:
+ res.append(obj)
+ obj = lib.stm_next_to_finalize(qindex)
+ return res
+
+
+
+ def test_no_finalizer(self):
+ self.start_transaction()
+ lp1 = stm_allocate(48)
+ stm_major_collect()
+ assert not self.get_queues_triggered()
+
+ def test_no_finalizer_in_minor_collection(self):
+ self.start_transaction()
+ lp1 = stm_allocate(48)
+ lib.stm_enable_finalizer(0, lp1)
+ stm_minor_collect()
+ assert not self.get_queues_triggered()
+
+ def test_finalizer_in_major_collection(self):
+ self.start_transaction()
+ for repeat in range(4):
+ lp1 = stm_allocate(48)
+ lp2 = stm_allocate(48)
+ lp3 = stm_allocate(48)
+ if repeat % 2 == 0: # register on young
+ lib.stm_enable_finalizer(0, lp1)
+ lib.stm_enable_finalizer(1, lp2)
+ lib.stm_enable_finalizer(1, lp3)
+ assert not self.get_queues_triggered()
+ self.push_roots([lp1, lp2, lp3])
+ stm_minor_collect()
+ lp1, lp2, lp3 = self.pop_roots()
+ if repeat % 2 == 1: # register on old
+ lib.stm_enable_finalizer(0, lp1)
+ lib.stm_enable_finalizer(1, lp2)
+ lib.stm_enable_finalizer(1, lp3)
+ print repeat, lp1, lp2, lp3
+ stm_major_collect()
+ assert {0, 1} == self.get_queues_triggered()
+ assert [lp1] == self.get_to_finalize(0)
+ assert [lp2, lp3] == self.get_to_finalize(1)
+
+ def test_finalizer_from_other_thread(self):
+ self.start_transaction()
+ lp1 = stm_allocate(48)
+ lib.stm_enable_finalizer(0, lp1)
+ stm_set_char(lp1, 'H')
+ self.expect_content_character = 'H'
+ self.push_root(lp1)
+ stm_minor_collect()
+ lp1 = self.pop_root()
+ print lp1
+ #
+ self.switch(1)
+ self.start_transaction()
+ stm_major_collect()
+ assert not self.get_queues_triggered()
+ #
+ self.switch(0)
+ assert {0} == self.get_queues_triggered()
+ assert [lp1] == self.get_to_finalize(0)
+
+ def test_finalizer_ordering(self):
+ self.start_transaction()
+ lp1 = stm_allocate_refs(1)
+ lp2 = stm_allocate_refs(1)
+ lp3 = stm_allocate_refs(1)
+ lib.stm_enable_finalizer(1, lp1)
+ lib.stm_enable_finalizer(1, lp2)
+ lib.stm_enable_finalizer(1, lp3)
+
+ print lp1, lp2, lp3
+ stm_set_ref(lp3, 0, lp1)
+ stm_set_ref(lp1, 0, lp2)
+
+ self.push_roots([lp1, lp2, lp3])
+ stm_minor_collect()
+ lp1, lp2, lp3 = self.pop_roots()
+
+ stm_major_collect()
+ assert {1} == self.get_queues_triggered()
+ assert [lp3] == self.get_to_finalize(1)
+
+ def test_finalizer_extra_transaction(self):
+ self.start_transaction()
+ lp1 = stm_allocate(32)
+ lib.stm_enable_finalizer(0, lp1)
+ self.push_root(lp1)
+ self.commit_transaction()
+
+ self.start_transaction()
+ lp1b = self.pop_root()
+ # assert lp1b == lp1 <- lp1 can be in nursery now
+ assert not self.get_queues_triggered()
+ self.commit_transaction() # finalizer-obj moved to global queue
+ assert not self.get_queues_triggered()
+
+ self.start_transaction()
+ stm_major_collect()
+ assert {0} == self.get_queues_triggered()
+ self.commit_transaction() # invoke finalizers (triggers again...)
+ self.start_transaction()
+ assert {0} == self.get_queues_triggered()
+ assert [lp1b] == self.get_to_finalize(0)
+
+ def test_run_cb_for_all_threads(self):
+ self.start_transaction()
+ lp1 = stm_allocate(48)
+ lib.stm_enable_finalizer(0, lp1)
+ print lp1
+ #
+ self.switch(1)
+ self.start_transaction()
+ lp2 = stm_allocate(56)
+ lib.stm_enable_finalizer(1, lp2)
+ print lp2
+
+ assert not self.get_queues_triggered()
+ stm_major_collect()
+ assert len(self.get_to_finalize(1)) == 1
+ self.switch(0)
+ assert self.get_queues_triggered() == {0, 1}
+ assert len(self.get_to_finalize(0)) == 1
+
+ def test_run_major_collect_in_trigger(self):
+ self.run_major_collect_in_trigger = True
+ self.start_transaction()
+ lp1 = stm_allocate(32)
+ lp2 = stm_allocate(32)
+ lp3 = stm_allocate(32)
+ lib.stm_enable_finalizer(0, lp1)
+ lib.stm_enable_finalizer(1, lp2)
+ lib.stm_enable_finalizer(1, lp3)
+ print lp1, lp2, lp3
+ stm_major_collect()
+ assert 1 == len(self.get_to_finalize(0))
+ assert 2 == len(self.get_to_finalize(1))
+
+ def test_new_objects_w_finalizers(self):
+ self.switch(2)
+ self.start_transaction()
+ lp1 = stm_allocate_refs(3)
+ lp2 = stm_allocate_refs(3)
+ lib.stm_enable_finalizer(0, lp1)
+ lib.stm_enable_finalizer(0, lp2)
+ stm_set_ref(lp1, 0, lp2)
+
+ self.push_root(lp1)
+ stm_minor_collect()
+ lp1 = self.pop_root()
+ lp2 = stm_get_ref(lp1, 0)
+ # lp1, lp2 have WB_EXECUTED objs
+
+ assert not self.get_queues_triggered()
+ stm_major_collect()
+ assert [lp1] == self.get_to_finalize(0)
+
+
+class TestOldStyleRegularFinalizer(BaseTest):
expect_content_character = None
run_major_collect_in_finalizer = False
def setup_method(self, meth):
BaseTest.setup_method(self, meth)
#
+ @ffi.callback("stm_finalizer_trigger_fn")
+ def trigger():
+ # triggers not needed for oldstyle-finalizer tests
+ assert False
+ self._trigger_keepalive = trigger
+ triggers = ffi.new("stm_finalizer_trigger_fn*", trigger)
+ lib.stm_setup_finalizer_queues(1, triggers)
+ #
@ffi.callback("void(object_t *)")
def finalizer(obj):
print "finalizing!", obj
@@ -161,7 +368,12 @@
self._finalizer_keepalive = finalizer
def expect_finalized(self, objs):
- assert self.finalizers_called == objs
+ if isinstance(objs, int):
+ assert len(self.finalizers_called) == objs
+ else:
+ print objs
+ print self.finalizers_called
+ assert self.finalizers_called == objs
self.finalizers_called = []
def test_no_finalizer(self):
@@ -182,8 +394,11 @@
lp1 = stm_allocate_with_finalizer(48)
lp2 = stm_allocate_with_finalizer(48)
lp3 = stm_allocate_with_finalizer(48)
+ self.expect_finalized([])
+ self.push_roots([lp1, lp2, lp3])
+ stm_minor_collect()
+ lp1, lp2, lp3 = self.pop_roots()
print repeat, lp1, lp2, lp3
- self.expect_finalized([])
stm_major_collect()
self.expect_finalized([lp1, lp2, lp3])
@@ -192,6 +407,9 @@
lp1 = stm_allocate_with_finalizer(48)
stm_set_char(lp1, 'H')
self.expect_content_character = 'H'
+ self.push_root(lp1)
+ stm_minor_collect()
+ lp1 = self.pop_root()
print lp1
#
self.switch(1)
@@ -210,6 +428,11 @@
print lp1, lp2, lp3
stm_set_ref(lp3, 0, lp1)
stm_set_ref(lp1, 0, lp2)
+
+ self.push_roots([lp1, lp2, lp3])
+ stm_minor_collect()
+ lp1, lp2, lp3 = self.pop_roots()
+
stm_major_collect()
self.expect_finalized([lp3])
@@ -222,16 +445,16 @@
self.start_transaction()
lp1b = self.pop_root()
- assert lp1b == lp1
+ # assert lp1b == lp1 <- lp1 can be in nursery now
self.expect_finalized([])
- self.commit_transaction()
+ self.commit_transaction() # finalizer-obj moved to global queue
self.expect_finalized([])
self.start_transaction()
stm_major_collect()
self.expect_finalized([])
- self.commit_transaction()
- self.expect_finalized([lp1])
+ self.commit_transaction() # invoke finalizers
+ self.expect_finalized([lp1b])
def test_run_cb_for_all_threads(self):
self.start_transaction()
@@ -246,7 +469,7 @@
self.expect_finalized([])
stm_major_collect()
self.switch(0)
- self.expect_finalized([lp2, lp1])
+ self.expect_finalized(2)
def test_run_major_collect_in_finalizer(self):
self.run_major_collect_in_finalizer = True
diff --git a/c8/test/test_hashtable.py b/c8/test/test_hashtable.py
--- a/c8/test/test_hashtable.py
+++ b/c8/test/test_hashtable.py
@@ -43,8 +43,8 @@
BaseTest.setup_method(self, meth)
#
@ffi.callback("void(object_t *)")
- def light_finalizer(obj):
- print 'light_finalizer:', obj
+ def destructor(obj):
+ print 'destructor:', obj
try:
assert lib._get_type_id(obj) == 421419
self.seen_hashtables -= 1
@@ -54,20 +54,20 @@
self.errors.append(sys.exc_info()[2])
raise
- lib.stmcb_light_finalizer = light_finalizer
- self._light_finalizer_keepalive = light_finalizer
+ lib.stmcb_destructor = destructor
+ self._destructor_keepalive = destructor
self.seen_hashtables = 0
self.errors = []
def teardown_method(self, meth):
BaseTest.teardown_method(self, meth)
- lib.stmcb_light_finalizer = ffi.NULL
+ lib.stmcb_destructor = ffi.NULL
assert self.errors == []
assert self.seen_hashtables == 0
def allocate_hashtable(self):
h = stm_allocate_hashtable()
- lib.stm_enable_light_finalizer(h)
+ lib.stm_enable_destructor(h)
self.seen_hashtables += 1
return h
diff --git a/c8/test/test_list.py b/c8/test/test_list.py
--- a/c8/test/test_list.py
+++ b/c8/test/test_list.py
@@ -6,7 +6,7 @@
ffi = cffi.FFI()
ffi.cdef("""
struct list_s *list_create(void);
-void list_free(struct list_s *lst);
+void _list_free(struct list_s *lst);
struct list_s *list_append(struct list_s *lst, uintptr_t item);
uintptr_t list_count(struct list_s *lst);
uintptr_t list_item(struct list_s *lst, uintptr_t index);
@@ -144,5 +144,5 @@
assert lib.list_count(a) == 17 + 7
for i, expected in enumerate(range(103, 120) + range(113, 120)):
assert lib.list_item(a, i) == expected
- lib.list_free(b)
- lib.list_free(a)
+ lib._list_free(b)
+ lib._list_free(a)
diff --git a/c8/test/test_queue.py b/c8/test/test_queue.py
--- a/c8/test/test_queue.py
+++ b/c8/test/test_queue.py
@@ -13,8 +13,8 @@
BaseTest.setup_method(self, meth)
#
@ffi.callback("void(object_t *)")
- def light_finalizer(obj):
- print 'light_finalizer:', obj
+ def destructor(obj):
+ print 'destructor:', obj
try:
assert lib._get_type_id(obj) == 421417
self.seen_queues -= 1
@@ -24,20 +24,20 @@
self.errors.append(sys.exc_info()[2])
raise
- lib.stmcb_light_finalizer = light_finalizer
- self._light_finalizer_keepalive = light_finalizer
+ lib.stmcb_destructor = destructor
+ self._destructor_keepalive = destructor
self.seen_queues = 0
self.errors = []
def teardown_method(self, meth):
BaseTest.teardown_method(self, meth)
- lib.stmcb_light_finalizer = ffi.NULL
+ lib.stmcb_destructor = ffi.NULL
assert self.errors == []
assert self.seen_queues == 0
def allocate_queue(self):
q = stm_allocate_queue()
- lib.stm_enable_light_finalizer(q)
+ lib.stm_enable_destructor(q)
self.seen_queues += 1
return q
diff --git a/c8/test/test_zrandom.py b/c8/test/test_zrandom.py
--- a/c8/test/test_zrandom.py
+++ b/c8/test/test_zrandom.py
@@ -411,13 +411,20 @@
global_state.add_noconfl_obj(r)
def op_allocate_ref(ex, global_state, thread_state):
- num = str(global_state.rnd.randrange(1, 1000))
+ g_rnd = global_state.rnd
+ num = str(g_rnd.randrange(1, 1000))
r = global_state.get_new_root_name(True, num)
thread_state.push_roots(ex)
ex.do('%s = stm_allocate_refs(%s)' % (r, num))
ex.do('# 0x%x' % (int(ffi.cast("uintptr_t", ex.content[r]))))
thread_state.transaction_state.add_root(r, "ffi.NULL", True)
+ if g_rnd.randrange(10) == 0:
+ # XXX: should empty the queues sometimes! but calling next_to_finalize
+ # *may* turn inevitable (but only if there is something in the queue).
+ # so that's a bit involved...
+ ex.do('lib.stm_enable_finalizer(%s, %s)' % (g_rnd.randrange(2), r))
+
thread_state.pop_roots(ex)
thread_state.reload_roots(ex)
thread_state.register_root(r)
@@ -585,6 +592,25 @@
N_THREADS = self.NB_THREADS
ex = Exec(self)
ex.do("################################################################\n"*10)
+ ex.do("""
+
[email protected]("stm_finalizer_trigger_fn")
+def trigger0():
+ pass
[email protected]("stm_finalizer_trigger_fn")
+def trigger1():
+ pass
+self._trigger_keepalive = [trigger0, trigger1]
+triggers = ffi.new("stm_finalizer_trigger_fn[]", self._trigger_keepalive)
+lib.stm_setup_finalizer_queues(2, triggers)
+#
[email protected]("void(object_t *)")
+def finalizer(obj):
+ pass
+lib.stmcb_finalizer = finalizer
+self._finalizer_keepalive = finalizer
+ """)
+
ex.do('# initialization')
global_state = GlobalState(ex, rnd)
@@ -624,7 +650,7 @@
]
possible_actions = [item for sublist in possible_actions for item in
sublist]
print possible_actions
- for _ in range(2000):
+ for _ in range(3000):
# make sure we are in a transaction:
curr_thread = op_switch_thread(ex, global_state, curr_thread)
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit