diff --git a/src/backend.cmod b/src/backend.cmod index db05aa48fdecf02c35876d3ff7a33155d81c05dd..56e10ca98a668ec387167c10453c68ccbd6d3b67 100644 --- a/src/backend.cmod +++ b/src/backend.cmod @@ -1676,6 +1676,26 @@ PIKECLASS Backend if (box->ref_obj && box->events) add_ref (box->ref_obj); } + /** + * Unlink the box from the active list it is on (if any), + * and return 1 if it was active (and thus needs to be freed). + */ + static int unlink_box(struct fd_callback_box *box) + { + if (box->next) { + /* The box is active in some backend. Unlink it. */ + struct fd_callback_box *pred = box->next; + /* Find the predecessor. */ + while (pred->next != box) { + pred = pred->next; + } + pred->next = box->next; + box->next = NULL; + return 1; + } + return 0; + } + PMOD_EXPORT void unhook_fd_callback_box (struct fd_callback_box *box) { /* Accept an unhooked box; can happen when we're called from an @@ -1696,6 +1716,20 @@ PIKECLASS Backend /* Make sure no further callbacks are called on this box. */ box->revents = 0; box->rflags = 0; + + /* Unlink the box from the active list in case it is there, + * otherwise the active list may get cut, which could cause + * the remaining boxes on the list to be lost from the backend.. + */ + if (unlink_box(box) && box->ref_obj) { + /* Use gc safe method to allow calls from within the gc. */ + /* box->ref_obj is only converted from a counted to + * non-counted ref, so it shouldn't be clobbered by the free. */ + union anything u; + u.object = box->ref_obj; + gc_free_short_svalue (&u, T_OBJECT); + } + if (box->ref_obj && box->events) { /* Use gc safe method to allow calls from within the gc. */ /* box->ref_obj is only converted from a counted to @@ -1703,6 +1737,7 @@ PIKECLASS Backend union anything u; u.object = box->ref_obj; gc_free_short_svalue (&u, T_OBJECT); + box->events = 0; } } @@ -1752,16 +1787,12 @@ PIKECLASS Backend if (old) { if (box->fd >= 0) update_fd_set (old, box->fd, box->events, 0, box->flags); remove_fd_box (box); - if (box->next) { - /* The box is active in the old backend. Unlink it. */ - struct fd_callback_box *pred = box->next; - /* Find the predecessor. */ - while (pred->next != box) { - pred = pred->next; - } - pred->next = box->next; - box->next = NULL; - if (box->ref_obj) free_object(box->ref_obj); + + /* Unlink the box from the active in the old backend, + * and free it if it was there. + */ + if (unlink_box(box) && box->ref_obj) { + free_object(box->ref_obj); } } box->backend = new; @@ -1795,7 +1826,7 @@ PIKECLASS Backend add_fd_box (box); new_fd = box->fd; box->revents = 0; - box->rflags = 0; + box->rflags = 0; if (new_fd >= 0) update_fd_set (box->backend, new_fd, 0, box->events, box->flags); } @@ -2221,7 +2252,7 @@ PIKECLASS Backend } box->revents = 0; - box->rflags = 0; + box->rflags = 0; /* We don't want to keep this fd anymore. * Note: This disables any further callbacks. diff --git a/src/backend.h b/src/backend.h index 7841bba00c66cb8058a8bce5a14abb3de4fef071..54f2c4911063d1024cc032ef81c121d8b61a4a9a 100644 --- a/src/backend.h +++ b/src/backend.h @@ -119,10 +119,13 @@ struct fd_callback_box struct object *ref_obj; /**< If set, it's the object containing the box. * It then acts as the ref from the backend to * the object and is refcounted by the backend - * whenever any event is wanted. */ + * whenever any event is wanted. + * + * It receives a ref for each when next and/or + * events are non-zero. */ struct fd_callback_box *next; /**< Circular list of active fds in a backend. * NULL if the fd is not active in some - * backend. Note: ref_obj MUST be freed if + * backend. Note: ref_obj MUST be freed when * the box is unlinked. */ int fd; /**< Use change_fd_for_box to change this. May * be negative, in which case only the ref