15 мая 2023 года "Исходники.РУ" отмечают своё 23-летие!
Поздравляем всех причастных и неравнодушных с этим событием!
И огромное спасибо всем, кто был и остаётся с нами все эти годы!

Главная Форум Журнал Wiki DRKB Discuz!ML Помощь проекту


MFC Internals and CString Across Threads

Chris Kirby -- cmk@isdsa.pgh.wec.com
Thursday, March 06, 1997

Environment: NT 4.0, VC++ 4.2b

I have been reading MFC Internals and I have a question about CString
across threads.  The book mentions how CString uses a reference count to
minimize duplication of strings in memory.  It also mentions [p101] how MFC
uses InterlockedIncrement() and InterlockedDecrement() to ensure that
modifications to the reference count in different threads is done safely.

This seems to imply that you could share CStrings safely accross thread
boundaries, but the code does not look thread safe to me.

Assuming threads A and B are sharing a CString, and A wants to modify its
copy then CopyBeforeWrite() would be called before the modification.

(some MFC code)
void CString::CopyBeforeWrite()
{
	if ( GetData()->nRefs > 1 )
	{
		CStringData * pData = GetData();
		Release();	<== we just gave up our reference

		<== the other thread could give up its reference and
			therefore delete the CString's data

		AllocBuffer( pData->nDataLength );
		memcpy( m_pchData, pData->data(),	<== may be gone
			 (pData->nDataLength + 1)*sizeof(TCHAR) );
	}
}

Now that I think of it, wouldn't all the various GetData()->nRefs also not
be thread safe?




Mike Blaszczak -- mikeblas@nwlink.com
Sunday, March 09, 1997

[Mini-digest: 2 responses]

At 17:06 3/6/97 -0500, Chris Kirby wrote:

>Environment: NT 4.0, VC++ 4.2b
>I have been reading MFC Internals and I have a question about CString
>across threads. 

Isn't it customary to write directly to the authors when you have a
question about the material in a book?

>The book mentions how CString uses a reference count to
>minimize duplication of strings in memory.  It also mentions [p101] how MFC
>uses InterlockedIncrement() and InterlockedDecrement() to ensure that
>modifications to the reference count in different threads is done safely.

Right. MFC's thread safety goal is to have class-based thread safety
without object-based thread safety.  If the reference counting stuff
wasn't thread safe, you could run into trouble because you might have
two CString objects that actually referred to the same data (which is the
whole point of reference counting).  You should be able to use each object
in separate threads without fear, but you wouldn't be able to if the reference
counting stuff wasn't doing interlocked references.

>This seems to imply that you could share CStrings safely accross thread
>boundaries, but the code does not look thread safe to me.

Not to me, it doesn't.  It means what it says: the incrementing and
decrementing of reference counts is thread-safe.  This _doesn't_ mean that
sharing a single CString object across threads is safe.  I think you're
reading too much into what the authors wrote.

>Now that I think of it, wouldn't all the various GetData()->nRefs also not
>be thread safe?

Yep.  _Anything_ referencing the pointer return from GetData() is not
thread-safe at the object level, from operator[] to Find().


.B ekiM
http://www.nwlink.com/~mikeblas/
These words are my own. I do not speak on behalf of Microsoft.
           This performance was not lip-synched.

-----From: "Kenneth A. Argo" 

My experience is that CString IS NOT thread safe and has problems when trying to pass objects between threads.


Kenneth A. Argo
AutoCyte, Inc.

Success cannot be achieved, it can only be sought.



-----Original Message-----
From:	Chris Kirby [SMTP:cmk@isdsa.pgh.wec.com]
Sent:	Thursday, March 06, 1997 5:06 PM
To:	mfc-l@netcom.com
Subject:	MFC Internals and CString Across Threads

Environment: NT 4.0, VC++ 4.2b

I have been reading MFC Internals and I have a question about CString
across threads.  The book mentions how CString uses a reference count to
minimize duplication of strings in memory.  It also mentions [p101] how MFC
uses InterlockedIncrement() and InterlockedDecrement() to ensure that
modifications to the reference count in different threads is done safely.

