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' orcut') 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 casesabc1' and ab1', and also inab2' case (bindlist annihilation). After applying lookahead
safeguard from my previous post, Pd will crash in cases abc2' andab2' (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)