chromatic wrote:
On Saturday 08 March 2008 07:31:08 Ron Blaschke wrote:
I've been looking into a failure of F<t/dynoplibs/myops.t> on Windows,
but I don't think the problem is limited to that platform.
$ prove t\dynoplibs\myops.t
t\dynoplibs\myops......5/10
t\dynoplibs\myops......6/10 # Failed test 'three alarm'
# at t\dynoplibs\myops.t line 118.
# Exited with error code: 255
# Received:
# alarm1
# alarm2
# alarm3
#
# Expected:
# /alarm1
# alarm2
# alarm3/
#
The test output is good, but the error code is 255. Actually, the test
exits with an access violation.
After stepping through things a couple of times with a debugger I think
the cause for this is that through the final DOD run (Parrot_exit ->
Parrot_really_destroy -> Parrot_do_dod_run) the scheduler is destroyed
*before* the last task.
When the task is finally destroyed, the scheduler is already 0xdeadbeef.
Here's the final stack trace.
Parrot_cx_delete_task
Parrot_Timer_destroy
Parrot_dod_free_pmc
Parrot_dod_sweep
Parrot_dod_ms_run
Parrot_do_dod_run
Parrot_really_destroy
Parrot_exit
At this line in Parrot_cx_delete_task.
VTABLE_delete_keyed_int(interp, interp->scheduler, tid);
That's as far as I got. I don't know enough about the DOD details to
make a good guess on the cause. Maybe both scheduler and task are
determined to be not-reachable, but the destruction sequence is not
ordered?
The destruction sequence is not ordered, so if a task happens to have a PMC
header created prior to the scheduler's PMC header, then this problem will
occur. (We reuse PMC headers, so this can happen in not-easily-deterministic
ways.)
Making the scheduler a constant PMC would likely fix this, but then anything
to which the scheduler points also has to be constant.
It might be possible to finish the scheduled tasks prior to destroying all
PMCs.
Not sure if I'm going into the wrong direction here, but can't this
happen with any PMC? Say I write my own scheduler and task PMCs,
working similarly? Or any other PMCs where destroy calls out to another
PMC?
I guess this depends on the contract of destroy. If destroy may not
call out to any other PMC, a special case for the scheduler is fine,
because it really is special.
If destroy may call out there needs to be additional or different
handling, to ensure the referenced object is not collected first. In
that case, it sounds akin to finalizers in a JVMisch, CLRisch senes.
With all the fun border cases, like a PMC resurrecting, for example by
registering itself at a live PMC.
At least this last issue seems to be covered by PDD 17 by "...make sure
that you don't leave any references to it in any Parrot structure by the
end of the method."
In this sense, C<interp->scheduler> in Timer counts as a reference, and
doesn't follow the rule. Or does it? I'm confused.
Thanks,
Ron