Topic: Is C++ STL MT-Safe?


Author: Nathan Myers <ncm@cantrip.org>
Date: 1996/04/26
Raw View
David Brownell wrote:
>
> Nathan Myers <ncm@cantrip.org> wrote:
> > Brownell:
> > > ... Containers are
> > > often used by multiple threads, so would benefit from shared/exclusive
> > > (read/write) locking being available with standardized templates.
> > > There are interactions with iterators too:  most iterators will want a
> > > stable "snapshot" view (as in single-threaded code) without preventing
> > > other threads from using the container concurrently.
> >
> > People often expect this of containers, and often implement it.
> > (The Booch components are an example.)  To me it looks like a serious
> > architectural error.  That is, it mixes architectural levels in a way
> > that leads to serious problems as layers are added to a design.
>
> As I'd pointed out earlier, that (very common) policy is only one of
> the policies applications need.  The error would be using the wrong
> library facility in your application; it's not "architectural" at all.
> Not quite like "strtok()" vs. "strtok_r()", but at about that same
> level of the design process:  If you do locking yourself, you use the
> unlocked container template.  Otherwise, use the locked one.

With too many template parameters (and the complexity implied) you
reach a point where you are better off writing another template.
(Such a template could of course use the standard template
in its implementation.)

I only mention this here because many people look at the standard
library and say we passed that point long ago.

> > An STL container is a building block -- an implementation primitive.
>
> I read this as your personal preference for writing more layers of code ...

Please, nobody likes unnecessary layers of code; I mean that
it is the intent of the library working group.  Notice that the stdlib
containers don't have virtual members.  In a sense they are just intended
as example implementations of the Container requirements.

Also, the STL algorithms don't know about containers, so when would
they lock and unlock it?

(One approach is to make "locking iterators" that hold a lock during
their lifetime; but then you need an interface to create an iterator
with the right choice of semantics.)

> > While it can also be used as an interface element, MT issues are only
> > one of the problems that result.  In general, MT locking needs to be
> > done on a system-defined domain whose boundary only accidentally (and
> > often temporarily) coincides with any given container.   Defining the
> > boundary in terms of that container makes it impossible to put any other
> > elements (e.g. a cache) within the boundary later.  Providing locking
> > primitives for the container only seduces designers to this error, to
> > their later chagrin.
>
> My experience doesn't match your conclusion:  no such problems, no chagrin.

Your mileage may vary.  I know that in parts of the financial industry
none of the careful lifecycle practices we follow are used because a
program must be finished in a week or the project is cancelled, and
in any case the program is not maintained, but discarded another week
or two later.  In other parts (also of the financial industry!) a program
is maintained for decades.

I don't know where you fit on this continuum.

It's tempting to think "X is really useful, X should be in the standard
library".  Certainly many committee members have thought this often.  But
the question of the committee is more like, "what is the smallest standard
library that is sufficient?".

If locking containers are as useful as you say, then you should be able
to sell (or buy) a library that provides them. If the containers in there
conform to the standard Container requirements, anybody else's code can
operate on them exactly as if they were part of the standard, and only
the function that constructs one need know the difference.