This seems to imply that you could share CStrings safely accross thread
boundaries, but the code does not look thread safe to me.

Assuming threads A and B are sharing a CString, and A wants to modify its
copy then CopyBeforeWrite() would be called before the modification.

(some MFC code)
void CString::CopyBeforeWrite()
{
	if ( GetData()->nRefs > 1 )
	{
		CStringData * pData = GetData();
		Release();	<== we just gave up our reference

		<== the other thread could give up its reference and
			therefore delete the CString's data

		AllocBuffer( pData->nDataLength );
		memcpy( m_pchData, pData->data(),	<== may be gone
			 (pData->nDataLength + 1)*sizeof(TCHAR) );
	}
}

Now that I think of it, wouldn't all the various GetData()->nRefs also not
be thread safe?




Shanku Niyogi -- shanku@accent.net
Sunday, March 16, 1997

[Mini-digest: 2 responses]

Person A asked if strings were thread-safe.
Person B replied that they weren't.
Mike B. replied that they are only thread-safe on the class level.
If something isn't thread-safe on a given level, then it is
     not completely thread-safe.
Conclusion: CStrings are not thread-safe, and none of the parties
     above are incorrect.

BTW, one does not always directly write to the authors when they
have a question about the material in a book - this would make
classrooms extinct, and schooling a very time-intensive task.  

----------
> From: Mike Blaszczak 
> To: mfc-l@netcom.com
> Subject: Re: MFC Internals and CString Across Threads
> Date: Wednesday, March 12, 1997 1:10 PM
> 
> [Mini-digest: 3 responses]
> 
> 
> >-----From: "Kenneth A. Argo" 
> 
> >My experience is that CString IS NOT thread safe and
> >has problems when trying to pass objects between threads.
> 
> Would you care to share a reproducable case, or at least some
> pertinent details?  
> 
> CString, like other classes in MFC, is thread-safe at the class
> level.  CString, like other classes in MFC, is not thread-safe
> at the object level. Maybe the behaviour you describe is
> entirely by-design. Maybe the behaviour you noticed constitutes
> a bug in MFC. Since you don't even bother to explain which
> access pattern you're using, nobody can glean any useful
> information from your note. 
> 
> 
> .B ekiM
> http://www.nwlink.com/~mikeblas/
> These words are my own. I do not speak on behalf of Microsoft.
>            This performance was not lip-synched.
> 
> -----From: Chris Kirby 
> 
> At 06:29 PM 3/9/97 -0800, Mike Blaszczak wrote:
> >
> >At 17:06 3/6/97 -0500, Chris Kirby wrote:
> >
> >>Environment: NT 4.0, VC++ 4.2b
> >>I have been reading MFC Internals and I have a question about CString
> >>across threads. 
> >
> >Isn't it customary to write directly to the authors when you have a
> >question about the material in a book?
> 
> While the book highlights how MFC CStrings actually work, it is the MFC
> code that we are discussing.  Its use of InterlockedIncrement() and
> InterlockedDecrement() show MFC is concerned about multiple threads
> referring to the same string, but after looking at the code I do not
think
> that it is thread safe.  Since this is the MFC code I feel that this list
> is an appropriate place to discuss it.
> 
> >>The book mentions how CString uses a reference count to
> >>minimize duplication of strings in memory.  It also mentions [p101] how
MFC
> >>uses InterlockedIncrement() and InterlockedDecrement() to ensure that
> >>modifications to the reference count in different threads is done
safely.
> >
> >Right. MFC's thread safety goal is to have class-based thread safety
> >without object-based thread safety.  If the reference counting stuff
> >wasn't thread safe, you could run into trouble because you might have
> >two CString objects that actually referred to the same data (which is
the
> >whole point of reference counting).  You should be able to use each
object
> >in separate threads without fear, but you wouldn't be able to if the
> reference
> >counting stuff wasn't doing interlocked references.
> 
> Even with reference counting being thread safe, I think MFC will still
run
> into problems with two CString objects that refer to the same data if
they
> are in different threads.  This is because the InterlockedIncrement() and
> InterlockedDecrement() only assure that changes to the reference count
are
> done safely.  There are many places in the code where it releases data
but
> still assumes it is there because of the reference count. [see below]
>  
> Overall, I think that CStrings in multiple threads is fine, but CStrings
> with references to the same data in multiple threads is not safe.
> 
> - Chris
> 
> 
> (some MFC code)
> 
> void CString::CopyBeforeWrite()
> {
> 	// checks to see if the CString is sharing its data
> 	// (not thread safe because we could be sharing with 1 other string
> 	//  that releases its reference while in here)
> 
> 	if ( GetData()->nRefs > 1 )
> 	{
> 		CStringData * pData = GetData();
> 		Release();	<== we just gave up our reference
> 
> 		<== the other thread could give up its reference and
> 			since the reference count is now 0, it will
> 			therefore delete the CString's data
> 
> 		// the function is still using pData, even though it
> 		// called Release()
> 		AllocBuffer( pData->nDataLength );
> 		memcpy( m_pchData, pData->data(),	<== may be gone
> 			 (pData->nDataLength + 1)*sizeof(TCHAR) );
> 	}
> }
> 
> 
> -----From: Lior Messinger <100274.2607@CompuServe.COM>
> 
> Hi,
> 
> I have follwoed this thread, and I'm a little confused... I would like to
ask
> Mike B., in reponse to what he said:
> 
> "..Right. MFC's thread safety goal is to have class-based thread safety
> without object-based thread safety..."
> 
> What does this mean?
> 
> "...Not to me, it doesn't.  It means what it says: the incrementing and
> decrementing of reference counts is thread-safe.  This _doesn't_ mean
that
> sharing a single CString object across threads is safe.  I think you're
> reading too much into what the authors wrote.
> >Now that I think of it, wouldn't all the various GetData()->nRefs also
not
> >be thread safe?
> Yep.  _Anything_ referencing the pointer return from GetData() is not
> thread-safe at the object level, from operator[] to Find()...."
> 
> If sharing CString accross threads isn't safe - then what is the whole
point
> (it's probablythe same question from above)?
> 
> Thanks,
> Lior Messinger.
> 
> .B ekiM
> http://www.nwlink.com/~mikeblas/
> These words are my own. I do not speak on behalf of Microsoft.
>            This performance was not lip-synched.
> 
-----From: Mike Blaszczak 

>-----From: Chris Kirby 

>Overall, I think that CStrings in multiple threads is fine, but CStrings
>with references to the same data in multiple threads is not safe.

I had a longer look at the code this afternoon, and I think you're right:
it's possible that the other thread's object is touching the data when
the first thread's object is trying to copy it.

We probably should document this issue, but I don't think we'll fix it--
protecting the data would cause too great of a performance hit, and still
affect applications that didn't care about the thread safety of the
object.

>-----From: Lior Messinger <100274.2607@CompuServe.COM>

>I have follwoed this thread, and I'm a little confused... I would like to ask
>Mike B., in reponse to what he said:

>"..Right. MFC's thread safety goal is to have class-based thread safety
>without object-based thread safety..."

>What does this mean?

The idea is that saying "something is thread-safe" just doesn't
make any sense: it's not enough information.  A class is thread safe if
you can create one object of the class in one thread, and another object
of the class in another thread, and use the objects in both threads without
worrying about synchronization. Generally, this means that the
implementation of the class isn't dependant on any read-write data that's
static.  But problems can be caused by other things, like we just saw
with CString.

A class can be said to be thread-safe across objects if it allows you
to share a single object across threads.  Thread A creates an object,
and gives a pointer to that object to Thread B.  Thread A and Thread B
freely party on the object any way they want--if that works, the class
the object was created from can be said to be thread safe.

My book is done, so I'm tired and hungover, and that means this isn't
as clear as it should be. I've been through this issue many times on
the ISAPI list.  For a longer explanation, you might want to paw through
the ISAPI-L archives.


.B ekiM
http://www.nwlink.com/~mikeblas/
These words are my own. I do not speak on behalf of Microsoft.
       One is too many and a million is not enough.




Become an MFC-L member | Вернуться в корень Архива |