February 07, 2014
On Friday, 7 February 2014 at 08:30:35 UTC, Walter Bright wrote:
> On 2/6/2014 7:08 PM, bearophile wrote:
>> Walter Bright:
>>
>>> It's not a matter of taste. If your input is subject to a DoS attack, don't
>>> put exceptions in the control flow.
>>
>> Perhaps the world of today malicious attacks on the software you write should be
>> assumed as the default situation, and then the language+library has to offer
>> something less paranoiac on request.
>>
>> That's why some languages have changed their sorting and hashing routines to
>> make them a little slower but safer on default.
>
> DoS attack resistance requires faster code, not slower code.

I think bearophile is referring to a practice of avoiding fast average-case, slow worst-case algorithms in favour of faster worst-cases.

If an algorithm has best-case O(n*log(n)) and worst case O(n^2), it's often not practical to build for the worst case, but anything less than that can make you vulnerable to malicious input as part of DOS.

In comparison, an algorithm with O(n*log^2(n)) average and worst-case might be acceptable in the average case, but will hold up better in the face of attack.


I'm not sure how relevant the point is to the general discussion.
February 07, 2014
John Colvin:

> I think bearophile is referring to

Yes, you have explained well my point. Thank you.

Bye,
bearophile
February 07, 2014
On Friday, 7 February 2014 at 14:42:18 UTC, Dicebot wrote:
> On Friday, 7 February 2014 at 14:26:48 UTC, Marc Schütz wrote:
>> Hmm... then what _does_ qualify as exceptional in your opinion?
>>
>> A logic error (i.e. a mistake on the programmers side) doesn't, IMO, it should abort instead. On the other hand, there is the class of situations where e.g. a system call returns an error (say, "permission denied" when opening a file, or out of disk space). Or more generally, an external service, like a database or a remote server. However, I can't see how these are fundamentally different from invalid user input, and indeed, there's often not even a clear separation, e.g. when a user asked you to read a file they don't have access to.
>>
>> So, what's left then?
>
> It is exceptional situation if input is supposed to be valid but surprisingly is not. For example, calling `decodeGrapheme` on external string without making sure it is valid first.

If the function expects it to be valid but you pass it an invalid value, you're breaking the contract, which is a logic error and thus should be checked for by assert, not by an exception.
=> Case number one: logic errors, no exceptions should be used here.

If however the function doesn't require it to be valid (for `decodeGrapheme` the docs don't say anything, so I assume it doesn't), then it needs to be able to handle invalid input, for example by throwing an exception.
=> This is an example of case number two: user errors, exceptions are okay here.

But Brad Anderson seems to disagree on case two (or maybe case one?). Or is there a third type of situation not covered by these two cases?

> Same goes for file - trying open a missing file is exceptional, but checking for file presence is not.

I agree here, checking for presence is not exceptional.
February 07, 2014
On Friday, 7 February 2014 at 14:26:48 UTC, Marc Schütz wrote:
> or a remote server. However, I can't see how these are fundamentally different from invalid user input, and indeed, there's often not even a clear separation, e.g. when a user asked you to read a file they don't have access to.

I agree. Any situation where it makes sense to say:

"Ouch, this is not going to work out, roll back, roll back, let's move out of this module! We need to try a different approach. We are not going to continue with anything productive down this lane, lets go back to the context and get into a new direction."

is suitable for exceptions and it makes code reuse, evolution and modification to error reporting easy.

- validation and veracity checking
- authentication failures
- database failures
- transactional retries
- serious allocation issues
- timeouts

are all fiiine for exceptions.

You get to write a request handler like this:

{
  auto sid = request.authenticate();
  auto data = validator(request.getPost('label1','label2','label3'));
  auto key = model.create_and_put(sid,data);
  response.writeJson(key);
  response.status = 201;
  return;
}

And you can change the error reporting at the request dispatcher level rather than sifting through 20 different spaghetti-like request handlers trying to figure out if you got it right:

