I'm trying to understand how morph works. perlscalar's morph looks like this:
void morph (INTVAL type) {
if (SELF->vtable->base_type == type)
return;
if (SELF->vtable->base_type == enum_class_PerlString) {
PObj_custom_mark_CLEAR(SELF);
SELF->vtable = Parrot_base_vtables[type];
return;
}
if (type == enum_class_PerlString) {
/*
* if we morph to a string, first clear str_val
* so that after changing the vtable a parallel
* reader doesn't get a gargabe pointer
*/
PMC_str_val(SELF) = NULL;
PObj_custom_mark_SET(SELF);
SELF->vtable = Parrot_base_vtables[type];
return;
}
if (type == enum_class_BigInt || type == enum_class_Complex) {
PMC_str_val(SELF) = NULL;
SELF->vtable = Parrot_base_vtables[type];
DYNSELF.init();
return;
}
SELF->vtable = Parrot_base_vtables[type];
}
I think that there are 2 bugs here
1: Morphing from enum_class_PerlString to enum_class_BigInt or
enum_class_Complex looks broken. The return in the second if clause will
quit the function and the DYNSELF.init() will never get called.
Can anyone easily write a regression test that demonstrates this
I think that the cure is to remove
SELF->vtable = Parrot_base_vtables[type];
return;
An optimisation on top would be to change the following
if (type == enum_class_PerlString) {
to
else if (type == enum_class_PerlString) {
2: The code isn't thread safe, even though it thinks that it is:
if (type == enum_class_PerlString) {
/*
* if we morph to a string, first clear str_val
* so that after changing the vtable a parallel
* reader doesn't get a gargabe pointer
*/
PMC_str_val(SELF) = NULL;
PObj_custom_mark_SET(SELF);
SELF->vtable = Parrot_base_vtables[type];
return;
}
I see no write barrier. There's no reason why on a dual CPU machine the
parallel reader doesn't see the writes to memory in the order:
SELF->vtable = Parrot_base_vtables[type];
PMC_str_val(SELF) = NULL;
and manage go SEGV between the two.
Nicholas Clark