Topic: Bug in STL specifications.


Author: fjh@mundook.cs.mu.OZ.AU (Fergus Henderson)
Date: 1997/06/15
Raw View
Branko Cibej <branko.cibej@hermes.si> writes:

>Bradd W. Szonye wrote:
>
>> Actually, there is a way, in the standard, to express the idea of
>> "changes
>> in stages": atomicity. There is a type (in <cstdlib>) "sig_atomic_t"
>> which
>> defines the largest integral type which the compiler can update
>> atomically.
>
>I believe you are mistaken. Signal handling is a Posix feature and is not
>part of standard C and therefore not part of (draft) standard C++.

No, Bradd W. Szonye was correct, except that the header file is
<csignal> or <signal.h>, not <cstdlib>.  Signal handling and sig_atomic_t
are both included in standard C.  (However, the signal handling features
in standard C are quite restrictive, and I believe Posix extends them.)

--
Fergus Henderson <fjh@cs.mu.oz.au>   |  "I have always known that the pursuit
WWW: <http://www.cs.mu.oz.au/~fjh>   |  of excellence is a lethal habit"
PGP: finger fjh@128.250.37.3         |     -- the last words of T. S. Garp.
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: danm@ziplink.net (Dan Muller)
Date: 1997/06/15
Raw View
In article <339D45D7.3031@acm.org>, greghunt@acm.org says...
> It might be worth pointing out that the range of synchronisation
> mechanisms provided in different evironments is huge and the trade offs
> are best NOT left to a compiler to evaluate.  It would be a source of
> tremendous stress if the C++ standard were to mandate synchronisation
> for certain types of memory.

Agreed, and I was not suggesting that. However, the latitude currently
allowed to optimizers by the language makes it very difficult, I think,
to implement the necessary synchronization tools without non-standard
compiler extensions. Some of the articles in this thread point to the
problem. I was only suggesting a minimal accommodation to the need for
explicitly reigning in optimizers, in order to be able to build the
necessary tools for any environment. I was not suggesting anything having
to do with memory synchronization; unfortunately, many people seem to be
misconstruing "volatile" as addressing synchronization. It does not. It
only restricts the assumptions that the compiler can make about a
program's synchronous behavior.

> I may be a little behind the times with standard C, but does volatile
> now imply atomic?
Not it does not, although some participants in this thread seem to have
that impression.

> Dan Muller wrote:
>
> > The volatile keyword is the only concession in C/C++ to asynchronous
> > program behavior. Rather than using volatile for data shared by multiple
> > threads, I think that accessing such data in non-inline routines that are
> > guarded from beginning to end would provide enough protection or
> > practical purposes. It is unlikely that compilers would optimize such
> > data accesses beyond the boundaries of the routine.
> >
> > Perhaps it would be nice if the language itself provided some way of
> > marking a block of code, rather than data, such that optimizations were
> > not permitted to render it unsafe for accessing shared data.

--
Dan Muller   danm@ziplink.net
http://www.ziplink.net/~danm
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: jpotter@falcon.lhup.edu (John Potter)
Date: 1997/06/16
Raw View
On 15 Jun 97 05:38:07 GMT, fjh@mundook.cs.mu.OZ.AU (Fergus Henderson)
wrote:
: jpotter@falcon.lhup.edu (John Potter) writes:
: >We have cv-unqualified types
: >and only those types have cv-qualified versions.

: The "only" in that sentence is not implied by the draft.

: The draft does not anywhere formally define the concept
: "cv-qualified version of a type".  Instead, it gives a sort
: of definition-by-example, and I guess you're supposed to infer
: the definition.  The wording could certainly be improved.

Hum, the classic problem
 implied A, infered B
 implied nothing, infered B
 implied A, infered nothing
and rarely
 implied C, infered C.
In this case, I took 3.9.3/1 to be a formal definition of cv-qualified
by exhaustively listing all possibilities, not just some examples.  By
3.9.3/4, int is less cv-qualified than int const, but that can not be
used to infer that int is a cv-qualification of int because it is
defined to be cv-unqualified and is not listed as one of the
cv-qualifications.  I am not sure that this is important to 3.10/15
anyway.

More importantly, the term "object type" includes the cv-qualification
and, I assume, the lack thereof.  From the beginning of 3.9.3/1 it
could be infered that the term "type" is the cv-unqualifed version or
any cv-(un)qualified version.

[ snip dynamic in 3.10/15 ]

: >This paragraph talks about access not
: >modification and the lvalue to rvalue conversion is an access except
: >when it is the arguement of sizeof, and maybe some others I missed.  I
: >make no claim to knowing the intent of this paragraph other than the
: >footnote which says that it defines how an object may be aliased.

It may be worth noting that it gives a list of accesses which are not
explicetly undefined behavior.  It does not say that these accesses
will have defined behavior, only that they are not certain to have
undefined behavior.  An example which clearly has undefined behavior,
because it is certainly not on the list:
 float f(1.);
 cout << reinterpret_cast<int&>(f);

: >It has been used to claim that const objects may not be modified, but
: >surely that is stated elsewhere.

: Yes, the prohibition on modifying const objects is stated elsewhere:
: in 7.1.5.1/4 and 3.8/8.

Yes, thank you, I found 7.1.5.1/4 but missed 3.8/9 (CD2 /9)

: >Consider
: > int const ci(5);
: > int i(const_cast<int&>(ci));
: >Undefined behavior because ci is accessed via an lvalue which is not a
: >cv-qualified version of its type.

: Correct.

Now, what can be infered from the use in 3.10/15 of the term "type"
and not the term "object type"?  Does "type of the object" imply
"object type of the object" which includes cv-qualification?  Is it
just poor wording which should be "object type"?  Is it purposely used
to indicate that cv-(un)qualified is intended?

If the latter, then the first four items on the list could be
interpreted as:
 If when the cv-qualifiers and signedness of both the lvalue and
 object are ignored, they have the same type, then the access is not
 explicetly undefined behavior.
All of these types have the same size, allignment, and value
representation making aliasing possible.

