[comp.lang.modula3] running out of memory

doug@snitor.uucp (Doug Moen) (02/09/91)

In article <9102080401.AA29237@jumbo.pa.dec.com> gnelson (Greg Nelson) writes:
>    2) The NEW expression's behaviour is not defined in the case that
>    not enough memory is left.  Is this a checked runtime error, or
>    does it return NIL?  If it returns NIL, then it isn't returning
>    a "new" value.  If it is a checked runtime error, how do you
>    allocate all available memory without crashing?
>
>The report should have defined this to be a checked runtime error.
>If you want to allocate all available memory without crashing, you
>have to use a lower-level, implementation-dependent interface.

I strongly disagree.  The report should have defined this to raise
an exception.

There are many programs for which crashing on memory exhaustion
is unacceptable behaviour.  Examples include: text editors, shells,
window managers, daemons providing essential system services,
and just about any interactive program.  In order to write any of
these programs in Modula-3, it is necessary to:
 - use a low-level, implementation-dependent interface for allocating memory
   (which means the program isn't portable)
 - avoid using any library module that calls NEW.  This means avoiding
   most standard modules, including Text and Thread.
These are very serious restrictions, which lead me to believe that
Modula-3 is simply not suitable for writing interactive programs
or daemons that are required to be robust.

The text editor I use is written in C, and does not crash when it runs
out of memory or temp file space.  Instead, it displays an error message,
and permits me to save my changes before exiting.  This robustness has
saved my bacon on at least one occasion.

Last year, I wrote an image editor using Motif & the X Toolkit.  This
editor has prodigious memory requirements, since the images we deal with
can be quite large.  The X Toolkit aborts your program if it runs
out of memory.  As a result, it is possible to crash my editor simply
by opening a large number of windows.  Of course, when the editor crashes,
you lose all of your work.  This has actually happened to users of my
program.  I find this most upsetting, since to fix the bug, I would have
to reimplement Motif and the X Toolkit.

The preface to the Modula-3 report says:
  "The goal of Modula-3 is to be as simple and safe as it can be
   while meeting the needs of modern systems programmers."
Modula-3 can't meet these goals until NEW has been redefined
to raise an exception on memory exhaustion.

emery@aries.mitre.org (David Emery) (02/11/91)

Ada provides an exception (STORAGE_ERROR) that is raised when the
allocator cannot return any memory.  I have likened this to a
parachute that opens on impact, in that it meets its absolute
requirements but is unusable in most practical situations.  It is not
enough to have an exception, you also have to define what can be done
to handle the exception.  In the case of Ada, STORAGE_ERROR can be
raised in (at least) 2 circumstances:
	1.  out of heap
	2.  out of stack
There is no good way to tell the difference between these two
occurances.  Furthermore, if you've run out of stack, you probably
can't do anything at this point, because anything interesting (such as
calling a cleanup routine) would require more stack space.  

My point here is that if you want to be able to recover from storage
exhaustion, you need more than notification.  You also need some
understanding of what is permissible and not permissible after the
exception has been raised.  Finally, you'd like very much to have this
defined in such a way that your program is portable (i.e.  maybe the
language should define a minimum permissible set of
operations/actions, which can be extended by an implementation.)

				dave emery

rminnich@super.ORG (Ronald G Minnich) (02/11/91)

In article <jr5onv9pm@snitor.uucp> doug@snitor.uucp (Doug Moen) writes:
>In article <9102080401.AA29237@jumbo.pa.dec.com> gnelson (Greg Nelson) writes:
>> ... <discussion of memory allocation>
>>The report should have defined this to be a checked runtime error.
>>If you want to allocate all available memory without crashing, you
>>have to use a lower-level, implementation-dependent interface.
>I strongly disagree.  The report should have defined this to raise
>an exception.

It is worth remembering that 13 or so years ago the choice was
between a real low-level language (C) and a nicer language (from many
points of view) Pascal. It is amazing how similar the arguments
are back then and now between, e.g., Mod 3 and C++. Some of them
are identical (initialization, for example)! 

