On Fri, Apr 26, 2013 at 11:11 PM, Brian Anderson <bander...@mozilla.com>wrote:

> On 04/26/2013 09:04 AM, Patrick Walton wrote:
>
>> On 4/26/13 6:53 AM, Gábor Lehel wrote:
>>
>>> Probably a crazy idea, but would it be possible to do something where if
>>> the parent task launches a child task, and then does nothing else except
>>> wait for the child to finish, it's not actually implemented by launching
>>> a new task, but instead by calling the closure in place?
>>>
>>
>> The new scheduler is designed to allow *almost* this -- `spawn` will
>> simply grab a cached stack segment (assuming there's one available) and
>> context switch to the task immediately.
>>
>
> Yeah, and I'm trying to make it so most of the resources held by tasks,
> even the task object itself, is cacheable. So when you spawn in an ideal
> scenario you occupy the dead husk of some other task and just start
> running. We can also probably lazily initialize *all* the local services
> (logging, local heap, gc, etc). We're going to have a lot of options to
> reduce the expense of spawning once things are written in Rust.


That sounds great.

But (I thought) one of our premises was that it's not viable to provide
only expect-success versions of IO functions, which raise conditions and/or
fail the task on encountering an error (instead of returning a
bool/Option/Result), because if you _do_ want to catch the error (e.g. in a
Result) and react some other way, you have to launch a new task to do it
(e.g. via try()), which is not fast enough for the use case.

But given what you wrote above, would it actually be fast enough? Or why
not?

Initially I wasn't interested, but now I also want to comment on close().
If I didn't miss something, the use cases for close() were:

    1. Maybe you want to close the file early

Which thanks to affine types can be accomplished by simply `let _ = file;`
to make it go out of scope. (You could also write a function called close()
which consumes it, with an empty implementation.)

    2. What if you want to see the error code?

Perhaps the consuming close() could return it?

    3. What if it's in a managed box, in which case all of that doesn't
work?

In that case instead of @FileStream you should use @mut Option<FileStream>.
Then if you want to close the file, you assign None to it. That way you get
to retain the invariant that if you have a FileStream, it's open. If a file
is not open, either you don't have a FileStream, or "you" don't exist
because the task failed. That sounds pretty nice.

(FWIW the above was already mentioned in a previous message, but I missed
or forgot about it, so I hope there's no harm in repeating it in case
anyone else is similar.)

The fly in the ointment would seem to be that close() isn't the only way a
file can "go bad". After all, it can get into an erronous state after
almost any operation. But if in all of those cases, the operation responds
by failing the task unless a condition handler can repair the error, then
the invariant continues to hold. The type system enforces that tasks can't
share data, so if you want to try() an operation on a file, you have to
relinquish your ownership over the file, and if you want to get it back,
the only way is to put it in the Result of the try() -- which will be an
Err in the case of an error. Kind of like proto!. (Affine types are really,
really neat.)

Summa summarum it seems to me that if all IO functions either consume the
file (e.g. close()) or respond to unrecovered errors by fail!()ing, then
the "validity" of files can be tracked in the type system. I think that
would be really nice to have. Its viability is predicated on try() being
fast enough, which ties back to the previous discussion.

-- 
Your ship was destroyed in a monadic eruption.
_______________________________________________
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev

Reply via email to