Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Thread safety of cout

6 views
Skip to first unread message

Maciej Sobczak

unread,
May 11, 2002, 5:34:05 AM5/11/02
to
Hi,

The problem of cout's thread safety (ie. whether it is safe to use it from
different threads, no matter what effects can it have on the output data)
was discussed here a couple of times.

Having read some of the posts and flipping through the Standard, my current
understanding is that:

1. The Standard does not mandate any synchronization for any standard class,
including iostreams.
2. The Standard does not explicitely mandate any synchronization for cout
and cerr objects, but bearing in mind that these objects are singletons,
decent library implementations use some internal locking at least to prevent
corrupting cout's internal state when two or more threads try to use it.
Note that I'm not talking about the output data (which can get intermixed)
here, but only about potential program crash.

In other words, thread safety of cout is not mandatory, but it is a QoI
issue.

Please comment on it and bring me back on the right rail if I'm wrong.

Cheers,

--
Maciej Sobczak
http://www.maciejsobczak.com/


[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]
[ about comp.lang.c++.moderated. First time posters: do this! ]

James Kanze

unread,
May 13, 2002, 1:37:27 PM5/13/02
to
"Maciej Sobczak" <mac...@maciejsobczak.com> writes:

|> The problem of cout's thread safety (ie. whether it is safe to use
|> it from different threads, no matter what effects can it have on
|> the output data) was discussed here a couple of times.

|> Having read some of the posts and flipping through the Standard,
|> my current understanding is that:

|> 1. The Standard does not mandate any synchronization for any
|> standard class, including iostreams.

Correct.

|> 2. The Standard does not explicitely mandate any synchronization
|> for cout and cerr objects, but bearing in mind that these objects
|> are singletons, decent library implementations use some internal
|> locking at least to prevent corrupting cout's internal state when
|> two or more threads try to use it. Note that I'm not talking
|> about the output data (which can get intermixed) here, but only
|> about potential program crash.

I'm not sure I agree.

First, I would expect that cout et al. use the same policy as any
other stream; except in toy programs, I've never actually written a
line which starts "cout << ...". (I often output to cout, but always
in functions which take an ostream& as a parameter.) So there is no
way my code could benefit from any special handling.

Second, since I can't output without some external synchronization (to
avoid the intermixing of the output data), any additional internal
synchronization would be simply wasted CPU cycles.

A stronger argument for internal synchronization could possibly be
made for cerr.

|> In other words, thread safety of cout is not mandatory, but it is
|> a QoI issue.

Right, but what a quality implementation should do about it is still
open.

--
James Kanze mailto:ka...@gabi-soft.de
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
Ziegelhüttenweg 17a, 60598 Frankfurt, Germany Tel. +49(0)179 2607481

Alexander Terekhov

unread,
May 13, 2002, 1:53:20 PM5/13/02
to
< Followup-To: comp.programming.threads >

Maciej Sobczak wrote:
>
> Hi,
>
> The problem of cout's thread safety (ie. whether it is safe to use it from
> different threads, no matter what effects can it have on the output data)
> was discussed here a couple of times.
>
> Having read some of the posts and flipping through the Standard, my current
> understanding is that:
>
> 1. The Standard does not mandate any synchronization for any standard class,
> including iostreams.

Yep -- C++ standard (ISO/IEC 14882:1998). However, "some"
issues of thread-safety w.r.t. *"C"* streams ARE covered
by "POSIX-C-extensions standard" -- ISO/IEC 9945-1:1996,
new (2001) version of which (not yet passed ISO/IEC though,
AFAIK; only IEEE and The Open Group itself) is free and
available online:

http://www.opengroup.org/onlinepubs/007904975/toc.htm

> 2. The Standard does not explicitely mandate any synchronization for cout
> and cerr objects, but bearing in mind that these objects are singletons,

Some singletons could be totally *thread-private*, BTW
(global/process-wide does NOT necessarily mean
always-thread-shared, *in effect*, IMO).

> decent library implementations use some internal locking at least to prevent
> corrupting cout's internal state when two or more threads try to use it.

To me, that's not really the "best" approach, unless
I'm missing something.

> Note that I'm not talking about the output data (which can get intermixed)
> here, but only about potential program crash.

Garbage input/output is WORSE than "potential program
crash" (i.e. lack of proper sync -- violation of 4.10
rules to begin with), in my view.

> In other words, thread safety of cout is not mandatory, but it is a QoI
> issue.

Well, but what's your implied meaning of "thread-safety"
term, in general? To me, it does NOT necessarily mean
synchronized-to-death-no-matter-whether-you-need-it-or-not. ;-)

> Please comment on it and bring me back on the right rail if I'm wrong.