Nathan Myers
ncm@cantrip.org  http://www.cantrip.org/
---
[ 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: David Brownell <brownell@ix.netcom.com>
Date: 1996/04/26
Raw View
Nathan Myers <ncm@cantrip.org> wrote:

> Iostream does provide (as of the Santa Cruz meeting) member types
> basic_ostream<>::sentry and basic_istream<>::sentry. ...

I'll have to look at the latest DWP then -- this sounds like it might
address some of the issues which I mentioned.

[ moderator's note: It isn't in the latest DWP. It will be in
  the next one. -sdc ]


> > This issue applies to STL as well as to iostreams.  Containers are
> > often used by multiple threads, so would benefit from shared/exclusive
> > (read/write) locking being available with standardized templates.
> > There are interactions with iterators too:  most iterators will want a
> > stable "snapshot" view (as in single-threaded code) without preventing
> > other threads from using the container concurrently.
>
> People often expect this of containers, and often implement it.
> (The Booch components are an example.)  To me it looks like a serious
> architectural error.  That is, it mixes architectural levels in a way
> that leads to serious problems as layers are added to a design.

As I'd pointed out earlier, that (very common) policy is only one of
the policies applications need.  The error would be using the wrong
library facility in your application; it's not "architectural" at all.
Not quite like "strtok()" vs. "strtok_r()", but at about that same
level of the design process:  If you do locking yourself, you use the
unlocked container template.  Otherwise, use the locked one.


> An STL container is a building block -- an implementation primitive.

I read this as your personal preference for writing more layers of code,
in contrast to mine for expecting the library to handle the 80% case so
that only in the 20% case do I need to worry.  A container is much more
complex than an "int", and I don't see any benefit from precluding kinds
of containers which provide thread-safe atomic insert/extract.  It's a
lot more complex to do some of those insertions than it is to lock!


> While it can also be used as an interface element, MT issues are only
> one of the problems that result.  In general, MT locking needs to be
> done on a system-defined domain whose boundary only accidentally (and
> often temporarily) coincides with any given container.   Defining the
> boundary in terms of that container makes it impossible to put any other
> elements (e.g. a cache) within the boundary later.  Providing locking
> primitives for the container only seduces designers to this error, to
> their later chagrin.

My experience doesn't match your conclusion:  no such problems, no chagrin.

There are lots of containers that just need simple exclusion policies.  In
fact, that's what those caches look like!  Stuffing the caches can be a
heavy-weight activity, true ... that's a _second_ application boundary, one
which (with a good design) doesn't kick in often at all.  (And that's the
area of that "20%' solution:  that one benefits strongly from custom
coding.  As you implied later.)

- Dave


