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 **********************************************************************