I'm NOT sure whether you are wrong or right... this entire
topic w.r.t. file/stream-thread-safety is NOT really
"all-clear" to me. BTW, somewhat "related" c.p.t stuff/thread
(with not that many replies... yet ;-) unfortunately):

http://groups.google.com/groups?threadm=3CD8F947.36A32C55%40web.de
(Subject: Re: thread safe)

regards,
alexander.

Jonathan de Boyne Pollard

unread,
Jul 2, 2002, 6:53:03 AM7/2/02
to

MS> 2. The Standard does not explicitely mandate any synchronization for cout
MS> and cerr objects, but bearing in mind that these objects are singletons,

AT> Some singletons could be totally *thread-private*, BTW

A "thread-private singleton" implies one singleton object for each
thread. This contradicts the C++ standard, which clearly indicates that
there is _one_ object named (for example) "std::cin".

Jonathan de Boyne Pollard

unread,
Jul 2, 2002, 6:55:56 AM7/2/02
to

MS> In other words, thread safety of cout is not mandatory, but it
MS> is a QoI issue.

AT> Well, but what's your implied meaning of "thread-safety"
AT> term, in general?

One metric is whether it is possible for an applications programmer to
write an application with multiple threads that uses (say) "std::cout"
in more than one thread, and have defined behaviour for that
application. Currently, it is possible, by the simple expedient of
using a guard of some sort around all code that manipulates
"std::cout".

People who want "thread safety of cout" actually want such a mechanism
to be embedded in the library. One of the several problems, from the
point of view of standardisation, is picking the correct locking
granularity. Another is picking the lock type. A third is in defining
the interface by which the mechanism is exposed to the applications
programmer who may want to manipulate it himself/herself. Then there
are the problems from the point of view of the applications programmer,
such as (as you alluded to) that of the imposed mandatory inefficiency.

Alexander Terekhov

unread,
Jul 2, 2002, 3:39:21 PM7/2/02
to

Jonathan de Boyne Pollard wrote:
>
> MS> 2. The Standard does not explicitely mandate any synchronization for cout
> MS> and cerr objects, but bearing in mind that these objects are singletons,
>
> AT> Some singletons could be totally *thread-private*, BTW
>
> A "thread-private singleton" implies one singleton object for each
> thread.

No. *thread-specific* things [something along the lines of
sort-of-thread-specific-static-storage-duration provided by
__declspec(thread), for example] would imply "one singleton
object for each thread"; 'dynamic' TSD aside for a moment.

> This contradicts the C++ standard, which clearly indicates that
> there is _one_ object named (for example) "std::cin".

Uhmm. I don't see any contradictions here. "std::cin" *COULD*
be {used as} totally 'thread-private' too, BTW. ;-)

regards,
alexander.

William E. Kempf

unread,
Jul 3, 2002, 7:01:01 AM7/3/02
to
"Jonathan de Boyne Pollard" <J.deBoyn...@tesco.net> wrote in message
news:3D1ADD1F...@tesco.net...

>
> MS> In other words, thread safety of cout is not mandatory, but it
> MS> is a QoI issue.
>
> AT> Well, but what's your implied meaning of "thread-safety"
> AT> term, in general?
>
> One metric is whether it is possible for an applications programmer to
> write an application with multiple threads that uses (say) "std::cout"
> in more than one thread, and have defined behaviour for that
> application. Currently, it is possible, by the simple expedient of
> using a guard of some sort around all code that manipulates
> "std::cout".
>
> People who want "thread safety of cout" actually want such a mechanism
> to be embedded in the library. One of the several problems, from the
> point of view of standardisation, is picking the correct locking
> granularity. Another is picking the lock type. A third is in defining
> the interface by which the mechanism is exposed to the applications
> programmer who may want to manipulate it himself/herself. Then there
> are the problems from the point of view of the applications programmer,
> such as (as you alluded to) that of the imposed mandatory inefficiency.

