April 01, 2013
01-Apr-2013 15:08, Lars T. Kyllingstad пишет:
> It's time to clean up this mess.
>
> http://wiki.dlang.org/DIP33

Overall neat.

1. Where to define this hierarchy.
Captain the obvious says std.exception, but can we fit all of them there?

2. ProcessException is IMHO a system exception. Plus some RTOses systems may not have processes in the usaul POSIX sense. It needs some thought.

3. I assume that NetworkingException should rather be NetworkException. Not only that but I truly hope it deals with network-only events (like resolver errors, host unreachable etc.) not with normal I/O Exceptions happening on sockets. It could get tricky to separate the two on some OSes.

4. A quiz as an extension of 3 - where a e.g. serial port exceptions would fit in this hierarchy? Including Parity errors, data overrun etc.
Let's think a bit ahead and pick names wisely.
Maybe turn NetworkException --> Comm(unication)Exception, I dunno.

5. Continuing the above - a failed call (in-system) of a general purpose IPC library would be a ... SystemException? A network exception? Should it matter to the user?

6. I like ParseException. Wondering if could be generalized a bit - in a sense it's a "bad format"/"malformed" exception. For instance corrupt DB file reporting ParseException (= bad format) is rather wacky.

7. DocParseExcpetion ---> TextParseException there is a notion of a binary "document" (e.g. BSON databases like Mongo DB).

8. For IOExcpetion we might consider adding an indication on which file handle the problem happened and/or if it's closed/invalid/"mostly fine" that.

That's it so far. We'll see if there is more ;)
-- 
Dmitry Olshansky
April 01, 2013
On Monday, 1 April 2013 at 12:12:56 UTC, Lars T. Kyllingstad wrote:
>
> I think OutOfMemory should not be restricted by nothrow, and I propose to solve it as described above.

More precisely:  In principle, I think OutOfMemory *should* be restricted by nothrow, but it would break too much code, and be far too annoying, to be feasible. :)

Lars
April 01, 2013
On Monday, 1 April 2013 at 14:58:42 UTC, Jacob Carlborg wrote:
> On 2013-04-01 13:08, Lars T. Kyllingstad wrote:
>> It's time to clean up this mess.
>>
>> http://wiki.dlang.org/DIP33
>
> In general I think it looks good. I also think it's really needed.
>
> Don't IOException and ProcessException need a "kind" field as well?

Maybe.  The ones that have such fields will probably need more Kind enum members too.  What's in the DIP are simply the ones that I could think of when I wrote it.

Lars
April 01, 2013
On Monday, 1 April 2013 at 17:28:11 UTC, H. S. Teoh wrote:
> On Mon, Apr 01, 2013 at 01:08:15PM +0200, Lars T. Kyllingstad wrote:
>> It's time to clean up this mess.
>> 
>> http://wiki.dlang.org/DIP33
>
> I'd prefer "NetworkException" instead of "NetworkingException" (long
> name with no added advantage).

Agreed.  I have changed it now.


> About the use of enums in FilesystemException, NetworkingException,
> etc.: I understand the rationale for them, but this also makes them
> closed for extension. Is there anything fundamentally wrong with
> creating subclasses of these exceptions instead of attempting to cover
> *all* possible problems in a single enum?

Phobos/druntime devs will have the opportunity to add enum fields to cover every error category in those libraries, and the users themselves can still extend the classes the "normal" way.  As you may have noticed, the first member of each enum is "unknown", which is meant for errors that do not fall into any of the other categories.  (There may be a better name for this, like "other", and maybe there should be a separate enum member called "userDefined"?)

My problem with subclasses is that they are a rather heavyweight addition to an API.  If they bring no substance (such as extra data), I think they are best avoided.


> I like the use of chaining to attach errno or windows system errors to
> exceptions. This solves the problem of errno's not being easily mapped
> to one of the standard exception classes. It's sort of the reverse of
> what chaining was intended for (another exception being thrown while the
> first one was in transit), but I haven't actually seen any real use case
> for the latter, so we might as well use it for the purpose here.
>
> The only thing is, this makes library code a bit trickier to write.
> Maybe Phobos needs to provide some kind of standard (though
> system-specific) way of mapping errno, windows error codes, etc., into
> one of the standard exception types, so that this mapping won't have to
> be duplicated all over the place. Obviously it can't be completely
> automatic, since some errno's may map to different exceptions depending
> on context, but *some* amount of mapping would be highly desirable to
> avoid code duplication.

This is a good idea.


