Topic: MT safety in C++ (Was: c++ and linux)


Author: ncm@nospam.cantrip.org (Nathan Myers)
Date: 1998/08/24
Raw View
Matt Austern <austern@sgi.com> wrote:
>
>ncm@nospam.cantrip.org (Nathan Myers) writes:
>
>> Since the standard provides the type ostream::sentry, and
>> encourages implementers to associate locking with the
>> lifetime of objects of this type, it would seem to me a
>> disservice to users to introduce non-portable extensions
>> to accomplish the same thing.
>
>I don't recall seeing anything in Clause 27 that encourages
>implementors to use sentry objects for locking.  There's
>certainly nothing to that effect in 27.6.1.1.2 [lib.istream::sentry]
>or 27.6.2.3 [lib.ostream::sentry].

I'm writing from Robert Graves' old study in Deia on Mallorca,
so my reference materials are limited, but I'm sure you will
find mention of use of sentries for locking if you look
more carefully; perhaps in 22.

--
Nathan Myers
ncm@nospam.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    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: phalpern@truffle.ma.ultranet.com (Pablo Halpern)
Date: 1998/08/24
Raw View
AllanW@my-dejanews.com wrote:

>A second method would eliminate any stalls. The stream output
>could be written into per-thread buffers, instead of global
>buffers. Then, when endl was written, the per-thread buffer
>would be written to the real output stream in a single
>thread-safe operation.

I like this one the best, since it minimizes lock-unlock operations and
it keeps mixed output in logical chunks.  However, I would prefer that
this were not part of the "standard" library shipped with an MT-capable
compiler.  Rather, the compiler vendor (or 3rd party or you, yourself)
can supply an mt_streambuf implementation that does this work.  The
programmer chooses to use this implementation if it meets her needs.  My
reasoning is as follows:

1. The correct locking strategy is very application-specific.  The
mt_streambuf concept is one logical and useful approach, but it should
not be imposed on all MT programs.

2. Single-threaded code or multi-threaded code that doesn't share
iostreams or streambufs need not pay the overhead of locking.  Only
streams that are explicitly to be shared among threads needs to use the
locking implementation.

3. MT programmers need to be reminded that streams are shared resources
and that proper mutex discipline is needed for them as much as for
memory. Hidden mutex locking gives a false sense of security that the
library will automagically take care of all mutex issues.

-------------------------------------------------------------
Pablo Halpern                   phalpern@truffle.ultranet.com

I am self-employed. Therefore, my opinions *do* represent
those of my employer.


