hi,
like on so many occasions already, my original hasty response to ``dynamic etern/subpatch instantiation'' post was not precise.
I must say, first of all, that in all cases reported below, it is hard to find any real bugs. These are `misfeatures', which do not break any promises -- and the safeguards could have been omitted for performance sake, or code simplicity, or whatever other reason...
original ``duplicated toplevel patch'' crash. The proof is here (toppatch.pd can be any patch file, [del 1000] is inserted in order to wait through any window raising delays):
#N canvas 213 96 518 300 12; #X obj 25 41 loadbang; #X msg 25 91 bang ; pd open toppatch.pd . ; pd open toppatch.pd .; #X obj 25 176 del 1000; #X msg 25 231 ; pd-toppatch.pd menuclose; #X connect 0 0 1 0; #X connect 1 0 2 0; #X connect 2 0 3 0;
static void bindlist_anything(t_bindlist *x, t_symbol *s, int argc, t_atom *argv) { t_bindelem *e = x->b_list; #if CRASHY /* the loop we have now */ for (e = x->b_list; e; e = e->e_next) pd_typedmess(e->e_who, s, argc, argv); #else /* safer loop */ while (e) { t_pd *who = e->e_who; e = e->e_next; pd_typedmess(who, s, argc, argv); } #endif }
theme in both cases was self-destruction, but my example was much more nasty. A direct cause of crashing in this case was such:
#N canvas 296 127 450 300 12; #X obj 44 40 loadbang; #X obj 44 79 del 1000; #X msg 44 117 ; pd-crash find r ; x please do not crash; #N canvas 43 0 376 332 crash 1; #X obj 110 138 outlet; #X obj 110 68 r x; #X connect 1 0 0 0; #X restore 44 202 pd crash; #X msg 44 239 ; pd-crash cut; #X connect 0 0 1 0; #X connect 1 0 2 0; #X connect 3 0 4 0;
void outlet_anything(t_outlet *x, t_symbol *s, int argc, t_atom *argv) { t_outconnect *oc = x->o_connections; char c; if (&c < stacklimit) outlet_stackerror(x); #if CRASHY /* the loop we have now */ else for (oc = x->o_connections; oc; oc = oc->oc_next) typedmess(oc->oc_to, s, argc, argv); #else /* safer loop */ else while (oc) { t_pd *dest = oc->oc_to; oc = oc->oc_next; typedmess(dest, s, argc, argv); } #endif }
the two safeguards are not applied to bang message, i.e. we need to embellish also bindlist_bang() and outlet_bang().
a [del 1000] object inserted after any patch-modifying loadbang.
Btw. under Gnome this delay may need to be even longer (I think there is a window raising bug in Gnome or Sawfish; in my Gnome system it is so annoying, that I finally deleted `after-raise' command from pdtk_canvas_new in pd.tk; no such trouble under fvwm2).
be crashes, if the fatal message was clicked, instead of being loadbanged. For example, using safer version of outlet_bang(), the following (if named `crash-message.pd') will not crash upon loading, but will crash after clicking ``please do not crash'' message.
#N canvas 296 127 424 295 12; #X obj 44 18 loadbang; #X obj 44 65 del 1000; #X msg 44 112 ; pd-crash-message.pd selectall ; x please do not crash; #X obj 44 195 r x; #X msg 44 243 ; pd-crash-message.pd cut; #X connect 0 0 1 0; #X connect 1 0 2 0; #X connect 3 0 4 0;
The simplest safeguard would be just an insertion of ``if (!y) return;'' into message_click().
any canvas which is not visible.
Crashtof
hi,
in my previous longish post I reported that sending self-destruction
messages to a canvas (menuclose' or
cut') may prove deadly. This
`misfeature', if taken on its own, is not important -- after all,
hacking has a price, ``one gets what one asked for'', etc.
But there is another weak spot -- receive symbol of gui objects. This symbol may be changed via `receive' message, and in certain ways such a change may easily crash Pd: during bindlist traversal any bindlist element may send a message causing unfortunate modification of its bindlist, i.e. leaving a fatal dangling pointer.
To prove the point, here is an attached test patch gui-crashes.pd', which crashes Pd in self-unbinding cases
abc1' and ab1', and also in
ab2' case (bindlist annihilation). After applying lookahead
safeguard from my previous post, Pd will crash in cases abc2' and
ab2' (next element unbinding).
While self-destruction related crashes may be avoided easily, there are no simple safeguards against arbitrary un/rebinding of objects.
One quite elaborate (and costly) solution is to use some kind of a locking strategy. An example of how to lock current bindlist element is attached in `m_pd.c.diff'.
Another standard solution is to use `smart' pointers.
Yet another solution is to get rid of (or at least to warn against) any message being a potential bindlist modifier.
Krzysztof
Btw. ``if (glist_isvisible())'' tests are missing before redraw commands in iemgui_send/receive() of 0.35test4.
#N canvas 240 51 439 297 12; #X msg 64 35 abc1; #X msg 190 35 abc2; #X msg 316 35 abc3; #X msg 133 165 ab1; #X msg 267 165 ab2; #N canvas 0 0 523 344 abc1 0; #X obj 226 21 inlet; #X obj 226 62 symbol; #X msg 226 102 receive empty , receive $1 ; $1 bang; #X obj 226 157 t a a a a; #X obj 26 216 bng 30 250 50 0 empty empty empty 20 8 64 8 -262144 -1 -1; #X obj 156 216 bng 30 250 50 0 empty empty empty 20 8 64 8 -262144 -1 -1 ; #X obj 276 216 bng 30 250 50 0 empty empty empty 20 8 64 8 -262144 -1 -1; #X obj 26 266 print one; #X obj 156 295 print two; #X obj 276 266 print three; #X msg 81 102 receive empty; #X obj 402 225 print abc1; #X connect 0 0 1 0; #X connect 1 0 2 0; #X connect 2 0 3 0; #X connect 3 0 4 0; #X connect 3 1 5 0; #X connect 3 2 6 0; #X connect 3 3 11 0; #X connect 4 0 10 0; #X connect 4 0 7 0; #X connect 5 0 8 0; #X connect 6 0 9 0; #X connect 10 0 4 0; #X restore 64 90 pd abc1; #N canvas 0 0 523 344 abc2 0; #X obj 226 21 inlet; #X obj 226 62 symbol; #X msg 226 102 receive empty , receive $1 ; $1 bang; #X obj 226 157 t a a a a; #X obj 26 216 bng 30 250 50 0 empty empty empty 20 8 64 8 -262144 -1 -1; #X obj 156 216 bng 30 250 50 0 empty empty empty 20 8 64 8 -262144 -1 -1 ; #X obj 276 216 bng 30 250 50 0 empty epmty empty 20 8 64 8 -262144 -1 -1; #X obj 26 266 print one; #X obj 156 295 print two; #X obj 276 266 print three; #X obj 402 225 print abc2; #X msg 81 102 receive empty; #X connect 0 0 1 0; #X connect 1 0 2 0; #X connect 2 0 3 0; #X connect 3 0 4 0; #X connect 3 1 5 0; #X connect 3 2 6 0; #X connect 3 3 10 0; #X connect 4 0 11 0; #X connect 4 0 7 0; #X connect 5 0 8 0; #X connect 6 0 9 0; #X connect 11 0 5 0; #X restore 190 90 pd abc2; #N canvas 0 0 523 344 abc3 0; #X obj 226 21 inlet; #X obj 226 62 symbol; #X msg 226 102 receive empty , receive $1 ; $1 bang; #X obj 226 157 t a a a a; #X obj 26 216 bng 30 250 50 0 empty empty empty 20 8 64 8 -262144 -1 -1; #X obj 156 216 bng 30 250 50 0 empty empty empty 20 8 64 8 -262144 -1 -1 ; #X obj 276 216 bng 30 250 50 0 empty empty empty 20 8 64 8 -262144 -1 -1; #X obj 26 266 print one; #X obj 156 295 print two; #X obj 276 266 print three; #X msg 81 102 receive empty; #X obj 402 225 print abc3; #X connect 0 0 1 0; #X connect 1 0 2 0; #X connect 2 0 3 0; #X connect 3 0 4 0; #X connect 3 1 5 0; #X connect 3 2 6 0; #X connect 3 3 11 0; #X connect 4 0 10 0; #X connect 4 0 7 0; #X connect 5 0 8 0; #X connect 6 0 9 0; #X connect 10 0 6 0; #X restore 316 90 pd abc3; #N canvas 0 0 523 344 ab1 0; #X obj 226 21 inlet; #X obj 226 62 symbol; #X msg 226 102 receive empty , receive $1 ; $1 bang; #X obj 26 216 bng 30 250 50 0 empty empty empty 20 8 64 8 -262144 -1 -1; #X obj 156 216 bng 30 250 50 0 empty empty empty 20 8 64 8 -262144 -1 -1 ; #X obj 26 266 print one; #X obj 156 295 print two; #X msg 81 102 receive empty; #X obj 298 216 print ab1; #X obj 226 157 t a a a; #X connect 0 0 1 0; #X connect 1 0 2 0; #X connect 2 0 9 0; #X connect 3 0 7 0; #X connect 3 0 5 0; #X connect 4 0 6 0; #X connect 7 0 3 0; #X connect 9 0 3 0; #X connect 9 1 4 0; #X connect 9 2 8 0; #X restore 133 221 pd ab1; #N canvas 0 0 523 344 ab2 0; #X obj 226 21 inlet; #X obj 226 62 symbol; #X msg 226 102 receive empty , receive $1 ; $1 bang; #X obj 26 216 bng 30 250 50 0 empty empty empty 20 8 64 8 -262144 -1 -1; #X obj 156 216 bng 30 250 50 0 empty empty empty 20 8 64 8 -262144 -1 -1 ; #X obj 26 266 print one; #X obj 156 295 print two; #X msg 81 102 receive empty; #X obj 226 157 t a a a; #X obj 298 216 print ab2; #X connect 0 0 1 0; #X connect 1 0 2 0; #X connect 2 0 8 0; #X connect 3 0 7 0; #X connect 3 0 5 0; #X connect 4 0 6 0; #X connect 7 0 4 0; #X connect 8 0 3 0; #X connect 8 1 4 0; #X connect 8 2 9 0; #X restore 267 221 pd ab2; #X connect 0 0 5 0; #X connect 1 0 6 0; #X connect 2 0 7 0; #X connect 3 0 8 0; #X connect 4 0 9 0;
--- m_pd-orig.c Fri Dec 21 11:04:52 2001 +++ m_pd.c Thu Dec 27 12:57:57 2001 @@ -46,83 +46,121 @@ { t_pd *e_who; struct _bindelem *e_next;
} t_bindelem;
typedef struct _bindlist { t_pd b_pd; t_bindelem *b_list;
} t_bindlist;
+#define bindelem_lock(x) ((x)->e_locked = 1)
+/* Return 1 if a not visited object is being left after bindlist annihilation,
+*/ +static int bindelem_unlock(t_bindelem **x, t_symbol *s) +{
/* symbol has no bindings, iff we were unbound as the last object */
/* bindlist is gone, iff we hold the last remaining object */
/* there is at least one other object bound */
pd_unbind((*x)->e_who, s);
if (*s->s_thing == bindlist_class) *x = x1;
else if (x1)
{
*x = 0;
return (1); /* the only remaining object was not visited yet */
}
+}
static void bindlist_bang(t_bindlist *x) { t_bindelem *e = x->b_list; -#if CRASHY /* the loop we have now */
pd_bang(e->e_who);
-#else /* safer loop */
pd_bang(who);
pd_bang(e->e_who);
-#endif }
static void bindlist_float(t_bindlist *x, t_float f) { t_bindelem *e = x->b_list; -#if 1 /* the loop we have now */
pd_float(e->e_who, f);
-#else /* safer loop */
pd_float(who, f);
pd_float(e->e_who, f);
-#endif }
static void bindlist_symbol(t_bindlist *x, t_symbol *s) {
}
static void bindlist_pointer(t_bindlist *x, t_gpointer *gp) {
}
static void bindlist_list(t_bindlist *x, t_symbol *s, int argc, t_atom *argv) {
}
static void bindlist_anything(t_bindlist *x, t_symbol *s, int argc, t_atom *argv) { t_bindelem *e = x->b_list; -#if CRASHY /* the loop we have now */
pd_typedmess(e->e_who, s, argc, argv);
-#else /* safer loop */
pd_typedmess(who, s, argc, argv);
pd_typedmess(e->e_who, s, argc, argv);
-#endif }
void m_pd_setup(void) @@ -147,6 +185,7 @@ t_bindelem *e = (t_bindelem *)getbytes(sizeof(t_bindelem)); e->e_next = b->b_list; e->e_who = x;
e->e_locked = e->e_freed = 0;
b->b_list = e;
}
else
@@ -155,10 +194,13 @@ t_bindelem *e1 = (t_bindelem *)getbytes(sizeof(t_bindelem)); t_bindelem *e2 = (t_bindelem *)getbytes(sizeof(t_bindelem)); b->b_list = e1;
b->b_symbol = s;
e1->e_who = x;
e1->e_next = e2;
e1->e_locked = e1->e_freed = 0;
e2->e_who = s->s_thing;
e2->e_next = 0;
e2->e_locked = e2->e_freed = 0;
s->s_thing = &b->b_pd;
}
}@@ -178,14 +220,22 @@ t_bindelem *e, *e2; if ((e = b->b_list)->e_who == x) {
b->b_list = e->e_next;
freebytes(e, sizeof(t_bindelem));
if (e->e_locked) e->e_freed = 1;
else
{
b->b_list = e->e_next;
freebytes(e, sizeof(t_bindelem));
}
}
else for (e = b->b_list; e2 = e->e_next; e = e2)
if (e2->e_who == x)
{
e->e_next = e2->e_next;
freebytes(e2, sizeof(t_bindelem));
if (e2->e_locked) e2->e_freed = 1;
else
{
e->e_next = e2->e_next;
freebytes(e2, sizeof(t_bindelem));
}
break;
}
if (!b->b_list->e_next)