On Mon, May 8, 2017 at 01:42 JPR via 4D_Tech <4d_tech@lists.4d.com> wrote:

> I'm not sure about this topic... About what do you try to demonstrate...

It's a pretty standard question about exception handling.

> Is it a crash test? A kind of stress test? Do you really want to find a
way to crash 4D?
I'll answer your questions in order: No. No. No. There's a likely scenario
that isn't documented, so I wanted to find out the actual behavior. That's
just a bit of routine, technical due diligence. I'd do this with any
product.

> What I said during this training is basically <snip>

It sounds like you offered some sensible guidelines and such in your class,
but your points don't have much of anything to do with the issue I've
raised.

* I objected to EXECUTE for a very
* long time because of this very reason. Unsafe at any speed.

> That's true. But you know, Life is unsafe at any speed.
? That's just an odd thing to say.

> Why to object to a command? Just don't use it if you do not feel safe
with it,
> and let people willing to use it do it at their own risks. I feel BASE
jumping
> very unsafe, so I don't do it, and I still feel good about people who
like it...

I don't understand your response or how it applies to the issue I've
raised. You're not offering any sort of technical answer, just a confusing
metaphor. I won't follow you in this direction.

I'm talking about a pretty straightforward technical issue, not some kind
of exotic misuse of 4D's commands. For background, where did I get the
notion that CALL WORKER is meant to communicate with preemptive processes?
From watching Laurent Esnault's 2016 Summit presentation. His presentation
was clear, well reasoned, and informative. He explained how CALL WORKER
satisfies a requirement that arose out of the need to communicate with
preemptive workers. In other words, CALL WORKER was *created* to talk to
preemptive workers! So, what I'm talking about is a 100% orthodox use of
the 4D commands as explained by their creator. CALL WORKER has broader
applications, but it sounds like its genesis was to talk to preemptive
workers.

Now take another look at the title of my feature request:

   "Provide developer control when a preemptive worker receives
non-preemptive code"
   http://forums.4d.fr/Post//19391591/1/

That sounds entirely reasonable, no? If a piece of code accepts inputs,
it's basic defensive coding to have a way of dealing with exceptions. We
don't have this now in 4D for preemptive workers that receive code that
cannot be run preemptively. That's bad. 4D should fix this. This is
*entirely* within 4D's powers and absolutely *not* within our powers.

Now, it is easy enough to say (as someone already has) "don't do that!"
Well, of course! But that's as helpful as saying "Don't make mistakes!" or
"Be smarter!" It's worthless advice because there's no way to follow it. If
I could be smarter, I would be. But I can't. I would love to make fewer
mistakes but, by nature, they're something I'm unaware of. That's why
programmers learn to build tools to catch, manage, and prevent mistakes.
I'm not asking 4D to tolerate something illegal, I'm asking it to complain!
But to tell me about that complaint. Right now, there's a whole bunch of
unpredictable/inconsistent/undocumented behavior that could lead to some
seriously messed up outcomes. Since 4D is proud of this new feature (and
why not?), I'd hope that they want to make it more useful, reliable, and
easy to use successfully.

Most people aren't that far into the details on CALL WORKER, etc. yet, but
there's no need to understand those commands to appreciate the basic
quality-oriented coding issues. Back to talking about bad inputs... Take a
simple example. I've got a routine called CalculateRate

   CalculateRate (Longint) : Real

$1 must only be a whole number between 1-5, so the entire set of legal
values is:

1   2   3   4   5
That's it, all other values are illegal. Now, imagine four illegal calls
like these:

C_REAL($rate1;$rate2;$rate3;$rate4)
$rate:=CalculateRate(Pi)
$rate2:=CalculateRate("5")
$rate3:=CalculateRate(Current date)
$rate4:=CalculateRate(0)

Honestly, none of them look wrong per se. Any of those parameters could
make sense, depending on what sort of rate you're trying to calculate. But
in this case $1 has been defined as a longint, so the compiler detects that
the first three calls are illegal. However, the compiler is blind to the
out-of-range value in the $rate4 line. This is because 4D doesn't have any
syntax for declaring an input as allowing only a specific range of values,
etc. 4D's type system is non-extensible, so we're limited to 4D's built-in
types. <skip discussions of abstract types, etc. or of a a custom code
scanner here.> And even if the compiler did see the flaw in the fourth
call, there are other cases that only arise at runtime:

C_LONGINT($rate_code)
$rate_code:=Number_GetRandom(0;5)

This variable gets a longint from the series 0, 1, 2, 3, 4, 5. Ouch! It's
going to generate a bad input roughly 1/6 of the time. You'll only see this
at runtime. That's a silly example, but we've all generated or allowed bad
inputs from good intentions before in less artificial and silly ways.

Now imagine this code:

// CalculateFinalTotal
C_REAL($0;$final_total)
C_REAL($1;$total)
C_REAL($2;$rate_code)

$total:=$1
$rate_code:=$2

C_REAL($rate)
$rate:=CalculateRate ($rate_code)