> Another nice thing to have (not sure how practical it will be) is to add
> more information to the exceptions under Exception. To truly free us
> from the tendency to just invent GetoptException, XMLException,
> RegexException, etc., we need to consider that sometimes you *do* want
> to know where the exception came from. For example, you could be calling
> std.getopt from some generic initialization code that does other stuff
> too, both of which may throw a ConversionException, say. Sometimes you
> need to distinguish between them (display a command-line syntax help in
> one case, just display an error in the other case, depending on where
> the exception came from). One solution is to add a locus field to
> Exception:
>
> 	class Exception : Error {
> 		...
> 		/// Where this exception came from
> 		/// E.g., "std.getopt", "std.xml",
> 		/// "my.program.init.complexconv", etc..
> 		string locus;
> 	}
>
> This way the catching code doesn't have to downcast, guess, or do some
> ugly non-portable hacking to figure out what to do.
>
> This field should probably be automatically filled by Exception's ctor,
> so that it doesn't require additional burden on the programmer.
>
> I'm not 100% sure the module name should be used in this field, but the
> idea is that it should contain some way of identifying the origin of the
> exception that can be programmatically identified.

My first though was: "Isn't this what the stack trace is for?", but then again, that's rather bothersome to parse programmatically.  I'm not completely sold on the idea of tagging exceptions with their modules of origin, but I don't have any good alternatives, either.

Lars
April 01, 2013
01-Apr-2013 20:00, John Colvin пишет:
> On Monday, 1 April 2013 at 12:12:56 UTC, Lars T. Kyllingstad wrote:
>
>> But if all cleanup code is bypassed, what is the point in using the
>> exception mechanism in the first place?  Why not just abort() and be
>> done with it?
>>
>> I can think of two reasons for throwing an Error rather than aborting
>> directly:
>> 1. You want a kind of "graceful" shutdown, in which destructors *are*
>> called and make their best attempt at cleaning things up.
>> 2. You want to catch it at some point, and perform some manual cleanup.
>>
>> But if (1) does not happen, can you even hope to do something useful
>> with (2)?  Your program is in the worst possible state it can be!
>
> I'm no expert on these things, but:
>
> Any chance of being in an invalid state - > undefined behaviour
>
> Undefined behaviour - > your destructors/cleanup routine could in theory
> do anything.
>

While a solid point I'd argue the opposite is more applicable.
The proponents of "Undefined bahaviour" is "anything can happen" let's just die fail flat on 2 counts:

1. Label all "bad things" s as undefined where it's more often system-defined or implementation defined. Out of memory is another one. Processor dependent behavior is another one (e.g. shift beyond word wideness).

2. Second that "anything can happen" thus "let's not try destructors and cleanup" just call abort.  In fact if you escalate the point of "anything" there is no guarantee that abort call will ...e-hm... actually call the process termination routine (or that C run-time is intact).

> Therefore, you're better off not trying to cleanup if program state
> could be invalid.

Data is corrupted no matter if you just fail to write it in a consistent state (sudden assertion in some 3-rd party library) or corrupt accidentally by bad write (during cleanup on corrupted RAM).

Therefore you should always try to orderly cleanup but do not rely on it to actually work at all circumstances (thus backups, commits/save points, watchdogs and whatnot).


-- 
Dmitry Olshansky
April 01, 2013
On Monday, 1 April 2013 at 18:40:48 UTC, Dmitry Olshansky wrote:
> 01-Apr-2013 15:08, Lars T. Kyllingstad пишет:
>> It's time to clean up this mess.
>>
>> http://wiki.dlang.org/DIP33
>
> Overall neat.
>
> 1. Where to define this hierarchy.
> Captain the obvious says std.exception, but can we fit all of them there?

Some of them have to be in core.exception.  Personally, I think the rest of the base hierarchy should be in std.exception.  In fact, I think the "standard hierarchy" should be defined as "what's in core.exception and std.exception".

If we identify cases where modules absolutely *must* extend these classes for extremely specific purposes, this can be done within the module.

> 2. ProcessException is IMHO a system exception. Plus some RTOses systems may not have processes in the usaul POSIX sense. It needs some thought.

Well, I *don't* think ProcessException is a SystemException. :)  And if druntime/Phobos is to be used on OSes that don't have processes, there are other adaptations that have to be made that are probably more fundamental.


> 3. I assume that NetworkingException should rather be NetworkException. Not only that but I truly hope it deals with network-only events (like resolver errors, host unreachable etc.) not with normal I/O Exceptions happening on sockets. It could get tricky to separate the two on some OSes.

I have changed the name.  I have the same view of the NetworkException/IOException separation as you.

> 4. A quiz as an extension of 3 - where a e.g. serial port exceptions would fit in this hierarchy? Including Parity errors, data overrun etc.
> Let's think a bit ahead and pick names wisely.
> Maybe turn NetworkException --> Comm(unication)Exception, I dunno.

Good question, I dunno either. :)  I agree we should think about it.


> 5. Continuing the above - a failed call (in-system) of a general purpose IPC library would be a ... SystemException? A network exception? Should it matter to the user?

Note that I am only saying that the standard exceptions should cover *most* error categories.  100% is not feasible, and we shouldn't even try.  This may be one of the exceptions (haha) to the rule.

> 6. I like ParseException. Wondering if could be generalized a bit - in a sense it's a "bad format"/"malformed" exception. For instance corrupt DB file reporting ParseException (= bad format) is rather wacky.

I agree, and welcome all suggestions for better names.


> 7. DocParseExcpetion ---> TextParseException there is a notion of a binary "document" (e.g. BSON databases like Mongo DB).

Agreed.


> 8. For IOExcpetion we might consider adding an indication on which file handle the problem happened and/or if it's closed/invalid/"mostly fine" that.

Which form do you suggest that such an indicator should take? Bear in mind that it should be general enough to cover all, or at least most, kinds of I/O exceptions.

Lars
April 01, 2013
On Monday, 1 April 2013 at 17:42:07 UTC, Jesse Phillips wrote:
> On Monday, 1 April 2013 at 11:08:16 UTC, Lars T. Kyllingstad wrote:
>> It's time to clean up this mess.
>>
>> http://wiki.dlang.org/DIP33
>
> I like the idea, I think some specifics may need worked out (already being discussed)
>
> You're likely purposely avoiding this. But when the subject of a "Exception Hierarchy" comes up thoughts of having specific modules to house the exceptions come up. I don't think this is the primary concern, but my position is to keep the exceptions in their std implementation/use.

You're right, I was purposely avoiding it in the DIP. :) But I have given the matter some thought; see my answer to Dmitry.

Lars
April 01, 2013
On 2013-04-01 21:22, Lars T. Kyllingstad wrote:

> Maybe.  The ones that have such fields will probably need more Kind enum
> members too.  What's in the DIP are simply the ones that I could think
> of when I wrote it.

I was think of what you wrote in the text: "failure to start a process, failure to wait for a process".

-- 
/Jacob Carlborg
April 01, 2013
On Monday, April 01, 2013 21:17:27 Lars T. Kyllingstad wrote:
> On Monday, 1 April 2013 at 12:12:56 UTC, Lars T. Kyllingstad
> 
> wrote:
> > I think OutOfMemory should not be restricted by nothrow, and I propose to solve it as described above.
> 
> More precisely: In principle, I think OutOfMemory *should* be restricted by nothrow, but it would break too much code, and be far too annoying, to be feasible. :)

There's really no point in making it so that OutOfMemory prevents nothrow, as it's pretty much assumed by the runtime that OutOfMemory means that you're toast (hence why it's not an Exception). If anything making it prevent nothrow would be a disaster, rendering nothrow nigh on unusable in many situations where it should work just fine. The whole point of nothrow is to indicate that no Exceptions can be thrown (and therefore anything that's required by exceptions doesn't have to be done). Errors don't fall in that category at all, especially in light of the fact that there's no guarantee that any clean- up will be done when a non-Exception is thrown.

- Jonathan M Davis
April 01, 2013
On Monday, April 01, 2013 13:23:49 monarch_dodra wrote:
> On Monday, 1 April 2013 at 11:08:16 UTC, Lars T. Kyllingstad
> 
> wrote:
> > It's time to clean up this mess.
> > 
> > http://wiki.dlang.org/DIP33
> 
> A quick comment about your "Error" section. You say:
> 
> "In general, Errors should not be caught, primarily because they indicate that the program logic is compromised, and that the program may therefore be in an invalid state from which there is no recovery".
> 
> It is actually much worst than that: errors bypass the entire exception handling mechanism, blasting through code that would handle destructors, and even flying through functions that are nothrow. They don't just indicate a "potential" invalid state, they actually *put* the program in an invalid state, from which there is no recovery.
> 
> That is the main mechanical difference between an "error" and an "exception", it is not just a philosophical "logic vs runtime".
> 
> --------
> 
> Under this situation, I'm wondering how the "OutOfMemory" is
> dealt with (you don't explain).

It's not an Exception, so no clean-up is guaranteed. _Any_ Throwable which is not an Exception risks no clean-up being done. Error really has nothing to do with it beyond the fact that it's not an Exception.

Now, the reality of the matter is that the current implementation _does_ generally do clean-up when non-Exceptions are thrown (the major exception being code dealing with nothrow functions IIRC), but according to Walter, there's no guarantee that that's the case (and I'm not sure that he's particularly happy that any clean-up happens at all for non-Exceptions like it does right now).

- Jonathan M Davis