From: Maciej Sobczak <no.spam@no.spam.com>
Subject: Controlled types and exception safety
Date: Wed, 30 Nov 2005 14:57:07 +0100
Date: 2005-11-30T14:57:07+01:00 [thread overview]
Message-ID: <dmkb3j$h4q$1@sunnews.cern.ch> (raw)
Hi,
I try to understad how the Controlled types work and I spotted one
"small issue" that makes it difficult to write exception-safe code.
The "exception-safe" means that code behaves "correctly" in the presence
of exceptions, for some chosen definition of "correctly".
In C++ we define the following levels of exception-safety:
- level 0 (no guarantee) - in the presence of exception, anything can
happen, memory may become corrupted, data structures may become
completely mangled, etc.
- level 1 (basic guarantee) - in the presence of exception, no resources
are leaked and objects are in a coherent, but not necessarily
predictable state.
- level 2 (strong guarantee) - in the presence of exception, the program
state (to the relevant extent) remains unchanged. This is similar to the
commit-or-rollback semantics known from databases.
- level 3 (nothrow guarantee) - the code simply guarantees that there
are no exceptions.
Why is this classification useful? Let's say that I have an abstract
data type that implements some data structure - a stack, for example. I
can classify the stack's operations by assigning them any of the above
four levels, so that I know what can be expected when an exception is
thrown for any reason (like inability to allocate more memory, or
alike). For example, if the Push method of the stack gives me the strong
guarantee (level 2 above), then I *know* that by calling this method
either the new element will be appended to the stack, or the stack will
remain unchanged, so that even if the exception is thrown, I don't have
to worry about the stack's internal consistency.
This is useful.
This is useful also in assignment operations. Since stack can be a
dynamic data structure, assigning one stack object to another may
involve destroying one existing data structure *and* creating a new one
(a copy) in its place. Similarly, the quality implementation should
provide the strong guarantee, so that I *know* that either the stack was
properly copied, or there was a problem during assignment and an
exception was thrown, but nothing changed in any of the objects involved.
Let's say that I want to write a stack in Ada. Making it a Controlled
type seems to be a good idea, so that we have hooks for initialization,
adjusting and finalization. Let's say that I have two stack objects, X
and Y:
X, Y : Stack;
These objects were populated with some data, so that each of them
manages its own internal dynamic data structure.
Now, I do this:
X := Y;
and the following happens (this is what I understand, please correct me
if I'm wrong):
1. X is finalized. This allows me to clean up (free) its internal data.
2. Y is *shallow-copied* to X, so that in effect X and Y share their state.
3. X is adjusted. This allows me to duplicate its internal structure so
that it becomes independent from Y.
later:
4. Both X and Y are finalized. This allows me to clean up (free) their
resources.
For everything to work correctly it's important that two separate stack
objects *never* share their internal dynamic data structure, otherwise
bad things can happen. It would be also fine not to leak memory.
Now, the interesting part: let's say that during adjustment (3.) some
error happened (like low memory condition or whatever) that resulted in
raising an exception (note: this exception might be actually raisen not
by the stack code, but by the assignment operation of the stack
elements, even somewhere in the middle of this process).
Bad things will happen in subsequent finalization of those objects,
unless I handle it by cleaning up everything that I already managed to
duplicate (but still, this leaves me with the empty stack).
I think that the inherent problem comes from the fact that the
finalization of X was forced *before* its adjustment.
The canonical C++ way is to *first* make a copy of new value (because
this is when errors might occur, so that even if they occur, there was
no change in the destination object) and *then* inject the duplicate
into the destination object, getting rid of its old state (and this is
assumed to be nothrow).
The "Ada way" looks like selling the house *before* looking for the new one.
What do you do to avoid surprises?
--
Maciej Sobczak : http://www.msobczak.com/
Programming : http://www.msobczak.com/prog/
next reply other threads:[~2005-11-30 13:57 UTC|newest]
Thread overview: 37+ messages / expand[flat|nested] mbox.gz Atom feed top
2005-11-30 13:57 Maciej Sobczak [this message]
2005-11-30 15:06 ` Controlled types and exception safety Dmitry A. Kazakov
2005-11-30 16:19 ` Maciej Sobczak
2005-12-01 0:05 ` Stephen Leake
2005-12-01 9:21 ` Dmitry A. Kazakov
2005-12-01 10:46 ` Maciej Sobczak
2005-12-01 15:08 ` Dmitry A. Kazakov
2005-12-02 4:17 ` Randy Brukardt
2005-12-02 9:29 ` Maciej Sobczak
2005-12-02 18:12 ` tmoran
2005-12-02 19:15 ` Robert A Duff
2005-12-02 21:42 ` tmoran
2005-12-06 9:00 ` Maciej Sobczak
2005-12-06 9:50 ` Dmitry A. Kazakov
2005-12-06 18:34 ` Jeffrey R. Carter
2005-12-06 19:34 ` Randy Brukardt
2005-12-06 21:20 ` Dmitry A. Kazakov
2005-12-07 1:57 ` Jeffrey R. Carter
2005-12-08 0:50 ` Randy Brukardt
2005-12-08 19:37 ` Jeffrey R. Carter
2005-12-09 2:36 ` Randy Brukardt
2005-12-09 6:33 ` Jeffrey R. Carter
2005-12-09 20:35 ` Randy Brukardt
2005-12-10 7:53 ` Jeffrey R. Carter
2005-12-06 20:43 ` Dmitry A. Kazakov
2005-12-07 2:00 ` Jeffrey R. Carter
2005-12-07 10:01 ` Dmitry A. Kazakov
2005-12-02 23:21 ` Robert A Duff
2005-11-30 17:46 ` Jean-Pierre Rosen
2005-11-30 21:02 ` Jeffrey R. Carter
2005-11-30 22:06 ` Björn Persson
2005-11-30 23:52 ` Randy Brukardt
2005-12-01 5:26 ` Jeffrey R. Carter
2005-12-02 23:51 ` Robert A Duff
2005-12-06 11:41 ` Peter C. Chapin
2005-12-06 12:50 ` Jean-Pierre Rosen
2005-12-06 13:06 ` Dmitry A. Kazakov
replies disabled
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox