1) *ALL* PMCs which are created outside parrot must be registered
(unless they're otherwise anchored)
2) No call into parrot from the outside needs to pass in a stack top
(though it may via a separate call if it so chooses)
3) The embedding wrapper is responsible for setting and resetting the
top of stack.
While I don't want the extension interface to register PMCs by
default (since that's generally unneccesary) I'm OK beefing up the
interface to have an allocate-and-register version.
--
Dan
--------------------------------------"it's like this"-------------------
Dan Sugalski even samurai
d...@sidhe.org have teddy bears and even
teddy bears get drunk
Proof-of-concept:
--- src/embed.c 2 May 2004 10:47:54 -0000 1.113
+++ src/embed.c 3 May 2004 19:08:23 -0000
@@ -666,6 +666,17 @@
void
Parrot_runcode(Interp *interpreter, int argc, char *argv[])
{
+ if(!interpreter->lo_var_ptr) {
+ int top;
+ if(Interp_flags_TEST(interpreter, PARROT_DEBUG_FLAG)) {
+ PIO_eprintf(interpreter,
+ "*** Parrot VM: Setting stack top. ***\n");
+ }
+ interpreter->lo_var_ptr=⊤
+ Parrot_runcode(interpreter, argc, argv);
+ interpreter->lo_var_ptr=NULL;
+ return;
+ }
/* Debugging mode nonsense. */
if (Interp_flags_TEST(interpreter, PARROT_DEBUG_FLAG)) {
if (Interp_flags_TEST(interpreter, PARROT_BOUNDS_FLAG)) {
--- src/interpreter.c 2 May 2004 10:47:54 -0000 1.306
+++ src/interpreter.c 3 May 2004 19:08:23 -0000
@@ -1726,6 +1726,7 @@
}
else {
SET_NULL(interpreter->parent_interpreter);
+ SET_NULL(interpreter->lo_var_ptr);
}
interpreter->resume_flag = RESUME_INITIAL;
--
Brent "Dax" Royal-Gordon <br...@brentdax.com>
Perl and Parrot hacker
Oceania has always been at war with Eastasia.
> 3) The embedding wrapper is responsible for setting and resetting the
> top of stack.
I don't think that this is quite right. The embedding wrapper needs to
set (and reset) the top of stack only if it's not set.
Otherwise this scenario could happen:
Eternal Program
|
+-> Embedding wrapper (sets stack top)
|
+-> More parrot code with something unanchored on stack *
|
+-> Call back out to a subroutine in the external program
|
+-> Embedding wrapper (sets stack top)
|
+-> More parrot code that triggers GC
|
+-> GC
And the GC routine won't spot that pointer on the stack at *
If the embedding wrapper (well wrappers onto the data access functions and
onto the runloop) both set a stack top if it's not yet set, and clear it on
exit if they set it, then I think that parrot won't fall foul of the above.
Effectively the embedder is playing by the same rules as Leo said that we
have to have for a nested call to the runloop - anything calling the runloop
must ensure that all PMCs are anchored.
Nicholas Clark
Ah, english. Some day I'll be able to use it effectively. :(
That's what I wanted to write. It didn't come out anywhere near that, but...
I think Brent's patch is pretty much dead-on what we want. Worth
applying and seeing what it does to things.
> That's what I wanted to write. It didn't come out anywhere near that, but...
>
> I think Brent's patch is pretty much dead-on what we want. Worth
> applying and seeing what it does to things.
It appears to unconditionally clear the low stack pointer on exit.
Which isn't going to do nice things with a nested call back into parrot
through the embedding interface. Therefore all it needs is a state variable
to remember if it needed to set the stack pointer (and an evil regression
test that embeds parrot, calls parrot, calls back out, and calls in again)
Nicholas Clark
It only unconditionally sets the stacktop back to NULL if it was NULL
in the first place. If you enter that function from within the
runloop (at which point the stacktop will be set) it uses a different
code path.
> Proof-of-concept:
Looks good.
Thanks, applied.
leo
With the appended hack patch which sets/clears the stack top pointer,
ponie will run with the default parrot gc (rather than gc=mallc)
Yes, no prototypes anywhere, no idea what the names should be,
not sure which file they should be in, and I only wrapped the minimal set
of functions that ponie is currently using.
Plus I called the function in the same file that I was wrapping, which
imposes a needles extra level of function call overhead. What would seem
more sane is a way to make two versions of all the extending/embedding
functions, one which sets/clears the stack top (for embedders' use) and
one which does not (for extenders' use, or anyone who is in a position to
structure their own code/project to set the stack top once and in the
correct place)
How should this be?
Nicholas Clark
diff -d -u -r1.26 extend.c
--- src/extend.c 3 May 2004 12:29:02 -0000 1.26
+++ src/extend.c 7 May 2004 20:15:20 -0000
@@ -789,6 +789,60 @@
dod_unregister_pmc(interpreter, pmc);
}
+
+void *
+Parrot_Embed_PMC_get_pointer(Parrot_Interp interp, Parrot_PMC pmc) {
+ if(interp->lo_var_ptr) {
+ return Parrot_PMC_get_pointer(interp, pmc);
+ } else {
+ void *result; /* Doubles up as an indicator of stack top. */
+ interp->lo_var_ptr=&result;
+ result = Parrot_PMC_get_pointer(interp, pmc);
+ interp->lo_var_ptr=NULL;
+ return result;
+ }
+}
+
+Parrot_PMC
+Parrot_Embed_PMC_new(Parrot_INTERP interp, Parrot_Int type) {
+ if(interp->lo_var_ptr) {
+ return Parrot_PMC_new(interp, type);
+ } else {
+ Parrot_PMC result;
+ interp->lo_var_ptr=&result;
+ result = Parrot_PMC_new(interp, type);
+ interp->lo_var_ptr=NULL;
+ return result;
+ }
+}
+
+void
+Parrot_Embed_register_pmc(Parrot_INTERP interp, Parrot_PMC pmc)
+{
+ if(interp->lo_var_ptr) {
+ Parrot_register_pmc(interp, pmc);
+ } else {
+ int top;
+ interp->lo_var_ptr=⊤
+ Parrot_register_pmc(interp, pmc);
+ interp->lo_var_ptr=NULL;
+ }
+}
+
+
+Parrot_Int
+Parrot_Extend_PMC_typenum(Parrot_INTERP interp, const char *class) {
+ if(interp->lo_var_ptr) {
+ return Parrot_PMC_typenum(interp, class);
+ } else {
+ Parrot_Int result;
+ interp->lo_var_ptr=&result;
+ result = Parrot_PMC_typenum(interp, class);
+ interp->lo_var_ptr=NULL;
+ return result;
+ }
+}
+
/*
=back
> +void *
> +Parrot_Embed_PMC_get_pointer(Parrot_Interp interp, Parrot_PMC pmc) {
> + if(interp->lo_var_ptr) {
> + return Parrot_PMC_get_pointer(interp, pmc);
> + } else {
> + void *result; /* Doubles up as an indicator of stack top. */
> + interp->lo_var_ptr=&result;
> + result = Parrot_PMC_get_pointer(interp, pmc);
This shouldn't really be necessary. A pointer fetch shouldn't trigger
DOD. Except you attach enough magic to the PMC so that it starts running
a piece of code on a call to C<get_pointer> :)
> +Parrot_PMC
> +Parrot_Embed_PMC_new(Parrot_INTERP interp, Parrot_Int type) {
> + if(interp->lo_var_ptr) {
> + return Parrot_PMC_new(interp, type);
> + } else {
> + Parrot_PMC result;
> + interp->lo_var_ptr=&result;
> + result = Parrot_PMC_new(interp, type);
> + interp->lo_var_ptr=NULL;
And the other usage of these stacktop setting is still somewhat
debatable, but it depends.
There are several issues 2) might be considered a bug:
1) Parrot does not walk the CPU stack, if lo_var_ptr isn't set.
2) Not walking the stack currently implies that the hardware CPU
registers are *not* marked. Parrot does basically[1] a setjmp(3),
which places CPU registers into a local on the CPU stack and then
scans the whole stack that now includes these CPU registers.
3) Setting the lo_var_ptr at this level of the stack doesn't protect
anything above (assuming a grow down stack). So you are better off
with just setting the stack top once to anything above that level and
not reset the limit (this might not work for the situations with
callbacks only that Arthur described). But if you have a more linear
program flow, or if you have a place somewhere above in the stack
then the stack top should be set there.
4) When you start entering a run loop for the first time the stack top
is automatically set by Parrot at the run loop level, except
you did call Parrot_init_stacktop, which resets the RESUME_INITIAL
flag.
5) Any PMC[2] above that lo_var_ptr that *is not* anchored otherwise,
has to be anchored somehow. But Parrot_register_pmc /
dod_register_pmc is rather expensive, it's a hash operation.
6) If you are e.g. stuffing PMCs into a PMC array and the array is
anchored, the array items shouldn't be additionally anchored with
Parrot_register_pmc. The mark function of the array marks it's
items anyway. But this somehow depends on the usage of the
array. Dunno if it can happen, that you have other references[3] to
array items around and the array itself goes out of scope.
7) Parrot_register_pmc/Parrot_unregister_pmc otherwise work like an
increment/decrement of a refcount. When the count reaches zero,
the PMCs address gets deleted from the C<DOD_registry> hash.
leo
[1] there is some extra code e.g. for IA64 register files and sparc.
see cpu_dep.c
[2] should be any PObj, but dod_register_pmc has a PMC* function
signature. Albeit passing in e.g. a STRING wouldn't harm.
[3] unachored PMC* pointers.