{
  auto sid = request.authenticate();
  if (sid<0){
      ... return ...;
  }
  auto data = request.getPost('label1','label2','label3');
  if (data){
     data = validate(data);
     if (data){
        auto key = model.create_and_put(sid,data);
        if (item){
           auto ok = response.writeJson(key);
           if(ok){
              response.status = 201;
              return;
           }
           ....;
        } else {
           .... ;
        }
     } else {
        .... ;
     }
  } else {
    ... ;
  }
}


February 07, 2014
On Friday, 7 February 2014 at 11:37:16 UTC, Ola Fosheim Grøstad wrote:
> How slow is slow? Is it slower than in Go and Python?

One problem with allocating the exception is the stop-the-world thing. My cgi.d's built in httpd does some allocations in its constructor, which is run once per request. It can answer requests at a rate of about 6000/sec on my computer...

Until the allocation have gone too much and the GC starts running. Then all the pending requests stop, killing the throughput.

(BTW, interestingly, on Linux it uses separate process pools instead of threads. The GC does NOT stop the world since the other processes can keep going. But, if the requests are fairly uniform - as is typically the case with benchmarks - each process hits the GC threshold at about the same time.... ironically, it is the deterministic nature of the GC that leads to the performance killer there.)
February 07, 2014
On Friday, 7 February 2014 at 11:06:47 UTC, Dicebot wrote:
> Yes, I even had some simple proof-of-concept drafts of such approach for vibe.d but have never finished it. User input is not a problem if Phobos will provide more strongly typed @nothrow tools.

Yeah, I think using separate types for printing to users is often
a good idea too, since then the type system can help with i18n.
February 07, 2014
On Friday, 7 February 2014 at 15:33:01 UTC, Adam D. Ruppe wrote:
> On Friday, 7 February 2014 at 11:37:16 UTC, Ola Fosheim Grøstad wrote:
>> How slow is slow? Is it slower than in Go and Python?
>
> One problem with allocating the exception is the stop-the-world thing. My cgi.d's built in httpd does some allocations in its constructor, which is run once per request. It can answer requests at a rate of about 6000/sec on my computer...
>
> Until the allocation have gone too much and the GC starts running. Then all the pending requests stop, killing the throughput.
>
> (BTW, interestingly, on Linux it uses separate process pools instead of threads. The GC does NOT stop the world since the other processes can keep going. But, if the requests are fairly uniform - as is typically the case with benchmarks - each process hits the GC threshold at about the same time.... ironically, it is the deterministic nature of the GC that leads to the performance killer there.)

It's obviously not a solution, but you could change that by having each process call GC.reserve() with a different size.
February 07, 2014
On Friday, 7 February 2014 at 08:32:04 UTC, Walter Bright wrote:
> That doesn't work, as nothing prevents code from squirreling away the caught exception object handle.

scope would. I'm just saying.

We could also just document it as undefined behavior and leave matters in the user's hands, but this wouldn't jive nicely with @safe :(
February 07, 2014
On Friday, 7 February 2014 at 15:41:59 UTC, Adam D. Ruppe wrote:
> On Friday, 7 February 2014 at 08:32:04 UTC, Walter Bright wrote:
>> That doesn't work, as nothing prevents code from squirreling away the caught exception object handle.
>
> scope would. I'm just saying.
>
> We could also just document it as undefined behavior and leave matters in the user's hands, but this wouldn't jive nicely with @safe :(

Thread stores an uncaught exception reference so it can be rethrown on join().  But I suppose a case could be made that an uncaught exception could either be discarded or abort the app.
February 07, 2014
On Friday, 7 February 2014 at 15:44:08 UTC, Sean Kelly wrote:
> But I suppose a case could be made that an uncaught exception could either be discarded or abort the app.

It could also make a copy at that time on to the regular GC heap and store that (the members of the throwable class are still GC'd so all the store function has to do is a shallow copy, using the RTTI to get the correct size to copy, onto the gc heap). It'd surely be fewer exceptions to get through that than the thrown, caught, and subsequentely discarded typical case.