Threads and Progress Monitors

1 view
Skip to first unread message

Dave Whipp

unread,
May 29, 2003, 1:47:35 PM5/29/03
to perl6-l...@perl.org
OK, we've beaten the producer/consumer thread/coro model to death. Here's a
different use of threads: how simple can we make this in P6:

sub slow_func
{
my $percent_done = 0;
my $tid = thread { slow_func_imp( \$percent_done ) };
thread { status_monitor($percent_done) and sleep 60 until
$tid.done };
return wait $tid;
}

I think this would work under Austin's A17; but it feels a bit clunky. The
fact that the "sleep 60" isn't broken as soon as the function is done is
untidy, though I wouldn't want to start killing thread.

perhaps:

{
...
$tid = thread { slow... }
status_monitor(\$percent_done) and wait(60 | $tid) until $tid.done;
return $tid.result;
}

The thing is, that "wait 60" probably can't work -- replace C<60> with
C<$period>, and the semantics change. There are some obvious hacks that
could work here: but is there a really nice solution. Ideally, we won't need
the low level details such as C<$tid>


Dave.


Austin Hastings

unread,
May 29, 2003, 2:31:00 PM5/29/03
to Dave Whipp, perl6-l...@perl.org

sub slow_func_imp {
my $pct_done = 0;
...
yield $pct_done++; # Per my recent message
...
}

sub slow_func {
my $tid := thread &slow_func_imp;

status_monitor($tid.resume)
while $tid.active;
}


Michael Lazzaro

unread,
May 29, 2003, 2:55:47 PM5/29/03
to Dave Whipp, perl6-l...@perl.org

On Thursday, May 29, 2003, at 10:47 AM, Dave Whipp wrote:

> OK, we've beaten the producer/consumer thread/coro model to death.
> Here's a
> different use of threads: how simple can we make this in P6:

Hey, good example. Hmm...

Well, for starters I think it wouldn't be a big deal to associate a
"progress" attribute with each thread object. It should be that
thread's responsibility to fill it out, if it wants to -- so you
shouldn't ever have to pass \$percent_done as an argument, it should be
a basic attribute of every thread instance. That might encourage
people to add progress calculations to their threads after-the-fact,
without changing the basic interface of what they wrote.

I'll also claim that I would still prefer the auto-parallel,
auto-lazy-blocking behavior on the thread results we've mused about
previously. So coming from the semantics end, I'd love to see it
written like this:

# Declaring a threaded calculation

sub slow_func_impl is threaded {
while (...stuff...) {
... do stuff ...
&_.thread.progress += 10.0; # or however you want to
guesstimate[*] this
}
return $result;
}

# If you don't care about getting the actual thread object, just the
result,
# call it this way:

{
...
my $result = slow_func_impl(...);
...
return $result;
}

# But if you want to get the thread object, so you can monitor it's
progress,
# call it this way:

{
...
my $tid = thread &slow_func_impl(...);
while $tid.active {
status_monitor($tid.progress);
sleep 60;
}
return $tid.result;
}

To my eye, that looks pretty darn slick.

MikeL

[*] Huh. Imagine my surprise to find out that my spellcheck considers
"guesstimate" to be a real word. And I always thought that was just a
spasmostical pseudolexomangloid.

Dave Whipp

unread,
May 29, 2003, 3:45:37 PM5/29/03
to perl6-l...@perl.org
"Michael Lazzaro" <mlaz...@cognitivity.com> wrote in> # But if you want

to get the thread object, so you can monitor it's

> {


> ...
> my $tid = thread &slow_func_impl(...);
> while $tid.active {
> status_monitor($tid.progress);
> sleep 60;
> }
> return $tid.result;
> }
>
> To my eye, that looks pretty darn slick.