$final_total:=$total*$rate

$0:=$final_total

That should compile fine, it looks fine...but what if $2 is 0? What happens
then? Imagine this call:

$final_total:=CalculateFinalTotal(100;0)

What is $final_total? What should it be? That depends on how CalculateRate
behaves. Let's assume that CalculateRate is defensive and detects the bad
rate code. (Thus avoiding a zero divide internally or what have you.) So
let's say that it returns 0. Your final total will be 0, the 100 is lost.
And don't divide by that result! Now imagine that CalculateRate returns a
negative value to indicate an error, say -1. Now the final total is -100.
This could also really screw up your calculations. What if that final total
is used in a larger calculation? The bad data spreads and spreads. So what
is the right response to such errors. *There isn't one.* You're getting
garbage, it's a programming flaw and it needs to be fixed. There is no
self-healing here, just detection. That detection alerts you to the problem
before more damage is done to the data and lets you correct the problem.
The problem is in the code, but it poisons the data.

It always makes my heart sink when I talk to developers who have discovered
a hidden bug that has been messing up their data for a long time.
Sometimes, there's just no way to unring the bell. It's horrible. And the
problem I raised about CALL WORKER and preemptive threads absolutely rises
to this level of risk. It's potentially a big deal.

There are lots of ways to deal with the common scenario that I've just
described. But one thing people don't always think through consciously is
how the responsibility is divided. <Skip discussion of Bertrand Meyer's
concept of programming by contract here.> Here is a call chain that
produces bad outcomes:

MyRoutine
    ....
    $final:=CalculateFinalTotal(100;0)
                  CalculateRate(0)

Who is to blame? Who is responsible for detecting the error? Well, the way
that I look at things, the CalculateRate routine is responsible for
*detecting bad inputs*. But the fault lies in "MyRoutine", the one that set
the bad value. It's MyRoutine's job to pass CalculateFinalTotal a good rate
to pass along to CalculateRate. It is CalculateRate's responsibility to
detect the bad input and raise or set an exception. (I use an error stack.)
That's a pretty clear and consistent division of responsibility, at least
in my mind and my code. Breaking it down:

MyRoutine: Responsible for handing in a valid input
CalculateRate: Responsible for detecting the bad input and reporting on it
CalculateFinalTotal: Responsible for checking that no error was generated
by the call to CalculateRate

Put another way:

CalculateRate holds CalculateFinalTotal for passing in good input.
CalculateFinalTotal holds CalculateRate responsible for detecting bad input
If you think things through this way it makes it easier to do good input
validation without endlessly rechecking inputs. In the example above,
CalculateFinalTotal doesn't check the range of input values coming from
MyRoutine. CalculateFinalTotal doesn't know the legal range, CalculateRate
does. So, no redundant (and potentially inconsistent) rules. But the
detection happens *near to the code that manages the input.* But I digress
a bit.

Short story: You get a lot more reliable code this way and it's easier to
think about.
Okay, so how to do that? There are lots of ways, depending on your
situation, requirements, and style. 4D provides the ASSERT command. You can
write your own input validation routines (that's what I do), you can
compile with range checking code injected by the 4D compiler (I do -
although I consider a range error that shows up in runtime as a substantial
flaw on my part), and you can add an ON ERR CALL handler for big exceptions.

CALL WORKER("Preemptive_Worker";"MethodWithCooperativeCode")

It's an input:

     "MethodWithCooperativeCode"

There's no way for us to validate it. ON ERR CALL doesn't work reliably.
The worker may be killed and restarted. You may end up with a runtime error
dialog blocking everything _even_ with an ON ERR CALL running. 4D may start
more and more instances of the flawed worker *with identical names* until
4D crashes or you force it to quit. I've seen all of that and, mostly, I
wasn't looking for any of it.

Now, I've already heard "Don't do that!", as noted. That's utterly
pointless advice. Who makes mistakes on purpose? Apart from due diligence
research, my mistakes are unintentional. As I sometimes say at this
juncture, if I'm of median intelligence and skill in the 4D developer
community and I see something as easy to screw up, then at least half of
all developers will also find it so. If I'm one tick above median, that
means the majority of developers will potentially run into problems. In the
real world, I might make a mistake and other people also might make
mistakes. Doesn't it seem reasonable to have a proper, consistent exception
handler? Yes it does.

Provide developer control when a preemptive worker receives non-preemptive
code
http://forums.4d.fr/Post//19391591/1/

P.S. The other approach to risky features is to say "Don't use it, it's not
worth the trouble." That seems like a pointless shame in this case. But
without reliability, I don't care at all about speed.
**********************************************************************
4D Internet Users Group (4D iNUG)
FAQ:  http://lists.4d.com/faqnug.html
Archive:  http://lists.4d.com/archives.html
Options: http://lists.4d.com/mailman/options/4d_tech
Unsub:  mailto:4d_tech-unsubscr...@lists.4d.com
**********************************************************************

Reply via email to