[ 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: David Brownell <brownell@ix.netcom.com>
Date: 1996/04/26
Raw View
Jonathan de Boyne Pollard wrote:
>
>     void f ()
>     {
>         extern ImplementationSemaphore cout_lock ;
>         ImplementationSemaphore::Lock lock(cout_lock) ;
>         cout << ... lots of stuff that may throw an exception ... ;
>     }
>
> Multithreading control primitives do not belong in iostreams at the user
> level, for two reasons.

Hmmm, Nathan Myers said that they provide appropriate primitives now:  there
are "sentry" types.  (New as of the Santa Cruz meeting!  I need to know more.)

Note the basic problem with the above solution:   every library will end up
defining its own convention about how to lock the iostream, and the libraries
will not agree.  So your function "f()" above won't work in the same address
space as my (library) function "g()":

 extern pthread_mutex_t cout_lock;

 void g ()
 {
  CriticalSection section (&cout_lock);

  cout << "lots of " << ... ;
  cout << "stuff " << ... ;
  cout << "that must never be interleaved.";
 }


> One incidental point.
>
> If, despite the above, it was decided to provide locking of iostreams over
> multiple primitives (the locking of single primitives internally being
> mandated by the "as if" rule), Standard C++ would have to address the very
> different mechanisms of inter-thread synchronisation that are available
> on different platforms, and their subtle niceties.  You would end up with
> a standard where a lot of the behaviour of "multi-primitive" iostreams
> synchronisation was either unspecified or implementation defined.

Mutual exclusion is not something that can be implementation-specific!
Either a thread is locked out, or not.  How the C++ library implements
mutual exclusion is its own business.

- Dave


[ 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: David Brownell <brownell@ix.netcom.com>
Date: 1996/04/27
Raw View
Nathan Myers wrote:
>
> Your mileage may vary.  I know that in parts of the financial industry
> none of the careful lifecycle practices we follow are used because a
> program must be finished in a week or the project is cancelled, and
> in any case the program is not maintained, but discarded another week
> or two later.  In other parts (also of the financial industry!) a program
> is maintained for decades.
>
> I don't know where you fit on this continuum.

Towards the latter end, though clearly few C++ programs have been around
for even half a decade.  Commercial distributed system software, relying
critically on threading capabilities to get high throughput and support
numerous concurrent clients.


> It's tempting to think "X is really useful, X should be in the standard
> library".  Certainly many committee members have thought this often.  But
> the question of the committee is more like, "what is the smallest standard
> library that is sufficient?".

That's the problem.  Their conclusion was that "the smallest standard that
is sufficient" should neglect threading issues ... but had to include lots
of other (newish) language mechanisms instead.

To avoid a rant, I'll just say that Java doesn't have the consequent
problems and that's a major reason that most of the developers I know
are switching their allegiance from C++ to Java.  The Java tools are
much better than C++ tools were four years ago, particularly on PCs;
maturity of the environment is rapidly becoming a non-issue.

- Dave
---
[ 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: Nathan Myers <ncm@cantrip.org>
Date: 1996/04/29
Raw View
David Brownell wrote:
>
> [I do] commercial distributed system software, relying
> critically on threading capabilities to get high throughput and support
> numerous concurrent clients.

We agree that robust, efficient multithreading is essential in a wide
variety of application areas.

> > It's tempting to think "X is really useful, X should be in the standard
> > library".  Certainly many committee members have thought this often.  But
> > the question of the committee is more like, "what is the smallest standard
> > library that is sufficient?".

> That's the problem.  Their conclusion was that "the smallest standard that
> is sufficient" should neglect threading issues ... but had to include lots
> of other (newish) language mechanisms instead.

The committee is populated with people who have a critical need to
support multithreading.  Unfortunately at this time there is no
consensus on the correct semantics.  In five years, that will be
different, and multithreading may be high on the list of things to
nail down.

The "lots of (newish) language mechanisms" support all kinds of programming,
including the multithreaded varieties.

> To avoid a rant, I'll just say that Java doesn't have the consequent
> problems and that's a major reason that most of the developers I know
> are switching their allegiance from C++ to Java.  The Java tools are
> much better than C++ tools were four years ago, particularly on PCs;
> maturity of the environment is rapidly becoming a non-issue.

Here is a quote from Koenig & Stroustrup, "Foundations for Native C++
Styles", December '95 Software Practice & Experience:

    Judging from the net ... the relationship between languages is
  supposed to be antagonistic and dominated by fierce commercial
  rivalries.  This has left few -- if any -- traces in the definition
  of C++ ...
    C++ owes a good part of its success to the fact that it was able to
  build on the existing C community.  In our experience, the only
  alternative [way to nurture a new language] ... is to pick a set of
  problems for which no widely acceptable solution exists.

Java is an example of a language which addresses a domain where there
was no existing solution.  Runtime-bound languages have always coexisted
with compile-time-bound languages, and I see no reason for that to change,
nor do I see any reason to declare "allegiance".  Where Java solves
problems, it will be used, just as DBASE II and BASIC (even Lotus 123) were
used heavily a few years ago.  No one expected them to displace C; instead,
other languages arose that may someday have a chance to do that.

If users of C++ fail to invent ways to use C++ effectively in multithreaded
environments, then other languages will be used more for those applications.
But be careful what you conclude: I gather that Sun's Java system is written
in C++ -- multithreading, garbage collection, and all.

(David, I recognize your frustration because I feel it too, about several
areas of the standard library, and the core language as well. ["Why can't
I do type deduction from constructor arguments?"]  But a standard that
is never released benefits nobody.  C++ needs some attention to make it
more useful in multithreaded environments; let's give it that attention.
Depending on the standards committees will only slow us down.)

But back to technical matters...

It is useful to have a template parameter on a library component if
the component will be used in some (other) template context where that
parameter is passed in.  There is little benefit from parameterizing
the component unless the special semantics the parameter implies can
be encapsulated for use in the contexts that don't know the choice.

I'm not aware that anybody has discovered how to encapsulate multi-
threading semantics for use in contexts where the locking policy is
not known.  Further, such semantics as can be encapsulated tend to
affect design choices that are not parameterizable (e.g. whether to
inline) with templates.

Given these facts, I don't see what benefit to expect from
parameterizing containers on thread-locking policy, compared to
implementing containers specifically for the purpose.  This seems
especially true given that the standard containers can be used to
implement such containers, so that many of their members might
be one-liners (inline or otherwise).

Of course, not seeing something doesn't mean it doesn't exist...
If such an encapsulation is possible it will be very useful.

Nathan Myers
ncm@cantrip.org  http://www.cantrip.org/
---
[ 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: David Brownell <brownell@ix.netcom.com>
Date: 1996/04/21
Raw View
Nathan Myers <http://www.cantrip.org/> wrote:
>
> David Brownell wrote:
> >
> > Tom Payne wrote:
> > > Is there agreement on what is meant by the term "thread safe."  (I have
> > > seen definitions that seemed quite inadequate.)
> >
> > IMHO "Thread-safe" is a misleading goal.  You actually want an API that's
> > natural to use in a threaded environment ... in some cases, that means an
> > API that's OK to use from concurrent threads (e.g. add to containers),
> > but in other cases it's reasonable to have objects that are only usable
> > from a single thread (e.g. iterators).
>
> In this sense, STL (and the whole std library) is already MT-safe.

I disagree ... "the whole std library" isn't "natural" to use,
minimally since iostreams don't provide the analogue of stdio
flockfile().  Programmers have to invent their own synchronization; you
don't have the guarantees C programmers have.  Namely, that code like
this will not interleave output from two threads except between chunks
of fully formatted data:

 flockfile (stdout);
 /* many calls to emit a chunk of formatted data ... */
 putc, putc, putc, printf, puts, putc, putc, printf, ...
 funlockfile (stdout);

Currently, the C++ library interfaces don't provide primitives to
synchronize use of the library objects by different threads.  It's
_very_ natural to expect the library to define such a convention.  If
the standard library doesn't include one, there will be many -- and the
different conventions won't interoperate, so new kinds of bugs will
have found a good home.

This issue applies to STL as well as to iostreams.  Containers are
often used by multiple threads, so would benefit from shared/exclusive
(read/write) locking being available with standardized templates.
There are interactions with iterators too:  most iterators will want a
stable "snapshot" view (as in single-threaded code) without preventing
other threads from using the container concurrently.


> Of course this depends on it being implemented properly, but there are
> no interfaces in the standard C++ library that cry out for "_r" versions,
> as were found in the C library.  This is not accidental.

I hope you're right about this, but keep in mind that defining
interfaces that don't use "global" state (and so don't need "_r"
versions) is only one part of the interface definition problem.
Providing synchronization support is another part of the problem, as is
resolving performance issues that can sometimes be created due to lock
contention.  And let's not forget nailing down any locking hierarchy
rules to be followed by subclassers!


> This is not to say that the STL containers can be shared between threads
> without some kind of synchronization.

With respect to STL one might make the same argument Booch made (as I
recall) when he first did his generic components in Ada:  policy
flexibility is needed.

Today's STL components require external synchronization, which is one
policy.  But that really doesn't help in common scenarios such as
threads sharing containers and the resources they hold.  From where I
sit, it's more useful to have those (often) tricky MT algorithms made
generic than to have the same thing done with simple linked list
algorithms.
--
David Brownell
http://www.netcom.com/~brownell
---
[ 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: "Nathan Myers <ncm@cantrip.org>" <ncm@cantrip.org>
Date: 1996/04/22
Raw View
David Brownell wrote:
>
> Nathan Myers <http://www.cantrip.org/> wrote:
> >
> > David Brownell wrote:
> > >
> > > Tom Payne wrote:
> > > > Is there agreement on what is meant by the term "thread safe."  (I
> > > > have seen definitions that seemed quite inadequate.)
> > >
> > > IMHO "Thread-safe" is a misleading goal.  You actually want an API
> > > that's natural to use in a threaded environment ... in some cases, that
> > > means an API that's OK to use from concurrent threads (e.g. add to
> > > containers), but in other cases it's reasonable to have objects that are
> > > only usable from a single thread (e.g. iterators).
> >
> > In this sense, STL (and the whole std library) is already MT-safe.
>
> I disagree ... "the whole std library" isn't "natural" to use,
> minimally since iostreams don't provide the analogue of stdio
> flockfile().  Programmers have to invent their own synchronization; you
> don't have the guarantees C programmers have.  Namely, that code like
> this will not interleave output from two threads except between chunks
> of fully formatted data:
>
>         flockfile (stdout);
>         /* many calls to emit a chunk of formatted data ... */
>         putc, putc, putc, printf, puts, putc, putc, printf, ...
>         funlockfile (stdout);

Iostream does provide (as of the Santa Cruz meeting) member types
basic_ostream<>::sentry and basic_istream<>::sentry.  These replace
the non-exception-safe member functions opfx(), osfx(), ipfx(), and
isfx().  On a multithreaded implementation the constructor/
destructor for these types forms the natural place to put locking.
(Of course the Draft doesn't require this; you will need to pester
your standard library vendor to make sure this was done.)

Since user operators >> and << are expected to create instances of
these types during activation, this makes code written not knowing
about threads safe to use anyway, if it's written correctly.

If you need coarser control than provided by operators << and >>, you
can create sentry objects yourself as well.

> Currently, the C++ library interfaces don't provide primitives to
> synchronize use of the library objects by different threads.  It's
> _very_ natural to expect the library to define such a convention.  If
> the standard library doesn't include one, there will be many -- and the
> different conventions won't interoperate, so new kinds of bugs will
> have found a good home.
>
> This issue applies to STL as well as to iostreams.  Containers are
> often used by multiple threads, so would benefit from shared/exclusive
> (read/write) locking being available with standardized templates.
> There are interactions with iterators too:  most iterators will want a
> stable "snapshot" view (as in single-threaded code) without preventing
> other threads from using the container concurrently.

People often expect this of containers, and often implement it.
(The Booch components are an example.)  To me it looks like a serious
architectural error.  That is, it mixes architectural levels in a way
that leads to serious problems as layers are added to a design.

An STL container is a building block -- an implementation primitive.
While it can also be used as an interface element, MT issues are only
one of the problems that result.  In general, MT locking needs to be
done on a system-defined domain whose boundary only accidentally (and
often temporarily) coincides with any given container.   Defining the
boundary in terms of that container makes it impossible to put any other
elements (e.g. a cache) within the boundary later.  Providing locking
primitives for the container only seduces designers to this error, to
their later chagrin.

A user-level object which acts as a monitor, and provides an interface
for access to the (presumably) member containers, gives the appropriate
level of control.  Such an object could use a standard "sentry" component
in its implementation.  Unfortunately no such standard component has
been proposed.  If one had been proposed, I expect it would have been
criticized as insufficiently general.  (How to describe its semantics
without mentioning multithreading would be an interesting, but perhaps
solvable, problem as well.)

> ...  From where I
> sit, it's more useful to have those (often) tricky MT algorithms made
> generic than to have the same thing done with simple linked list
> algorithms.

I agree.  But the Standard C++ Library is only one library, and the
ISO/ANSI WG is only one standards group.  The other standards bodies
(e.g. POSIX) have been too reluctant to define C++ bindings for their
work; probably that will continue until the C++ standard is formalized.

And, of course, somebody needs to invent workable encapsulations for
MT algorithms and protocols, before they can be standardized.  Volunteers?

Nathan Myers
ncm@cantrip.org  http://www.cantrip.org/
---
[ 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: JdeBP@jba.co.uk (Jonathan de Boyne Pollard)
Date: 1996/04/22
Raw View
David Brownell (brownell@ix.netcom.com) wrote:
| You don't have the guarantees C programmers have.  Namely, that code like
| this will not interleave output from two threads except between chunks
| of fully formatted data:
|
|  flockfile (stdout);
|  /* many calls to emit a chunk of formatted data ... */
|  putc, putc, putc, printf, puts, putc, putc, printf, ...
|  funlockfile (stdout);
|
| Currently, the C++ library interfaces don't provide primitives to
| synchronize use of the library objects by different threads.

Any why should it ?

    void f ()
    {
 extern ImplementationSemaphore cout_lock ;
 cout_lock.P() ;
 cout << ... lots of stuff ... ;
 cout_lock.V() ;
    }

or even the more exception-proof

    void f ()
    {
 extern ImplementationSemaphore cout_lock ;
 ImplementationSemaphore::Lock lock(cout_lock) ;
 cout << ... lots of stuff that may throw an exception ... ;
    }

Multithreading control primitives do not belong in iostreams at the user
level, for two reasons.

Firstly, they belong at a much lower level.  the "as if" rule demands that
iostreams appear to a multithreaded program in the same way as they appear
to a single-threaded program.  This, in turn, requires that the iostreams
implementation do internal locking, to prevent race conditions internally
(consider a buffered stream).  So any "naive" multithreaded program should
be able to use iostreams without having to perform any locking itself
(interleaved output considerations aside).

For example, AFAIAA

 #include <iostream.h>
 void one () { cout << "12" << flush ; }
 void two () ( cout << "34" << flush ; )
 int main (int, char **)
 {
  /* run one() and two() in parallel somehow */
  return 0 ;
 }

there is nothing in the standard that says what order the digits 1234
should appear on standard output (except that 1 precedes 2 and 3 precedes
4).  However, there is also nothing in the standard that precludes a
multithreaded implementation, and it is the National Body Position of at
least one ISO member that Standard C++ should do *nothing* to preclude the
possiblility of multithreading.  So the iostreams implementation is
required *not* to go haywire in a well-formed program, if that program is
multithreaded.

Secondly, they belong at a much higher level.  The "granularity" of your
example itself highlights that in general one's atomic operations are *not*
iostreams primitives, but higher-level entites (such as is the case in
"instance flattening", where the stream I/O can involve many different
classes at many different levels).  This is obviously something that is
specific to individual cases, and that should be coded at user level.

In other words, the locking that is required by the implementation you
should never see, and the locking that is required by the I/O abstractions
in your program design belongs outside of iostreams.

One incidental point.

If, despite the above, it was decided to provide locking of iostreams over
multiple primitives (the locking of single primitives internally being
mandated by the "as if" rule), Standard C++ would have to address the very
different mechanisms of inter-thread synchronisation that are available
on different platforms, and their subtle niceties.  You would end up with
a standard where a lot of the behaviour of "multi-primitive" iostreams
synchronisation was either unspecified or implementation defined.

Which, in effect, would leave you in exactly the same boat as the examples
above, since you would still be using standardisation primitives whose
behaviour was implementation-defined.  The standard would be longer, but
it wouldn't say much more that was of any value, and you still would have
to tailor your code to the implementation.  What would you have gained,
except the spurious satisfaction that multithreading was mentioned in the
standard ?
---
[ 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: JdeBP@jba.co.uk (Jonathan de Boyne Pollard)
Date: 1996/04/22
Raw View
David Brownell (brownell@ix.netcom.com) wrote:
| This issue applies to STL as well as to iostreams.  Containers are
| often used by multiple threads, so would benefit from shared/exclusive
| (read/write) locking being available with standardized templates.
| There are interactions with iterators too:  most iterators will want a
| stable "snapshot" view (as in single-threaded code) without preventing
| other threads from using the container concurrently.

And how is that different from the interlocking that is required for
single-threaded programs ?

If a function f() that is iterating over a container using an iterator
calls a function g() for each element of the container, and g() performs
operations on the same container, then the standard has to tell you what
behaviour you should expect from the iterator (even if it is as simple as
"all bets are off").

There is nothing *extra* that the standard need specify for the multithreaded
situation, where in one thread function f() is iterating over a container,
and in another thread function h() is performing operations on that container.

The "as if" rule implies that whatever happens to single-threaded programs
should also happen to multi-threaded programs.  If it is defined that the
iterator in the single-threaded case cannot ever "see" the container in an
indeterminate state (i.e. cannot ever see it in the middle of an insertion
operation), then it is also so defined for the multi-threaded case, and the
implementation should provide enough internal interlocks between a
container and its iterators so that this happens.
---
[ 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: "Nathan Myers <http://www.cantrip.org/>" <ncm@cantrip.org>
Date: 1996/04/18
Raw View
David Brownell wrote:
>
> Tom Payne wrote:
> >
> > David Brownell (brownell@ix.netcom.com) wrote:
> > :
> > : Someone else mentioned Rogue Wave.  Does anyone have URLS or something
> > : through which the different "MT-enhanced" APIs could be compared?  I've
> > : been organizing a collection of MT/C++ issues; one of the gaps is where
> > : the standard C++ library interfaces need to be "MT-safed" according to
> > : some useful and consistent policy.
> >
> > Is there agreement on what is meant by the term "thread safe."  (I have
> > seen definitions that seemed quite inadequate.)
>
> IMHO "Thread-safe" is a misleading goal.  You actually want an API that's
> natural to use in a threaded environment ... in some cases, that means an
> API that's OK to use from concurrent threads (e.g. add to containers),
> but in other cases it's reasonable to have objects that are only usable
> from a single thread (e.g. iterators).

In this sense, STL (and the whole std library) is already MT-safe.
Of course this depends on it being implemented properly, but there are
no interfaces in the standard C++ library that cry out for "_r" versions,
as were found in the C library.  This is not accidental.

This is not to say that the STL containers can be shared between threads
without some kind of synchronization.  Rather, it means that on a good
implementation, two copies of a string which happen to share representation
can be used by two threads, safely.  In other words, what is shared invisibly
is handled; what you can see is your responsibility.

Nathan Myers
ncm@cantrip.org  http://www.cantrip.org/
---
[ 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
]