Topic: ref counted std::string and thread safety.


Author: "Markus Mauhart" <Markus.Mauhart@chello.at>
Date: 2000/09/13
Raw View
"Syver Enstad" <syver.enstad@sensewave.com> wrote in message news:8pj2he$ihp$1@troll.powertech.no...

> Yes, It does, It's the MSVC Dinkumware library from VC++ 6.0 SP 4 with
> the Dinkum patches applied (but I have not edited it to remove the reference
> counting.)

> a deep copy. (Hmm, maybe I should disable the reference counting in the
> library headers).

IMHO you cannot disable the reference counting.
Changing the headers will not work, i.e. the patch doesnt solve what it claims.

Details:
The method explained by Dinkumware to "disable the reference counting" is to change the initialization of "enum _Mref {_FROZEN =
255};" to "enum _Mref {_FROZEN = 1};", to be done in file <xtring>.

But <xstring> also contains the following two declarations ..
"extern template class _CRTIMP basic_string<char, char_traits<char>, allocator<char> >;"
"extern template class _CRTIMP basic_string<wchar_t, char_traits<wchar_t>, allocator<wchar_t> >;"
This usage of 'extern' is described by MS as "You can use the extern keyword to prevent the automatic instantiation of members."
The effect is that the code for the two classes is taken from the MS shipped prebuilt libraries, either the DLL or the static linked
library.

You can observe the (non-)effect of the patch in the debugger with the following code:
{string a,b,c; a="123" ;b=a ;c=a}
On my machine, a,b,c then share the same character array (a._Ptr==b._Ptr==c._Ptr) and the reference counter is incremented to 2
(watch the byte (a._Ptr)[-1]).


Markus.

---
[ 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: "Syver Enstad" <syver.enstad@sensewave.com>
Date: Mon, 11 Sep 2000 23:08:43 GMT
Raw View
> [*] I realize this is just an illustrative fragment, but just a note for
> other readers: In real production code, this may not be the right level
> at which to do the serialization.

I'm all ears, what other level(s) did you have in mind? I don't pretend to
be good at writing multithreaded code. I just try to let the threads have as
little to do with each other as possible and mutex lock any shared
resources.

> Indeed, if your implementation of std::string
> uses reference counting (a.k.a. copy-on-write, or COW), then either:

Yes, It does, It's the MSVC Dinkumware library from VC++ 6.0 SP 4 with
the Dinkum patches applied (but I have not edited it to remove the reference
counting.)

> a) it's perfectly safe but certainly slower than non-thread-safe COW, if
> your library implementer added additional locking and unsharing inside
> std::string itself; or

I am pretty sure that, that is done. So in other words it's okay. That's
good to know, in my experience you can never be to sure about things when it
comes to multithreading. But it's going to be slow. As far as I can remember
you wrote in your CUJ article that multithreaded implementations of
std::string with Copy on Write was slower than a std::string that just made
a deep copy. (Hmm, maybe I should disable the reference counting in the
library headers).

Thanks!




---
[ 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: "Syver Enstad" <syver@osiris.no>
Date: Sun, 10 Sep 2000 06:55:18 GMT
Raw View
I just wondered:

Should the following be safe in a multithreaded program?

 void SomeClass::setString(const std::string str)
{
    Lock aLock(m_mutex);
    m_str = str;
}

All accesses to m_str will also be locked by the same mutex.

I couldn't fail to recognize that m_str and str was pointing to the same
char*.

Won't evil happen if:

SomeClass obj;

std::string str("Bla, bla");
obj.setString(str);
str = "Bob, bob); // another thread touches obj.m_str at the same time as
str is assigned to.

---
[ 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: Herb Sutter <hsutter@peerdirect.com>
Date: Mon, 11 Sep 2000 12:59:15 GMT
Raw View
This is a good example illustrating the extent to which calling code is
responsible for thread safety on the one hand, and to which objects must
protect themselves on the other hand.

"Syver Enstad" <syver@osiris.no> writes:
>Should the following be safe in a multithreaded program?
>
> void SomeClass::setString(const std::string str)
>{
>    Lock aLock(m_mutex);
>    m_str = str;
>}
>
>All accesses to m_str will also be locked by the same mutex.

Right. Your SomeClass code performs its required level of due
diligence[*]: It protects itself the right way, and does everything it
can to serialize access to the potentially thread-shared m_str resource.

[*] I realize this is just an illustrative fragment, but just a note for
other readers: In real production code, this may not be the right level
at which to do the serialization.

>I couldn't fail to recognize that m_str and str was pointing to the same
>char*.
>
>Won't evil happen if:
>
>SomeClass obj;
>
>std::string str("Bla, bla");
>obj.setString(str);
>str = "Bob, bob); // another thread touches obj.m_str at the same time as
>str is assigned to.

Very good observation. Indeed, if your implementation of std::string
uses reference counting (a.k.a. copy-on-write, or COW), then either:

a) it's perfectly safe but certainly slower than non-thread-safe COW, if
your library implementer added additional locking and unsharing inside
std::string itself; or

b) it's evil and just as fast as non-thread-safe COW, if your library
implementer forgot to add the additional protection.

See:

- my article "Optimizations That Aren't (In a Multithreaded World)" in
the July 1999 C/C++ Users Journal, which is devoted entirely to this
issue; and

- GotW #45 (http://www.peerdirect.com/resources/gotw045.html) which
contains an earlier/overlapping version of the material in the CUJ
article.

For related information about reference counting, see also GotW #43 and
#44.

Herb

---
Herb Sutter (mailto:hsutter@peerdirect.com)

CTO, PeerDirect Inc. (http://www.peerdirect.com)
Contributing Editor, C/C++ Users Journal (http://www.cuj.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              ]