elk/src/terminate.c

206 lines
6.1 KiB
C
Raw Normal View History

/* terminate.c: Termination functions, weak pointers.
*
* $Id$
*
* Copyright 1990, 1991, 1992, 1993, 1994, 1995, Oliver Laumann, Berlin
* Copyright 2002, 2003 Sam Hocevar <sam@zoy.org>, Paris
*
* This software was derived from Elk 1.2, which was Copyright 1987, 1988,
* 1989, Nixdorf Computer AG and TELES GmbH, Berlin (Elk 1.2 has been written
* by Oliver Laumann for TELES Telematic Services, Berlin, in a joint project
* between TELES and Nixdorf Microprocessor Engineering, Berlin).
*
* Oliver Laumann, TELES GmbH, Nixdorf Computer AG and Sam Hocevar, as co-
* owners or individual owners of copyright in this software, grant to any
* person or company a worldwide, royalty free, license to
*
* i) copy this software,
* ii) prepare derivative works based on this software,
* iii) distribute copies of this software or derivative works,
* iv) perform this software, or
* v) display this software,
*
* provided that this notice is not removed and that neither Oliver Laumann
* nor Teles nor Nixdorf are deemed to have made any representations as to
* the suitability of this software for any purpose nor are held responsible
* for any defects of this software.
*
* THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
*/
#include "config.h"
#include <stdarg.h>
#include <stdlib.h>
#include "kernel.h"
static WEAK_NODE *first;
void Call_Terminators();
void Init_Terminate () {
Register_After_GC (Call_Terminators);
}
/* Register an object with the given group and termination function;
* object can be marked as LEADER.
*/
void Register_Object (Object obj, GENERIC group, PFO term, int leader_flag) {
WEAK_NODE *p;
p = (WEAK_NODE *)Safe_Malloc (sizeof (*p));
p->obj = obj;
p->group = group;
p->term = term;
p->flags = leader_flag? WK_LEADER : 0;
p->next = first;
first = p;
}
void Deregister_Object (Object obj) {
WEAK_NODE *p, **pp;
Disable_Interrupts;
for (pp = &first; (p = *pp); ) {
if (WAS_FORWARDED(p->obj))
UPDATE_OBJ(p->obj);
if (EQ(p->obj, obj)) {
*pp = p->next;
free ((char *)p);
} else pp = &p->next;
}
Enable_Interrupts;
}
/* Search for an object of a given type (arg 1) and group (arg 2).
* Use the given match function (arg 3); it is called with an object
* and the remaining arguments of Find_Object() (a va_list).
* Null is returned when the object has not been found.
*/
/*VARARGS*/
Object Find_Object (int type, GENERIC group, MATCHFUN match, ...) {
WEAK_NODE *p;
va_list args;
va_start (args, match);
for (p = first; p; p = p->next) {
if (TYPE(p->obj) != type || p->group != group)
continue;
/*
* I believe updating the object is wrong here, as Find_Object() may
* be called from within GC (see Widget_Visit() in lib/xt/widget.c).
* If an object is updated here, it will no longer be regarded as
* alive in the call to Call_Terminators() later.
*
if (WAS_FORWARDED(p->obj))
UPDATE_OBJ(p->obj);
*/
if (match (p->obj, args)) {
va_end (args);
REVIVE_OBJ(p->obj);
return p->obj;
}
}
va_end (args);
return Null;
}
/* Each of the following functions terminates the objects in two passes.
* First, they are removed from the global list and added to a temporary
* list. In a second pass, this list is scanned to call the terminator
* functions and actually free the objects.
*
* This is to avoid that calling a terminator functions causes the global
* list to be clobbered recursively resulting in an inconsistent data
* structure.
*/
/* Terminate all objects belonging to the given group except leaders.
*/
void Terminate_Group (GENERIC group) {
WEAK_NODE *p, **pp, *q = 0;
Disable_Interrupts;
for (pp = &first; (p = *pp); ) {
if (p->group == group && !(p->flags & WK_LEADER)) {
if (WAS_FORWARDED(p->obj))
UPDATE_OBJ(p->obj);
*pp = p->next; /* move object to temporary list */
p->next = q;
q = p;
} else pp = &p->next;
}
while (q) { /* scan temporary list, call terminators and free objects */
WEAK_NODE *tmp = q;
if (q->term)
(void)q->term (q->obj);
q = q->next;
free ((char *)tmp);
}
Enable_Interrupts;
}
/* Terminate all objects of a given type.
*/
void Terminate_Type (int type) {
WEAK_NODE *p, **pp, *q = 0;
Disable_Interrupts;
for (pp = &first; (p = *pp); ) {
if (TYPE(p->obj) == type) {
if (WAS_FORWARDED(p->obj))
UPDATE_OBJ(p->obj);
*pp = p->next; /* move object to temporary list */
p->next = q;
q = p;
} else pp = &p->next;
}
while (q) { /* scan temporary list, call terminators and free objects */
WEAK_NODE *tmp = q;
if (q->term)
(void)q->term (q->obj);
q = q->next;
free ((char *)tmp);
}
Enable_Interrupts;
}
/* The after-GC function.
*/
void Call_Terminators () {
WEAK_NODE *p, **pp, *q = 0, **qq = &q;
Disable_Interrupts;
for (pp = &first; (p = *pp); ) {
if (IS_ALIVE(p->obj)) {
if (WAS_FORWARDED(p->obj))
UPDATE_OBJ(p->obj);
pp = &p->next;
} else {
*pp = p->next;
if (p->flags & WK_LEADER) {
*qq = p; /* move leader to end of temporary list */
qq = &p->next;
*qq = 0;
} else {
p->next = q; /* move non-leader to front of list */
if (qq == &q) qq = &p->next;
q = p;
}
}
}
/* Scan the temporary list, call terminators and free objects.
* As leaders have been appended to the list, they are now
* scanned after all non-leaders have been taken care of.
*/
while (q) {
WEAK_NODE *tmp = q;
if (q->term)
(void)q->term (q->obj);
q = q->next;
free ((char *)tmp);
}
Enable_Interrupts;
}