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

Levels of Thread Safety

5 views
Skip to first unread message

Carl Daniel

unread,
Dec 29, 2001, 9:17:42 AM12/29/01
to
In another (now very lengthy) thread on the topic of thread safety, I
proposed:

>>>>
It sounds to me like we're zeroing in on a set of thread-safety descriptions
which parallel the Abrahams exception safety guarantees.

0. No guarantee. The performance of the class is undefined in the presense
of multiple threads, even if each thread uses a unique set of instances of
the class.

1. Weak guarantee. The performance of the class is well-defined in the
presense of multiple threads as long as each thread uses a set of instances
distinct from that of any other thread.

2. Strong guarantee. The class state will remain valid even in the presense
of multiple threads calling any combination of member functions on shared
instances of the class.

3. Total ordering guarantee. The class state will reflect the results of
the operations applied by any number of threads as if those operations had
been performed in some unspecified order by a single thread.

The wording needs a lot of work, but it seems to me that the idea is worth
merit. Most classes provide the level 0 or 1 guarantees, and for most
classes, that is sufficient. Classes like ostream probably need to provide
the strong guarantee, since overlapping console I/O does happen, messy
though it may be. Classes like an inter-thread message queue must provide
the total ordering guarantee.
<<<<

The continuing discussion on that other thread shows one thing clearly:
there's little agreement on what "Thread Safe" means when used to describe a
class. The situation is a bit better when describing a function, but only a
bit.

What do people think about these classifications? Can we establish some
common terminology so that when, in the future, someone says that the class
provides the "Strong thread-safety guarantee" that everyone will agree what
it means?

Are these levels sufficient? Are they reduntant? Is there a level
somewhere between 1 and 2 which allows a single writer and multiple readers?
Are there so many different twists to "thread safety" that categorization is
futile?

-cd


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

Sergey P. Derevyago

unread,
Dec 29, 2001, 2:08:42 PM12/29/01
to
First of all, thread safety (unlike exception safety) is not a local issue.
I.e. we shouldn't speak about TS-classes, we have to speak about
TS-applications (with dead/livelock considerations involved and so on).

Carl Daniel wrote:
> 2. Strong guarantee. The class state will remain valid even in the presense
> of multiple threads calling any combination of member functions on shared
> instances of the class.

IMHO the "calling any combination of member functions on shared instances"
wording is practically useless because certain combinations are
deadlock-prone. We'd better don't re-invent the wheel but take a look at DBMS
theory and at the transaction isolation levels in particular.
--
With all respect, Sergey. http://cpp3.virtualave.net/
mailto : ders at skeptik.net

Andrei Alexandrescu

unread,
Dec 29, 2001, 5:15:15 PM12/29/01
to
"Carl Daniel" <cpda...@pacbell.net> wrote in message
news:2YdX7.17100$Ge.394...@newssvr14.news.prodigy.com...

> It sounds to me like we're zeroing in on a set of thread-safety
descriptions
> which parallel the Abrahams exception safety guarantees.
[snipetto]

> Are these levels sufficient? Are they reduntant? Is there a level
> somewhere between 1 and 2 which allows a single writer and multiple
readers?
> Are there so many different twists to "thread safety" that
categorization is
> futile?

First, I would note that "weak guarantee" would be clearer if you
mentioned that the class works if you use synchronization in client
code. I guess the way you formulated things kind of implies that, but
I'm not sure.

Second, I'm not sure I understand the difference between the strong
guarantee and the total ordering guarantee. Could you please give an
example?

There is an important level of thread safety beyond what you call "the
strong guarantee". I would call it "the bivalent guarantee".

