Tom Locke wrote:
>
> OK, here's what I'm hoping Parrot can provide for the language I'm building.
>
> My big requirement is for lightweight microthreads (hopefully *very*
> lightweight - I'm considering one scheduler that can handle *millions*
> of threads on a single machine).
Hmm, I can envision a microthread implementation wherein each
microthread is nothing more than a Continuation PMC.
Add an interpreter->microthreads PMC* pointer. (This could be a global
stored in interpreter->perl_stash->stash_hash, but that wouldn't be as
fast). Then, add the following ops:
inline op init_microthreads() {
if( !interpreter->microthreads )
interpreter->microthreads =
new_pmc(interpreter, enum_class_PerlArray);
goto NEXT();
}
=item B<mthread_create>(in PMC)
Create a new microthread, which will start with the Continuation in $1.
=cut
inline op mthread_create(in PMC) {
VTABLE_push(interpreter, interpreter->microthreads, $1);
goto NEXT();
}
=item B<mthread_create>(inconst INT)
Create a new Continuation, with the address set to $1, and
=cut
inline op mthread_create(inconst INT) {
opcode_t *dest = PTR2OPCODE_T(CUR_OPCODE + $1));
PMC * p = new_ret_continuation_pmc(interpreter, dest)
VTABLE_push(interpreter, interpreter->microthreads, p);
goto NEXT();
}
=item B<mthread_yieldcc>()
Create a new Continuation (which resumes after this op), schedule
it to activate later, and then switch to the next microthread.
=cut
inline op mthread_yieldcc() {
opcode_t *dest = expr NEXT();
PMC * p = new_ret_continuation_pmc(interpreter, dest);
VTABLE_push(interpreter, interpreter->microthreads, $1);
p = VTABLE_shift(interpreter, interpreter->microthreads);
dest = (opcode_t *)VTABLE_invoke(interpreter, p, dest);
goto ADDRESS(dest);
}
=item B<mthread_yield>(in PMC)
Schedule the Continuation in $1, then switch to the next microthread.
=cut
inline op mthread_yield(in PMC) {
opcode_t *dest = expr NEXT();
PMC * p;
VTABLE_push(interpreter, interpreter->microthreads, $1);
p = VTABLE_shift(interpreter, interpreter->microthreads);
dest = (opcode_t *)VTABLE_invoke(interpreter, p, dest);
goto ADDRESS(dest);
}
=item B<mthread_yield>(inconst INT)
Create a new Continuation, which resumes at address $1, then switch
to the next microthread.
=cut
inline op mthread_yield(inconst INT) {
opcode_t *dest = PTR2OPCODE_T(CUR_OPCODE + $1);
PMC * p = new_ret_continuation_pmc(interpreter, dest);
VTABLE_push(interpreter, interpreter->microthreads, p);
p = VTABLE_shift(interpreter, interpreter->microthreads);
dest = (opcode_t *)VTABLE_invoke(interpreter, p, dest);
goto ADDRESS(dest);
}
inline op mthread_fork() {
PMC * p = new_ret_continuation_pmc(interpreter, expr NEXT());
VTABLE_push(interpreter, interpreter->microthreads, p);
goto NEXT()
}
> Oh and I will need them to be serializable.
I hope you aren't planning on serializing just a single isolated
microthread... that wouldn't work well with what I've got in mind due to
how much stuff comes along when you serialize a continuation -- you'd
get almost the whole interpreter serialized.
If you want, instead, to serialize interpreter->microthreads, however...
well, you'd *still* get almost the whole interpreter serialized, but
you're getting more bang for your buck :)
> I can live with co-operative scheduling, perhaps running
> 'regular' threads over the top do provide a layer of preemptive
> scheduling.
Remember, each thread has it's own interpreter. This means that one
would have to share ->microthreads amongst multiple interpreters.
Besides the very minor nuisance of locking for ->push and ->shift,
there's the very big headach of figuring out what happens when you
invoke a continuation created in one interpreter in another interpreter.
An easier solution might be to create a timer event which, when handled,
does a yieldcc.
[PS: All code in this message is UNTESTED. If you'd *like* to test, be
my guest! :)]
--
$a=24;split//,240513;s/\B/ => /for@@=qw(ac ab bc ba cb ca
);{push(@b,$a),($a-=6)^=1 for 2..$a/6x--$|;print "[EMAIL PROTECTED]
]\n";((6<=($a-=6))?$a+=$_[$a%6]-$a%6:($a=pop @b))&&redo;}