: > char c1(reinterpret_cast<char&>(const_cast<int&>(ci));
: > char c2(const_cast<char&>(reinterpret_cast<char const&>(ci));
: >No problem here since ci is accessed via an lvalue of type char which
: >is explicetly permitted.

Hum, just noticed that
 char const c3(reinterpret_cast<char const&>(ci));
may, strangely, not be excluded from undefined behavior.  Is "possibly
cv-qualified" missing from the seventh item by accident or because
"char type" implies any cv-(un)qualified version?

: >Now, if I replace "dynamic type of the object" which has no meaning
: >with "cv-unqualified type of the object", it looks like valid aliases
: >to me.  All of the undefined behavior examples above become defined.

: Hmm.  I wonder whether the equivalent examples have undefined behaviour in C.

Gee, the C standard _is_ listed as a recommended reference.  I guess
that I really can't get through this without it.  I was a bit short on
cash and thought that I would wait for the next edition which, I hear,
is in work :-)

I am betting that if you wonder, you will check, and may let us know.

John
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: fjh@mundook.cs.mu.OZ.AU (Fergus Henderson)
Date: 1997/06/16
Raw View
jpotter@falcon.lhup.edu (John Potter) writes:

>fjh@mundook.cs.mu.OZ.AU (Fergus Henderson) wrote:
>: Hmm. I wonder whether the equivalent examples have undefined behaviour in C.
>
>I am betting that if you wonder, you will check, and may let us know.

Yep, now that my copy of the C standard is to hand, I have checked.

It looks like the relevent wording in the C++ draft was copied from the
C standard with only minor modifications.

ISO C 6.1.2.5/18:
 |  Any type so far mentioned is an _unqualified type_.  Each
 |  unqualified type has three corresponding _qualified versions_
 |  of its type: a _const-qualified_ version, a _volatile-qualified_
 |  version, and a version having both qualifications.  The
 |  qualified or unqualified versions of a type are distinct types
 |  that belong to the same type category and have the same
 |  representation and alignment requirements.

ISO C 6.3/7:
 |  An  object shall have its stored value accessed only by an
 |  lvalue expression that has one of the following types:
 |
 |  -- a type compatible with the declared type of the object,
 |
 |  -- a qualified version of a type compatible with the
 |     declared type of the object,
 |
 |  ...

The definition of "compatible type" is a bit complicated and spread out;
but from a brief look, it seems to require the qualifiers to be the same,
with possible exceptions for types declared in different translation units.

--
Fergus Henderson <fjh@cs.mu.oz.au>   |  "I have always known that the pursuit
WWW: <http://www.cs.mu.oz.au/~fjh>   |  of excellence is a lethal habit"
PGP: finger fjh@128.250.37.3         |     -- the last words of T. S. Garp.
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: fjh@murlibobo.cs.mu.OZ.AU (Fergus Henderson)
Date: 1997/06/11
Raw View
Greg Hunt <greghunt@acm.org> writes:

>I may be a little behind the times with standard C, but does volatile
>now imply atomic?  That is, does declaring something volatile
>necessarily result in its being handled atomically?

No.

--
Fergus Henderson <fjh@cs.mu.oz.au>   |  "I have always known that the pursuit
WWW: <http://www.cs.mu.oz.au/~fjh>   |  of excellence is a lethal habit"
PGP: finger fjh@128.250.37.3         |     -- the last words of T. S. Garp.
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: russvz@wwa.com (Russell Van Zandt)
Date: 1997/06/11
Raw View
On 06 Jun 97 03:57:23 GMT, Pete Becker <petebecker@acm.org> wrote:

>If you have multiple threads sharing a vector you are not programming in
>standard C++, and your compiler vendor needs to provide a solution for
>you.
> -- Pete

Where does the draft standard say that?

Russell Van Zandt
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: fjh@murlibobo.cs.mu.OZ.AU (Fergus Henderson)
Date: 1997/06/11
Raw View
"Bradd W. Szonye" <bradds@concentric.net> writes:

>Fergus Henderson <fjh@mundook.cs.mu.OZ.AU> wrote in article
>> ... your example has undefined behaviour, because by casting away
>> volatile in this manner it violates 3.10 [basic.lval] paragraph 15.
>
>I read the subclause you cited and had trouble drawing the same conclusion.

The reasoning is straightforward: 3.10/15 says

 | If a program attempts to access the stored value of an object through
 | an lvalue of other than one of the following types the behavior is
 | undefined:
 |  --the dynamic type of the object,
 |  --a cv-qualified version of the dynamic type of the object,
 |  ... (some other points that don't apply)

In this case the lvalue being used to access the object has type `UserType',
and the dynamic type is `volatile UserType', and `UserType' is not
a cv-qualified version of `volatile UserType' (rather, it is
a cv-UNqualified version).

--
Fergus Henderson <fjh@cs.mu.oz.au>   |  "I have always known that the pursuit
WWW: <http://www.cs.mu.oz.au/~fjh>   |  of excellence is a lethal habit"
PGP: finger fjh@128.250.37.3         |     -- the last words of T. S. Garp.
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: James Kanze <james-albert.kanze@vx.cit.alcatel.fr>
Date: 1997/06/11
Raw View
"Bradd W. Szonye" <bradds@concentric.net> writes:

|>  Oleg Zabluda <zabluda@math.psu.edu> wrote in article
|>  <5naege$1cd4@r02n01.cac.psu.edu>...
|>  >
|>  > [Multithreading] has nothing to do with the issue.
|>  > I didn't even want to mention them in the first post.
|>  > I mentioned them later just because some people kept saying
|>  > that 'volitile' is only for low-level device drivers.
|>
|>  Multithreading has a lot to do with the issue. The two major uses for
|>  'volatile' is for examining hardware ports and for sharing memory between
|>  threads and processes. Since most (all?) hardware ports are simple
|>  integers, that leaves multiprocessing as the major use of volatile objects
|>  of class type (whether through shared memory, threads in the same process
|>  space, memory-mapped files, etc).

Stated precisely: volatile does NOT mean atomic.  So in practice, it is
only useful on types for which the actual access is atomic.

    [Rest of Bradd's very good explination of why volatile is useless in
 this case deleted.]

--
James Kanze      home:     kanze@gabi-soft.fr        +33 (0)1 39 55 85 62
                 office:   kanze@vx.cit.alcatel.fr   +33 (0)1 69 63 14 54
GABI Software, Sarl., 22 rue Jacques-Lemercier, F-78000 Versailles France
     -- Conseils en informatique industrielle --
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: James Kanze <james-albert.kanze@vx.cit.alcatel.fr>
Date: 1997/06/11
Raw View
"Bradd W. Szonye" <bradds@concentric.net> writes:

|>  Not better support for volatile, but for synchronization. Volatility is
|>  already fairly well-defined. What is not well-defined are the conditions
|>  under which it's "safe" to work with volatile objects that do not happen to
|>  be "sig_atomic_t".

Correct.  Most of the semantics associated with volatile are
"implementation defined".  If memory serves me right, the C standard
only speaks of the actual behavior of volatile in the context of a
longjmp; for the rest, it basically just says that volatile is an
indication to the compiler that the value might change in ways not
visible to the compiler, and that accesses to a volatile object
constitute visible external behavior (i.e.: the order in which they take
place must be the same as that defined by the abstract machine).  This
last point is greatly weakened by the fact that the standard doesn't
really define when the abstract machine must access a value.  (Writing
is easy: whenever the semantics require the value to change, you have a
write.  But suppose I have the (vacuous) statement "a;", where a is a
volatile variable.  Is a accessed or not?)

One point that volatile distinctly does not address is atomicity.  Which
means that in practice, declaring something volatile because it really
will change outside the program is only useful if the accesses to that
something are in fact atomic.  In practice, this means basic types, and
not much else.  (And of course, this means that any use of volatile,
other than to maintain the guarantees mentioned with respect to longjmp,
is implementation defined.)

--
James Kanze      home:     kanze@gabi-soft.fr        +33 (0)1 39 55 85 62
                 office:   kanze@vx.cit.alcatel.fr   +33 (0)1 69 63 14 54
GABI Software, Sarl., 22 rue Jacques-Lemercier, F-78000 Versailles France
     -- Conseils en informatique industrielle --
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: Pete Becker <petebecker@acm.org>
Date: 1997/06/12
Raw View
Matt Austern wrote:
>
> James Kanze <james-albert.kanze@vx.cit.alcatel.fr> writes:
>
> > One point that volatile distinctly does not address is atomicity.  Which
> > means that in practice, declaring something volatile because it really
> > will change outside the program is only useful if the accesses to that
> > something are in fact atomic.  In practice, this means basic types, and
> > not much else.
>
> That's an important point.  In fact, though, it's even worse: there is
> no reason to believe that accessing basic types is always an atomic
> operation.  The standard makes no such guarantee, and there are real
> implementations where basic types are not atomic.

In fact, that's why we have the type sig_atomic_t. It's the only type
for which access is guaranteed to be atomic.
 -- Pete
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: Pete Becker <petebecker@acm.org>
Date: 1997/06/12
Raw View
John Potter wrote:
 >
 > On 11 Jun 1997 15:13:43 PDT, fjh@murlibobo.cs.mu.OZ.AU (Fergus
 > Henderson) wrote:
 >
 > : "Bradd W. Szonye" <bradds@concentric.net> writes:
 >
 > : >Fergus Henderson <fjh@mundook.cs.mu.OZ.AU> wrote in article
 > : >> ... your example has undefined behaviour, because by casting away
 > : >> volatile in this manner it violates 3.10 [basic.lval] paragraph 15.
 > : >
 > : >I read the subclause you cited and had trouble drawing the same
 > : >conclusion.
 >
 > : The reasoning is straightforward: 3.10/15 says
 >
 > :  | If a program attempts to access the stored value of an object through
 > :  | an lvalue of other than one of the following types the behavior is
 > :  | undefined:
 > :  |  --the dynamic type of the object,
 > :  |  --a cv-qualified version of the dynamic type of the object,
 > :  |  ... (some other points that don't apply)
 >
 > : In this case the lvalue being used to access the object has type
 > : `UserType', and the dynamic type is `volatile UserType', and `UserType'
 > : is not a cv-qualified version of `volatile UserType' (rather, it is
 > : a cv-UNqualified version).
 >
 > But isn't cv-unqualified one of the four cv qualifications?

No.

 >
 >  | 3.9.3/5
 >  | In this International Standard, the notation cv (or cv1,  cv2,
 >  | etc.), used  in  the description of types, represents an arbitrary
 >  | set of cv-qualifiers, i.e., one of {const}, {volatile},
 >  | {const,  volatile}, or the  empty  set.

The first rule of statutory construction is "begin at the beginning". In
this case, this means read paragraphs 1-4 of clause 3.9.3. Don't take
paragraph 5 out of context. Clause 3.9.3 defines the technical term
"cv-qualified" to mean "a const-qualified version, a volatile-qualified
version, or a const-volatile-qualified version". This is in distinction
to a "cv-unqualified type", which is the type that clauses 3.9.1 and
3.9.2 talk about. Note that 3.9.3/5 does not use the term
"cv-qualified", nor does it use the term "cv-unqualified", so it cannot
be read to give meaning to those terms.
 -- Pete
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: Oleg Zabluda <zabluda@math.psu.edu>
Date: 1997/06/12
Raw View
James Kanze <james-albert.kanze@vx.cit.alcatel.fr> wrote:
: Stated precisely: volatile does NOT mean atomic.

Correct.

: So in practice, it is
: only useful on types for which the actual access is atomic.

Incorrect.

It is perfectly acceptable to leave locking to the user.
Atomicity simply removes the need for locking. Therefore
volatile is useful on non-atomic types too.

Oleg.
--
Life is a sexually transmitted, 100% lethal disease.
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: Oleg Zabluda <zabluda@math.psu.edu>
Date: 1997/06/12
Raw View
John Potter <jpotter@falcon.lhup.edu> wrote:
: : The reasoning is straightforward: 3.10/15 says

: :  | If a program attempts to access the stored value of an object through
: :  | an lvalue of other than one of the following types the behavior is
: :  | undefined:
: :  |  --the dynamic type of the object,
: :  |  --a cv-qualified version of the dynamic type of the object,
: :  |  ... (some other points that don't apply)

: : In this case the lvalue being used to access the object has type `UserType',
: : and the dynamic type is `volatile UserType', and `UserType' is not
: : a cv-qualified version of `volatile UserType' (rather, it is
: : a cv-UNqualified version).

: But isn't cv-unqualified one of the four cv qualifications?

:  | 3.9.3/5
:  | In this International Standard, the notation cv (or cv1,  cv2,
:  | etc.), used  in  the description of types, represents an arbitrary
:  | set of cv-qualifiers, i.e., one of {const}, {volatile},
:  | {const,  volatile}, or the  empty  set.
:                                ^^^^^^^^^^

: Your point may be valid for some other reason.  Or, I may have missed
: some subtle difference.

'volatile UserType' is a cv-qualified version of UserType. UserType
is a cv-quaified version of UserType, because of what you've underlined.
UserType is not a cv-qualified version of 'volatile UserType'. Therefore
accessing a variable of type 'volatile UserType' through an object of
type UserType is an undefined behavior, because of what Fergus Henderson
quoted.

Oleg.
--
Life is a sexually transmitted, 100% lethal disease.
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: Oleg Zabluda <zabluda@math.psu.edu>
Date: 1997/06/12
Raw View
James Kanze <james-albert.kanze@vx.cit.alcatel.fr> wrote:
: Oleg Zabluda <zabluda@math.psu.edu> writes:

: I don't
: think that an extension to the meaning of volatile is a good idea,

I don't remember anybody proposing extending the meaning of
volatile. Volatile is a hint to a compiler to inhibit dangerous
optimizations, nothing more. It's up to a programmer to ensure
correct locking.

: |>  Even closer to the subject - like I said before, multithreading
: |>  has nothing to do with it. Any shared resource, not visible
: |>  to a compiler, will do. Many shared resources of this kind
: |>  can naturally appear even within the strictly standard high-level
: |>  c++. The content of a file, or a state of a file handle, for example.

: I'd be interested in seeing a real example where this is a problem.

Ok, since you've asked, I'll try to come up with possible real-life
examples that are _totally_ within the standard c++, and have nothing
to do with multithreading, just like I preach.

Example 1:

You are implementing two programs who have to share some data.
Being a good C++ programmer, you encapsulate such shared data,
together with appropriate synchronization, in a class. On platforms
that support shared memory, the data will be placed there.

For portability reasons, you have to implement the same mechanism
for platforms which don't support shared memory. So, you decide
to put such shared data in a file. You can achieve being able
to put data either in a file or into a shared memory by
either providing a class-specific or global operator new, or,
if you want to use std::vector<>, by providing a corresponding
allocator as well.

Either way, the container must be volatile, bacause it can change
in a way invisible to a compiler. For example, if one process
only writes the data, while the second only reads it, the second
pogram will declare such variable 'const volatile'.

Example 2:

Imagine you are implementing a spooling system. You reason that
a spool is a persistent deque. You decide that you can reuse
std::deque<> by providing an appropriate persistent allocator.
For simplicity, imagine that you have two processes. One is putting
stuff in the spool (lpr), while another one is removing stuff from
there (lpd). From both lpr's and lpd's point of view, the spool
is volitile std::deque<Spool_Job, Persistent_Allocator>.

: Synchronization of shared resources must normally involve a system call;

Not necessarily. One process might handle the shared data only
in summers, while another one might do it only in winters. Then
no synchronization is necessary (the fact that Fergus has a winter
at this time, doesn't change it :-)

: although the presence of a system call (not implementably in conforming
: C++) renders the program non-conforming, any implementation worth
: mentioning will treat the call as "visible external behavior", i.e.: as
: if the arguments involved were declared volatile at that point.

Oleg.
--
Life is a sexually transmitted, 100% lethal disease.
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: fjh@mundook.cs.mu.OZ.AU (Fergus Henderson)
Date: 1997/06/12
Raw View
russvz@wwa.com (Russell Van Zandt) writes:

>Pete Becker <petebecker@acm.org> wrote:
>
>>If you have multiple threads sharing a vector you are not programming in
>>standard C++, and your compiler vendor needs to provide a solution for
>>you.
>
>Where does the draft standard say that?

Pete Becker said two things.  The first one

>>If you have multiple threads sharing a vector you are not programming in
>>standard C++

is true.  It is not stated explicitly in the draft standard, but the
draft standard specifies what _is_ standard C++ -- of course it can't
specify all the things that _aren't_ standard C++.  In this case, it
is what the draft standard _doesn't_ say that means multithreading
is not standard C++.

Pete Becker's second statement

>>... your compiler vendor needs to provide a solution for you.

is more a matter of opinion.  As stated, I'd be inclined to disagree
(I could certainly imagine a compiler vendor not providing such a
solution and yet not going out of business), although if you
substituted "ought to" rather than "needs to", I'd probably be inclined
to agree.

--
Fergus Henderson <fjh@cs.mu.oz.au>   |  "I have always known that the pursuit
WWW: <http://www.cs.mu.oz.au/~fjh>   |  of excellence is a lethal habit"
PGP: finger fjh@128.250.37.3         |     -- the last words of T. S. Garp.
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: jpotter@falcon.lhup.edu (John Potter)
Date: 1997/06/12
Raw View
On 12 Jun 97 02:59:16 GMT, Pete Becker <petebecker@acm.org> wrote:
: John Potter wrote:
:  > But isn't cv-unqualified one of the four cv qualifications?

: No.

:  >
:  >  | 3.9.3/5
:  >  | In this International Standard, the notation cv (or cv1,  cv2,
:  >  | etc.), used  in  the description of types, represents an arbitrary
:  >  | set of cv-qualifiers, i.e., one of {const}, {volatile},
:  >  | {const,  volatile}, or the  empty  set.

: The first rule of statutory construction is "begin at the beginning". In
: this case, this means read paragraphs 1-4 of clause 3.9.3. Don't take
: paragraph 5 out of context. Clause 3.9.3 defines the technical term
: "cv-qualified" to mean "a const-qualified version, a volatile-qualified
: version, or a const-volatile-qualified version". This is in distinction
: to a "cv-unqualified type", which is the type that clauses 3.9.1 and
: 3.9.2 talk about. Note that 3.9.3/5 does not use the term
: "cv-qualified", nor does it use the term "cv-unqualified", so it cannot
: be read to give meaning to those terms.

Thanks for the lesson.  May we continue?  We have cv-unqualified types
and only those types have cv-qualified versions.  In particular,
cv-qualified types do not have cv-qualified versions.  int const
volatile is a cv-qualified version of int but it is not a cv-qualified
version of int const nor int volatile.
 int volatile vi(7);
 int const volatile cvi(static_cast<int const volatile&>(vi));
This is undefined behavior since it accesses vi using an lvalue which
is not a cv-qualified version of the type of vi.
 void foo (int const volatile& r);
 foo(vi);
This is a standard conversion; however, any use of r within foo will
invoke undefined behavior because it is an lvalue with a type which is
not a cv-qualification of the type of the referent.

Moving on to 3.10/15, I must understand the first item on the list
before I can understand most of the others.
  --the dynamic type of the object
Begin at the beginning.  Back to chapter one to find that dynamic type
applies to expressions not objects.  An lvalue can have a dynamic type
but an object can't.  The type of an object is determined when it is
created and never changes.  This paragraph talks about access not
modification and the lvalue to rvalue conversion is an access except
when it is the arguement of sizeof, and maybe some others I missed.  I
make no claim to knowing the intent of this paragraph other than the
footnote which says that it defines how an object may be aliased.  It
has been used to claim that const objects may not be modified, but
surely that is stated elsewhere.  Consider
 int const ci(5);
 int i(const_cast<int&>(ci));
Undefined behavior because ci is accessed via an lvalue which is not a
cv-qualified version of its type.
 char c1(reinterpret_cast<char&>(const_cast<int&>(ci));
 char c2(const_cast<char&>(reinterpret_cast<char const&>(ci));
No problem here since ci is accessed via an lvalue of type char which
is explicetly permitted.

Now, if I replace "dynamic type of the object" which has no meaning
with "cv-unqualified type of the object", it looks like valid aliases
to me.  All of the undefined behavior examples above become defined.

OK, I have done my homework.  Next lesson please.

John
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: Pete Becker <petebecker@acm.org>
Date: 1997/06/13
Raw View
Oleg Zabluda wrote:
 >
 > John Potter <jpotter@falcon.lhup.edu> wrote:
 > : : The reasoning is straightforward: 3.10/15 says
 >
 > : :  | If a program attempts to access the stored value of an object through
 > : :  | an lvalue of other than one of the following types the behavior is
 > : :  | undefined:
 > : :  |  --the dynamic type of the object,
 > : :  |  --a cv-qualified version of the dynamic type of the object,
 > : :  |  ... (some other points that don't apply)
 >
 > : : In this case the lvalue being used to access the object has type
 > : : `UserType', and the dynamic type is `volatile UserType', and `UserType'
 > : : is not a cv-qualified version of `volatile UserType' (rather, it is
 > : : a cv-UNqualified version).
 >
 > : But isn't cv-unqualified one of the four cv qualifications?
 >
 > :  | 3.9.3/5
 > :  | In this International Standard, the notation cv (or cv1,  cv2,
 > :  | etc.), used  in  the description of types, represents an arbitrary
 > :  | set of cv-qualifiers, i.e., one of {const}, {volatile},
 > :  | {const,  volatile}, or the  empty  set.
 > :                                ^^^^^^^^^^
 >
 > : Your point may be valid for some other reason.  Or, I may have missed
 > : some subtle difference.
 >
 > 'volatile UserType' is a cv-qualified version of UserType.

Correct.

 > UserType
 > is a cv-quaified version of UserType, because of what you've underlined.

No. 3.9.3/5 does not define the term "cv-qualified". UserType is
cv-unqualified.

 > UserType is not a cv-qualified version of 'volatile UserType'. Therefore
 > accessing a variable of type 'volatile UserType' through an object of
 > type UserType is an undefined behavior, because of what Fergus Henderson
 > quoted.

Correct.
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: Branko Cibej <branko.cibej@hermes.si>
Date: 1997/06/13
Raw View
Bradd W. Szonye wrote:

> > As far as I understand volatile objects are allowed to change
> > in stages. In fact, there is absolutely no way to even express
> > the meaning of "change in stages", because as far as the compiler
> > concerned, the change happens miraculously. It's up to the programmer
> > to ensure the consistency. Volitile is simply a hint for the compiler
> > to inhibit dangerous optimizations.
>
> Actually, there is a way, in the standard, to express the idea of
> "changes
> in stages": atomicity. There is a type (in <cstdlib>) "sig_atomic_t"
> which
> defines the largest integral type which the compiler can update
> atomically.

I believe you are mistaken. Signal handling is a Posix feature and is not
part of standard C and therefore not part of (draft) standard C++. The type
sig_atomic_t is usually defined in <sys/signal.h>, not <stdlib.h> or
<cstdlib>. I don't think there is _any_ type in the (draft) standard C++
library which is guaranteed to be updated atomically.

        Regards,
                Brane
--
Branko Cibej   <branko.cibej@hermes.si>
HERMES SoftLab, Litijska 51, 1000 Ljubljana, Slovenia
phone: (++386 61) 186 53 49  fax: (++386 61) 186 52 70
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: "Bradd W. Szonye" <bradds@concentric.net>
Date: 1997/06/13
Raw View
Oleg Zabluda <zabluda@math.psu.edu> wrote in article
<5no1nu$1kbc@r02n01.cac.psu.edu>...
>
> Ok, since you've asked, I'll try to come up with possible real-life
> examples that are _totally_ within the standard c++, and have nothing
> to do with multithreading, just like I preach.

Your examples don't have to do with multithreading, but they do have to do
with multiprocessing. One is just a special case of the other (I'm not sure
in which direction, but I think "multiprocessing" is the more general
term.) That's why in my own arguments I refer to multiprocessing and not
multithreading. It really doesn't matter, however, since all the same
arguments apply.
--
Bradd W. Szonye
bradds@concentric.net
http://www.concentric.net/~Bradds
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: Valentin Bonnard <bonnardv@pratique.fr>
Date: 1997/06/13
Raw View
Oleg Zabluda <zabluda@math.psu.edu> writes:

> James Kanze <james-albert.kanze@vx.cit.alcatel.fr> wrote:
> : Oleg Zabluda <zabluda@math.psu.edu> writes:

> : |>  Even closer to the subject - like I said before, multithreading
> : |>  has nothing to do with it. Any shared resource, not visible
> : |>  to a compiler, will do. Many shared resources of this kind
> : |>  can naturally appear even within the strictly standard high-level
> : |>  c++. The content of a file, or a state of a file handle, for example.
>
> : I'd be interested in seeing a real example where this is a problem.
>
> Ok, since you've asked, I'll try to come up with possible real-life
> examples that are _totally_ within the standard c++, and have nothing
> to do with multithreading, just like I preach.
>
> Example 1:

[...]

> So, you decide
> to put such shared data in a file. You can achieve being able
> to put data either in a file or into a shared memory by
> either providing a class-specific or global operator new,

> Example 2:
>
> Imagine you are implementing a spooling system. You reason that
> a spool is a persistent deque. You decide that you can reuse
> std::deque<> by providing an appropriate persistent allocator.

Both examples involve persistent data in files, which is accessed
with system calls/iostream. You have to ensure that the data isn't
cached at the library level, but the compiler won't optimise
disk I/O: a file can't be declared volatile ;-)

You can have a smart object:

class SmartDiskData {
    public:
        SmartDiskData& operator= (int);
        operator int () const;
};

SmartDiskData x;

x = 5;
cout << x;

The fact that you don't see the system calls in the source doesn't
mean that they aren't there.

If you are using shared memory, swaped on disk or not, your program
isn't std C++.

There is no need for volatile in std C++ programs, except with
longjmp; you simply can't share data in std C++.

If you really wnat to convince us, give a real example with real
C++ code.

--

Valentin Bonnard
mailto:bonnardv@pratique.fr
http://www.pratique.fr/~bonnardv (Informations sur le C++ en Francais)
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: fjh@mundook.cs.mu.OZ.AU (Fergus Henderson)
Date: 1997/06/15
Raw View
jpotter@falcon.lhup.edu (John Potter) writes:

>OK, I have done my homework.

Indeed, you have!

>We have cv-unqualified types
>and only those types have cv-qualified versions.

The "only" in that sentence is not implied by the draft.

The draft does not anywhere formally define the concept
"cv-qualified version of a type".  Instead, it gives a sort
of definition-by-example, and I guess you're supposed to infer
the definition.  The wording could certainly be improved.

>Moving on to 3.10/15, I must understand the first item on the list
>before I can understand most of the others.
>  --the dynamic type of the object
>Begin at the beginning.  Back to chapter one to find that dynamic type
>applies to expressions not objects.  An lvalue can have a dynamic type
>but an object can't.  The type of an object is determined when it is
>created and never changes.

This is another case of poor wording, I suppose.
I think it should just say "the type of the object".
I suspect the word "dynamic" is there only to stress that it is the
type of the object, not the type of the expression.

>This paragraph talks about access not
>modification and the lvalue to rvalue conversion is an access except
>when it is the arguement of sizeof, and maybe some others I missed.  I
>make no claim to knowing the intent of this paragraph other than the
>footnote which says that it defines how an object may be aliased.
>It has been used to claim that const objects may not be modified, but
>surely that is stated elsewhere.

Yes, the prohibition on modifying const objects is stated elsewhere:
in 7.1.5.1/4 and 3.8/8.

>Consider
> int const ci(5);
> int i(const_cast<int&>(ci));
>Undefined behavior because ci is accessed via an lvalue which is not a
>cv-qualified version of its type.

Correct.

> char c1(reinterpret_cast<char&>(const_cast<int&>(ci));
> char c2(const_cast<char&>(reinterpret_cast<char const&>(ci));
>No problem here since ci is accessed via an lvalue of type char which
>is explicetly permitted.
>
>Now, if I replace "dynamic type of the object" which has no meaning
>with "cv-unqualified type of the object", it looks like valid aliases
>to me.  All of the undefined behavior examples above become defined.

Hmm.  I wonder whether the equivalent examples have undefined behaviour in C.

--
Fergus Henderson <fjh@cs.mu.oz.au>   |  "I have always known that the pursuit
WWW: <http://www.cs.mu.oz.au/~fjh>   |  of excellence is a lethal habit"
PGP: finger fjh@128.250.37.3         |     -- the last words of T. S. Garp.
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: jbuck@synopsys.com (Joe Buck)
Date: 1997/06/04
Raw View
Oleg Zabluda <zabluda@math.psu.edu> writes:
>I think I've discovered a serious bug in STL specifications.
>It's easy to fix, but I think that if left unfixed, it'll
>amount to a defect report.

I disagree.

>volatile vector<int> v;
>vector<int>::iterator = v.begin(); // Oops. Compile-time error.
>                                   // (Thank God)

[ proposal to add volatile_iterator, begin() volatile, etc. ]

Sorry, I don't buy it.  While it may appear that it would be nice
if you could make volatile_anything and have everything be completely
orthogonal, in fact the uses of volatile are much more specialized:
representing device registers, representing a sig_atomic_t value
that is written by a signal handler, etc.  That is, relatively
low-level constructs.  Why would I want a volatile vector<int>?
Why should I bloat all my libraries to add volatile members to
all classes?
--
-- Joe Buck http://www.synopsys.com/pubs/research/people/jbuck.html

Help stamp out Internet spam: see http://spam.abuse.net/spam/
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: Matt Austern <austern@sgi.com>
Date: 1997/06/04
Raw View
jbuck@synopsys.com (Joe Buck) writes:

> Oleg Zabluda <zabluda@math.psu.edu> writes:
> >I think I've discovered a serious bug in STL specifications.
> >It's easy to fix, but I think that if left unfixed, it'll
> >amount to a defect report.
>
> I disagree.
>
> >volatile vector<int> v;
> >vector<int>::iterator = v.begin(); // Oops. Compile-time error.
> >                                   // (Thank God)
>
> [ proposal to add volatile_iterator, begin() volatile, etc. ]
>
> Sorry, I don't buy it.  While it may appear that it would be nice
> if you could make volatile_anything and have everything be completely
> orthogonal, in fact the uses of volatile are much more specialized:
> representing device registers, representing a sig_atomic_t value
> that is written by a signal handler, etc.  That is, relatively
> low-level constructs.  Why would I want a volatile vector<int>?
> Why should I bloat all my libraries to add volatile members to
> all classes?

Not to mention that adding volatile overloads of begin() and end()
isn't nearly enough: what about the other member functions?  Do we
define size() as "size() const volatile" (thus ensuring that
optimizers won't generate good code whenever size() is used), or do we
have both a "size() const" and a "size() const volatile" overload?
And how about copy constructors, and other functions whose arguments
are passed by const reference: do we want to change all of the
signatures to "const volatile T&"?

This issue would crop up in an awful lot of places.  Irrespective of
whether or not it's a good idea, fully volatilizing the entire library
would be a lot of work.
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: Oleg Zabluda <zabluda@math.psu.edu>
Date: 1997/06/04
Raw View
Joe Buck <jbuck@synopsys.com> wrote:
: While it may appear that it would be nice
: if you could make volatile_anything and have everything be completely
: orthogonal, in fact the uses of volatile are much more specialized:
: representing device registers, representing a sig_atomic_t value
: that is written by a signal handler, etc.  That is, relatively
: low-level constructs.

Volatile is not just for low-level stuff that hardware writes to.
It's for anything that can change in a way that is invisible to
the compiler. Unfortunately this includes such often-used situations,
like multithreaded programs and shared memory. Both multithreading
and shared memory are standartized by appropriate Posix, and is
perfectly legal, portable and very important.

: Why would I want a volatile vector<int>?
: Why should I bloat all my libraries to add volatile members to
: all classes?

If a vector is shared by several threads (say first writes
there, while the second reads from it), the vector must
be declared volatile.

Oleg.
--
Life is a sexually transmitted, 100% lethal disease.
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: Pete Becker <petebecker@acm.org>
Date: 1997/06/05
Raw View
Oleg Zabluda wrote:
>
> I think I've discovered a serious bug in STL specifications.
> It's easy to fix, but I think that if left unfixed, it'll
> amount to a defect report.
>
> volatile vector<int> v;
> vector<int>::iterator = v.begin(); // Oops. Compile-time error.
>                                    // (Thank God)
>
> Fix is trivial:
>
> 1. Introduce volatile_iterator, const_volatile_iterator,
>    volatile_reverse_iterator, const_volatile_reverse_iterator.
>
> 2. Introduce two more begin()'s, end()'s, rbegin()'s, rend()'s.
>
>    volatile_iterator       begin() volatile;
>    const_volatile_iterator begin() volatile const;
>    // ....
>
> I think all the changes will be limited to the Tables 66,67 and to
> the every container's specs in the "typedef" section.

Better solution: leave it alone. What can you sensibly do with a
volatile vector<int>, since you have no idea where the actual data
lives? (No credit for mentioning threads, since volatile is a very
brittle solution to multi-threading problems, and ought to be avoided in
that context).
 -- Pete
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: Oleg Zabluda <zabluda@math.psu.edu>
Date: 1997/06/05
Raw View
Matt Austern <austern@sgi.com> wrote:
: jbuck@synopsys.com (Joe Buck) writes:

: > Oleg Zabluda <zabluda@math.psu.edu> writes:
: > >I think I've discovered a serious bug in STL specifications.
: > >It's easy to fix, but I think that if left unfixed, it'll
: > >amount to a defect report.
: >
: > Why should I bloat all my libraries to add volatile members to
: > all classes?

: Not to mention that adding volatile overloads of begin() and end()
: isn't nearly enough: what about the other member functions?

Yes, earlier today I realized that as well. It's even worse
then you say. Other then the stuff I've already mentioned,
the changes must be made to allocator<> (add pointers/references
to volatile and const volitile), and to almost all container's
member functions.

: Do we
: define size() as "size() const volatile" (thus ensuring that
: optimizers won't generate good code whenever size() is used), or do we
: have both a "size() const" and a "size() const volatile" overload?

Yes, both have to be present. Otherwise it would contradict to the
"you don't pay for what you don't use" dogma.

: And how about copy constructors, and other functions whose arguments
: are passed by const reference: do we want to change all of the
: signatures to "const volatile T&"?

Not change. Add another one. Maybe making constructors template
will take care of it.

: This issue would crop up in an awful lot of places.

Yep. Same thing have to be done for every class in the standard
library.

: Irrespective of
: whether or not it's a good idea, fully volatilizing the entire library
: would be a lot of work.

Al Bundy would say "it's a part of being adult" :-). Well, maybe
volatilizing can be automated. Maybe the standard doesn't have
to change the sample headers of every existing class. Just say
in plain English somewhere that for every defined method, there
should be a corresponding volatile-correct method defined.
Insuring const-correctness was a lot of work too, yet
it was done.

Besides, in my view, if ensuring volatile-correctness in the
Standard Library is unsurmountable problem for the committee,
it woud indicate that something is very wrong with the "volitile"
part of the language. As of now, I don't think so.

Oleg.
--
Life is a sexually transmitted, 100% lethal disease.
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: Oleg Zabluda <zabluda@math.psu.edu>
Date: 1997/06/05
Raw View
Pete Becker <petebecker@acm.org> wrote:
: Better solution: leave it alone. What can you sensibly do with a
: volatile vector<int>, since you have no idea where the actual data
: lives?

First of all, I have complete control over where all the components
of a vector live. Not only I can supply my own allocator,
but I can in-place construct the thing as well as overload
the global operator new. I can make vector reside in shared
memory, on a hard drive or even in a database overseas.

: (No credit for mentioning threads, since volatile is a very
: brittle solution to multi-threading problems, and ought to be avoided in
: that context).

I think you probably misunderstood me. I wasn't saying that
declaring a vector volatile would miracuosly solve all your
problems. I was saying that in such situation a vector can
change in ways invisible for a compiler, and therefore must
be declared volatile to avoid dubious optimizations.

Either way, this discussion misses the point. I was sayng that
volatilizing STDlib must be done for orthogonality alone.
It's a Standard after all, not some crappy implementation.

Multithreading or allocation in a shared memory was just an example,
that's why I didn't put it in the original post.
Not doing volitilizing in a Standard Library requires a serious
reason IMHO. "It requires simple but tedious work" is not a good
reason as far as I am concerned, especially if it is possible to
say once and for all "all methods have corresponding volatile methods,
which will not be shown for brevity". Hopefully implementations
can automate volitilizing too.

If volitilizing even the Standard Library proves to be hard
(which I doubt) it would be an idication that a better language
support for volitile is needed.

Oleg.
--
Life is a sexually transmitted, 100% lethal disease.
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: Hyman Rosen <mti.mti.sgi.com!odin.corp.sgi.com!cardboard.mti.sgi.com!sgi.com!ncar.UCAR.EDU!uunet!jyacc!calumny!hymie>
Date: 1997/06/05
Raw View
Pete Becker <petebecker@acm.org> writes:
> Better solution: leave it alone. What can you sensibly do with a
> volatile vector<int>, since you have no idea where the actual data
> lives? (No credit for mentioning threads, since volatile is a very
> brittle solution to multi-threading problems, and ought to be avoided in
> that context).

But if you do have multiple threads sharing a vector, how do you
prevent the compiler from optimizing away multiple references to
the same element if you can't declare the vector volatile? No one
is suggesting that volatile is the means of synchronization, just
a way to tell the compiler to keep its grubby mitts off.
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: "Paul D. DeRocco" <pderocco@ix.netcom.crud.com>
Date: 1997/06/05
Raw View
Oleg Zabluda wrote:

> If a vector is shared by several threads (say first writes
> there, while the second reads from it), the vector must
> be declared volatile.

I would argue that "volatile" means that the value of something may
change, but that it is still guaranteed to be a valid value. If a
volatile object was allowed to change at any time, but was also allowed
to do so in stages, where the object state was invalid during the
intermediate stages, then the object would be plain unusable. But I
don't see how this is attainable, without extra-lingual tricks, for
complex objects.

--

Ciao,
Paul

(Please remove the extra "crud" from the return address,
which has been altered to foil junk mail senders.)
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: jbuck@synopsys.com (Joe Buck)
Date: 1997/06/05
Raw View
Joe Buck <jbuck@synopsys.com> wrote:
>: While it may appear that it would be nice
>: if you could make volatile_anything and have everything be completely
>: orthogonal, in fact the uses of volatile are much more specialized:
>: representing device registers, representing a sig_atomic_t value
>: that is written by a signal handler, etc.  That is, relatively
>: low-level constructs.

Oleg Zabluda <zabluda@math.psu.edu> writes:
>Volatile is not just for low-level stuff that hardware writes to.
>It's for anything that can change in a way that is invisible to
>the compiler. Unfortunately this includes such often-used situations,
>like multithreaded programs and shared memory.

Sorry, wrong.  "volatile" does not provide the guarantees you need for
such cases, and is the wrong way to solve problems involving multithreaded
programs and shared memory.

>If a vector is shared by several threads (say first writes
>there, while the second reads from it), the vector must
>be declared volatile.

That does not guarantee that your program will work, unfortunately.
The reason is that you haven't guaranteed, just by declaring the
vector volatile, that the threads will see the vector in a consistent
state.  You would need to add locks, or synchronized methods as in Java.
Even then you could easily produce deadlocks.


--
-- Joe Buck http://www.synopsys.com/pubs/research/people/jbuck.html

Help stamp out Internet spam: see http://spam.abuse.net/spam/
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: "Ian Griffiths" <ian.griffiths@dmv.co.uk.no.spams.please>
Date: 1997/06/05
Raw View
Joe Buck <jbuck@synopsys.com> wrote:
> Oleg Zabluda <zabluda@math.psu.edu> writes:
> >Volatile is not just for low-level stuff that hardware writes to.
> >It's for anything that can change in a way that is invisible to
> >the compiler. Unfortunately this includes such often-used situations,
> >like multithreaded programs and shared memory.
>
> Sorry, wrong.  "volatile" does not provide the guarantees you need for
> such cases, and is the wrong way to solve problems involving
multithreaded
> programs and shared memory.

No, but it's still necessary to have it for certain multithreaded
applications.


> >If a vector is shared by several threads (say first writes
> >there, while the second reads from it), the vector must
> >be declared volatile.
>
> That does not guarantee that your program will work, unfortunately.
> The reason is that you haven't guaranteed, just by declaring the
> vector volatile, that the threads will see the vector in a consistent
> state.  You would need to add locks, or synchronized methods as in Java.
> Even then you could easily produce deadlocks.

Yes indeed, but supposing you do use locks, that doesn't mean that the
compiler won't necessarily carry on making assumptions based around a
single-threaded view of the world and mess up your use of shared data even
though you've protected it with locks.  (This is usually only a problem if
one thread can alter data while another thread remains inside a single
function, but of course that can happen - all it requires is for a single
function to acquire and release a shared item twice.)

So volatile declarations can be necessary for multithreaded programs, but
they are not sufficient to guarantee correct operation.  The same is true
for synchronisation primitives.

In fact the original poster didn't say that the volatile items were not
also protected by some kind of inter-thread synchronisation, so there
wasn't anything wrong in the original post.


--
Ian Griffiths
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: fjh@mundook.cs.mu.OZ.AU (Fergus Henderson)
Date: 1997/06/05
Raw View
Pete Becker <petebecker@acm.org> writes:

>(No credit for mentioning threads, since volatile is a very
>brittle solution to multi-threading problems, and ought to be avoided in
>that context).

Could you please explain how to write reliable multi-threaded code
without using volatile?

Using volatile alone generally isn't enough, for anything other than
sig_atomic_t -- you also need some sort of syncronization (e.g. mutexes).
But unless I am quite mistaken, synchronization alone isn't enough either,
unless you are willing to rely on the hope that your compiler is
sufficiently dumb that the lack of `volatile' happens to not make any
difference.

For example, take the following code:

 Mutex m;
 static vector<int> v;

 void foo() {
  m.acquire();
  v.push_back(1);   // point 1
  m.release();

  m.acquire();
  v.push_back(2);   // point 2
  m.release();
 }

A smart compiler might notice that you never take the address of `v',
so the calls to `m.release()' and `m.acquire()' cannot modify `v'.
Hence it can load the fields of `v' into (callee-save) registers
at point 1 and use the values in the registers at point 2,
rather than reloading from memory.

This will of course potentially cause chaos if multiple threads
try to execute foo() at the same time.

> What can you sensibly do with a
> volatile vector<int>, since you have no idea where the actual data
> lives?

You're right, `volatile vector<int>' doesn't necessarily solve the
problem.  Even `volatile vector<volatile int>' wouldn't be enough.
(Though the very possibility opens another can of worms: is
`vector<volatile int>' allowed?  As far as I can tell from a quick
scan of the DWP, it should be allowed, but I'll bet no existing STL
implementation supports it.  Certainly the one that comes with GNU C++
doesn't.)

Oh well, it looks like you have a choice:
STL, multithreading, reliability --- pick any two.  :-(

In fact this problem is not restricted to STL, it would also apply to just
about any class library you care to name.

--
Fergus Henderson <fjh@cs.mu.oz.au>   |  "I have always known that the pursuit
WWW: <http://www.cs.mu.oz.au/~fjh>   |  of excellence is a lethal habit"
PGP: finger fjh@128.250.37.3         |     -- the last words of T. S. Garp.
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: Matt Austern <austern@sgi.com>
Date: 1997/06/05
Raw View
Oleg Zabluda <zabluda@math.psu.edu> writes:

> : Do we
> : define size() as "size() const volatile" (thus ensuring that
> : optimizers won't generate good code whenever size() is used), or do we
> : have both a "size() const" and a "size() const volatile" overload?
>
> Yes, both have to be present. Otherwise it would contradict to the
> "you don't pay for what you don't use" dogma.
>
> : And how about copy constructors, and other functions whose arguments
> : are passed by const reference: do we want to change all of the
> : signatures to "const volatile T&"?
>
> Not change. Add another one. Maybe making constructors template
> will take care of it.

Adding volatile overloads has problems of its own.  There are many
functions, throughout the library, that take two arguments by
reference.  (Think of comparison functions, for example, and all of
the complex<> and valarray<> arithmetic operations.)  That means that
each of those functions would have to be duplicated four times, to
take care of all possible combinations of const T& and const volatile
T&.

There are also some functions (in locale, for example) with three
arguments each of which could be volatile or not independently of the
others.  That means eight overloads.  I don't know if there are any
functions in the library that would require sixteen overloads if they
were to be fully volatilized, but it wouldn't surprise me.
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: Oleg Zabluda <zabluda@math.psu.edu>
Date: 1997/06/05
Raw View
Paul D. DeRocco <pderocco@ix.netcom.crud.com> wrote:
: Oleg Zabluda wrote:

: > If a vector is shared by several threads (say first writes
: > there, while the second reads from it), the vector must
: > be declared volatile.

: I would argue that "volatile" means that the value of something may
: change, but that it is still guaranteed to be a valid value. If a
: volatile object was allowed to change at any time, but was also allowed
: to do so in stages, where the object state was invalid during the
: intermediate stages, then the object would be plain unusable. But I
: don't see how this is attainable, without extra-lingual tricks, for
: complex objects.

As far as I understand volatile objects are allowed to change
in stages. In fact, there is absolutely no way to even express
the meaning of "change in stages", because as far as the compiler
concerned, the change happens miraculously. It's up to the programmer
to ensure the consistency. Volitile is simply a hint for the compiler
to inhibit dangerous optimizations.

Oleg.
--
Life is a sexually transmitted, 100% lethal disease.
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: Pete Becker <petebecker@acm.org>
Date: 1997/06/06
Raw View
Oleg Zabluda wrote:
>
> Pete Becker <petebecker@acm.org> wrote:
> : Better solution: leave it alone. What can you sensibly do with a
> : volatile vector<int>, since you have no idea where the actual data
> : lives?
>
> First of all, I have complete control over where all the components
> of a vector live. Not only I can supply my own allocator,
> but I can in-place construct the thing as well as overload
> the global operator new. I can make vector reside in shared
> memory, on a hard drive or even in a database overseas.

Sorry, I missed the point here.

>
> : (No credit for mentioning threads, since volatile is a very
> : brittle solution to multi-threading problems, and ought to be avoided in
> : that context).
>
> I think you probably misunderstood me. I wasn't saying that
> declaring a vector volatile would miracuosly solve all your
> problems. I was saying that in such situation a vector can
> change in ways invisible for a compiler, and therefore must
> be declared volatile to avoid dubious optimizations.

No, I think I said it mmore firmly than necessary. I meant by it that to
the extent that it solves simple multi-threading problems (single write,
multiple readers, for example), it's still too brittle to be a complete
solution. Nevertheless, a compiler that supports multiple threads does
so through extensions to standard C++. It's perfectly reasonable for
such a compiler to support volatile containers if that's appropriate.

>
> Either way, this discussion misses the point. I was sayng that
> volatilizing STDlib must be done for orthogonality alone.
> It's a Standard after all, not some crappy implementation.

"A foolish orthogonality is the hobgoblin of little minds". (With
apologies to Ralph Waldo Emerson). The principle of orthogonality tells
us that we ought to consider supporting volatile. I have considered it,
and it appears to be of minimal utility, so I do not think it justifies
the work that's involved in doing it.

>
> Multithreading or allocation in a shared memory was just an example,
> that's why I didn't put it in the original post.

What programming problem can you solve in standard C++ with volatile
throughout STL that you cannot solve as it is?

> Not doing volitilizing in a Standard Library requires a serious
> reason IMHO. "It requires simple but tedious work" is not a good
> reason as far as I am concerned, especially if it is possible to
> say once and for all "all methods have corresponding volatile methods,
> which will not be shown for brevity". Hopefully implementations
> can automate volitilizing too.
>
> If volitilizing even the Standard Library proves to be hard
> (which I doubt) it would be an idication that a better language
> support for volitile is needed.

Or that none is appropriate, as in the case of C style arrays. Sometimes
fixing an old concept is prohibitively expensive, and it's better to
leave it alone.
 -- Pete
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: Pete Becker <petebecker@acm.org>
Date: 1997/06/06
Raw View
Hyman Rosen wrote:
>
> Pete Becker <petebecker@acm.org> writes:
> > Better solution: leave it alone. What can you sensibly do with a
> > volatile vector<int>, since you have no idea where the actual data
> > lives? (No credit for mentioning threads, since volatile is a very
> > brittle solution to multi-threading problems, and ought to be avoided in
> > that context).
>
> But if you do have multiple threads sharing a vector, how do you
> prevent the compiler from optimizing away multiple references to
> the same element if you can't declare the vector volatile? No one
> is suggesting that volatile is the means of synchronization, just
> a way to tell the compiler to keep its grubby mitts off.

If you have multiple threads sharing a vector you are not programming in
standard C++, and your compiler vendor needs to provide a solution for
you. It is not the job of the standards committee to provide support for
extensions that compiler vendors might come up with.
 -- Pete
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: fjh@mundook.cs.mu.OZ.AU (Fergus Henderson)
Date: 1997/06/06
Raw View
"Ian Griffiths" <ian.griffiths@dmv.co.uk.no.spams.please> writes:

>(This [lack of `volatile'] is usually only a problem if
>one thread can alter data while another thread remains inside a single
>function, but of course that can happen - all it requires is for a single
>function to acquire and release a shared item twice.)

Compilers can and do perform inlining, even when functions are not
declared inline.  The problem can occur even if no single function
in your source code acquires and release a shared item twice.

--
Fergus Henderson <fjh@cs.mu.oz.au>   |  "I have always known that the pursuit
WWW: <http://www.cs.mu.oz.au/~fjh>   |  of excellence is a lethal habit"
PGP: finger fjh@128.250.37.3         |     -- the last words of T. S. Garp.
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: "Bradd W. Szonye" <bradds@concentric.net>
Date: 1997/06/07
Raw View
Pete Becker <petebecker@acm.org> wrote in article
<33970406.375A@acm.org>...
> Oleg Zabluda wrote:
> >
> > Either way, this discussion misses the point. I was sayng that
> > volatilizing STDlib must be done for orthogonality alone.
> > It's a Standard after all, not some crappy implementation.
>
> "A foolish orthogonality is the hobgoblin of little minds". (With
> apologies to Ralph Waldo Emerson). The principle of orthogonality tells
> us that we ought to consider supporting volatile. I have considered it,
> and it appears to be of minimal utility, so I do not think it justifies
> the work that's involved in doing it.

(Applause for creative use of one of my favorite quotations, and a good one
that applies to a lot of things in this industry.)

Volatile itself is important. Supporting volatile objects throughout the
standard library is not. The trouble with volatile objects: simply
providing the interface doesn't mean that the standard library will
implement their use correctly. For example, (assuming you've solved all the
other problems with shared memory and objects) consider putting an object
in shared memory. How do standard library components handle your
synchronization for you? They can't, unless there's a mutex around the
entire library (bad!) and the code on the other side of the shared memory
uses it too (iffy). So it looks like you need to manage the synchronization
yourself. Now, while the object is synchronized, is it volatile? No. So
copy the object, cast away volatile, or otherwise make the object
non-volatile. Now you get no complaint from library routines. Just be sure
to put the volatile back when you're done.

The library can't really cope with volatile objects, so providing the
volatile overloads would be lying to users; locked objects aren't really
volatile (at least temporarily), so requiring the library to cope with them
as such is lying to the library.
--
Bradd W. Szonye
bradds@concentric.net
http://www.concentric.net/~Bradds
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: "Bradd W. Szonye" <bradds@concentric.net>
Date: 1997/06/07
Raw View
Oleg Zabluda <zabluda@math.psu.edu> wrote in article
<5n7b3d$219e@r02n01.cac.psu.edu>...
> Paul D. DeRocco <pderocco@ix.netcom.crud.com> wrote:
>
> : I would argue that "volatile" means that the value of something may
> : change, but that it is still guaranteed to be a valid value. If a
> : volatile object was allowed to change at any time, but was also allowed
> : to do so in stages, where the object state was invalid during the
> : intermediate stages, then the object would be plain unusable. But I
> : don't see how this is attainable, without extra-lingual tricks, for
> : complex objects.
>
> As far as I understand volatile objects are allowed to change
> in stages. In fact, there is absolutely no way to even express
> the meaning of "change in stages", because as far as the compiler
> concerned, the change happens miraculously. It's up to the programmer
> to ensure the consistency. Volitile is simply a hint for the compiler
> to inhibit dangerous optimizations.

Actually, there is a way, in the standard, to express the idea of "changes
in stages": atomicity. There is a type (in <cstdlib>) "sig_atomic_t" which
defines the largest integral type which the compiler can update atomically.
Now, it's possible that the compiler could update larger (non-integral)
types atomically, but it's not guaranteed. The trouble with volatile is
that it's all-or-nothing. It's difficult to express the idea that "this
object is volatile except in synchronization blocks." However:

    // object is volatile here
    UserType volatile ut;
    {
        sync_lock(ut);
        // object is non-volatile here
        UserType & nvut = const_cast<UserType&>(ut);
        // use non-volatile calls on nvut here
        sync_unlock(ut);
        // non-volatile reference goes out of scope here
    }
    // object volatile here

I hope the example is clear. In the outer block, ut is volatile and the
compiler avoids agressive optimization. In the inner block, ut is
synchronized (the only way most objects can be used reliably) and you cast
away volatile since it's not necessary in this context. I'm sure there's a
way to create lock objects to "hide" the const_cast<> and make sure the
lock is given up automatically when it goes out of scope; my example is
just a quickie. In the presence of exceptions, you'd definitely need to
automate the unlock/re-volatilize step.

I also hope this points out that volatile overloads are not really
necessary: anything small enough to be atomic can be a volatile
sig_atomic_t; any class objects are locked explicitly and volatile
explicitly given up at those times. (Thanks to Matt Austern for pointing
out that class invariants and object consistency are a good argument
against actually using volatile objects directly.)
--
Bradd W. Szonye
bradds@concentric.net
http://www.concentric.net/~Bradds

---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: Oleg Zabluda <zabluda@math.psu.edu>
Date: 1997/06/07
Raw View
Pete Becker <petebecker@acm.org> wrote:
: Oleg Zabluda wrote:
: >
: > I think you probably misunderstood me. I wasn't saying that
: > declaring a vector volatile would miracuosly solve all your
: > problems. I was saying that in such situation a vector can
: > change in ways invisible for a compiler, and therefore must
: > be declared volatile to avoid dubious optimizations.

: No, I think I said it mmore firmly than necessary. I meant by it that to
: the extent that it solves simple multi-threading problems (single write,
: multiple readers, for example), it's still too brittle to be a complete
: solution. Nevertheless, a compiler that supports multiple threads does
: so through extensions to standard C++.

I think you've missed my point again. The Standard says nothing
about multithreading. Of course the committee tried not to introduce
anything that would make supporting them impossible. But the
Standard mandates that a compiler supports volatile quialifier,
and every conforming compiler must support it. It's up to
a programmer to decide how to use it.

: It's perfectly reasonable for
: such a compiler to support volatile containers if that's appropriate.

Normally, containers are a part of the library, not a compiler.
It might be argued that a strictly conforming implementation is
currently prohibited from supporting volatile containers in the
Standard library. Is strictly conforming implementation required
to reject all code, which is not explicitly prohibited but is not
explicitly declared either undefined, or unspecified or something
like that? Simply put - is a conforming implementation allowed
to support extensions, not explicitly prohibited by the Standard?

: > Either way, this discussion misses the point. I was sayng that
: > volatilizing STDlib must be done for orthogonality alone.
: > It's a Standard after all, not some crappy implementation.

: "A foolish orthogonality is the hobgoblin of little minds". (With
: apologies to Ralph Waldo Emerson). The principle of orthogonality tells
: us that we ought to consider supporting volatile. I have considered it,
: and it appears to be of minimal utility, so I do not think it justifies
: the work that's involved in doing it.

I take it as "I think that volitile keyword is of limited
utility and I do not think it justfies the work that's involved in
dealing with it".

: > Multithreading or allocation in a shared memory was just an example,
: > that's why I didn't put it in the original post.

: What programming problem can you solve in standard C++ with volatile
: throughout STL that you cannot solve as it is?

Same very programming problems for which the "volitile" keyword
was invented in the first place.

: > If volitilizing even the Standard Library proves to be hard
: > (which I doubt) it would be an idication that a better language
: > support for volitile is needed.

: Or that none is appropriate, as in the case of C style arrays. Sometimes
: fixing an old concept is prohibitively expensive, and it's better to
: leave it alone.

Your analogy is not very good. Which old concept? Officially STL
is 2 years old. It is still being changed every time a new DWP is
released. Besides, it's not broken, in this respect, just incomplete.
Fixing C-style arrays was seriously considered, but rejected. The
reason was that the nesessary fixes would break too much old code.
No such problems exist with volitilizing STDlib.

Oleg.
--
Life is a sexually transmitted, 100% lethal disease.
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: Oleg Zabluda <zabluda@math.psu.edu>
Date: 1997/06/07
Raw View
Pete Becker <petebecker@acm.org> wrote:
: Hyman Rosen wrote:
: >
: > But if you do have multiple threads sharing a vector, how do you
: > prevent the compiler from optimizing away multiple references to
: > the same element if you can't declare the vector volatile? No one
: > is suggesting that volatile is the means of synchronization, just
: > a way to tell the compiler to keep its grubby mitts off.

: If you have multiple threads sharing a vector you are not programming in
: standard C++,

That's incorrect. You program in standard c++. You compiler
knows nothing about multiple threads. Only about 'volitile'
qualifier. Multiple threads are generated by a user-supplied
functions, which is perfectly legal and standard c++.

Besides, multithreading has nothing to do with the issue.
I didn't even want to mention them in the first post.
I mentioned them later just because some people kept saying
that 'volitile' is only for low-level device drivers.

Now I almost regret that I've mention them at all, because now
some people keep arguing if multithreading is useful or useless,
or if they can be safely used at all. All this has nothing to do
with the issue. I can fill pages after pages with examples where
data changes in ways invisible to a compiler. But am not going to,
because it's irrelevant.

Standard defines the 'volatile' keyword and says that 'volatile'
qualifier can be apllied to any type. Yet, it can't be applied
to std::vector<int>. This is a major contradiction. To allow
this contradiction, we need a very serious reason. I'd like us
to establish if problems are serious enough or not. The only reason
I've heard so far is "it'll add too many functions to the library".

But since this does not contradict to the dogma "you don't pay
for what you don't use", I don't think it qualifies as a "serious
enough" reason, especially if, like I proposed, it can be explained
in one paragraph in the standard, and volatilizing the actual
implementation can either be automated, or done through a compiler
tricks. Like if a compiler sees

volitile vector<int> v;

it'll silently add 'volatile' qualifier to all vector's methods.
Often it has to compile vector's code anyway, since it's just a bunch
of templates, not object files.

Again, I am writing all those ideas as just an illustration of how
it can be automated. If if it can't it doesn't matter. Volatilizing
the library is, probably, the simplest task a C++ compiler vendor
or library vendor will ever encounter.

Oleg.
--
Life is a sexually transmitted, 100% lethal disease.
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: fjh@mundook.cs.mu.OZ.AU (Fergus Henderson)
Date: 1997/06/07
Raw View
"Bradd W. Szonye" <bradds@concentric.net> writes:

>The trouble with volatile is
>that it's all-or-nothing. It's difficult to express the idea that "this
>object is volatile except in synchronization blocks."

Yes, that is a problem.  It means that C or C++ programs that want to
use threading must declare objects that can be accessed from multiple
threads as `volatile', and must then pay the resulting efficiency cost
due to loss of optimizations, even though the program only accesses
such objects in synchronization blocks.

The reason that this is necessary is because the compiler doesn't know
what a synchronization block is.  Even though the user's program only
accesses objects inside synchronization blocks, the fact that the
compiler doesn't know what a synchronization block is means that it
might optimize the code in a way that violates this discipline, for
example by caching objects in registers across synchronization blocks.

It is unfortunate that you need to declare such objects volatile, because
then you lose _all_ optimizations, not just the ones that violate
the synchronization discipline.  But you _do_ need to declare them
volatile.

>However:
>
>    // object is volatile here
>    UserType volatile ut;
>    {
>        sync_lock(ut);
>        // object is non-volatile here
>        UserType & nvut = const_cast<UserType&>(ut);
>        // use non-volatile calls on nvut here
>        sync_unlock(ut);
>        // non-volatile reference goes out of scope here
>    }
>    // object volatile here
>
>I hope the example is clear. In the outer block, ut is volatile and the
>compiler avoids agressive optimization. In the inner block, ut is
>synchronized (the only way most objects can be used reliably) and you cast
>away volatile since it's not necessary in this context.

This example is fundamentally flawed.

Firstly, your example has undefined behaviour, because by casting away
volatile in this manner it violates 3.10 [basic.lval] paragraph 15.  A
conforming compiler could generate code for this example that causes
chaos even for a single-threaded program, let alone for a multi-threaded
program.

Secondly, even if you fix that by declaring the original object as
non-volatile, e.g. by changing

 UserType volatile ut;

to

 UserType volatile &ut = *new UserType;

you still haven't solved the problem.  A compiler could still optimize
things by moving the modifications of the non-volatile reference `nvut'
outside the synchronization block.

it is non-conforming, because casting away volatile on an
objected defined using volatile results in undefined behaviour.

>I also hope this points out that volatile overloads are not really
>necessary:

Your argument is unconvincing ;-)

In fact you are right, volatile overloads are not necessary -- there
are other ways of solving these problems.  And Pete Becker is right to
point out that multithreading is not standard, so in fact the standard
doesn't need to solve them.  But if you are using an implementation
that _does_ support multithreading, then that implementation needs to
also provide you with _some_ way of avoiding these problems.  I can
think of several possible ways:

 (1) The implementation could promise to be dumb enough to
     not perform optimizations that might break if the code
     was multithreaded.

 (2) The implementation could know about the synchronization
     primitives, and could avoid performing optimizations
     across synchronization blocks.  (This is basically
     what happens in Ada, where the synchronization primitives
     are built in as part of the language.  Java is probably
     the same too.)

 (3) The implementation could provide volatile versions of all
     the containers, and could require the user to use the volatile
     versions.

There's also one non-solution:

 (4) Hope and pray.

Existing practice seems to be (4) and/or (1).

Since (3) is not the only solution, since it is not existing practice,
and since it has very significant drawbacks (increase in complexity,
difficulty of use and lack of efficiency), it should not be part of
the standard.

--
Fergus Henderson <fjh@cs.mu.oz.au>   |  "I have always known that the pursuit
WWW: <http://www.cs.mu.oz.au/~fjh>   |  of excellence is a lethal habit"
PGP: finger fjh@128.250.37.3         |     -- the last words of T. S. Garp.
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: fjh@mundook.cs.mu.OZ.AU (Fergus Henderson)
Date: 1997/06/07
Raw View
"Bradd W. Szonye" <bradds@concentric.net> writes:

>... So it looks like you need to manage the synchronization
>yourself. Now, while the object is synchronized, is it volatile? No. So
>copy the object, cast away volatile, or otherwise make the object
>non-volatile.  Now you get no complaint from library routines. Just be sure
>to put the volatile back when you're done.

The alternative of copying the volatile object to a non-volatile one,
doing the update on the non-volatile one, and then copying the result
back is also unsatisfactory, for several reasons.  One reason is efficiency:
if the shared data structure is large, this can be prohibitively inefficient.
Another reason is that the standard library doesn't have any volatile
copy-constructors or assignment operators, so you can't even do the copy
without casting away volatile.

As I pointed out in another post, casting away volatile can result
in undefined behaviour, and even if you are careful to avoid that,
any attempted solution involving casting away volatile won't solve
the problem, because the compiler may optimize the non-volatile
accesses in a way that doesn't respect your synchronization discipline.

--
Fergus Henderson <fjh@cs.mu.oz.au>   |  "I have always known that the pursuit
WWW: <http://www.cs.mu.oz.au/~fjh>   |  of excellence is a lethal habit"
PGP: finger fjh@128.250.37.3         |     -- the last words of T. S. Garp.
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: fjh@mundook.cs.mu.OZ.AU (Fergus Henderson)
Date: 1997/06/07
Raw View
Oleg Zabluda <zabluda@math.psu.edu> writes:

>Pete Becker <petebecker@acm.org> wrote:
>
>: If you have multiple threads sharing a vector you are not programming in
>: standard C++,
>
>That's incorrect. You program in standard c++. You compiler
>knows nothing about multiple threads. Only about 'volitile'
>qualifier. Multiple threads are generated by a user-supplied
>functions, which is perfectly legal and standard c++.

Nope, Pete Becker is correct.  Your user-supplied function is
not standard C++, and any program which uses it is also not
standard C++.

--
Fergus Henderson <fjh@cs.mu.oz.au>   |  "I have always known that the pursuit
WWW: <http://www.cs.mu.oz.au/~fjh>   |  of excellence is a lethal habit"
PGP: finger fjh@128.250.37.3         |     -- the last words of T. S. Garp.
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: Oleg Zabluda <zabluda@math.psu.edu>
Date: 1997/06/07
Raw View
Fergus Henderson <fjh@mundook.cs.mu.OZ.AU> wrote:
: Oleg Zabluda <zabluda@math.psu.edu> writes:

: >Pete Becker <petebecker@acm.org> wrote:
: >
: >: If you have multiple threads sharing a vector you are not programming in
: >: standard C++,
: >
: >That's incorrect. You program in standard c++. You compiler
: >knows nothing about multiple threads. Only about 'volitile'
: >qualifier. Multiple threads are generated by a user-supplied
: >functions, which is perfectly legal and standard c++.

: Nope, Pete Becker is correct.  Your user-supplied function is
: not standard C++, and any program which uses it is also not
: standard C++.

Yeh, right. And the following is not a standard c++ either?

// foo.cc
void f() {};

//main.cc
void f();
int main() { f(); }

Oleg.
--
Life is a sexually transmitted, 100% lethal disease.
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: "Bradd W. Szonye" <bradds@concentric.net>
Date: 1997/06/08
Raw View
Oleg Zabluda <zabluda@math.psu.edu> wrote in article
<5nad20$1cd4@r02n01.cac.psu.edu>...
> Pete Becker <petebecker@acm.org> wrote:
> : Oleg Zabluda wrote:
> : >
> : > I think you probably misunderstood me. I wasn't saying that
> : > declaring a vector volatile would miracuosly solve all your
> : > problems. I was saying that in such situation a vector can
> : > change in ways invisible for a compiler, and therefore must
> : > be declared volatile to avoid dubious optimizations.

I still maintain that objects with class invariants are useless *while*
they are volatile. The library functions don't stand a chance if they try
to work on an object while it is volatile. Providing volatile overloads
would be misleading, since they would fool programmers into thinking it's
actually safe to pass such objects into the library. I think it's a much
better idea to "pin down" the object (through synchronization), cast away
volatile temporarily, and use the "normal" library functions on the
non-volatile object.

> Normally, containers are a part of the library, not a compiler.
> It might be argued that a strictly conforming implementation is
> currently prohibited from supporting volatile containers in the
> Standard library. Is strictly conforming implementation required
> to reject all code, which is not explicitly prohibited but is not
> explicitly declared either undefined, or unspecified or something
> like that? Simply put - is a conforming implementation allowed
> to support extensions, not explicitly prohibited by the Standard?

I think so (but I've recently been wrong, so I'll quote chapter and verse):

  17.3.4.4  Member functions                      [lib.member.functions]

2 An  implementation  can declare additional non-virtual member function
  signatures within a class:

  --by adding  arguments  with  default  values  to  a  member  function
    signature;27) The same latitude does not extend to  the  implementa-
    tion of virtual or global functions, however.

  --by  replacing a member function signature with default values by two
    or more member function signatures with equivalent behavior;

  --by adding a member function signature for a member function name.

3 A call to a member function signature described in  the  C++  Standard
  library  behaves  the  same as if the implementation declares no addi-
  tional member function signatures.28)

That is, the implementation *could* add your volatile function signatures,
so long as they do not add virtual function signatures (and, as a quality
of implementation issue, implement the right semantics).

> : > Either way, this discussion misses the point. I was sayng that
> : > volatilizing STDlib must be done for orthogonality alone.
> : > It's a Standard after all, not some crappy implementation.
>
> : "A foolish orthogonality is the hobgoblin of little minds". (With
> : apologies to Ralph Waldo Emerson). The principle of orthogonality tells
> : us that we ought to consider supporting volatile. I have considered it,
> : and it appears to be of minimal utility, so I do not think it justifies
> : the work that's involved in doing it.
>
> I take it as "I think that volitile keyword is of limited
> utility and I do not think it justfies the work that's involved in
> dealing with it".

I took that as "I think that supporting volatile in the library, given the
lack of support for synchronization in the library, causes as many problems
as it solves; therefore it does not justify the work that's involved in
doing it."

> : > Multithreading or allocation in a shared memory was just an example,
> : > that's why I didn't put it in the original post.
>
> : What programming problem can you solve in standard C++ with volatile
> : throughout STL that you cannot solve as it is?
>
> Same very programming problems for which the "volitile" keyword
> was invented in the first place.
>
> : > If volitilizing even the Standard Library proves to be hard
> : > (which I doubt) it would be an idication that a better language
> : > support for volitile is needed.

Not better support for volatile, but for synchronization. Volatility is
already fairly well-defined. What is not well-defined are the conditions
under which it's "safe" to work with volatile objects that do not happen to
be "sig_atomic_t".
--
Bradd W. Szonye
bradds@concentric.net
http://www.concentric.net/~Bradds
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: Oleg Zabluda <zabluda@math.psu.edu>
Date: 1997/06/08
Raw View
Oleg Zabluda <zabluda@math.psu.edu> wrote:
: Fergus Henderson <fjh@mundook.cs.mu.OZ.AU> wrote:
: : Oleg Zabluda <zabluda@math.psu.edu> writes:

: : >Pete Becker <petebecker@acm.org> wrote:
: : >
: : >: If you have multiple threads sharing a vector you are not programming in
: : >: standard C++,
: : >
: : >That's incorrect. You program in standard c++. You compiler
: : >knows nothing about multiple threads. Only about 'volitile'
: : >qualifier. Multiple threads are generated by a user-supplied
: : >functions, which is perfectly legal and standard c++.

: : Nope, Pete Becker is correct.  Your user-supplied function is
: : not standard C++, and any program which uses it is also not
: : standard C++.

: Yeh, right. And the following is not a standard c++ either?

: // foo.cc
: void f() {};

: //main.cc
: void f();
: int main() { f(); }

Sorry, I see your point now. You've said "your function can not
be implemented in standard c++", while I took it as "your function
is not included in stdlib".

Yeh, you are right. It might be argued that using, say,
posix-specific functions is not a standard c++. Although
strictly speaking it's true, but practically, such language
would be mostly useless. I kinda doubt that the committee
would adopt a language which makes implementing Posix
extensions impossible or even more difficult then absolutely
needed. Multithreading was standardized by posix.4a aka 1003.1c,
and therefore it's a standard as it gets, short of a direct
language support.

Even closer to the subject - like I said before, multithreading
has nothing to do with it. Any shared resource, not visible
to a compiler, will do. Many shared resources of this kind
can naturally appear even within the strictly standard high-level
c++. The content of a file, or a state of a file handle, for example.

Oleg.
--
Life is a sexually transmitted, 100% lethal disease.
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: "Bradd W. Szonye" <bradds@concentric.net>
Date: 1997/06/08
Raw View
Oleg Zabluda <zabluda@math.psu.edu> wrote in article
<5naege$1cd4@r02n01.cac.psu.edu>...
>
> [Multithreading] has nothing to do with the issue.
> I didn't even want to mention them in the first post.
> I mentioned them later just because some people kept saying
> that 'volitile' is only for low-level device drivers.

Multithreading has a lot to do with the issue. The two major uses for
'volatile' is for examining hardware ports and for sharing memory between
threads and processes. Since most (all?) hardware ports are simple
integers, that leaves multiprocessing as the major use of volatile objects
of class type (whether through shared memory, threads in the same process
space, memory-mapped files, etc).

> Now I almost regret that I've mention them at all, because now
> some people keep arguing if multithreading is useful or useless,
> or if they can be safely used at all. All this has nothing to do
> with the issue. I can fill pages after pages with examples where
> data changes in ways invisible to a compiler. But am not going to,
> because it's irrelevant.

It's not irrelevant. What happens when two processes working on a
'volatile' vector both decide that the vector needs resizing? They both
determine (correctly) that vector<>.capacity() is not enough, and proceed
to walk willy-nilly on the vector's allocated memory. Now, if you argue
that such won't happen because the vector access is synchronized to protect
class invariants... is such a synchronized vector still volatile? Not at
that time.

> Standard defines the 'volatile' keyword and says that 'volatile'
> qualifier can be apllied to any type. Yet, it can't be applied
> to std::vector<int>. This is a major contradiction. To allow
> this contradiction, we need a very serious reason. I'd like us
> to establish if problems are serious enough or not. The only reason
> I've heard so far is "it'll add too many functions to the library".

No, another reason I've heard for not providing the volatile methods is
that they effectively ignore unsynchronized updates to the object.
Automatic synchronization is generally too expensive. Providing volatile
overloads without automatic synchronization is misleading. An example is in
order.

A program uses a const, volatile vector 'cvvec'; a function attempts to
obtain the 11th element of this vector, using the const-volatile method
'at'. During the execution of cvvec.at(11):

    at() calls size() -- also const-volatile
    the vector contains (at least) 11 elements
    at() does not throw out of bounds
    another process uses cvvec.erase()
    at() accesses the '11th' element
    at() dies horribly
    programmer wonders what happened; after all, it didn't throw

It doesn't matter how the other process manages to change cvvec; it's
volatile. It doesn't matter what it uses to change it; there are more than
a couple ways to invalidate a vector's storage. (Another possibility is
that at() returns a "good" object whose own invariants are in an
inconsistent state.) What matters is that the 'volatile' methods lull the
programmer into thinking such things are safe, when they are not.

Now, if you have some way of guaranteeing that the object won't change
while the library is looking at it, then you don't really have a volatile
object, and no need for the overloads. You DO want the object to be
volatile "in between" those times so that the compiler doesn't cache values
in registers. However, once you've locked down the object so that it's
usable, it makes the most sense to temporarily cast away the volatile
qualifier:

    vector<foo> rcvvec & = const_cast<vector<foo>&>(cvvec);

and get rid of the 'unsafe' reference as soon as you've released your
locks.

> Again, I am writing all those ideas as just an illustration of how
> it can be automated. If if it can't it doesn't matter. Volatilizing
> the library is, probably, the simplest task a C++ compiler vendor
> or library vendor will ever encounter.

I'll agree that providing them is a simple matter of lexical substitution.
However, I'd also add it to the list of ways that C++ lets you blow your
whole leg off.
--
Bradd W. Szonye
bradds@concentric.net
http://www.concentric.net/~Bradds
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: danm@ziplink.net (Dan Muller)
Date: 1997/06/09
Raw View
In article <5ncr8p$10l2@r02n01.cac.psu.edu>, zabluda@math.psu.edu says...
> Fergus Henderson <fjh@mundook.cs.mu.OZ.AU> wrote:
> : Oleg Zabluda <zabluda@math.psu.edu> writes:
>
> : >Pete Becker <petebecker@acm.org> wrote:
> : >
> : >: If you have multiple threads sharing a vector you are not programming in
> : >: standard C++,
> : >
> : >That's incorrect. You program in standard c++. You compiler
> : >knows nothing about multiple threads. Only about 'volitile'
> : >qualifier. Multiple threads are generated by a user-supplied
> : >functions, which is perfectly legal and standard c++.
>
> : Nope, Pete Becker is correct.  Your user-supplied function is
> : not standard C++, and any program which uses it is also not
> : standard C++.
>
> Yeh, right. And the following is not a standard c++ either?
>
> // foo.cc
> void f() {};
>
> //main.cc
> void f();
> int main() { f(); }

Of course this is standard C++. But you are oversimplifying, and missing
Fergus' point, perhaps because Fergus didn't make it explicit. The C++
standard (and the C standard, too, I believe) necessarily includes a
description of a model of execution, in order to be able to accurately
describe the behavior of programs, and the model is sequential. No
accommodations are made for multiple threads of execution and
asynchronous behavior. User functions that introduce these elements cause
a program to become non-conforming, just as calling a routine that
scribbles through an uninitialized pointer does also.

The volatile keyword is the only concession in C/C++ to asynchronous
program behavior. Rather than using volatile for data shared by multiple
threads, I think that accessing such data in non-inline routines that are
guarded from beginning to end would provide enough protection or
practical purposes. It is unlikely that compilers would optimize such
data accesses beyond the boundaries of the routine.

Perhaps it would be nice if the language itself provided some way of
marking a block of code, rather than data, such that optimizations were
not permitted to render it unsafe for accessing shared data.

--
Dan Muller   danm@ziplink.net
http://www.ziplink.net/~danm
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: fjh@mundook.cs.mu.OZ.AU (Fergus Henderson)
Date: 1997/06/09
Raw View
Oleg Zabluda <zabluda@math.psu.edu> writes:

 >Fergus Henderson <fjh@mundook.cs.mu.OZ.AU> wrote:
 >: Oleg Zabluda <zabluda@math.psu.edu> writes:
 >
 >: >Pete Becker <petebecker@acm.org> wrote:
 >: >
 >: >: If you have multiple threads sharing a vector you are not programming in
 >: >: standard C++,
 >: >
 >: >That's incorrect. You program in standard c++. You compiler
 >: >knows nothing about multiple threads. Only about 'volitile'
 >: >qualifier. Multiple threads are generated by a user-supplied
 >: >functions, which is perfectly legal and standard c++.
 >
 >: Nope, Pete Becker is correct.  Your user-supplied function is
 >: not standard C++, and any program which uses it is also not
 >: standard C++.
 >
 >Yeh, right.

I don't understand your sarcasm here.

 >And the following is not a standard c++ either?
 >
 >// foo.cc
 >void f() {};
 >
 >//main.cc
 >void f();
 >int main() { f(); }

No, that example _is_ standard C++.  But that example doesn't
generate any threads.

--
Fergus Henderson <fjh@cs.mu.oz.au>   |  "I have always known that the pursuit
WWW: <http://www.cs.mu.oz.au/~fjh>   |  of excellence is a lethal habit"
PGP: finger fjh@128.250.37.3         |     -- the last words of T. S. Garp.
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: Jason Merrill <jason@cygnus.com>
Date: 1997/06/09
Raw View
>>>>> Dan Muller <danm@ziplink.net> writes:

> Perhaps it would be nice if the language itself provided some way of
> marking a block of code, rather than data, such that optimizations were
> not permitted to render it unsafe for accessing shared data.

Note that many compilers do:  In GCC, you can write

  asm volatile ("");

and the compiler will not do significant optimization across that
statement.  Most compilers have similar support for setjmp calls, which
have many of the same constraints.

Jason
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: Valentin Bonnard <bonnardv@pratique.fr>
Date: 1997/06/09
Raw View
[This is a copy of the post.]

Fergus Henderson <fjh@mundook.cs.mu.OZ.AU> writes:

> "Bradd W. Szonye" <bradds@concentric.net> writes:
>
> >The trouble with volatile is
> >that it's all-or-nothing. It's difficult to express the idea that "this
> >object is volatile except in synchronization blocks."
>
> Yes, that is a problem.  It means that C or C++ programs that want to
> use threading must declare objects that can be accessed from multiple
> threads as `volatile', and must then pay the resulting efficiency cost
> due to loss of optimizations, even though the program only accesses
> such objects in synchronization blocks.
>
> The reason that this is necessary is because the compiler doesn't know
> what a synchronization block is.  Even though the user's program only
> accesses objects inside synchronization blocks, the fact that the
> compiler doesn't know what a synchronization block is means that it
> might optimize the code in a way that violates this discipline, for
> example by caching objects in registers across synchronization blocks.

[...]

>
>  (1) The implementation could promise to be dumb enough to
>      not perform optimizations that might break if the code
>      was multithreaded.

What about forcing (1) by doing something that the compiler can't
understand ?

For some compilers taking the address of the object is sufficient
to keep the compiler from optimising (even &obj; works).

For better compilers writting throught a pointer (any pointer)
between syncro block should work (it is assumed that the pointer
can be an alias to any object whose address is taken).

IMO for any compiler calling a function which can't be inlined
and which have access to the object (global object or function
param) in lock and unlock should ensure that the effects of
functions on the object take place after the lock and before
the unlock.

Of course the compiler can have a __async_modify () which means
that the object might change. My point is that I think that there
is solution for every real compiler, and that the non-std libraries
provided for this compiler will do the right thing in lock and
unlock.

I think this choice is better than volatilising everything
(not just the STL, but the C lib too: strcpy (volatile char* ,
volatile const char*), ...).

--

Valentin Bonnard
mailto:bonnardv@pratique.fr
http://www.pratique.fr/~bonnardv (Informations sur le C++ en Francais)
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: fjh@mundook.cs.mu.OZ.AU (Fergus Henderson)
Date: 1997/06/10
Raw View
danm@ziplink.net (Dan Muller) writes:

>The volatile keyword is the only concession in C/C++ to asynchronous
>program behavior. Rather than using volatile for data shared by multiple
>threads, I think that accessing such data in non-inline routines that are
>guarded from beginning to end would provide enough protection [f]or
>practical purposes. It is unlikely that compilers would optimize such
>data accesses beyond the boundaries of the routine.

This does not strike me as a very robust or reliable way of writing code.

--
Fergus Henderson <fjh@cs.mu.oz.au>   |  "I have always known that the pursuit
WWW: <http://www.cs.mu.oz.au/~fjh>   |  of excellence is a lethal habit"
PGP: finger fjh@128.250.37.3         |     -- the last words of T. S. Garp.
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: Greg Hunt <greghunt@acm.org>
Date: 1997/06/11
Raw View
It might be worth pointing out that the range of synchronisation
mechanisms provided in different evironments is huge and the trade offs
are best NOT left to a compiler to evaluate.  It would be a source of
tremendous stress if the C++ standard were to mandate synchronisation
for certain types of memory.  For instance, in MVS synchronisation
facilities range from the atomicity of many machine instructions,
through Compare and Swap instructions, locks, enqueues, and larger and
more preposterously expensive things.  Sometimes CS is enough, the
atomicity of the underlying operations is rarely enough unless you have
trivial data, sometimes locks and enqueues are what you need and
sometimes you actually need some gigantic, global and permanent flag
(like a file system object) to protect what you are doing.

I may be a little behind the times with standard C, but does volatile
now imply atomic?  That is, does declaring something volatile
necessarily result in its being handled atomically?  I am thinking of a
case where a structure that contains a volatile integer is being copied,
does the structure copy HAVE to copy the integer atomically or can the
compiler generate an interruptible instruction (like a string
instruction) to copy the structure?

Dan Muller wrote:

> The volatile keyword is the only concession in C/C++ to asynchronous
> program behavior. Rather than using volatile for data shared by multiple
> threads, I think that accessing such data in non-inline routines that are
> guarded from beginning to end would provide enough protection or
> practical purposes. It is unlikely that compilers would optimize such
> data accesses beyond the boundaries of the routine.
>
> Perhaps it would be nice if the language itself provided some way of
> marking a block of code, rather than data, such that optimizations were
> not permitted to render it unsafe for accessing shared data.
>
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: "Bradd W. Szonye" <bradds@concentric.net>
Date: 1997/06/11
Raw View
Fergus Henderson <fjh@mundook.cs.mu.OZ.AU> wrote in article
<5nb1eu$k13@mulga.cs.mu.OZ.AU>...
> "Bradd W. Szonye" <bradds@concentric.net> writes:
>
> >The trouble with volatile is
> >that it's all-or-nothing. It's difficult to express the idea that "this
> >object is volatile except in synchronization blocks."
>
> Yes, that is a problem.  It means that C or C++ programs that want to
> use threading must declare objects that can be accessed from multiple
> threads as `volatile', and must then pay the resulting efficiency cost
> due to loss of optimizations, even though the program only accesses
> such objects in synchronization blocks.
>
> >However:
> >[example by me casting away volatile]
>
> This example is fundamentally flawed.
>
> Firstly, your example has undefined behaviour, because by casting away
> volatile in this manner it violates 3.10 [basic.lval] paragraph 15.  A
> conforming compiler could generate code for this example that causes
> chaos even for a single-threaded program, let alone for a multi-threaded
> program.

I read the subclause you cited and had trouble drawing the same conclusion.
I used to be fairly confident about when it's safe to cast away
cv-qualifiers and modify the object (and then didn't do it anyway), but I'm
not so confident anymore.

> Secondly, even if you fix that by declaring the original object as
> non-volatile...
> you still haven't solved the problem.  A compiler could still optimize
> things by moving the modifications of the non-volatile reference `nvut'
> outside the synchronization block.

You're right. As programmers often do when trying something "tricky" I
didn't consider all the consequences. (I have the same problem with playing
chess.) All this points up one thing to me: volatile is not really
sufficient to deal with multithreading. I guess what thread programmers
need is something in between. I'm not prepared to throw together a proposal
or anything, but the rough idea: a cv-qualifier 'synchronized' which can
express the changing access patterns of lockable objects, and some way of
specifying when the object is locked and when it is not. Such objects
shouldn't be accessible while synchronized.

One alternative: instead of messing with cv-qualifiers, write a wrapper
class which keeps track of the same things; something like auto_ptr<>. It
allows no access to the held object until it's synchronized, then allows
free access after that. It's still not a perfect solution, because you
still need some way to tell the compiler not to move some bits of code
across certain boundaries. Sequence points handle part of the problem.
Making sure that the compiler entirely respects the order could be
difficult, however, unless you're also the compiler writer.
--
Bradd W. Szonye
bradds@concentric.net
http://www.concentric.net/~Bradds
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: fjh@murlibobo.cs.mu.OZ.AU (Fergus Henderson)
Date: 1997/06/11
Raw View
James Kanze <james-albert.kanze@vx.cit.alcatel.fr> writes:

>If Posix has standardized multi-threading, then it is standard Posix,
>not standard C++.  Presumably, implementations which also conform to the
>Posix standard (as an extension to C++) will provide a solution to the
>problem.

Well, _hopefully_ they will do so, but it is rather optimistic to _presume_
they will.  Certainly neither the relevent Posix standard nor the draft
C++ standard requires them to do so.  And I wouldn't be surprised if many
implementors have simply never considered the issue.

>(For example, the functions managing the mutex lock might in
>fact be macros, resolving to compiler built-ins, which inhibit
>optimization across the lock.

I don't think that would help much, because the user might (for
whatever reason) wrap up the macros in user-defined functions,
and then you're back in the same boat.

>I'd be interested in seeing a real example where this is a problem.
>Synchronization of shared resources must normally involve a system call;
>although the presence of a system call (not implementably in conforming
>C++) renders the program non-conforming, any implementation worth
>mentioning will treat the call as "visible external behavior", i.e.: as
>if the arguments involved were declared volatile at that point.

I've already posted one such example where this could be a problem.

 void foo() {
  static UserType x;

  lock();
  ... modify x ...
  unlock();
  lock();
  ... modify x ...
  unlock();
 }

Here `x' is not passed to the system calls, and its address is never
taken.  Hence a compiler could well cache its value in (callee-save)
registers across the calls to unlock() and lock().

I'm yet to find any C or C++ compilers that actually perform this
optimization, but there's nothing in the relevant standards
to prevent them from doing so.

--
Fergus Henderson <fjh@cs.mu.oz.au>   |  "I have always known that the pursuit
WWW: <http://www.cs.mu.oz.au/~fjh>   |  of excellence is a lethal habit"
PGP: finger fjh@128.250.37.3         |     -- the last words of T. S. Garp.
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: Oleg Zabluda <zabluda@math.psu.edu>
Date: 1997/06/04
Raw View
I think I've discovered a serious bug in STL specifications.
It's easy to fix, but I think that if left unfixed, it'll
amount to a defect report.

volatile vector<int> v;
vector<int>::iterator = v.begin(); // Oops. Compile-time error.
                                   // (Thank God)

Fix is trivial:

1. Introduce volatile_iterator, const_volatile_iterator,
   volatile_reverse_iterator, const_volatile_reverse_iterator.

2. Introduce two more begin()'s, end()'s, rbegin()'s, rend()'s.

   volatile_iterator       begin() volatile;
   const_volatile_iterator begin() volatile const;
   // ....


I think all the changes will be limited to the Tables 66,67 and to
the every container's specs in the "typedef" section.

Oleg.
--
Life is a sexually transmitted, 100% lethal disease.
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]