On Thu, May 24, 2012 at 9:16 PM, David Rajchenbach-Teller
<[email protected]> wrote:
> On 5/24/12 12:40 PM, Bennie Kloosteman wrote:
>>> * The system _must_ not prevent developers from calling C code from Rust.
>>> * The system _must_ not prevent developers from passing a pointer to a
>>> Rust function to C code that will call back to it.
>>> * The system _must_ not prevent, some day, developers from calling Rust
>>> from JavaScript.
>>> * The system _must_ not prevent, some day, developers from calling
>>> JavaScript from Rust.
>>
>> These should be unsafe - I believe this is the case with rust.
>
> I am not sure if you mean this in relation with all four points or with
> the last one. So, indeed, I see no reason to change the fact that calls
> to C are considered unsafe.
>
> For calls to JavaScript, I have not given it much thought. I suppose
> that they could be either unsafe or somehow managed.
externals calls either must work 100% ( including type conversions) or
be marked unsafe... if it breaks type or memory safety you need to say
that.
Speaking of which we can handle C errors in calls but what about C++
exceptions ? Does any C++ code we hook need exception wrappers ?
Also any error system rust should work when called from C ( unlike
C++ exceptions) .
>
>>> * Issues _must_ not be restricted to integers (or to one single type).
>>
>> Yes but returning error codes and then looking up further issue
>> detail separately is quite useful since your passing around little
>> information it gives the highest chance in a partially unsafe
>> environment of working out what happened . eg if the stack is
>> corrupted or you have major issues its useful that the error is memory
>> logged ( eg in ring 0 in a rust based kernel ) and then retrieved .
>> A simple integer return value has a much better chance..
>
> Interesting point.
> Are we talking about implementing Servo with a ring-like mechanism?
>
> If so, I suspect that we may simply not want this kind of issues to
> cross ring boundaries. Otherwise, if we let high-level issues cross
> boundaries, we could be sending closures, for instance, which sounds
> like a bad idea.
>
> For the moment, I believe that we should assume that the issue mechanism
> will not be used for crossing ring boundaries.
Is rust or this error handling solely for Servo ? What about writing
say a kernel or Linux kernel modules in rust ? To me system
programming is where Rust should be. If its all working that's fine
but I was showing some horrible worst cases and its still nice to get
some error information rather than a horrible crash .
A similar thing can occur with any external call to C or asm. But the
concept is the same try to get at least an error code returned even if
the most abysmal circumstances.
>
>> This could be a 64 bit value with the low 32 bits the error and the
>> upper 32 bit some sort of hash code.
>>
>> A more detailed but not guaranteed representation should be a detailed
>> structure. with time stamps etc I would even say this detail should
>> be created asynchronously via a new task while the actual error is
>> returned immediately .
>>
>> errno is a pain , but not because it returns an int but because all
>> the information you can retrieve on it is a error string.
>>
>> Actually you could log all the detail and just reference an index to
>> the log , Though this would require recent log entries to be
>> recallable and have more detail in the log fields eg error category
>> and level ( which are useful in a log anyway)
>
> All these are interesting points, although perhaps they arrive a little
> too early in the thread :)
>
Im not so young anymore so when I train of thoughts I like to get it
on E-paper ;-)
>>> * The default behavior in case of untreated issue _should_ be to
>>> gracefully kill the task or the application.
>>
>> If its integrated with tracing and logging the default behaviour
>> depends on the error type
>>
>> eg Trace , Log, Warning or Error continue , Critical = halt.
>
> A tracer/logger with this kind of semantics could certainly be built on
> top of the issue mechanism. I tend to believe that that they should be
> integrated from the start, though.
Agree,
>
>>> * Whenever an untreated issue kills a task/application, it _should_
>>> produces a report usable by the developer for fixing the issue.
>>> * It _should_ be possible to deactivate that killing behavior. There
>>> _may_ be limitations.
>>> * It _should_ be possible to deactivate that killing behavior
>>> conditionally (i.e. only for some errors).
>>
>> also a task can monitor child tasks for errors ( I don't think Rust
>> has events but something like that) and choose on error to abort or
>> continue depending on the error condition. .
>
> Sounds interesting. I suspect (but I am not sure) that this is something
> that could/should be built on top of a more elementary issue mechanism.
> Could you provide a scenario that we could use as a working base?
Yes it probably should be built on top ..but I really like the model
where items received from a pipe/channel are broken out and fired as
events ( which multiple callers can hook).
Note an interesting issue here , if the task is in a bad error state
it possibly may not use the "pipe/channel" for communication to the
parent .. How is this handled ? SIgnals ?
>
>
>>> * The system _should_ eventually have a low runtime cost – in
>>> particular, the case in which no killing happens should be very fast.
>>
>> I don't think the performance of an error system is important at all ,
>> its more important to have stack and meta data to resolve the issue
>> especially in debug builds ( and meta data is very slow) . This leads
>> to people using errors to pass/check state which is a bad habit
>
> Could you elaborate on that last sentence?
int I = 0;
try{
i = Int32.Parse( string)
}
catch (Exception ex)
{
// handle cant convert
}
vs
int I = 0 ;
if ( Int32.TryParse ( &I , string)
{ // handle cant convert }
>
> I am starting to believe that collecting [meta]data might be something
> that we want to configure on a usage basis. For instance, in function
> |move|, introduced a few e-mails ago (I just reposted it at
> https://gist.github.com/2781413 for convenience), we may be interested
> in the metadata of calls to lines 10 and 12, but gathering metadata for
> the calls in lines 2 and 6 is generally a waste of resources.
Yes it should be optional eg you don't always want it on traces and
logs , most of the time on errors ( always on debug) and always on
critical errors.
>
> If we think of this as exceptions, it could be that we want several the
> ability to specify if/when we want to gather metadata at the level of an
> equivalent of |try|. I believe that this fits nicely with what Graydon
> had in mind, by the way.
>
>> I would add
>> * Tasks are the error scope / unit
>>
>> * should we have an exception like syntax that automatically creates a
>> sub task for the embedded statements ( and hence means those
>> statements cannot result in failure of the parent tasks) . Since
>> errors are scoped to the task , the easy way is a new task .
>
> This looks quite heavyweight to me. So, if you don't care about
> performance, sure. But I'm clinging to the hope that we can make
> something faster, so I disagree for the moment :)
It is heavy weight but most Rust errors will not have exception
scopes... just functions that return an errorno or pointerto an Error
object , The concept of exception scope is useful though eg a single
threaded message pump is normally
foreach ( var message in buffer)
{
try {
ProcessMessage( message);
}
catch (ThreadAbortException ex) { // handle }
catch (Exception ex) { // handle and continue to next message }
}
We want every possible error handled so the loop never stops ..no
matter what happens . eg an out of bounds array , null pointer or core
dump in an external call should kill the message but not the message
pump. Obviously the unsafe / external core dump is difficult and may
not be possible but it is desirable.
So these are a counterpoint to fail , we could say failscope and fail .
>> * It should be integrated with tracing and logging.
>
> Looks like two distinct things to me. Why couldn't you build a tracing +
> logging tool on top of it?
Above you say it should be integrated … Yes it can be built in the
standard libs but It should still be integrated and more importantly
the logging and tracing considered in the design not in isolation ,
look how hard it is in C , in theory every time you return a null or
non zero return code you should be logging / tracing .
ie ptr = malloc(N);
if ( ptr == NULL)
{
//get error no
// log / trace
// return to user
}
Why does the caller need to do the logging ? You may have 1000s of
calls so you need to make wrappers just for the logging . malloc
should do it ( depending on app configuration) especially when logging
is built into the language. Since the standard lib doesn't the
caller has to do it … The best place to log errors is the one that
generated the error and by integrating the error system with logging
it can be done automatically.
I have written a ton of code over the years ( when I bother in
quality programs) like catch (exception) { if blah blah / log /
trace ; if severity is < n rethrow } and the c style error code way
is abysmal here if you want to log . Yet 99% of the time all you want
is some sort of log everything if the severity is > N in a config
file concept without writing the code.
Maybe something like :
1. Fail returns a ref to Error type it creates
2. Stdlib Errors Return a ref to Error type ( which can be the
Error.Success so you can go if ( function() != Error.Success)
3. When error type is created , error type has a hook for logging /
tracing on construction. by default this should log depending on a
configurable level.
4. Most errors should be statically defined and allow strongly typed checking
BTW this is an interesting article it covers .NET /C++ Exceptions vs
ErrorCodes vs COM HRESULT / IErrorInfo at the end
http://blogs.msdn.com/b/cbrumme/archive/2003/10/01/51524.aspx
Ben
_______________________________________________
Rust-dev mailing list
[email protected]
https://mail.mozilla.org/listinfo/rust-dev