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 | Вернуться в корень Архива |