March 08, 2012
On Wednesday, March 07, 2012 22:58:44 Chad J wrote:
> On 03/07/2012 10:40 PM, Jonathan M Davis wrote:
> > On Wednesday, March 07, 2012 22:36:50 Chad J wrote:
> >> On 03/07/2012 10:08 PM, Jonathan M Davis wrote:
> >>> On Wednesday, March 07, 2012 20:44:59 Chad J wrote:
> >>>> On 03/07/2012 10:21 AM, Steven Schveighoffer wrote:
> >>>>> You can use sentinels other than null.
> >>>>> 
> >>>>> -Steve
> >>>> 
> >>>> Example?
> >>> 
> >>> Create an instance of the class which is immutable and represents an invalid value. You could check whether something is that value with the is operator, since there's only one of it. You could even make it a derived class and have all of its functions throw a particular exception if someone tries to call them.
> >>> 
> >>> - Jonathan M Davis
> >> 
> >> Makes sense.  Awfully labor-intensive though.  Doesn't work well on
> >> 
> >> classes that can't be easily altered.  That is, it violates this:
> >>> - Do not modify the implementation of UnreliableResource.  It's not
> >>> always
> >>> possible.
> >> 
> >> But, maybe it can be turned it into a template and made to work for arrays too...
> > 
> > Personally, I'd probably just use null. But if you want a sentinel other than null, it's quite feasible.
> > 
> > - Jonathan M Davis
> 
> Wait, so you'd use null and then have the program unconditionally crash whenever you (inevitably) mess up sentinel logic?

Yes. Proper testing will find most such problems. And it's not like having a non-null sentinel is going to prevent you from having problems. It just means that you're not distinguishing between a variable that you forgot to initialize and one which you set to the sentinel value. Your program can die from a variable being null in either case. And in _both_ cases, it's generally unsafe to continue executing your program anyway.

And honestly, in my experience, null pointers are a very rare thing. You catch them through solid testing.

- Jonathan M Davis
March 08, 2012
On 03/07/2012 11:17 PM, Jonathan M Davis wrote:
> On Wednesday, March 07, 2012 22:58:44 Chad J wrote:
>> On 03/07/2012 10:40 PM, Jonathan M Davis wrote:
>>> On Wednesday, March 07, 2012 22:36:50 Chad J wrote:
>>>> On 03/07/2012 10:08 PM, Jonathan M Davis wrote:
>>>>> On Wednesday, March 07, 2012 20:44:59 Chad J wrote:
>>>>>> On 03/07/2012 10:21 AM, Steven Schveighoffer wrote:
>>>>>>> You can use sentinels other than null.
>>>>>>>
>>>>>>> -Steve
>>>>>>
>>>>>> Example?
>>>>>
>>>>> Create an instance of the class which is immutable and represents an
>>>>> invalid value. You could check whether something is that value with the
>>>>> is operator, since there's only one of it. You could even make it a
>>>>> derived class and have all of its functions throw a particular exception
>>>>> if someone tries to call them.
>>>>>
>>>>> - Jonathan M Davis
>>>>
>>>> Makes sense.  Awfully labor-intensive though.  Doesn't work well on
>>>>
>>>> classes that can't be easily altered.  That is, it violates this:
>>>>> - Do not modify the implementation of UnreliableResource.  It's not
>>>>> always
>>>>> possible.
>>>>
>>>> But, maybe it can be turned it into a template and made to work for
>>>> arrays too...
>>>
>>> Personally, I'd probably just use null. But if you want a sentinel other
>>> than null, it's quite feasible.
>>>
>>> - Jonathan M Davis
>>
>> Wait, so you'd use null and then have the program unconditionally crash
>> whenever you (inevitably) mess up sentinel logic?
>
> Yes. Proper testing will find most such problems. And it's not like having a
> non-null sentinel is going to prevent you from having problems. It just means
> that you're not distinguishing between a variable that you forgot to
> initialize and one which you set to the sentinel value. Your program can die
> from a variable being null in either case. And in _both_ cases, it's generally
> unsafe to continue executing your program anyway.
>

The important difference in using explicit sentinel values here is that they are not null, and thus very unlikely to have been caused by memory corruption.  It allows us to distinguish between the two sources of empty variables.

With a better way to do sentinel values, I can isolate my cleaner looking code from the scarier looking code that comes from any number of places.

I also am not too worried about null values that come from stuff that was simply forgotten, instead of intentionally nulled.  I DO tend to catch those really early in testing, and they are unlikely to happen to begin with due to the close association between declaration and initialization.

> And honestly, in my experience, null pointers are a very rare thing. You catch
> them through solid testing.
>
> - Jonathan M Davis


Sorry, your testing doesn't help me as well as you probably wish it does.  Our experiences must be very different.  I run into a lot of cases where things can't be tested automatically, or at least not easily.  Think along the lines of graphics operations, interactively driven code (ex: event lifetimes), network code, etc.  Testing can help things between endpoints, but it doesn't help much where the rubber meets the road.

And that's just game dev.  Then I go to work at my job, the one that makes money, and experience code from the 80s.  Rewriting it is completely impractical for near-term projects (though a complete phase-out of crufty old crap is on the horizon one way or another!). Yes it has bugs.  If I had an attitude of "crash on every little nit" then these things wouldn't last a few seconds (OK, exaggeration).  So I recover as well as possible, and occasionally rewrite strategically important pieces.  But the world is NOT perfect, so relying on it being perfect is %100 unhelpful to me.  Also, "quit your job" is not an acceptable solution. ;)  Now, in principle, we will never have to deal with D code like that.  Nonetheless, these experiences do make me severely afraid of lacking the tools that keep me safe.

And then there are still those occasional weird problems where sentinel values are needed, and its so stateful that there's a vanishingly close-to-zero chance that testing will catch the stuff that it needs to.
So I test it as well as I can and leave a "if all else fails, DO THIS" next to the dubious code.  Indiscriminate segfaulting deprives me of this last-ditch option.  There is no longer even a way to crash elegantly.  It all just goes to hell.

Long story short: in practice, I find that recovering from sentinel dereference is not only VERY safe, but also orders of magnitude less frustrating for both my users and me.

(Memory corruption, on the other hand, is something I am very unfamiliar with, and sort of afraid of.  So I'm willing to ditch nulls.)
March 08, 2012
On 06/03/12 17:05, Sean Kelly wrote:
> On Mar 6, 2012, at 3:14 AM, Don Clugston<dac@nospam.com>  wrote:
>>
>> Responding to traps is one of the very few examples I know of, where Windows got it completely right,
>> and *nix got it absolutely completely wrong. Most notably, the hardware is *designed* for floating point traps to be fully recoverable. It makes perfect sense to catch them and continue.
>> But unfortunately the *nix operating systems are completely broken in this regard and there's nothing we can do to fix them.
>
> Does SEH allow recovery at the point of error like signals do?

Yes, it does. It really acts like an interrupt. You can, for example, modify registers or memory locations, then perform the equivalent of an asm { iret; }, so that you continue at the next instruction. Or, you can pass control to any function, after unwinding the stack by any number of frames you chose. And, you regain control if any other exception occurs during the unwinding, and you're given the chance to change strategy at that point.

An SEH handler behaves a bit like setjmp(), it's not a callback.
Most importantly, in comparison to Posix, there are NO LIMITATIONS about what you can do in an SEH exception handler. You can call any function you like.

The documentation is terrible, but it's really a beautiful design.

 Sometimes I think it would be enough if the Posix spec were worded in a way that allowed exceptions to be thrown from signal handlers.

I think that would be difficult to allow. Some of the restrictions seem to be quite fundamental. (Otherwise, I'm sure they would have got rid of them by now!)
March 08, 2012
On Thu, 08 Mar 2012 03:58:22 -0500, Don Clugston <dac@nospam.com> wrote:

> On 06/03/12 17:05, Sean Kelly wrote:
>> On Mar 6, 2012, at 3:14 AM, Don Clugston<dac@nospam.com>  wrote:
>>>
>>> Responding to traps is one of the very few examples I know of, where Windows got it completely right,
>>> and *nix got it absolutely completely wrong. Most notably, the hardware is *designed* for floating point traps to be fully recoverable. It makes perfect sense to catch them and continue.
>>> But unfortunately the *nix operating systems are completely broken in this regard and there's nothing we can do to fix them.
>>
>> Does SEH allow recovery at the point of error like signals do?
>
> Yes, it does. It really acts like an interrupt. You can, for example, modify registers or memory locations, then perform the equivalent of an asm { iret; }, so that you continue at the next instruction. Or, you can pass control to any function, after unwinding the stack by any number of frames you chose. And, you regain control if any other exception occurs during the unwinding, and you're given the chance to change strategy at that point.
>
> An SEH handler behaves a bit like setjmp(), it's not a callback.
> Most importantly, in comparison to Posix, there are NO LIMITATIONS about what you can do in an SEH exception handler. You can call any function you like.
>
> The documentation is terrible, but it's really a beautiful design.
>
>   Sometimes I think it would be enough if the Posix spec were worded in a way that allowed exceptions to be thrown from signal handlers.
>
> I think that would be difficult to allow. Some of the restrictions seem to be quite fundamental. (Otherwise, I'm sure they would have got rid of them by now!)

IIRC, SEH is patented...

-Steve
March 08, 2012
I think what Chad is looking for is a BlackHole/WhiteHole equivalent which doesn't need abstract functions but which figures out all the methods of a class at compile time and creates a subclass that throws/does nothing on method invocation. An 'alias this' field would be used that is default-initialized with this sentry object. I don't know why we don't have __traits(allFunction). We have 'getVirtualFunctions' but it requires a function name, but using allMembers to filter out function names is damn difficult if you ask me. I've never had an easy time interacting with __traits.
March 08, 2012
On Thu, Mar 08, 2012 at 02:57:00PM +0100, Andrej Mitrovic wrote: [...]
> I don't know why we don't have __traits(allFunction). We have 'getVirtualFunctions' but it requires a function name, but using allMembers to filter out function names is damn difficult if you ask me.

	foreach (name; __traits(allMembers, typeof(obj))) {
		static if (__traits(compiles, &__traits(getMember, obj,
				name)))
		{
			alias typeof(__traits(getMember, obj, name))
				type;
			static if (is(type==function)) {
				// name refers to a function of type
				// 'type' here
			}
		}
	}

> I've never had an easy time interacting with __traits.

Me too. I'm guessing that __traits is the way it is due to ease of implementation in the compiler. It's certainly not very friendly to use.


T

-- 
Маленькие детки - маленькие бедки.
March 08, 2012
On Mar 8, 2012, at 12:58 AM, Don Clugston <dac@nospam.com> wrote:

> On 06/03/12 17:05, Sean Kelly wrote:
>> On Mar 6, 2012, at 3:14 AM, Don Clugston<dac@nospam.com>  wrote:
>>> 
>>> Responding to traps is one of the very few examples I know of, where Windows got it completely right,
>>> and *nix got it absolutely completely wrong. Most notably, the hardware is *designed* for floating point traps to be fully recoverable. It makes perfect sense to catch them and continue.
>>> But unfortunately the *nix operating systems are completely broken in this regard and there's nothing we can do to fix them.
>> 
>> Does SEH allow recovery at the point of error like signals do?
> 
> Yes, it does. It really acts like an interrupt. You can, for example, modify registers or memory locations, then perform the equivalent of an asm { iret; }, so that you continue at the next instruction. Or, you can pass control to any function, after unwinding the stack by any number of frames you chose. And, you regain control if any other exception occurs during the unwinding, and you're given the chance to change strategy at that point.
> 
> An SEH handler behaves a bit like setjmp(), it's not a callback.
> Most importantly, in comparison to Posix, there are NO LIMITATIONS about what you can do in an SEH exception handler. You can call any function you like.

Wow, sounds like paradise compared to signals.


> The documentation is terrible, but it's really a beautiful design.
> 
> Sometimes I think it would be enough if the Posix spec were worded in a way that allowed exceptions to be thrown from signal handlers.
> 
> I think that would be difficult to allow. Some of the restrictions seem to be quite fundamental. (Otherwise, I'm sure they would have got rid of them by now!)

I'm sure it's too late now. And I imagine there was a good reason for it that I'm not aware of, just like the restrictions on kernel calls (which could have blocked signals during execution). Performance was probably part of it. Ah well. The last signal issue I ran into was a deadlock caused by a logger routine calling ctime_r inside a sigchild handler.  What a pain.
March 08, 2012
could it be a good idea to add something like an check-scope for modules,functions,etc. for typical fails to easy the detection?

wild-idea-and-syntax-list:
@CheckForNull
@CheckForNaN
@CheckForUnnormalFloat
...

---- x.d

modul x

@CheckForNull // will introduce NullChecks for the complete module

..
..
..

-----

int test()
{
@CheckForNull; // introduces Null-Checks on the function scope
@CheckForNaN; // introduces NaN-Checks on the function scope
   ...very long evil function...
}

total waste of compiler-developer time - or something to make D better in the end?





March 08, 2012
On 3/8/12, H. S. Teoh <hsteoh@quickfur.ath.cx> wrote:
> 	foreach (name; __traits(allMembers, typeof(obj))) {
> 		static if (__traits(compiles, &__traits(getMember, obj,
> 				name)))
> 		{
> 			alias typeof(__traits(getMember, obj, name))
> 				type;
> 			static if (is(type==function)) {
> 				// name refers to a function of type
> 				// 'type' here
> 			}
> 		}
> 	}
>
>> I've never had an easy time interacting with __traits.

Yesterday I've tried the same thing but it didn't work because I was missing the &__traits call. With that in place, here's a very hardcoded example of what you can do in D:

http://paste.pocoo.org/show/562933/

So now you can catch the exception if the object was uninitialized. And you didn't have to modify the target class at all.
March 08, 2012
Sorry,
mixin(getOverloads!(UnreliableResource)()); should be:
mixin(getOverloads!(Base)());