The only granularity that would be possible is in the call to the methods of
the iostream (such as operator<<). Larger granularity requires the user to
invoke the synchronization, at which point it's best left to the user to do
all of the synchronization. Since synchronizing only at method invocations
isn't very practical (think about the simple statement: std::cout << "foo"
<< std::endl; which has two calls and thus a race) for the end user because
of the race, s/he must provide their own synchronization. (Note that it may
still be beneficial/necessary for the lower synchronization granularity, it
just doesn't help the end user produce meaningful output.)

Bill Kempf

Alexander Terekhov

unread,
Jul 3, 2002, 11:50:23 PM7/3/02
to

"William E. Kempf" wrote:
[...]

> The only granularity that would be possible is in the call to the methods of
> the iostream (such as operator<<). Larger granularity requires the user to
> invoke the synchronization,

Uhmm, some auto_ptr-like [with move-on-copy semantics] auto_unlock proxy
might work probably here as well... to provide I/O "statement" locking
granularity w/o explicit user invocations [w.r.t. locking calls].

> at which point it's best left to the user to do all of the synchronization.

Agreed... and the example of somewhat (IMHO) 'not best' [unless I am
just missing and/or misunderstanding something] approach can be found
here:

http://www.opengroup.org/onlinepubs/007904975/functions/flockfile.html
(*recursive* stdio locks)

"....
All functions that reference ( FILE *) objects shall behave as if they"
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

< all *stdio.h* "functions that reference ( FILE *)" meant here, I guess >

"use flockfile() and funlockfile() internally to obtain ownership of these
( FILE *) objects.
...."

http://www.opengroup.org/onlinepubs/007904975/functions/fwrite.html

"....
For each object, size calls shall be made to the fputc() function"
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
...."

Also, consider:

"....
Application writers and implementors should be aware that there are
potential deadlock problems on FILE objects. For example, the line-
buffered flushing semantics of stdio (requested via {_IOLBF}) require
that certain input operations sometimes cause the buffered contents
of implementation-defined line-buffered output streams to be flushed.
If two threads each hold the lock on the other's FILE, deadlock ensues.
This type of deadlock can be avoided by acquiring FILE locks in a
consistent order. In particular, the line-buffered output stream deadlock
can typically be avoided by acquiring locks on input streams before locks
on output streams if a thread would be acquiring both."

Sure; but I think that my life would be much nicer WITHOUT flockfile
'built-in'/implicit locking, however.

"In summary, threads sharing stdio streams with other threads can use
flockfile() and funlockfile() to cause sequences of I/O performed by
a single thread to be kept bundled. The only case where the use of
flockfile() and funlockfile() is required is to provide a scope
protecting uses of the *_unlocked() functions/macros. This moves
the cost/performance tradeoff to the optimal point. ...."

Yeah, but the only little problem is that we have >>ONLY FOUR<<
"*_unlocked() functions/macros":

int getc_unlocked(FILE *);
int getchar_unlocked(void);
int putc_unlocked(int, FILE *);
int putchar_unlocked(int);

unless I'm missing something.

regards,
alexander.

P.S. http://www.opengroup.org/onlinepubs/007904975/functions/putc_unlocked.html

"....
RATIONALE

Some I/O functions are typically implemented as macros for performance
reasons (for example, putc() and getc()). For safety, they need to be
synchronized, but it is often too expensive to synchronize on every
character. Nevertheless, it was felt that the safety concerns were more
important; consequently, the getc(), getchar(), putc(), and putchar()
functions are required to be thread-safe. However, unlocked versions
are also provided with names that clearly indicate the unsafe nature
of their operation but can be used to exploit their higher performance.
These unlocked versions can be safely used only within explicitly locked
program regions, using exported locking primitives. In particular, a
sequence such as:

flockfile(fileptr);
putc_unlocked('1', fileptr);
putc_unlocked('\n', fileptr);
fprintf(fileptr, "Line 2\n");
funlockfile(fileptr);

is permissible, and results in the text sequence:

1
Line 2

being printed without being interspersed with output from other
threads.

It would be wrong to have the standard names such as getc(), putc(),
and so on, map to the "faster, but unsafe" rather than the "slower,
but safe'' versions. In either case, you would still want to inspect
all uses of getc(), putc(), and so on, by hand when converting existing
code. Choosing the safe bindings as the default, at least, results in
correct code and maintains the "atomicity at the function" invariant.
To do otherwise would introduce gratuitous synchronization errors into
converted code. Other routines that modify the stdio ( FILE *) structures
or buffers are also safely synchronized.

Note that there is no need for functions of the form getc_locked(),
putc_locked(), and so on, since this is the functionality of getc(),
putc(), et al. It would be inappropriate to use a feature test macro
to switch a macro definition of getc() between getc_locked() and
getc_unlocked(), since the ISO C standard requires an actual function
to exist, a function whose behavior could not be changed by the feature
test macro. Also, providing both the xxx_locked() and xxx_unlocked()
forms leads to the confusion of whether the suffix describes the behavior
of the function or the circumstances under which it should be used.

Three additional routines, flockfile(), ftrylockfile(), and funlockfile()
(which may be macros), are provided to allow the user to delineate a
sequence of I/O statements that are executed synchronously.

The ungetc() function is infrequently called relative to the other
functions/macros so no unlocked variation is needed.

FUTURE DIRECTIONS

None.

...."

0 new messages