You might be a bit frustrated if the &slow_func_impl took 61 seconds :-(.
How do we interrupt the C<sleep>? Possibly in the same way as we'd timeout a
blocking IO operations. But I wonder if this could work:

my $tid = thread &slow_func_impl(...);

until wait $tid, timeout=>60
{
status_monitor($tid.progress);
}
return $tid.result;

Here I assume that C<wait> returns a true value if its waited condition
occurs, but false if it times out.

Hmm, A few days ago I tried indroducing a syntax for thread with a
sensitivity list in place of an explict loop-forever thread. Perhaps I can
reuse that syntax:

my $tid = thread &slow_func_impl(...);

thread $tid | timeout(60)
{
when $tid => { return $tid.result }
default => { status_monitor $tid.progress }
}

Perhaps a different keyword would be better: C<always> as the looping
counterpart to C<wait> -- then extend C<wait> to accept a code block.

Dave.


John Macdonald

unread,
May 29, 2003, 2:21:58 PM5/29/03
to Dave Whipp, perl6-l...@perl.org
On Thu, May 29, 2003 at 10:47:35AM -0700, Dave Whipp wrote:
> OK, we've beaten the producer/consumer thread/coro model to death. Here's a
> different use of threads: how simple can we make this in P6:
>
> sub slow_func
> {
> my $percent_done = 0;
> my $tid = thread { slow_func_imp( \$percent_done ) };
> thread { status_monitor($percent_done) and sleep 60 until
> $tid.done };
> return wait $tid;
> }

At first glance, this doesn't need a thread - a
coroutine is sufficient. Resume the status update
coroutine whenever there has been some progress.
It doesn't wait and poll a status variable, it just
let the slow function work at its own speed without
interruption until there is a reason to change the
display.

In fact, it probably doesn't need to be a coroutine
either. A subroutine - display_status( $percent ) -
should't require any code state to maintain, just a
bit if data so all it needs is a closure or an object.

At second glance, there is a reason for a higher
powered solution. If updating the display to a new
status takes a significant amount of time, especially
I/O time, it would both block the slow function
unnecessarily and would update for every percent
point change. Using a separate process or thread
allows the function to proceed without blocking, and
allows the next update to jmp ahead to the current
actual level, skipping all of the levels that occurred
while the previous display was happening. Instead of
sleep, though, I'd use a pipeline and read it with
a non-blocking read until there is no data. Then,
if the status has changed since the last update, do
a display update and repeat the non-blocking read.
If the status has not changed, do a blocking read to
wait for the next status change.

Dave Whipp

unread,
May 29, 2003, 7:05:32 PM5/29/03
to perl6-l...@perl.org
"John Macdonald" <j...@algate.perlwolf.com> wrote > At first glance, this

doesn't need a thread - a
> Instead of
> sleep, though, I'd use a pipeline and read it with
> a non-blocking read until there is no data. ...

++ For the lateral thinking. Definitely a valid solution to the problem, as
given. So I'll change the problem prevent it: the slow fn is a 3rd-party
blob with no access to source code and no progress indication.

sub slow_fn {
print "starting slow operation: this sometimes takes half an hour!\n";
my $tid = thread { slow_fn_imp @_ };
$start = time;
loop {
wait $tid | timeout(60);
return $tid.result if $tid.done;
print "... $(time-$start) seconds\n";
}
}

Still a bit too complex for my taste: perhaps we can use C<timeout> to
generate exceptions:

my lazy::threaded $result := { slow_fn_imp @_ };
loop {
timeout(60);
return $result;
CATCH Timeout { print "...$(time)\n" }
}

At last, no C<tid>! (Reminder: the suggested semantics of the threaded
variable were that a FETCH to it blocks until the result of the thread is
available).


Dave.


Luke Palmer

unread,
May 29, 2003, 7:48:25 PM5/29/03
to da...@whipp.name, perl6-l...@perl.org
Dave wrote:
> Still a bit too complex for my taste: perhaps we can use C<timeout> to
> generate exceptions:
>
> my lazy::threaded $result := { slow_fn_imp @_ };
> loop {
> timeout(60);
> return $result;
> CATCH Timeout { print "...$(time)\n" }
> }
>
> At last, no C<tid>! (Reminder: the suggested semantics of the threaded
> variable were that a FETCH to it blocks until the result of the thread is
> available).

To nitpick:

my $result is lazy::threaded := { slow_fn_imp @_ };

Because lazy::threaded isn't the I<return> type, it's the I<variable>
type.

loop {
timeout(60);
return $result;
CATCH {
when Timeout { print "...$(time)\n"
}
}

Because C<CATCH> is like C<given $!>.

I like that elegant use of threaded variables, by the way.

Now write the C<timeout> function :-P.

Luke

Michael Lazzaro

unread,
May 29, 2003, 8:06:14 PM5/29/03
to Dave Whipp, perl6-l...@perl.org

On Thursday, May 29, 2003, at 12:45 PM, Dave Whipp wrote:
> "Michael Lazzaro" <mlaz...@cognitivity.com> wrote in> # But if
> you want
> to get the thread object, so you can monitor it's
>> {
>> ...
>> my $tid = thread &slow_func_impl(...);
>> while $tid.active {
>> status_monitor($tid.progress);
>> sleep 60;
>> }
>> return $tid.result;
>> }
>>
>> To my eye, that looks pretty darn slick.
>
> You might be a bit frustrated if the &slow_func_impl took 61 seconds
> :-(.
> How do we interrupt the C<sleep>? Possibly in the same way as we'd
> timeout a
> blocking IO operations.

Personally, I'd be happy with just making the C<sleep> a smaller
number, like one second, or a fifth of a second, or whatever. You want
the status_monitor to be updated no more often than it needs to be, but
often enough that it's not lagging.

But if you really wanted wake-immediately-upon-end, I'd add that as a
variant of C<sleep>. For example, you might want a variant that
blocked until a given variable "changed", just like in debuggers; that
would allow:

{


my $tid = thread &slow_func_impl(...);
while $tid.active {
status_monitor($tid.progress);

sleep( 60, watch => \($tid.progress) ); # do you even
need the '\'?
}
return $tid.result;
}

... which would sleep 60 seconds, or until the .progress attribute
changed, whichever came first.

You could make more builtins for that, but I think I'd like them to
just be C<sleep> or C<wait> variants. Obvious possibilities:

sleep 60; # sleep 60 seconds
sleep( block => $tid ); # sleep until given thread is complete
sleep( watch => \$var ); # sleep until given var changes value

sleep( 60, block => $tid, watch => [\$var1, \$var2, \$var3] );
five tests

$tid.sleep(...); # sleep the given thread, instead of this one


MikeL

Michael Lazzaro

unread,
May 29, 2003, 8:16:37 PM5/29/03
to Luke Palmer, da...@whipp.name, perl6-l...@perl.org

On Thursday, May 29, 2003, at 04:48 PM, Luke Palmer wrote:
> To nitpick:
>
> my $result is lazy::threaded := { slow_fn_imp @_ };

Pursuing this lazy-threaded variables notion, a question. Given:

sub slow_func is threaded { # me likey this
auto-parallelizing syntax!
...
}

Would we want to say that _both_ of these have the lazy-blocking
behavior?

my $result := slow_func();
print $result;

my $result = slow_func();
print $result;

Or would the first one block at C<print>, but the second block
immediately at the C<=>?

The obvious answer is that the := binding "passes through" the
lazyness, but the = assignment doesn't. But I wonder if that isn't a
bit too obscure, to put it mildly.

MikeL

Dave Whipp

unread,
May 29, 2003, 9:44:41 PM5/29/03
to perl6-l...@perl.org
"Luke Palmer" <fibo...@babylonia.flatirons.org> wrote in message
news:ygcel2h...@babylonia.flatirons.org...

> my $result is lazy::threaded := { slow_fn_imp @_ };
> loop {
> timeout(60);
> return $result;
> CATCH {
> when Timeout { print "...$(time)\n"
> }
> }
>
> Now write the C<timeout> function :-P.


Well, the obvious is

{
temp %SIG{ALRM} = { print "..."; alarm(60) }
alarm(60);
LAST {alarm(0) };
return $result;
}

Needless to say, I don't like that. But the good thing now is that I don't
need the loop. Now, if we had some form of Parrot-level signaling (instead
of OS-level); and if we had resumable exceptions; then perhaps I'd be able
to write

sub slow_fn {
my $tick = Timer.new(60);
CATCH { when $tick => { print "..."; continue } }
return slow_fn_imp @_ ;
}

I think this hides the threads pretty effectively. Is a parrot-level timer
to much to ask for? How about resumable exceptions? But perhaps I'm still
being too clever, when a simpler solution exists:

sub slow_fn {
my $tick = Timer.new(60, { print "..." });
return slow_fn_imp @_;
}

Now if I could just get the compiler to not complain about that unused
variable...


Dave.


Dulcimer

unread,
May 29, 2003, 11:36:54 PM5/29/03
to Dave Whipp, perl6-l...@perl.org

> sub slow_fn {
> my $tick = Timer.new(60, { print "..." });
> return slow_fn_imp @_;
> }
>
> Now if I could just get the compiler to not complain about that
> unused variable...

Maybe I'm being dense....
Why not just
sub slow_fn {
Timer.new(1, { print "." });
return slow_fn_imp @_;
}

or maybe even

sub slow_fn {
my $tick = 1;
Timer.new({$tick++}, { print "." });
return slow_fn_imp @_;
}
For a slowly slowing timer....
?

Or to my taste,
sub slow_fn {


Timer.new(60, { print "..." });
return slow_fn_imp @_;
}

__________________________________
Do you Yahoo!?
Yahoo! Calendar - Free online calendar with sync to Outlook(TM).
http://calendar.yahoo.com

Dave Whipp

unread,
May 30, 2003, 12:16:06 AM5/30/03
to perl6-l...@perl.org
Dulcimer wrote:
>>sub slow_fn {
>> my $tick = Timer.new(60, { print "..." });
>> return slow_fn_imp @_;
>>}
>>
>>Now if I could just get the compiler to not complain about that
>>unused variable...
>
>
> Maybe I'm being dense....
> Why not just
> sub slow_fn {
> Timer.new(1, { print "." });
> return slow_fn_imp @_;
> }

The problem is that I want the timer to last for the duration of the
slow_fn_imp. If I don't assign it to a variable, then it may be GCed at
any time.

I've just realised, however, that I'm relying on it being destroyed on
leaving the scope. I'm not sure that the GC guarentees that. I might need

sub slow_fn {
my $timer is last { .stop } = Timer.new(60, { print "." });
return slow_fn_imp @_;
}

but that's starting to get cluttered again.


Dave.

Paul Johnson

unread,
May 30, 2003, 4:16:20 AM5/30/03
to Dave Whipp, perl6-l...@perl.org

Dave Whipp said:

> I've just realised, however, that I'm relying on it being destroyed on
> leaving the scope. I'm not sure that the GC guarentees that.

GC doesn't, but I would be surprised if Perl 6 doesn't and in that case
Parrot will be accommodating.

Take a look at the recent p6i archives for the gory details.

--
Paul Johnson - pa...@pjcj.net
http://www.pjcj.net

Dulcimer

unread,
May 30, 2003, 10:11:07 AM5/30/03
to Dave Whipp, perl6-l...@perl.org

--- Dave Whipp <da...@whipp.name> wrote:
> Dulcimer wrote:
> >>sub slow_fn {
> >> my $tick = Timer.new(60, { print "..." });
> >> return slow_fn_imp @_;
> >>}
> >>
> >>Now if I could just get the compiler to not complain about that
> >>unused variable...
> >
> >
> > Maybe I'm being dense....
> > Why not just
> > sub slow_fn {
> > Timer.new(1, { print "." });
> > return slow_fn_imp @_;
> > }

Geez. I read my response this morning, which I wrote just before going
to bed, and realized that I must've been dead on my feet.

> The problem is that I want the timer to last for the duration of the
> slow_fn_imp. If I don't assign it to a variable, then it may be GCed
> at any time.

I was making several assumptions which don't hold, apparently, such as
that the underlying Timer would iterate until stopped. Not an ideal
default, lol.... I thopught the point was to have the function print
dots repeatedly, tho?

> I've just realised, however, that I'm relying on it being destroyed
> on leaving the scope. I'm not sure that the GC guarentees that.
> I might need
>
> sub slow_fn {
> my $timer is last { .stop } = Timer.new(60, { print "." });
> return slow_fn_imp @_;
> }
>
> but that's starting to get cluttered again.

I don't really consider that "clutter". It's clear and to the point,
and Does What You Want. How about

sub slow_fn {
my $timer is last { .stop } =

new Timer secs => 1, reset => 1, code => {print "."};
return slow_fn_imp @_;
}

so that the timer goes off after a second, prints a dot, and resets
itself to go off again after another second? And I still like the idea
of an expanding temporal window between dots:

sub slow_fn {
my $pause = 1;
my $timer is last { .stop } = new Timer secs => $pause++,
reset => {$pause++},
code => {print "."};
return slow_fn_imp @_;
}

As a sidenote, although it would actually reduce readability here, I'm
still trying to wrap my brain thoroughly around the new dynamics of $_.
Would this work correctly maybe?

sub slow_fn {
my $timer is last { .stop } = new Timer secs => $_=1,
reset => {$_++},
code => {print "."};
return slow_fn_imp @_;
}

Isn't that $_ proprietary to slow_fn such that it *would* work?

Dave Whipp

unread,
May 30, 2003, 12:08:44 PM5/30/03
to perl6-l...@perl.org
"Dulcimer" <ydb...@yahoo.com> wrote > so that the timer goes off after a

second, prints a dot, and resets
> itself to go off again after another second? And I still like the idea
> of an expanding temporal window between dots:
>
> sub slow_fn {
> my $pause = 1;
> my $timer is last { .stop } = new Timer secs => $pause++,
> reset => {$pause++},
> code => {print "."};
> return slow_fn_imp @_;
> }

I'm thinking there's a way to avoid the $pause variable:

sub slow_fn
{
my $tmp = new Timer( secs=>1, code => { print "." and
.reset(.count+1) });
return slow_fn_imp @_;
}

But exposing the object like that still bothers be: I shouldn't need the
$tmp, nor the .new. When someone writes the Std::Timer module, we can add a
macro to it such that:

sub slow_fn
{
timeout(1) { print "." and .reset(.count+1) };
return slow_fn_imp @_;
}

I think the implementation is obvious, given the previous example of the
inline code. Though s/timeout/???/.


A semantic question: what output would you expect for this:

sub silly
{
timeout(5) { print ".HERE."; sleep 4; print ".THERE." };
for 1..5 -> $count { sleep 2; print "$count" };
}

possible answers are

12.HERE.34.THERE.5
or
12.HERE..THERE.345

I'm thinking probably the latter, because its easier to launch a thread in
the codeblock than to un-launch it.

> As a sidenote, although it would actually reduce readability here, I'm
> still trying to wrap my brain thoroughly around the new dynamics of $_.
> Would this work correctly maybe?
>
> sub slow_fn {
> my $timer is last { .stop } = new Timer secs => $_=1,
> reset => {$_++},
> code => {print "."};
> return slow_fn_imp @_;
> }
>
> Isn't that $_ proprietary to slow_fn such that it *would* work?

I had to stare at it for a few moments, but yes: I think it should work (if
we define a .reset attribute that accepts a codeblock).

Dave.


Dulcimer

unread,
May 30, 2003, 12:34:06 PM5/30/03
to Dave Whipp, perl6-l...@perl.org
> > sub slow_fn {
> > my $pause = 1;
> > my $timer is last { .stop } = new Timer secs => $pause++,
> > reset => {$pause++},
> > code => {print "."};
> > return slow_fn_imp @_;
> > }
>
> I'm thinking there's a way to avoid the $pause variable:
>
> sub slow_fn
> {
> my $tmp = new Timer(
> secs=>1, code => { print "." and .reset(.count+1) });
> return slow_fn_imp @_;
> }
>
> But exposing the object like that still bothers be: I shouldn't need
> the $tmp, nor the .new.

I'm not so sure I agree with losing the new(). I kinda like that just
for readability. Less isn't always more. :)

Ok, how about this:

sub slow_fn {
temp &_.timer is last { .stop } = new Timer (
secs => 1, code => { .reset += print "." }
);
return slow_fn_imp @_;
}

That's only superficially different, but is a little more aesthetically
satisfying, somehow. Then again, I'm a perverse bastard, lol... :)

On the other hand, if this threads, does each call to slow_fn() get a
unique &_, or did I just completely hose the whole process? Could I say

my temp &_.timer is last { .stop } = new Timer ( ... ); # ?

or is it even necessary with temp?

> When someone writes the Std::Timer module, we can
> add a macro to it such that:
>
> sub slow_fn
> {
> timeout(1) { print "." and .reset(.count+1) };
> return slow_fn_imp @_;
> }

Dunno -- I see what you're doing, but it's a little *too* helpful.
I'd rather see a few more of the engine parts on this one.

> I think the implementation is obvious, given the previous example of
> the inline code. Though s/timeout/???/.

alarm? trigger(), maybe?



> A semantic question: what output would you expect for this:
>
> sub silly {
> timeout(5) { print ".HERE."; sleep 4; print ".THERE." };
> for 1..5 -> $count { sleep 2; print "$count" };
> }
> possible answers are
> 12.HERE.34.THERE.5
> or
> 12.HERE..THERE.345
> I'm thinking probably the latter, because its easier to launch a
> thread in the codeblock than to un-launch it.

un-launch? If they're threaded, aren't they running asynchronously?
I see 12.HERE.34.THERE.5 as the interleaved output. I have no idea what
you mean by "un-launch", sorry.

> > As a sidenote, although it would actually reduce readability
> > here, I'm still trying to wrap my brain thoroughly around the
> > new dynamics of $_. Would this work correctly maybe?
> >
> > sub slow_fn {
> > my $timer is last { .stop } = new Timer secs => $_=1,
> > reset => {$_++},
> > code => {print "."};
> > return slow_fn_imp @_;
> > }
> >
> > Isn't that $_ proprietary to slow_fn such that it *would* work?
>
> I had to stare at it for a few moments, but yes: I think it should
> work (if we define a .reset attribute that accepts a codeblock).

lol -- I was assuming we'd have to make reset accept codeblocks, and
yes, I'd expect you to have to stare a bit. It's ugly, and I'd rather
create a new variable that do this, tho we've seen that you don't need
to.

Dave Whipp

unread,
May 30, 2003, 12:58:23 PM5/30/03
to perl6-l...@perl.org
"Dulcimer" <ydb...@yahoo.com> wrote

> > But exposing the object like that still bothers be: I shouldn't need
> > the $tmp, nor the .new.
>
> I'm not so sure I agree with losing the new(). I kinda like that just
> for readability. Less isn't always more. :)
>
> Ok, how about this:
>
> sub slow_fn {
> temp &_.timer is last { .stop } = new Timer (
> secs => 1, code => { .reset += print "." }
> );
> return slow_fn_imp @_;
> }

Wrong semantics: First, you're assuming that .reset is an attribute, rather
than a command (Yes, I believe the command/query separation, where
possible). Second, My intention was that if C<print> ever fails (e.g. broken
pipe), then I'd stop resetting the timer. Your program meerly stops
incrementing the timeout.

Even if we assume that the "temp &_.prop" thing works, I'm not sure I'd want
it littering my code. I could see it being used in a macro defn though.

> Dunno -- I see what you're doing, but it's a little *too* helpful.
> I'd rather see a few more of the engine parts on this one.

We can expose a few more parts by having the macro return the timer object,
so you could write:

my $timer = timeout(60) { ... };

> > A semantic question: what output would you expect for this:
> >
> > sub silly {
> > timeout(5) { print ".HERE."; sleep 4; print ".THERE." };
> > for 1..5 -> $count { sleep 2; print "$count" };
> > }
> > possible answers are
> > 12.HERE.34.THERE.5
> > or
> > 12.HERE..THERE.345
> > I'm thinking probably the latter, because its easier to launch a
> > thread in the codeblock than to un-launch it.
>
> un-launch? If they're threaded, aren't they running asynchronously?
> I see 12.HERE.34.THERE.5 as the interleaved output. I have no idea what
> you mean by "un-launch", sorry.

Sorry, it was my feeble attempt at humor.

What I was getting at is that, if we assume the codeblock executes asa
coroutine, then you'd get the latter output. If you wanted a thread, you
could write:

timeout(5) { thread { ... } };

but if we assume that the codeblock is launched as an asynchronous thread,
then there is no possible way the coerce it back into the coroutine (i.e. to
"un-launch" it).


Now here's another semantics question: would we want the following to be
valid?

sub slow
{
timeout(60) { return undef but "Error: timed out" };
return @slow_imp;
}

How about:

sub slow
{
timeout(60) { throw TimeoutException.new("Error: slow_fn timed out") };
return @slow_imp;
}

Dave.


Dulcimer

unread,
May 30, 2003, 3:51:13 PM5/30/03
to Dave Whipp, perl6-l...@perl.org

--- Dave Whipp <da...@whipp.name> wrote:
> "Dulcimer" <ydb...@yahoo.com> wrote
> > > But exposing the object like that still bothers be: I shouldn't
> > > need the $tmp, nor the .new.
> >
> > I'm not so sure I agree with losing the new(). I kinda like that
> > just for readability. Less isn't always more. :)
> >
> > Ok, how about this:
> >
> > sub slow_fn {
> > temp &_.timer is last { .stop } = new Timer (
> > secs => 1, code => { .reset += print "." }
> > );
> > return slow_fn_imp @_;
> > }
>
> Wrong semantics: First, you're assuming that .reset is an attribute,
> rather than a command (Yes, I believe the command/query separation,
> where possible).

Ok. And that leads to the next thing --

> Second, My intention was that if C<print> ever fails (e.g. broken
> pipe), then I'd stop resetting the timer. Your program meerly stops
> incrementing the timeout.

Agreed.

> Even if we assume that the "temp &_.prop" thing works, I'm not sure
> I'd want it littering my code. I could see it being used in a macro
> defn though.

Maybe. It isn't pretty, but I've seen worse. Hell, I've posted worse.
:)

> > Dunno -- I see what you're doing, but it's a little *too* helpful.
> > I'd rather see a few more of the engine parts on this one.
>
> We can expose a few more parts by having the macro return the timer
> object, so you could write:
>
> my $timer = timeout(60) { ... };

Ok, poorly phrased on my part. I just meant I'd like to visually see
more of what's going on in the code. In other words, I'm not fond of
the syntax proposal. I find C<timeout(60) { ... }> too terse, and would
rather see a more verbose version. Merely a style issue, though. Still,
your response to what it *looked* like I meant is a good idea, too.

> > > A semantic question: what output would you expect for this:
> > >
> > > sub silly {
> > > timeout(5) { print ".HERE."; sleep 4; print ".THERE." };
> > > for 1..5 -> $count { sleep 2; print "$count" };
> > > }
> > > possible answers are
> > > 12.HERE.34.THERE.5
> > > or
> > > 12.HERE..THERE.345
> > > I'm thinking probably the latter, because its easier to launch a
> > > thread in the codeblock than to un-launch it.
> >
> > un-launch? If they're threaded, aren't they running asynchronously?
> > I see 12.HERE.34.THERE.5 as the interleaved output. I have no idea
> > what you mean by "un-launch", sorry.
>
> Sorry, it was my feeble attempt at humor.

lol -- and I was too dense to get it. :)

> What I was getting at is that, if we assume the codeblock executes
> asa coroutine, then you'd get the latter output. If you wanted a
> thread, you could write:
>
> timeout(5) { thread { ... } };
>
> but if we assume that the codeblock is launched as an asynchronous
> thread, then there is no possible way the coerce it back into the
> coroutine (i.e. to "un-launch" it).

Ah. Ok, but if that's the case, you could as easily write it

timeout(5) { coro { ... } };

and have the compiler build it accordingly. The same logic works either
way from that end. Thread seem more sensible for a timeout, but as a
general rule I'd probably prefer to see implicit coro's rather than
implicit threads as the default.

> Now here's another semantics question: would we want the following to
> be valid?
>
> sub slow
> {
> timeout(60) { return undef but "Error: timed out" };
> return @slow_imp;
> }

Dunno....returns from a single routing that are in different threads
could produce some real headaches.

> How about:
>
> sub slow {
> timeout(60) {
> throw TimeoutException.new("Error: slow_fn timed out")
> };
> return @slow_imp;
> }

I like thata lot better, but I'm still not sure how it would fly.
(Sorry for the reformat, btw -- got pretty cramped on my screen.)

Dave Whipp

unread,
May 30, 2003, 4:37:42 PM5/30/03
to perl6-l...@perl.org

"Dulcimer" <ydb...@yahoo.com> wrote in message
news:200305301951...@web41213.mail.yahoo.com...

> I find C<timeout(60) { ... }> too terse, and would
> rather see a more verbose version

I'm obviously more lazy than you ;-).

> Ah. Ok, but if that's the case, you could as easily write it
>
> timeout(5) { coro { ... } };
>
> and have the compiler build it accordingly. The same logic works either
> way from that end.

Given that its a macro, its probably true that I could do some up-front
manipulation like that. But its a lot more work than launching a thread.
However, given that it is a macro, we could eliminate the outer-curlies.
Lets see if I know how to write a macro...

macro timeout is parsed( rx:w/
$secs:= <Perl.expression>
$code_type := ( thread | coro )?
$code := <Perl.block>/)
{
$code_type eq "thread" and $code = "{ thread $code }";
"my \$$(Perl.tmpvarname) = Timer.new(secs=>$secs, code=>$code);"
}

timeout(60) coro { ... }
timeout(60) thread { ... }
timeout(60) { ... } # default is coro

Even if it works, I have a feeling that the macro has plenty of scope for
improvement.

> > Now here's another semantics question: would we want the following to
> > be valid?
> >
> > sub slow
> > {
> > timeout(60) { return undef but "Error: timed out" };

> > return slow_imp;


> > }
>
> Dunno....returns from a single routing that are in different threads
> could produce some real headaches.
>
> > How about:
> >
> > sub slow {
> > timeout(60) {
> > throw TimeoutException.new("Error: slow_fn timed out")
> > };

> > return slow_imp;


> > }
>
> I like thata lot better, but I'm still not sure how it would fly.

Actually, I think that both have pretty-much the same problems. I assume
@Dan could work out a way to get the mechanics to work (basically a mugging:
we kill a thread/coro and steal its continuation point). But the semantics
of the cleanup could cause a world of pain. But realistically, a common use
of a timeout is to kill something that's been going on too long. I suppose
we could require some level of cooperation the the "dead" code.


Dave.


Reply all
Reply to author
Forward
0 new messages