Re: Question for Randy or Frank
- From: "Beth" <BethStone21@xxxxxxxxxxxxxxxxxxxxxxx>
- Date: Thu, 02 Jun 2005 10:04:41 GMT
Frankie say:
> Randy wrote:
> ...
> > With a single statement at the beginning of your program, specifically:
> >
> > ?@noframe := false;
> >
> > You can turn off the automatic code generation throughout the source
> > file, so you don't have to repeat the "@noframe" option on each
> > procedure you write.).
>
> That's either a "posto", or an example of how fiendishly
> unintuitive HLA is. I'd expect I'd want "true" there... (or
> "?@frame := false;")
It's a "posto"...
[ Or, at least, whenever I tried the "@noframe" thing to create one of
those "minimal HLA program" examples, it worked in the "intuitive" way that
setting "@noframe" to "true" was the way to get no stack frame...which
could be a case of a "two negatives make a positive" that Randy made an
"unintuitive" mistake twice (he intended it "the wrong way around" but then
accidentally implemented it "the wrong way around", turning it back around
to "the right way around" ;)...but it's far, far, far more likely that,
indeed, it's just a small "posto" here in this one posting ;) ]
> > > > Do I understand correctly that Windows "raise"s an exception
> > > > on, say, "file not found", but in Linux I have to "raise" it
> > > > myself (done in the library, of course)?
> > >
> > > Nope; It's about the same..."file not found" does not "raise"
> > > an exception in Windows...
> >
> > Correct. This is an HLA standard library facility.
> > HLA also raises exceptions for things like string overflow and what-not
> > that you won't get under Windows.
>
> I was misled by seeing in "excepts/raise.hla" that it was
> "only for Linux"... I see that the stdlib routines do
> "raise" exceptions for stuff like an open failure in the
> Windows routines, too. Done in another file, perhaps... or
> Windows has an API for it?...
I haven't looked at the files specifically, so this is just a "guess"...
But the need for the "excepts/raise.hla" file, which is "Linux only", is
probably more to do with the fact that Windows and Linux work differently
in how they approach this "exception handling" stuff...as Randy has
mentioned, he basically adopted Windows' SEH "model" and then just made
Linux's "signals" fit into that kind of scheme...
Hence, my "guess" here is that what "excepts/raise.hla" does is provide the
code which makes the Linux version "emulate" the Windows' "model" via the
"signals" mechanism...the need for having this extra "Linux only" file is
because Randy chose to "bend" Linux to make it behave in the "Windows way"
and this is the "extra code" needed to do that...
Anyway, the OS itself (either Windows or Linux) doesn't generate any of
these "file not found" exceptions...these are extra "private use"
exceptions that Randy has invented for his "standard library"...
There are two basic ways to deal with "errors" in the library routines...by
providing a "return code" which signals errors (either from the function
itself or in some "errno" global variable or similar device...the "errno
way", we can call this generically :)...and then it's the "responsibility"
of the well-written program to "check" this "error code" after making calls
to see if something went wrong (and react accordingly)...
The inherent "problems" with this "errno" method, though, is that:
1. The programmer mustn't forget (or get too lazy to bother) to "check" the
"error codes"...or, you know, we end up with those "compounded" problems:
You call an "open" function, which returns a NULL "handle"...despite this,
the program doesn't bother to check and then tries "writing to the file"
who's "file handle" is NULL...this causes another "error" to happen...but,
again, the program doesn't bother to check and carries on...and a whole
series of "errors" can be triggered...the program not bothering to "check"
for any of them...then, when "testing", things go catastrophically
wrong...but the programmer doesn't really have the slightest clue
_where_...
2. Lots of "extra code" is added to the program to put in all these
"checks" after every function / API call made...you know, the "call
SomeAPI; cmp eax, NULL; je ErrorRoutine" sequence, every time you make any
function call (and the "ErrorRoutine" there could be simple or it could be
complex...depends if there's just one simple "general error" that could
result or a whole load of possibilities and some big "switch" to do
different things for each possible error)...it's a small amount of
"overhead", true, but, as we'll see, the "exception handling" approach
actually doesn't require it in the same way...
Then there's the "exception handling" approach to errors (which the HLA
standard library seems to prefer :)...OSes like Windows and Linux, of
course, already have some support for "exceptions" because the CPU itself
can "throw" these (e.g. "page fault", "general protection fault", "divide
by zero", etc. :)...BUT, in making this basic "system" generic enough (as
the two OSes do), then it's also possible to extend the same "mechanism"
for other purposes like "errors"...
Basically - taking Windows here for the example (but the Linux approach is
"similar-ish" in concept, if slightly different in actual implemenation) -
when the CPU "throws" an actual hardware "exception", this generates an
interrupt...the OS "catches" that (it has an interrupt routine "installed"
in the right place to "catch" these exceptions)...some of these
"exceptions" are, of course, delibrate and the system uses them directly
(for example, "virtual memory" is implemented by _delibrately_ causing
"page faults" and then swapping pages in / out of RAM to / from the disk,
as needed :)...an application never actually ever "sees" these ones because
they are dealt with internally by the system and never "passed on"...
BUT, for those execeptions that aren't part of the system's own
operations - actually "application errors" - the system has a "mechanism"
where applications "register" a callback routine (the "exception
handler")...when something goes wrong like this - "divide by zero" - then
the "callback" routine is executed by the OS...the routine should, of
course, check what "exception" it's received and do whatever is necessary
to sort the problem out...
We need such a system, anyway, to sensibly deal with the hardware
exceptions that the CPU itself can generate...a "system" by which to
"notify" the application of these things (in DOS, it was slightly different
because access to "interrupts" and such was direct and not prohibited...so,
you'd manually just "hook" the interrupt you were interested in...of
course, with Windows and Linux, they are slightly different and don't let
you go around setting up or "hooking" interrupts willy-nilly as you feel
like...hence, instead, they specifically have to provide this "support" - a
means to "pass on" these exceptions - for the application or it wouldn't be
able to do anything about them at all :)...
And the little "trick" here, is that the "support" is made "generic"...in
Windows, the "code" supplied to identify the "exception" is 32-bits (there
aren't that many CPU exceptions, of course...it has delibrately been made
larger, so that a few "software exceptions" can be "invented" too
:)...also, Windows allows "parameters" to be sent with an exception...of
the CPU exceptions, only "access violation" supplies "parameters", which is
basically the address that triggered the "access violation"...but the
"exception handling system" actually allows up to some 15 "parameters"
(actually, this is defined via a "constant" in the header files, so I
suppose it's delibrately designed that they can change it "in the
future"...but it's set to 15 in the header files I was looking at, which
are Windows 95 "era" header files...they might already have upped the
"maximum" since then; I've not specifically checked :)...no CPU exception
actually provides that many "parameters"...again, the whole purpose here is
to make the system a bit more "general" that it can also be used - as Randy
uses it for his library - to add in some "software exceptions"...
And what Randy has done is basically grab some of the "private use"
exception codes and nominally call them things like "file not found
exception", "string overflow exception" and so forth (being "private use",
they _ONLY_ mean these things in the context of Randy's HLA library...for
some other library, they could mean something completely different)...then,
when one of the HLA functions detects an "error", it "raises" / "throws"
the particular "exception"...
This is the alternative method to the "errno" error code method...this
time, the program doesn't need to "check" anything...it can call the
library functions as it likes...but it can, instead, register an "exception
handler"...if something goes wrong, then the HLA library "raises" the
exception, this makes the OS run the registered "exception handler"...this
"handler" then has the responsibility to "sort out" the problem...
The advantages of this approach are:
1. The programmer does not need to continually "check" some "error
code"...when an "error" is detected, the "exception handler" is executed
instead...rather than just setting "errno", returning and then Hoping that
the programmer "feels responsible" enough to bother checking it...
Obviously, in the case of HLA, this is a very good strategy because, as a
"teaching language", this is the more "tolerant" method...you know,
beginners can't be bothered / don't realise / don't think of "checking for
error codes"...their programs, thus, carry on and potentially create that
"series of errors" situation (trying to "carry on" and write to a file with
a "NULL" handle...blindly "reading" from a non-existent file and then, not
realising this, carrying on to try to "process" what didn't, in fact, come
from any file because that "open file" error ages back never
succeeded...not that the programmer bothered to check for this at any stage
;)...
2. No need for that "extra code"; If things go wrong, then it just "jumps"
over to the "exception handler"...that said, mind you, the "if (errno ==
3)" statements end up being replaced by "try...catch...endtry" blocks
instead, in practice...the "try" stuff, by the way, just simply "registers"
a new "exception handler" for that particular block of code (if an
"exception" is thrown inside the "try" block, then the "catch" code of that
block gets "first look" at the exception to try to sort it out...in this
way, you can be a bit more "selective" in how you deal with exceptions,
rather than solely "one big exception handler" wrapping the entire program
that deals with everything :)...
3. These "exception handlers" (in the "structured exception handling"
scheme, which is what the "structured" adjective is referring to, really ;)
can be "chained"...hence, Windows itself has a "system exception
handler"...this is the routine that does the usual stuff we're all familiar
with...shows the "illegal operation" dialogue box and shuts down the
program (by "convention" and "standard", the default action OSes and stuff
like the C++ library do is simply "Abort! Abort! Abort!"...it's deemed
potentially "too dangerous" to attempt anything else (could cause all
manner of problems from "deadlock" to "overwriting important files" and
such)...you know, to react to an "exception" sensibly, the code needs some
"understanding" and "intelligence" about what the program is doing...the OS
can't actually know this - can't know that there's some "buffer" in the
program that it would be wise to "save" before exiting - so takes the
"safe" route of just aborting the program...it's good that this is the
"convention" and "standard" because Microsoft do, of course, have this
tendency to think that they must always "do something" in order to "be
helpful" - witness "Clippy" and "don't show this patronising dialogue box
ever again" and other nightmares you'd wish Microsoft would STOP being so
damned "helpful" about - as they are basically insane "control freaks" with
little grasp on what's actually "helpful"...the best help, as the
"standards" and "conventions" correctly identify, is to "*** out"...if the
program has "important data to save" then it can install its own "exception
handler" that does so! Provide the "support" for that, yes, but please
leave the "fascist attitudes" of controlling everything and everyone to
"conform" to Microsoft's twisted view of what's "good" at the door, thank
you very much ;)...
Anyway, Windows supplies a "system-wide default" for "exceptions", so that
if the application does nothing to register any "exception handler", then
it's this "system exception handler" that gets control...this displays the
"illegal operation" crap (or, worse, if "internal" and highly serious
kernel problems, then the dreaded BSOD appears instead!)...
An application can also register its own "exception handler" and this gets
"first go" (though, it is allowed to "pass it on" to the next handler up
the "chain" and effectively pass it to the "system handler" and get the
"illegal operation" box, if it wants to do that :)...also, in Windows, it
has two basic types of "exception handler"...there's a "global" one (you
can only have one of these installed: This is the "last chance" exception
handler, really)...and then there are "per thread" exception handlers
(which may be "chained": Basically, the latest "handler" to be registered
is sent the exception first...but it may choose to not deal with it and
"throw" it again, then the next exception handler up the "chain" gets a
chance...again, it can "throw" it, if it's an exception it's not interested
in...and so forth...the "global" exception handler gets it, if everything
else "throws" it...and if even that one "throws" it too, then it ends up
with the Windows "system" handler, which does not ignore or "pass on" any
exception (the literal system "catch all" handler) but displays "illegal
operation" and shuts the thing down)...
Windows actually uses the FS register to point to a "per thread information
block"...in that block is a pointer to the currently installed "exception
handler" and also another pointer to the "next handler in the chain" (so
that these handlers may be "chained" through an effective "linked list" of
these "next handler" pointers :)...Windows breaks the strict "flat mode"
for the FS register in order to provide this "per thread" stuff (in a
strict "flat mode", of course, all the segment registers would be identical
and point to the same "flat" 4GB address space)...
Anyway, in languages like C++ and HLA, this is "simplified" using the
"try...catch...endtry" block...these are just "macros" (in HLA's case,
anyway ;) that do all the "jiggery-pokery" with the FS register to
"install" a new "handler" in the "chain"...which is fairly easy stuff:
Create one of these "blocks" on the stack, which has the address of the
exception handler and also the address of the "next handler in the
chain"...point FS at this...due to each one of these handlers also having
this "next handler in the chain" pointer, this creates a basic "linked
list"...when exceptions are "thrown", Windows simply follows the "linked
list" until it finds a "handler" that's actually willing to "handle" the
exception...
The basic concept of this "chaining" scheme, of course, is to allow a
program to be "localised" in how it deals with errors...yet, what it
doesn't deal with can be "deferred" to a slightly "less local"
handler...and so forth...all the way up to the "global handler", which is
the "last chance" to deal with errors...because if it doesn't deal with it,
then it goes to Windows "system handler" and we all know what happens if
Windows gets it: "Illegal operation!", it screams and then shuts down the
program there and then, in a slightly "ungraceful" manner (the "Abort!
Abort! Abort!" convention :)...
This is the basic idea...and now you might see where my comment that,
depressingly, most programs don't even bother with "exception handling"
comes from (in saying that LuxAsm actually _bothering_ to handle its
exceptions would actually be _MORE_ than most programs often bother to do
that to criticise LuxAsm when it'll be _TRYING_ to deal with errors...while
most other programs out there do _NOTHING_ and rely only on Windows'
"illegal operation!" dialogue box...well, you might see my
frustation...when you try, you get criticised...but when you don't bother
at all, no-one says anything...a distinctly "unfair" criticism, yes?
;)...we can tell that this is the case because at the slightest "problem",
up comes the "illegal operation!" box and the program disappears...rather
unhelpfully...
Of course, we can't be totally sure that the programs are 100% "doing
nothing" because it could be that they "caught" the exception, saved
"important data" and then just passed it onto the Windows "system handler"
in order to provide "familiarity"...because, after all, using Windows, who
isn't "familiar" with the crash dialogue boxes and BSOD? Though, a "nice"
option here is that a program could replace this with its own "custom"
dialogue box with something more "friendly" like:
"Oh, crap...sorry about this...but the program has, like, totally screwed
it all up! I'm afraid it'll now have to shut down...but, don't worry, we've
saved the 'important data' already...when you try reloading the program,
you'll find that the file 'error.dat' can be loaded and you _SHOULD_ be
able to carry on from where you were before...Hopefully...anyway, a
thousand apologies for the screw up there...don't forget to send a 'bug
report' about this and we'll see if we can fix up the program so it doesn't
do this crap on you ever again"...
But, as "time to market" is so important and "developer time" is so
precious, the first thing to go is adding in "exception handling"...that
is, if they even know it's there and how it works (finding details on it
was not particularly easy...thanks to Jeremy Gordon for taking the time and
effort to find out and writing an excellent article on it! :)...
Anyway, the basic story is that Randy decided that the "exception handling"
way was the best way to deal with errors for the "HLA standard
library"...which, indeed, certainly makes a lot of sense, anyway...but even
more so for "beginners", as the "exception handling" way flags up the
errors immediately at the point that they actually happen...much more
useful to help "beginners" with debugging and diagnosis of their
programs...
And, by the way, you know that small amount of code that HLA "inserts" at
the start of programs? It's solely a few lines to set up the "default
exception handler" for the HLA standard library...so that the "exception
handler" which actually understands the "private use" exceptions Randy's
come up with - "file not found", "string overflow", etc. - are reported
properly if and when things go wrong...you obviously don't need such code
if you're not using the HLA library...or you could alternatively set up
your own "exception handler" to deal with it "your way" instead...
Though, Randy, did you ever consider doing it the way C compilers tend to
do it? Instead of the HLA compiler directly inserting it into the code, you
could have an object file with the "startup code" already compiled and then
you just "link in" the "startup code"...this, of course, being "more
flexible" (e.g. when your HLA compiler directly inserts it into the program
code, it's kind of "hard-wired" to just your HLA library...the "C way" of
doing it, of course, is designed so that "alternative" libraries could be
"linked in" instead)...although, it's probably not a big issue...who but me
even cares about this? I bet no-one else has even mentioned it, as your
main "audience" are beginners who aren't too interested in messing around
with "startup code"...but, perhaps, for HLA v2.0, you could take this
approach, as a "ready for possible future expansion" thing...wouldn't
really change how most people use HLA but "leaves the door open" for
possible "alternative libraries" (or different "startup code" that, like
the C startup code, "parses command-lines" or something ;) and other such
things...
> ...
> > Actually, all HLA programs have a default exception handler surrounding
> > the whole main program, so HLA exceptions never get to that level
> > (unless you've managed to fry the system).
>
> Or cause "SIGBUS" to be generated...
A "bus error" would, I think, qualify as a "Kentucky Fried System" kind of
error...
Though, it is interesting: Doesn't the HLA default exception handler catch
"SIGBUS", Randy? Obviously, there's nothing much you can do about it...but
it could be useful to print out a similar "friendly error" as the rest of
the exceptions...indeed, the words "bus error" sound incredibly "drastic"
to me...inciting "panic" that your motherboard has melted or something...a
gentle "assurance" that more "mild" reasons can generate it in a "friendly
message" might be useful there, to avoid a "War of the Worlds" style riot
exploding on the streets ;)...
Beth :)
---
"The second pillar of peace and security in our world is the willingness of
free nations, when the last resort arrives, to retain aggression and evil
by force."
[ George Walker Bush Jnr. ]
.
- Follow-Ups:
- Re: Question for Randy or Frank
- From: Frank Kotler
- Re: Question for Randy or Frank
- From: randyhyde
- Re: Question for Randy or Frank
- Prev by Date: Re: Betov finally admits: Rosasm is NOT a real assembler.....
- Next by Date: Re: Question for Randy or Frank
- Previous by thread: Re: Question for Randy or Frank
- Next by thread: Re: Question for Randy or Frank
- Index(es):