1.5: Bivalent guarantee: The class provides two functionally identical
interfaces, one that enforces the strong guarantee, and one that
enforces the weak guarantee so it allows efficient external locking.
Doug Schmidt has a good article about this pattern
(http://www.cs.wustl.edu/~schmidt/PDF/locking-patterns.pdf). He uses
naming conventions and protection levels to differentiate between the
two interfaces. My controversial article
(http://www.cuj.com/experts/1902/alexandr.htm) describes how to
implement the two interfaces more elegantly and have the compiler
check external locking. (In a nutshell, the volatile interface
provides the strong guarantee and the regular interface provides the
weak guarantee.)

Incidentally, I'm now working on an article for InformIT that shows
how to implement the bivalent guarantee, again by using the type
system to enforce external locking, but without hijacking the volatile
keyword.

I also think there is a multi-object transaction guarantee. The idea
is that when you want to do a multi-object transaction, you want to
lock multiple objects and you want to do that in a ordered manner
(order does matter). I'm not sure if that's something imposed to the
class or from the outside of the class, I have to think a bit more
about that.


Andrei

Carl Daniel

unread,
Dec 29, 2001, 5:16:22 PM12/29/01
to
"Sergey P. Derevyago" <non-ex...@iobox.com> wrote in message
news:3C2DDA38...@iobox.com...

> First of all, thread safety (unlike exception safety) is not a local
issue.
> I.e. we shouldn't speak about TS-classes, we have to speak about
> TS-applications (with dead/livelock considerations involved and so on).
>

Thread safety, like exception safety, is not totally a local issue. To
claim that thread safety guarantees are not applicable to classes is simply
not true.

> Carl Daniel wrote:
> > 2. Strong guarantee. The class state will remain valid even in the
presense
> > of multiple threads calling any combination of member functions on
shared
> > instances of the class.
> IMHO the "calling any combination of member functions on shared instances"
> wording is practically useless because certain combinations are
> deadlock-prone. We'd better don't re-invent the wheel but take a look at
DBMS
> theory and at the transaction isolation levels in particular.

That's true for some classes, but not for others. I believe it is useful
for some classes to provide this level of thread safety guarantee (and even
the stronger total-ordring guarantee).

None of the above says that local (class level) attention to thread safety
is sufficient - it is not. But it is necessary.

It is in that spirit that I offer the proposed thread-safety guarantees for
classes: a necessary, but not sufficient part of constructing correct
multi-threaded applications.

-cd

Arnold Hendriks

unread,
Dec 30, 2001, 4:27:02 PM12/30/01
to
Carl Daniel <cpda...@pacbell.net> wrote:
> Are these levels sufficient? Are they reduntant? Is there a level
> somewhere between 1 and 2 which allows a single writer and multiple readers?
There may also be classes which by nature support level 1 on some
member functions, and level 2 on others. Imagine a class that represents
an UDP socket. It may be perfectly okay to use non-const read() and write()
calls without any locking, but the opening and closing of the socket must
be synchronized, and cannot be mixed with read() and write() calls.

Anyway, this probably is true for all classes in some respect. Whether
or not the member functions can be called without locks, construction and
destruction must still be synchronized.

--
Arnold Hendriks <a.hen...@b-lex.com>
B-Lex Information Technologies, http://www.b-lex.com/

Carl Daniel

unread,
Dec 30, 2001, 4:31:44 PM12/30/01
to

"Andrei Alexandrescu" <andre...@hotmail.com> wrote in message
news:a0l9j7$lhmll$1...@ID-14036.news.dfncis.de...

> "Carl Daniel" <cpda...@pacbell.net> wrote in message
> news:2YdX7.17100$Ge.394...@newssvr14.news.prodigy.com...
> > Are these levels sufficient? Are they reduntant? Is there a level
> > somewhere between 1 and 2 which allows a single writer and multiple
> readers?
> > Are there so many different twists to "thread safety" that
> categorization is
> > futile?
>
> First, I would note that "weak guarantee" would be clearer if you
> mentioned that the class works if you use synchronization in client
> code. I guess the way you formulated things kind of implies that, but
> I'm not sure.

Yes, that was the intent. What I termed "the weak guarantee" would be
equivalent to the guarantee stated in the documentation for SGI's STL. The
term "Thread Neutral" has been receiving a favorable response to refer to
this level of thread safety.

>
> Second, I'm not sure I understand the difference between the strong
> guarantee and the total ordering guarantee. Could you please give an
> example?

Honestly, I'm not sure there is a difference, but perhaps there is. My
reasoning goes along these lines: clearly there are classes for which the
total ordering guarantee is required. Two examples come to mind: an
inter-thread message queue, and the implicit class which implements
malloc/free/realloc, etc (of course, it's entirely possible, if unlikely,
that these functions could be implemented by a class on some implementation,
so the example is not completely artificial).

For an example of the String guarantee, the situation which comes to my mind
is that of stream output (in the general sense, not restricted to
IOStreams). For example, on Windows, a file handle is a process-global
entity. Multiple threads may write to a single handle. The I/O system
guarantees that all of the output goes somewhere, and that the file state is
not damaged by such access, but makes no guarantees whatsoever about the
ordering of the data written by the various threads.

>
> There is an important level of thread safety beyond what you call "the
> strong guarantee". I would call it "the bivalent guarantee".
>

Interesting, though that sounds to me like a pattern - a class construction
idiom and not really an intermediate definition of thread safety.

> I also think there is a multi-object transaction guarantee. The idea
> is that when you want to do a multi-object transaction, you want to
> lock multiple objects and you want to do that in a ordered manner
> (order does matter). I'm not sure if that's something imposed to the
> class or from the outside of the class, I have to think a bit more
> about that.
>

I think so as well. While I've never encountered the need for this kind of
synchronization between threads in any program I've written, I can imagine
the need for such. I imagine that both the application and the idioms for
constructing such an application which is deadlock-free will closely
parallel a multi-client database application.

As James Kanze pointed out in the other long thread, there are so many
factors in thread safety that the most important thing is that each class
define exactly what, if any, guarantees it provides about thread safety. My
goal with this proposal is to establish a common set of terminology for
describing exception safety.

-cd

Carl Daniel

unread,
Dec 31, 2001, 6:33:56 AM12/31/01
to
"Arnold Hendriks" <a.hen...@b-lex.com> wrote in message
news:a0mutt$1s7$1...@news.btcnet.nl...

> Carl Daniel <cpda...@pacbell.net> wrote:
> > Are these levels sufficient? Are they reduntant? Is there a level
> > somewhere between 1 and 2 which allows a single writer and multiple
readers?
> There may also be classes which by nature support level 1 on some
> member functions, and level 2 on others. Imagine a class that represents
> an UDP socket. It may be perfectly okay to use non-const read() and
write()
> calls without any locking, but the opening and closing of the socket must
> be synchronized, and cannot be mixed with read() and write() calls.
>
> Anyway, this probably is true for all classes in some respect. Whether
> or not the member functions can be called without locks, construction and
> destruction must still be synchronized.

Yes, that is certainly true. It's probably not very meaningful to talk
about the thread-safety of an entire class by trying to shoehorn it into one
of a few definitions.

The idiom forwarded by Andrei Alexandrescu in another post is a prime
example: a class with two "identical" interfaces, one which provides thread
safety and one which does not.

-cd

Sergey P. Derevyago

unread,
Jan 3, 2002, 7:34:17 AM1/3/02
to
Carl Daniel wrote:
> Thread safety, like exception safety, is not totally a local issue. To
> claim that thread safety guarantees are not applicable to classes is simply
> not true.
The claim is: TS guarantees are not so useful with regard to classes as ES
guarantees are :)

IMHO we should talk about those guarantees which allow multiple threads to
run _simultaneously_ on _multiple_ CPUs. And it seems like thread-neutral is
the only guarantee in question:
1. It uses no locking so threads can really work in parallel.
2. It performes well regardless of a presence of MT.

In particular, I often dream about a parametrized namespace std, where I can
write:

void thread_1()
{
using namespace std<1>;
//...
}


void thread_2()
{
using namespace std<2>;
//...
}

and take the (mostly ;) thread-neutral guarantee: std<1> and std<2> use no
shared data so my threads do run in parallel. And I have only one class of
shared data/locking in the application -- _my_ shared data/locking.


--
With all respect, Sergey. http://cpp3.virtualave.net/
mailto : ders at skeptik.net

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

0 new messages