[ 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    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: saroj@bear.com
Date: 1998/08/24
Raw View
In article <6rpn5g$r8k$1@shell7.ba.best.com>,
  ncm@nospam.cantrip.org (Nathan Myers) wrote:
>
> Matt Austern <austern@sgi.com> wrote:
> >
> >ncm@nospam.cantrip.org (Nathan Myers) writes:
> >
> >> Since the standard provides the type ostream::sentry, and
> >> encourages implementers to associate locking with the
> >> lifetime of objects of this type, it would seem to me a
> >> disservice to users to introduce non-portable extensions
> >> to accomplish the same thing.
> >
> >I don't recall seeing anything in Clause 27 that encourages
> >implementors to use sentry objects for locking.  There's
> >certainly nothing to that effect in 27.6.1.1.2 [lib.istream::sentry]
> >or 27.6.2.3 [lib.ostream::sentry].
>
> I'm writing from Robert Graves' old study in Deia on Mallorca,
> so my reference materials are limited, but I'm sure you will
> find mention of use of sentries for locking if you look
> more carefully; perhaps in 22.
>

If I do not want to pay any penalty for unshared streams, will it
help me? If a stream locks/unlocks for every insert/extract operation,
it'd be unfortunate.

  -- Saroj Mahapatra

-----== Posted via Deja News, The Leader in Internet Discussion ==-----
http://www.dejanews.com/rg_mkgrp.xp   Create Your Own Free Member Forum


[ 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    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: Christopher Eltschka <celtschk@physik.tu-muenchen.de>
Date: 1998/08/17
Raw View
AllanW@my-dejanews.com wrote:

[...]

> A second method would eliminate any stalls. The stream output
> could be written into per-thread buffers, instead of global
> buffers. Then, when endl was written, the per-thread buffer
> would be written to the real output stream in a single
> thread-safe operation.

This IMHO is the best solution I've seen yet.
Indeed, the only thing to make thread-safe would be the flush.
However, one problem remains: What if the buffer runs full and
cannot be extended? This must cause a flush. But I think if the
buffer is sufficiently large (or can grow sufficiently large),
this should not be a problem (if the minimum flush size is documented).
If you have to output really huge amounts of data unmixed, you'll
have to lock.
A debug version of the streams could even give a warning on
implicit flush.


[ 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    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: Valentin Bonnard <bonnardv@pratique.fr>
Date: 1998/08/17
Raw View
Nathan Myers <ncm@nospam.cantrip.org> writes:

> The standard provides "istream::sentry" and "ostream::sentry" objects,
> and MT-safety can be tied to these without harming performance, and
> without introducing new interfaces for use in normal operators <<
> and >>.  When possible, that is the correct way to introduce MT-safety.

Or perhaps you don't want to merge two outputs into one
(I consider the output of 'cat foo & ; cat bar' confusing).

So no locking at all makes sense too.

--

Valentin Bonnard                mailto:bonnardv@pratique.fr
info about C++/a propos du C++: http://pages.pratique.fr/~bonnardv/


[ 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    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: ncm@nospam.cantrip.org (Nathan Myers)
Date: 1998/08/18
Raw View
 Valentin Bonnard <bonnardv@pratique.fr> wrote:
>
>Nathan Myers <ncm@nospam.cantrip.org> writes:
>
>> The standard provides "istream::sentry" and "ostream::sentry" objects,
>> and MT-safety can be tied to these without harming performance, and
>> without introducing new interfaces for use in normal operators <<
>> and >>.  When possible, that is the correct way to introduce MT-safety.
>
>Or perhaps you don't want to merge two outputs into one
>(I consider the output of 'cat foo & ; cat bar' confusing).
>

Maybe.  Sometimes you want the program to continue doing
defined things even if the output on one of the streams
(e.g. debug output on cerr) doesn't make sense.  For that,
some level of locking is needed.

--
Nathan Myers
ncm@nospam.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    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: David R Tribble <david.tribble@noSPAM.central.beasys.com>
Date: 1998/08/18
Raw View
Nathan Myers <ncm@nospam.cantrip.org> writes:
>> The standard provides "istream::sentry" and "ostream::sentry"
>> objects, and MT-safety can be tied to these without harming
>> performance, and without introducing new interfaces for use in normal
>> operators << and >>.  When possible, that is the correct way to
>> introduce MT-safety.

Valentin Bonnard <bonnardv@pratique.fr> wrote:
>> Or perhaps you don't want to merge two outputs into one
>> (I consider the output of 'cat foo & ; cat bar' confusing).

Nathan Myers wrote:
> Maybe.  Sometimes you want the program to continue doing
> defined things even if the output on one of the streams
> (e.g. debug output on cerr) doesn't make sense.  For that,
> some level of locking is needed.

As a real-world example, we implemented some debugging (tracing)
functions that deal with threading.  Most calls to the tracing
functions are done to print a whole line to the trace file, so
we lock the debug "context" in that thread (blocking if another
thread has already locked the tracing output stream) for the
duration of writing the whole line.  We use simple "primitive"
enter_context() and leave_context() functions to do this (which
are built on top of the O/S mutex mechanism).

For some tracing functions we want to lock the context in order
to print several lines (such as when dumping the contents of an
object, one line per member), so those functions simple call the
low-level locking functions directly.

Rarely do we need to lock our output stream for single characters;
the smallest granularity we (almost) always use is whole lines
of text.

I/O buffering can be done in the stdio functions using setvbuf(),
specifying what kind of buffering we want: _IONBF, _IOLBF, or
_IOFBF; perhaps locking should be done in a similar fashion.

-- David R. Tribble, dtribble@technologist.com --


[ 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    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: ncm@nospam.cantrip.org (Nathan Myers)
Date: 1998/08/19
Raw View
David R Tribble <dtribble@technologist.com> wrote:

>Nathan Myers <ncm@nospam.cantrip.org> writes:
>>> The standard provides "istream::sentry" and "ostream::sentry"
>>> objects, and MT-safety can be tied to these without harming
>>> performance, and without introducing new interfaces for use in normal
>>> operators << and >>.  When possible, that is the correct way to
>>> introduce MT-safety.

>Valentin Bonnard <bonnardv@pratique.fr> wrote:
>>> Or perhaps you don't want to merge two outputs into one
>>> (I consider the output of 'cat foo & ; cat bar' confusing).

>Nathan Myers wrote:
>> Maybe.  Sometimes you want the program to continue doing
>> defined things even if the output on one of the streams
>> (e.g. debug output on cerr) doesn't make sense.  For that,
>> some level of locking is needed.

>As a real-world example, we implemented some debugging (tracing)
>functions that deal with threading.  Most calls to the tracing
>functions are done to print a whole line to the trace file, so
>we lock the debug "context" in that thread (blocking if another
>thread has already locked the tracing output stream) for the
>duration of writing the whole line.  We use simple "primitive"
>enter_context() and leave_context() functions to do this (which
>are built on top of the O/S mutex mechanism).

>For some tracing functions we want to lock the context in order
>to print several lines (such as when dumping the contents of an
>object, one line per member), so those functions simple call the
>low-level locking functions directly.

>Rarely do we need to lock our output stream for single characters;
>the smallest granularity we (almost) always use is whole lines
>of text.

>I/O buffering can be done in the stdio functions using setvbuf(),
>specifying what kind of buffering we want: _IONBF, _IOLBF, or
>_IOFBF; perhaps locking should be done in a similar fashion.


I don't see any need to mix up locking with buffer policy.
Since the standard provides the type ostream::sentry, and
encourages implementers to associate locking with the
lifetime of objects of this type, it would seem to me a
disservice to users to introduce non-portable extensions
to accomplish the same thing.

As a user of a multi-threaded iostream library, I would ask my
stdlib vendor to support locking in these objects.  They should
be "stackable" locks, so that a thread that already has one can
claim it again, and actually release it only after there are no
more sentry objects on the stream.  This allows whatever granularity
of locking you want beyond any provided by the ostream members
themselves, and lets you bypass the ostream and operate directly
on the streambuf.

Perhaps someday there will be a POSIX binding that specifies this.

--
Nathan Myers
ncm@nospam.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    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: Matt Austern <austern@sgi.com>
Date: 1998/08/19
Raw View
ncm@nospam.cantrip.org (Nathan Myers) writes:

> I don't see any need to mix up locking with buffer policy.
> Since the standard provides the type ostream::sentry, and
> encourages implementers to associate locking with the
> lifetime of objects of this type, it would seem to me a
> disservice to users to introduce non-portable extensions
> to accomplish the same thing.

I don't recall seeing anything in Clause 27 that encourages
implementors to use sentry objects for locking.  There's
certainly nothing to that effect in 27.6.1.1.2 [lib.istream::sentry]
or 27.6.2.3 [lib.ostream::sentry].



[ 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    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: saroj@bear.com
Date: 1998/08/19
Raw View
In article <6re7ak$j68$1@shell7.ba.best.com>,
  ncm@nospam.cantrip.org (Nathan Myers) wrote:
>
> David R Tribble <dtribble@technologist.com> wrote:
>
[snipped]

> As a user of a multi-threaded iostream library, I would ask my
> stdlib vendor to support locking in these objects.  They should
> be "stackable" locks, so that a thread that already has one can
> claim it again, and actually release it only after there are no
> more sentry objects on the stream.

I have not given a great deal of thought on this, but recursive
mutexes are frowned upon by many thread experts. According to them,
new code should rarely need recursive-mutex.

> This allows whatever granularity
> of locking you want beyond any provided by the ostream members
> themselves, and lets you bypass the ostream and operate directly
> on the streambuf.
>
> Perhaps someday there will be a POSIX binding that specifies this.

-----== Posted via Deja News, The Leader in Internet Discussion ==-----
http://www.dejanews.com/rg_mkgrp.xp   Create Your Own Free Member Forum


[ 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    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: AllanW@my-dejanews.com
Date: 1998/08/20
Raw View

> In article <6re7ak$j68$1@shell7.ba.best.com>,
>   ncm@nospam.cantrip.org (Nathan Myers) wrote:
> > As a user of a multi-threaded iostream library, I would ask my
> > stdlib vendor to support locking in these objects.  They should
> > be "stackable" locks, so that a thread that already has one can
> > claim it again, and actually release it only after there are no
> > more sentry objects on the stream.

In article <6rf9vn$9cu$1@nnrp1.dejanews.com>,
  saroj@bear.com wrote:
> I have not given a great deal of thought on this, but recursive
> mutexes are frowned upon by many thread experts. According to them,
> new code should rarely need recursive-mutex.

I'm not a "thread expert," but it seems to me that using "stackable"
locks can simplify locking logic considerably.

Consider a user-defined insertion operator for MyClass objects; it
writes all the important MyClass data to an output stream. Naturally
it must lock at the beginning and unlock at the end. At some point
it writes member m_myint, using the library-supplied insertion
operator for integers. How do we prevent the integer insertion
operator from corrupting the lock?

Method 1 is to use a counted lock, so that if the code sets the
lock 3 times the lock isn't released until the code releases the
lock 3 times. This is the cleanest, fastest method.

Method 2 forces the code to maintain it's own "nesting count."
When any insert operator sets the lock, it also increments the
count; when finished, it decrements the count, and clears the
lock if and only if the count is now 0. This accomplishes the
same goal as Method 1, except that it's much more error prone.

Method 3 uses an "already locked" flag, or else interrogates
the lock to see if it's already set by this thread. If so, it
proceeds without setting or clearing the lock; if not, then it
must set it before starting and clear it when done. This is
much like Method 2, except that it's not only error prone, but
slow.

--
AllanW@my-dejanews.com is a "Spam Magnet" -- never read.
Please reply in USENET only, sorry.

-----== Posted via Deja News, The Leader in Internet Discussion ==-----
http://www.dejanews.com/rg_mkgrp.xp   Create Your Own Free Member Forum


[ 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    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: Pete Becker <petebecker@acm.org>
Date: 1998/08/21
Raw View
AllanW@my-dejanews.com wrote:
>
> Method 2 forces the code to maintain it's own "nesting count."
> When any insert operator sets the lock, it also increments the
> count; when finished, it decrements the count, and clears the
> lock if and only if the count is now 0. This accomplishes the
> same goal as Method 1, except that it's much more error prone.
>
> Method 3 uses an "already locked" flag, or else interrogates
> the lock to see if it's already set by this thread. If so, it
> proceeds without setting or clearing the lock; if not, then it
> must set it before starting and clear it when done. This is
> much like Method 2, except that it's not only error prone, but
> slow.

And, of course, methods 2 and 3 require some sort of locking mechanism
in order to avoid simultaneous reads and updates of the control
variables. If your processor supports atomic read-and-update you can use
that, provided you don't mind writing highly non-portable code. If it
doesn't have it, you're stuck with using a lock.

--
Pete Becker
Dinkumware, Ltd.
http://www.dinkumware.com


[ 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    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: ncm@nospam.cantrip.org (Nathan Myers)
Date: 1998/08/13
Raw View
<saroj@bear.com> wrote:
>  Matt Austern <austern@sgi.com> wrote:
>> saroj@bear.com writes:
>>
>> > sbumpc_unlocked() is similar to POSIX getc_unlocked(). POSIX getc()
>> > locks (by design).
>>
>> ... Nathan's suggestion is that all
>> istream and ostream operations should do automatic locking and that no
>> streambuf operations should.
>>
>> ... yet another [suggstion] is that
>> the library shouldn't do automatic locking at all.  It would ensure
>> that concurrent access to distinct streams is safe, but users who want
>> to access the same stream simultaneously from multiple threads would
>> do their own locking.
>
>Yes, this is the best possible scheme. It also agrees with what is
>considered the best practice for library thread-safety: lock only
>the internal shared state. This will result in as little locking
>as possible.

This is the motivation for having istream and ostream operations
lock the streambuf.  A streambuf may be shared among more than
one stream object.

--
Nathan Myers
ncm@nospam.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    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: AllanW@my-dejanews.com
Date: 1998/08/13
Raw View
In article <6qqr9a$pog$1@shell7.ba.best.com>,
  ncm@nospam.cantrip.org (Nathan Myers) wrote:
>
>  <saroj@bear.com> wrote:
> >
> >In article <6qotvt$243$1@shell7.ba.best.com>,
> >  ncm@nospam.cantrip.org (Nathan Myers) wrote:
> >>
> >> <saroj@bear.com> wrote:
> >> >
> >> >In article <35CC756E.417C@pratique.fr>,
> >> >  bonnardv@pratique.fr wrote:
> >> >> Could we have a project/subgroup on MT safety, and publish
> >> >> a MT safe C++ standard (actually a simple diff with the
> >> >> C++ standard) ?
> >> >>
> >> >
> >> >Actually, this is sorely needed. We should have sbumpc_unlocked,
> >> >snextc_unlocked, ... functions in streambuf and xxx_unlocked
> >> >functions in iostream (like getc_unlocked, flockfile
> >> >in C binding of POSIX).
> >>
> >> An addendum on MT-safety would be welcome, but locking at the level of
> >> streambuf inline members is exactly the wrong way to go about it.
> >> The whole point of these members is speed, and locking per-character
> >> provides very little benefit for any real program.
> >>
> >> The standard provides "istream::sentry" and "ostream::sentry" objects,
> >> and MT-safety can be tied to these without harming performance, and
> >> without introducing new interfaces for use in normal operators <<
> >> and >>.  When possible, that is the correct way to introduce MT-safety.
> >>
> >
> >sbumpc_unlocked() is similar to POSIX getc_unlocked(). POSIX getc()
> >locks (by design). C++ solution of course, can be different. Sun C++
> >iostream has a flag (MT::unsafe_object), which if set, disables
> >locking; so you can use normal >> and << operators. This is similar
> >to your suggestion of using sentry object to control locking, but
> >it is only for formatted operations. How about unformatted ones?
>
> No matter what locking individual members of a stream do, your
> program must coordinate access to the stream or you will have
> chaos.
>
> If you always create a sentry object before using unformatted
> operations on the stream, and maintain it for the full duration
> of operations that must be contiguous, then you will have no
> concurrency problem and no performance problem.
>
> POSIX getc() locking is just unfortunate, evidence of confused design.
> It harms performance without solving or providing tools to solve
> the real problem.

I don't know POSIX at all, but I can think of a reason why they
might want to lock around getc() operations, even though it won't
help the user much.

Imagine a naive user writing her very first MT application, on some
OS which doesn't do any locking in it's stream/buffer classes. Let's
call this POFIVE :-]  Miss Naive, without thinking, causes each
thread to log a message on the console whenever anything noteworthy
happens.  Miss Naive is naive, but not dumb; with her excellent
testing skills (not taught in schools -- but it should be), it's
less than a week before she hits a test case where two threads try to
log a message at the same time. *POW* The program crashes with a
Segmentation Fault (or whatever they call it on POFIVE). "Darn,"
says polite Miss Naive. "This POFIVE run-time library isn't even
thread safe! What good is it?" And she gives up in disgust, vowing to
never touch an other POFIVE machine ever again.

Scoot forward in time; Miss Naive is trying the same program again,
but this time she's doing it on POSIX. Again, it's less than a week
before the first time her program displays two log messages at the
same time. But this time, the results are very different. "That's
gibberish," she notes of the chaos. "I can't read that; it looks
like -- wait a moment, what was it supposed to say? Yes, I can see
the letters of the message strewn about inside the gibberish; if I
cross them off -- yes, the rest of it is a different message,
probably from the other thread! AHA! Now I see what happened!"
Remembering text from her "MT for Dummies" book, which she recently
finished reading, she quickly flips to the index and looks up
"critical section." No POSIX bashing for Miss Naive -- she's free to
continue her Microsoft bashing, like everyone else.

So, just because locking here doesn't solve the whole problem isn't
a reason not to do it at all. Maybe for debug builds only?

--
AllanW@my-dejanews.com is a "Spam Magnet" -- never read.
Please reply in USENET only, sorry.

-----== Posted via Deja News, The Leader in Internet Discussion ==-----
http://www.dejanews.com/rg_mkgrp.xp   Create Your Own Free Member Forum


[ 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    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: AllanW@my-dejanews.com
Date: 1998/08/13
Raw View
In article <6qssjg$e52$1@nnrp1.dejanews.com>,
  abrahams@motu.com wrote:
>
> In article <fxtpve6kuqd.fsf@isolde.engr.sgi.com>,
>   Matt Austern <austern@sgi.com> wrote:
> > (The motivation for leaving out automatic locking is that automatic
> > locking has the wrong level of granularity.  Something that in C you
> > would do with a single call to printf, in C++ you would do with a half
> > dozen invocations of operator<<.  You probably want to lock the entire
> > sequence, not each character or each operator<< individually.)
>
> One way to handle this (with a differently-designed library, of course),
> would be to have all streaming operations return a stream object which
> maintained the lock and delegated all further operations to the original
> stream. That would at least lock operator<<() sequences into contiguity.

In other words, a proxy element.  That would be tough to write. I
guess you could do it -- besides overloading *ALL* of the
operator<< functions, you would also have to return a temporary
proxy object BY VALUE. That way, the temporary would eventually
get destroyed, giving you a chance to unlock it.

Even so, it helps code like this:
    std::cout << "Hello, "
        << title << ' ' << firstname << ' ' << middle_init
        << ' ' << lastname << ".\n"
        << "May I please call you " << firstname << "?\n"
        << "Type Y if this is okay, "
        << "or type N if you prefer me to call you"
        << title << ' ' << lastname << " : ";
    std::cin >> yesno;
But it wouldn't help code like this:
    std::cout << "Hello, ";
    std::cout << title << ' ' << firstname << ' ' << middle_init;
    std::cout << ' ' << lastname << ".\n";
    std::cout << "May I please call you " << firstname << "?\n";
    std::cout << "Type Y if this is okay, ";
    std::cout << "or type N if you prefer me to call you";
    std::cout << title << ' ' << lastname << " : ";
    std::cin >> yesno;
Which I would have thought was logically equivalent.

Perhaps what we really need is a manipulator. It's use would be
optional on single-threaded applications; on multi-user applications
it would ensure that all output was grouped. Then we would have:
    std::cout << lockstream << "Hello, ";
    std::cout << title << ' ' << firstname << ' ' << middle_init;
    std::cout << ' ' << lastname << ".\n";
    std::cout << "May I please call you " << firstname << "?\n";
    std::cout << "Type Y if this is okay, ";
    std::cout << "or type N if you prefer me to call you";
    std::cout << title << ' ' << lastname << " : ";
    // DANGEROUS -- lock is active during input, blocking all other threads!
    std::cin >> yesno;
    std::cout << unlockstream;

--
AllanW@my-dejanews.com is a "Spam Magnet" -- never read.
Please reply in USENET only, sorry.

-----== Posted via Deja News, The Leader in Internet Discussion ==-----
http://www.dejanews.com/rg_mkgrp.xp   Create Your Own Free Member Forum


[ 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    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: Christopher Eltschka <celtschk@physik.tu-muenchen.de>
Date: 1998/08/14
Raw View
AllanW@my-dejanews.com wrote:
>
> In article <6qssjg$e52$1@nnrp1.dejanews.com>,
>   abrahams@motu.com wrote:
> >
> > In article <fxtpve6kuqd.fsf@isolde.engr.sgi.com>,
> >   Matt Austern <austern@sgi.com> wrote:
> > > (The motivation for leaving out automatic locking is that automatic
> > > locking has the wrong level of granularity.  Something that in C you
> > > would do with a single call to printf, in C++ you would do with a half
> > > dozen invocations of operator<<.  You probably want to lock the entire
> > > sequence, not each character or each operator<< individually.)
> >
> > One way to handle this (with a differently-designed library, of course),
> > would be to have all streaming operations return a stream object which
> > maintained the lock and delegated all further operations to the original
> > stream. That would at least lock operator<<() sequences into contiguity.
>
> In other words, a proxy element.  That would be tough to write. I
> guess you could do it -- besides overloading *ALL* of the
> operator<< functions, you would also have to return a temporary
> proxy object BY VALUE. That way, the temporary would eventually
> get destroyed, giving you a chance to unlock it.
>
> Even so, it helps code like this:
>     std::cout << "Hello, "
>         << title << ' ' << firstname << ' ' << middle_init
>         << ' ' << lastname << ".\n"
>         << "May I please call you " << firstname << "?\n"
>         << "Type Y if this is okay, "
>         << "or type N if you prefer me to call you"
>         << title << ' ' << lastname << " : ";
>     std::cin >> yesno;
> But it wouldn't help code like this:
>     std::cout << "Hello, ";
>     std::cout << title << ' ' << firstname << ' ' << middle_init;
>     std::cout << ' ' << lastname << ".\n";
>     std::cout << "May I please call you " << firstname << "?\n";
>     std::cout << "Type Y if this is okay, ";
>     std::cout << "or type N if you prefer me to call you";
>     std::cout << title << ' ' << lastname << " : ";
>     std::cin >> yesno;
> Which I would have thought was logically equivalent.

As ST code, this of course *is* equivalent.
On MT code, it's reasonable to do it this way. The << chaining
is sort of replacement of a function call with variadic arguments
(I'm almost shure that the << synax had never been invented, had
there been a typesafe way to give variadic elements). Therefore
one could see the statement as the protected area.

Also, you would expect (assuming locked printf's)

  printf("%s ", "Hello");
  printf("%s\n", "world");

to be different in locking than

  printf("%s %s\n", "Hello", "world");

wouldn't you?

The surprise comes with those two pieces of code:

std::cout << "Hello" << endl;
do_something();

and

std::cout << "Hello" << endl, do_something();

which one would expect to be the same. However, the second version
would keep the cout locked during do_something().
(OTOH, this could be used to lock during complicated operations,
say:

std::vector<int> myvec;
cout << "", copy(myvec.begin(), myvec.end(),
                 std::ostream_iterator<int>(cout, '\n'));

This would protect the complete copy, although there are several
output statements.)

>
> Perhaps what we really need is a manipulator. It's use would be
> optional on single-threaded applications; on multi-user applications
> it would ensure that all output was grouped. Then we would have:
>     std::cout << lockstream << "Hello, ";
>     std::cout << title << ' ' << firstname << ' ' << middle_init;
>     std::cout << ' ' << lastname << ".\n";
>     std::cout << "May I please call you " << firstname << "?\n";
>     std::cout << "Type Y if this is okay, ";
>     std::cout << "or type N if you prefer me to call you";
>     std::cout << title << ' ' << lastname << " : ";
>     // DANGEROUS -- lock is active during input, blocking all other threads!
>     std::cin >> yesno;
>     std::cout << unlockstream;

There I'd prefer the sentry solution mentioned earlier in this
thread.


[ 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    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: AllanW@my-dejanews.com
Date: 1998/08/14
Raw View

> > In article <fxtpve6kuqd.fsf@isolde.engr.sgi.com>,
> >   Matt Austern <austern@sgi.com> wrote:
> > > (The motivation for leaving out automatic locking is that automatic
> > > locking has the wrong level of granularity.  Something that in C you
> > > would do with a single call to printf, in C++ you would do with a half
> > > dozen invocations of operator<<.  You probably want to lock the entire
> > > sequence, not each character or each operator<< individually.)

> In article <6qssjg$e52$1@nnrp1.dejanews.com>,
>   abrahams@motu.com wrote:
> > One way to handle this (with a differently-designed library, of course),
> > would be to have all streaming operations return a stream object which
> > maintained the lock and delegated all further operations to the original
> > stream. That would at least lock operator<<() sequences into contiguity.

In article <6qvsfq$8b9$1@nnrp1.dejanews.com>, I wrote:
> In other words, a proxy element.  That would be tough to write. I
> guess you could do it -- besides overloading *ALL* of the
> operator<< functions, you would also have to return a temporary
> proxy object BY VALUE. That way, the temporary would eventually
> get destroyed, giving you a chance to unlock it.
>
> Even so, it helps code like this:
>     std::cout << "Hello, "
>         << title << ' ' << firstname << ' ' << middle_init
>         << ' ' << lastname << ".\n"
>         << "May I please call you " << firstname << "?\n"
>         << "Type Y if this is okay, "
>         << "or type N if you prefer me to call you"
>         << title << ' ' << lastname << " : ";
>     std::cin >> yesno;
> But it wouldn't help code like this:
>     std::cout << "Hello, ";
>     std::cout << title << ' ' << firstname << ' ' << middle_init;
>     std::cout << ' ' << lastname << ".\n";
>     std::cout << "May I please call you " << firstname << "?\n";
>     std::cout << "Type Y if this is okay, ";
>     std::cout << "or type N if you prefer me to call you";
>     std::cout << title << ' ' << lastname << " : ";
>     std::cin >> yesno;
> Which I would have thought was logically equivalent.
>
> Perhaps what we really need is a manipulator. It's use would be
> optional on single-threaded applications; on multi-user applications
> it would ensure that all output was grouped. Then we would have:
>     std::cout << lockstream << "Hello, ";
>     std::cout << title << ' ' << firstname << ' ' << middle_init;
>     std::cout << ' ' << lastname << ".\n";
>     std::cout << "May I please call you " << firstname << "?\n";
>     std::cout << "Type Y if this is okay, ";
>     std::cout << "or type N if you prefer me to call you";
>     std::cout << title << ' ' << lastname << " : ";
>     // DANGEROUS -- lock is active during input, blocking all other threads!
>     std::cin >> yesno;
>     std::cout << unlockstream;

But I just realized that there's a better way, without any changes
to the standard.

Tell me if I'm wrong, but a stream isn't guaranteed to be flushed
until the program writes std::endl, right? So there are two ways
to take advantage of this.

First, a library could have every insert do an implied "lock."
Inserting endl would also do an implied "unlock" at the end.
So when inserting ANYTHING into the stream, your thread would
own the stream until the inserts were completed -- which is
marked by the endl. Sure, some code uses endl to mark the end
of a line, without being finished with it's output yet -- but
this would still be reasonably useful, and there's an easy
fix if the programmer deems it unacceptable.

A second method would eliminate any stalls. The stream output
could be written into per-thread buffers, instead of global
buffers. Then, when endl was written, the per-thread buffer
would be written to the real output stream in a single
thread-safe operation.

--
AllanW@my-dejanews.com is a "Spam Magnet" -- never read.
Please reply in USENET only, sorry.

-----== Posted via Deja News, The Leader in Internet Discussion ==-----
http://www.dejanews.com/rg_mkgrp.xp   Create Your Own Free Member Forum


[ 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    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: ncm@nospam.cantrip.org (Nathan Myers)
Date: 1998/08/12
Raw View
 <saroj@bear.com> wrote:
>
>In article <6qotvt$243$1@shell7.ba.best.com>,
>  ncm@nospam.cantrip.org (Nathan Myers) wrote:
>>
>> <saroj@bear.com> wrote:
>> >
>> >In article <35CC756E.417C@pratique.fr>,
>> >  bonnardv@pratique.fr wrote:
>> >> Could we have a project/subgroup on MT safety, and publish
>> >> a MT safe C++ standard (actually a simple diff with the
>> >> C++ standard) ?
>> >>
>> >
>> >Actually, this is sorely needed. We should have sbumpc_unlocked,
>> >snextc_unlocked, ... functions in streambuf and xxx_unlocked
>> >functions in iostream (like getc_unlocked, flockfile
>> >in C binding of POSIX).
>>
>> An addendum on MT-safety would be welcome, but locking at the level of
>> streambuf inline members is exactly the wrong way to go about it.
>> The whole point of these members is speed, and locking per-character
>> provides very little benefit for any real program.
>>
>> The standard provides "istream::sentry" and "ostream::sentry" objects,
>> and MT-safety can be tied to these without harming performance, and
>> without introducing new interfaces for use in normal operators <<
>> and >>.  When possible, that is the correct way to introduce MT-safety.
>>
>
>sbumpc_unlocked() is similar to POSIX getc_unlocked(). POSIX getc()
>locks (by design). C++ solution of course, can be different. Sun C++
>iostream has a flag (MT::unsafe_object), which if set, disables
>locking; so you can use normal >> and << operators. This is similar
>to your suggestion of using sentry object to control locking, but
>it is only for formatted operations. How about unformatted ones?

No matter what locking individual members of a stream do, your
program must coordinate access to the stream or you will have
chaos.

If you always create a sentry object before using unformatted
operations on the stream, and maintain it for the full duration
of operations that must be contiguous, then you will have no
concurrency problem and no performance problem.

POSIX getc() locking is just unfortunate, evidence of confused design.
It harms performance without solving or providing tools to solve
the real problem.

--
Nathan Myers
ncm@nospam.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    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: Matt Austern <austern@sgi.com>
Date: 1998/08/12
Raw View
saroj@bear.com writes:

> sbumpc_unlocked() is similar to POSIX getc_unlocked(). POSIX getc()
> locks (by design). C++ solution of course, can be different. Sun C++
> iostream has a flag (MT::unsafe_object), which if set, disables
> locking; so you can use normal >> and << operators. This is similar
> to your suggestion of using sentry object to control locking, but
> it is only for formatted operations. How about unformatted ones?

Unformatted istream and ostream operations use sentry objects too.
Operations on streambufs don't, so Nathan's suggestion is that all
istream and ostream operations should do automatic locking and that no
streambuf operations should.

That's certainly one possibile scheme for a multithreaded I/O library.
Another (already mentioned) is that the library should do
per-character locking with sbumpc and sputc; and yet another is that
the library shouldn't do automatic locking at all.  It would ensure
that concurrent access to distinct streams is safe, but users who want
to access the same stream simultaneously from multiple threads would
do their own locking.

(The motivation for leaving out automatic locking is that automatic
locking has the wrong level of granularity.  Something that in C you
would do with a single call to printf, in C++ you would do with a half
dozen invocations of operator<<.  You probably want to lock the entire
sequence, not each character or each operator<< individually.)


[ 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    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: abrahams@motu.com
Date: 1998/08/12
Raw View
In article <fxtpve6kuqd.fsf@isolde.engr.sgi.com>,
  Matt Austern <austern@sgi.com> wrote:
> (The motivation for leaving out automatic locking is that automatic
> locking has the wrong level of granularity.  Something that in C you
> would do with a single call to printf, in C++ you would do with a half
> dozen invocations of operator<<.  You probably want to lock the entire
> sequence, not each character or each operator<< individually.)

One way to handle this (with a differently-designed library, of course),
would be to have all streaming operations return a stream object which
maintained the lock and delegated all further operations to the original
stream. That would at least lock operator<<() sequences into contiguity.

-Dave

-----== Posted via Deja News, The Leader in Internet Discussion ==-----
http://www.dejanews.com/rg_mkgrp.xp   Create Your Own Free Member Forum


[ 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    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: saroj@bear.com
Date: 1998/08/13
Raw View
In article <fxtpve6kuqd.fsf@isolde.engr.sgi.com>,
  Matt Austern <austern@sgi.com> wrote:
>
> saroj@bear.com writes:
>
> > sbumpc_unlocked() is similar to POSIX getc_unlocked(). POSIX getc()
> > locks (by design). C++ solution of course, can be different. Sun C++
> > iostream has a flag (MT::unsafe_object), which if set, disables
> > locking; so you can use normal >> and << operators. This is similar
> > to your suggestion of using sentry object to control locking, but
> > it is only for formatted operations. How about unformatted ones?
>
> Unformatted istream and ostream operations use sentry objects too.
> Operations on streambufs don't, so Nathan's suggestion is that all
> istream and ostream operations should do automatic locking and that no
> streambuf operations should.
>
> That's certainly one possibile scheme for a multithreaded I/O library.
> Another (already mentioned) is that the library should do
> per-character locking with sbumpc and sputc; and yet another is that
> the library shouldn't do automatic locking at all.  It would ensure
> that concurrent access to distinct streams is safe, but users who want
> to access the same stream simultaneously from multiple threads would
> do their own locking.
>
> (The motivation for leaving out automatic locking is that automatic
> locking has the wrong level of granularity.  Something that in C you
> would do with a single call to printf, in C++ you would do with a half
> dozen invocations of operator<<.  You probably want to lock the entire
> sequence, not each character or each operator<< individually.)
>

Yes, this is the best possible scheme. It also agrees with what is
considered the best practice for library thread-safety: lock only
the internal shared state. This will result in as little locking
as possible.

      -- Saroj Mahapatra

-----== Posted via Deja News, The Leader in Internet Discussion ==-----
http://www.dejanews.com/rg_mkgrp.xp   Create Your Own Free Member Forum


[ 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    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: ncm@nospam.cantrip.org (Nathan Myers)
Date: 1998/08/11
Raw View
<saroj@bear.com> wrote:
>
>In article <35CC756E.417C@pratique.fr>,
>  bonnardv@pratique.fr wrote:
>> Could we have a project/subgroup on MT safety, and publish
>> a MT safe C++ standard (actually a simple diff with the
>> C++ standard) ?
>>
>
>Actually, this is sorely needed. We should have sbumpc_unlocked,
>snextc_unlocked, ... functions in streambuf and xxx_unlocked
>functions in iostream (like getc_unlocked, flockfile
>in C binding of POSIX).

An addendum on MT-safety would be welcome, but locking at the level of
streambuf inline members is exactly the wrong way to go about it.
The whole point of these members is speed, and locking per-character
provides very little benefit for any real program.

The standard provides "istream::sentry" and "ostream::sentry" objects,
and MT-safety can be tied to these without harming performance, and
without introducing new interfaces for use in normal operators <<
and >>.  When possible, that is the correct way to introduce MT-safety.

--
Nathan Myers
ncm@nospam.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    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: saroj@bear.com
Date: 1998/08/11
Raw View
In article <6qotvt$243$1@shell7.ba.best.com>,
  ncm@nospam.cantrip.org (Nathan Myers) wrote:
>
> <saroj@bear.com> wrote:
> >
> >In article <35CC756E.417C@pratique.fr>,
> >  bonnardv@pratique.fr wrote:
> >> Could we have a project/subgroup on MT safety, and publish
> >> a MT safe C++ standard (actually a simple diff with the
> >> C++ standard) ?
> >>
> >
> >Actually, this is sorely needed. We should have sbumpc_unlocked,
> >snextc_unlocked, ... functions in streambuf and xxx_unlocked
> >functions in iostream (like getc_unlocked, flockfile
> >in C binding of POSIX).
>
> An addendum on MT-safety would be welcome, but locking at the level of
> streambuf inline members is exactly the wrong way to go about it.
> The whole point of these members is speed, and locking per-character
> provides very little benefit for any real program.
>
> The standard provides "istream::sentry" and "ostream::sentry" objects,
> and MT-safety can be tied to these without harming performance, and
> without introducing new interfaces for use in normal operators <<
> and >>.  When possible, that is the correct way to introduce MT-safety.
>

sbumpc_unlocked() is similar to POSIX getc_unlocked(). POSIX getc()
locks (by design). C++ solution of course, can be different. Sun C++
iostream has a flag (MT::unsafe_object), which if set, disables
locking; so you can use normal >> and << operators. This is similar
to your suggestion of using sentry object to control locking, but
it is only for formatted operations. How about unformatted ones?

      -- Saroj Mahapatra


-----== Posted via Deja News, The Leader in Internet Discussion ==-----
http://www.dejanews.com/rg_mkgrp.xp   Create Your Own Free Member Forum


[ 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    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: Valentin Bonnard <bonnardv@pratique.fr>
Date: 1998/08/08
Raw View
> David R Tribble <david.tribble@noSPAM.central.beasys.com> writes:
>
> > (I've read
> > that there are difficult problems to solve in making MT-safe
> > versions of the STL, though.)
>
> There are technical issues, yes.

I don't think there are any non trivial problems with the STL
except in optimising allocators.

> A different problem is that "MT-safe" means different things to
> different people.

To me it means that the behaviour of a library under MT isn't
left undefined.

This definition implies that the statement

    This library cannot be used by more than one thread.

makes any library MT safe (but in a useless way).

Could we have a project/subgroup on MT safety, and publish
a MT safe C++ standard (actually a simple diff with the
C++ standard) ?

--

Valentin Bonnard                mailto:bonnardv@pratique.fr
info about C++/a propos du C++: http://pages.pratique.fr/~bonnardv/


[ 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    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: saroj@bear.com
Date: 1998/08/09
Raw View
In article <35CC756E.417C@pratique.fr>,
  bonnardv@pratique.fr wrote:
> > David R Tribble <david.tribble@noSPAM.central.beasys.com> writes:
> >
> > > (I've read
> > > that there are difficult problems to solve in making MT-safe
> > > versions of the STL, though.)
> >
> > There are technical issues, yes.
>
> I don't think there are any non trivial problems with the STL
> except in optimising allocators.
>
> > A different problem is that "MT-safe" means different things to
> > different people.
>
> To me it means that the behaviour of a library under MT isn't
> left undefined.
>
> This definition implies that the statement
>
>     This library cannot be used by more than one thread.
>
> makes any library MT safe (but in a useless way).
>
> Could we have a project/subgroup on MT safety, and publish
> a MT safe C++ standard (actually a simple diff with the
> C++ standard) ?
>

Actually, this is sorely needed. We should have sbumpc_unlocked,
snextc_unlocked, ... functions in streambuf and xxx_unlocked
functions in iostream (like getc_unlocked, flockfile
in C binding of POSIX). Sun C++ has such interface, but
unfortunately they are not in standard. Fortunately, SGI STL
is thread-safe and that is the right way to follow for other
STL libraries.

     -- Saroj Mahapatra

-----== Posted via Deja News, The Leader in Internet Discussion ==-----
http://www.dejanews.com/rg_mkgrp.xp   Create Your Own Free Member Forum


[ 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    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]