Problem was, Pascal tended to blow your
program out of the water on failed file opens, failed memory allocs, 
and so on. While Pascal was nice from many points of view, its 
unrealistic model for programming (e.g. if you can't alloc, die)
rendered it useless for most people, and, sad to say, C won.

I hope the same mistakes are not repeated in Modula 3. Failed memory
allocation should be an exception. Just about anything that can go
wrong should be able to be handled by the program.
ron
-- 
"Socialism is the road from capitalism to communism, but we never promised to 
                 feed you on the way!"-- old Russian saying
"Socialism is the torturous road from capitalism to 
                  capitalism" -- new Russian saying (Wash. Post 9/16)

gnelson (Greg Nelson) (02/12/91)

Doug Moen proposes that Modula-3 define an exception to be raised by
NEW when storage is exhausted, arguing that this is necessary in order
to write robust programs, such as a text editor that saves edits when
storage is exhausted.

Eliot Moss and Mick Jordan point out that if this approach is used
to provide robust programs, it should probably be extended to many
other checked runtime errors, perhaps to all of them.

With this approach, the main body of, say, a robust text-editor would
use a TRY-EXCEPT:

TRY
  <main body of text editor>
EXCEPT
  RunTimeError(ErrCode) =>
    Wr.PrintText(Stdio.stderr, CodeToMessage(errCode));
    Wr.PrintText(Stdio.stderr, "Saving edits...");
    <save edits>
END

Of course, as Dave Emery points out, <save edits> has to be coded very
carefully (for example, it must not allocate any storage).  In fact,
Wr.PrintText might allocate storage, so the TRY-EXCEPT-ELSE code above
is useless as it stands.  But we can imagine providing a very robust
library procedure that prints a TEXT on Stdio.stderr without allocating
any storage or depending on anything except the very lowest levels
of the runtime system, and use it instead of Wr.PrintText.

A more serious problem with the approach is that Modula-3 programs
are multi-threaded.  The TRY--EXCEPT--END construct above has to be
wrapped around every thread, not just the main thread, since storage
exhaustion could happen anywhere in the program.  

The programmer writing the text editor can wrap the TRY--EXCEPT--END
around all the threads forked directly by his program, although he
might complain that this is awkward.  But even this is not enough,
since library packages fork background worker threads.  For example,
the Trestle window system forks two threads to buffer the queue of
events from the X server.  The Trestle implementation can hardly be
expected to wrap the TRY--EXCEPT--END code around the threads that
it forks, since the action <save edits> is entirely foreign to Trestle.

The solution to these problems is to provide an interface through which
clients can register procedures to be executed when a program aborts
due to a checked runtime error.  A text editor could register a
procedure that saves the edits.  An elevator control program could
provide a procedure that stops the elevator by the most primitive
mechanism available.  A program development environment could enter
the debugger.  The design of this interface is quite system-dependent,
as these examples suggest, but it should not be difficult to provide
a single interface that works in, say, all standard Unix environments.
SRC Modula-3 does not yet provide such an interface, but Eric Muller
tells me that one is on the way.

Greg Nelson

muller@src.dec.com (Eric Muller) (02/12/91)

In article <9102120059.AA15796@jumbo.pa.dec.com>, gnelson (Greg Nelson) writes:

> Of course, as Dave Emery points out, <save edits> has to be coded very
> carefully (for example, it must not allocate any storage).  In fact,
> Wr.PrintText might allocate storage, so the TRY-EXCEPT-ELSE code above
> is useless as it stands.  But we can imagine providing a very robust
> library procedure that prints a TEXT on Stdio.stderr without allocating
> any storage or depending on anything except the very lowest levels
> of the runtime system, and use it instead of Wr.PrintText.

The SmallIO interface in the core library will provide that functionality
in SRC Modula-3 1.6.

> The design of this interface is quite system-dependent,
> as these examples suggest, but it should not be difficult to provide
> a single interface that works in, say, all standard Unix environments.
> SRC Modula-3 does not yet provide such an interface, but Eric Muller
> tells me that one is on the way.

New in RTMisc.i3:

TYPE Exitor <: REFANY;

PROCEDURE RegisterExitor (p: PROCEDURE (n: INTEGER)): Exitor;
(* Registers the procedure p to be executed when Exit is called or
   when a checked runtime error is dectected; it is passed
   the argument of Exit, -1 for checked runtime errors.
   The registered procedures are executed in the reverse order of
   registration. *)

PROCEDURE UnregisterExitor (e: Exitor);
(* removes e's procedure from the registered set. *)

PROCEDURE Exit (n: INTEGER);
(* call the registered exitors and terminate the program with status 'n' *)

-- 
Eric.

weikart@prl.dec.com (Chris Weikart) (02/12/91)

In article <42601@super.ORG> rminnich@super.ORG (Ronald G Minnich) writes:

> It is worth remembering that 13 or so years ago the choice was
> between a real low-level language (C) and a nicer language (from many
> points of view) Pascal. It is amazing how similar the arguments
> are back then and now between, e.g., Mod 3 and C++. Some of them
> are identical (initialization, for example)! 
>
> Problem was, Pascal tended to blow your
> program out of the water on failed file opens, failed memory allocs, 
> and so on. While Pascal was nice from many points of view, its 
> unrealistic model for programming (e.g. if you can't alloc, die)
> rendered it useless for most people, and, sad to say, C won.

It wasn't so simple. Pascal "lost" partly because of various rigidities,
but mostly because of U*ix.

Mike_Spreitzer.PARC@xerox.com (02/13/91)

Installing global failure-handlers will solve some problems, but how does it
solve the following?

Imagine a bitmap editor program.  It allows the user to open multiple windows
on bitmaps from multiple files.  At one point, with some windows already open,
a user asks to open a new window on the 23 megabyte contents of a particular
file.  Wouldn't it be nice if the editor could say something like
	"I'm sorry, I can't now edit a new bitmap that big",
rather than
	"Memory exhausted, saving all files and exiting..."
?  If we can only register procedures to do a little work before the editor
aborts, I don't see a good way to get the desired behavior.

Mike

macrakis@gr.osf.org (Stavros Macrakis) (02/13/91)

In article <9102120059.AA15796@jumbo.pa.dec.com> gnelson (Greg Nelson) writes:

   Doug Moen proposes that Modula-3 define an exception to be raised
   by NEW when storage is exhausted....  Of course, as Dave Emery
   points out, [the exception handler] has to be coded very
   carefully....  [Discussion of multithreading problems...]

   The solution to these problems is to provide an interface through which
   clients can register procedures to be executed when a program aborts
   due to a checked runtime error....

There are many cases where running out of memory is not a fatal error
which should give rise only to cleanup activities.  In particular, in
interactive systems, the execution of a given command may exhaust
memory, and thus have to be aborted, although the overall interactive
environment should recover gracefully.

An even simpler case: some network protocols include requests to
allocate buffers of given size.  If the buffer allocation fails, you
should send a NACK to the requester, not die.

	-s

Stavros Macrakis
Open Software Foundation Research Institute

Mail:  2 av de Vignate, 38610 Gieres (Grenoble), France
Net:   macrakis@gr.osf.org
Phone: +33/76.63.48.82  Fax: +33/76.51.05.32