Boost logo

Boost :

From: Csaba Szepesvari (szepes_at_[hidden])
Date: 1999-07-27 03:49:55


> >I have a related question: How would you use all these smart pointers in
> >multithreaded environments? (It's not a provocation but a very practical
> >issue - some kind of automatic garbage collection is usually essential in
> >multi-threaded programs e.g. dealing with GUIs.)
>
> I usually have the opposite problem: my simulators are single threaded
> (because the network batcher that allows me to use 100s of CPUs only
> handles single threads), and I would really like to have versions of libraries
> such as the STL that are *NOT* thread safe, since the thread mutexing
> often significantly impacts performance.

Yes, this is true enough. I also hate the unnecessary overhead when it comes to use STL (or any
other library) in single threaded environments. Note that the original Hewlett-Packard
implementation (ftp.cs.rpi.edu/pub/stl) was not thread-safe in any way and since the Microsoft
version is derived from the Hewlett-Packard version (the original one) it is not thread-safe.

SGI's version states the following:
"The SGI implementation of STL is thread-safe only in the sense that simultaneous accesses to
distinct containers are safe, and simultaneous read accesses to to shared containers are safe. If
multiple threads access a single container, and at least one thread may potentially write, then the
user is responsible for ensuring
mutual exclusion between the threads during the container accesses." They add that this is achieved
by ot having any static members of the containers. There are, however, some static members of the
allocators which need protection and which are protected.

Personally, I also prefer the templatized solution. However, still I feel that the issue I raised
has not been answered at all. Of course, I know that I can mutex everything and can do other things
(queuing requests, using ids with registries), protect counters, etc. (a good starting point on how
to protect reference counters and what is the price of that (in terms of speed) see the GotW
article http://www.cntc.com/resources/gotw045.html).

I think that example I provided raises a more fundamental issue. In fact (although I not stated it
explicitly in my previous mail), I assumed that you have a perfectly written reference counted
class in the sense that the reference counter is manipulated in a thread-safe way.

Let my try to explain the issue in a little more detail: Imagine that you call a method (for
simplicity let it be non-virtual) of an object. Of course, ***the object should be kept alive until
you return from the call*** (which may lost an indefinite time). In the case of employing a
reference counter this means that you need to increase the reference count *before every call* and
decrease it on returns. NB I have never seen a thread-safe reference counter implementation that
would do anything like that:-( One thing I could imagine (if I don't hack the compiler or the
language or whatever) is to protect every call by placing a temporary object on the stack,
something like

template <class T>
class TempRef
{
protected:
  T& t;
public:
  TempRef( T& _t ) : t( _t ) {};
  T* operator->() { return &t; };
};

template <class T>
TempRef<T> call( T& t )
{
  return TempRef<T>(t);
}

class A
{
public:
  double f() { return 1.0; };
};

void main()
{
  A a;
  call(a)->f();
}

Of course, this is not efficient (but thread-safe code can rarely be attributed to be efficient).
Another idea is to limit accesses to the object to go through various smart pointers. This means
that you should not be
able to expose (and store) the address of the object (you can do this). Then it is the
responsibility of the smart pointer to do the above steps (placing a temporary on the stack, etc.)
This has its own problems (the client would probably be limited in the usage, i.e. you probably
cannot protect the implementation against bad misuse), but perhaps someone could work out a
reasonable version.

> Certainly, both reference counts and mark/sweep garbage collection have
> been done any number of times in a multithreaded environment. That doesn't
> mean it's easy to do when starting from scratch, but it proves that it is possible.
>

True enough, garbage collectors can overcome this problem since calling a method is implemented by
pushing the address of the object on the stack (likewise it were the 0th parameter of the call) --
a fact that I overlooked in my previous mail. (Is this standardized?) However, my original question
targeted smart-pointer (basically reference counted and cow pointers) classes. I mean, it is
perfectly okay to say that we can't do it in a thread-safe way, but doing it in a semi threadsafe
way is dangerous to say the least.

> I see no reason why the live_ptr and regd_ptr I discussed in earlier email
> are any harder to do.

live_ptr-s (and regd_ptr-s also) have their own problems in multi-threaded environments. You should
not think more about them than they are: i.e.. the kind of protection they provide in
multi-threaded environments is very weak. (The pointee can disappear while you are in a call.) This
smart pointers are dangerous because people tend to think that they are safe.

> More importantly, you raise the issue of whether BOOST libraries should be multithread
> safe from the outset, or what.
>

See comments above. I suggest to include a statement in the description of every piece of code if
it is multi-thread safe. And sometimes, occasionally think about multi-thread safe versions.

Ideas?

- Csaba


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk