utilities: Don't use TLS buffers in FastFormatString classes (#1430)

The TLS buffers used by the FastFormatUnicode and FastFormatAscii
classes seem to be responsible for PCSX2 not terminating properly on
Windows under certain conditions (using MTVU before commit
1111e03901, using CDVDgigaherz without a
disc, possibly other conditions).

When PCSX2 shut downs and the FastFormatBuffers are being cleaned up,
the call to pthread_key_delete() would end up calling
WaitForSingleObject(e, INFINITE) and waiting indefinitely for an event
to trigger. It never does get triggered (for reasons unknown) and
therefore PCSX2 doesn't terminate properly.

Remove the usage of TLS buffers in the FastFormatString classes - it
fixes the termination issue on Windows and doesn't seem to have much
effect on performance.
This commit is contained in:
Jonathan Li 2016-06-29 13:13:51 +01:00 committed by GitHub
parent 1f4f55bcc9
commit 79d019b5bb
2 changed files with 23 additions and 169 deletions

View File

@ -132,6 +132,7 @@ struct ParsedAssignmentString
// accepts Ascii/UTF8 only.
//
typedef ScopedAlignedAlloc<char, 16> CharBufferType;
// --------------------------------------------------------------------------------------
// FastFormatAscii
// --------------------------------------------------------------------------------------
@ -139,9 +140,8 @@ struct ParsedAssignmentString
class FastFormatAscii
{
protected:
ScopedAlignedAlloc<char,16>* m_dest;
bool m_deleteDest;
CharBufferType m_dest;
public:
FastFormatAscii();
~FastFormatAscii() throw();
@ -151,8 +151,8 @@ public:
void Clear();
bool IsEmpty() const;
const char* c_str() const { return m_dest->GetPtr(); }
operator const char*() const { return m_dest->GetPtr(); }
const char* c_str() const { return m_dest.GetPtr(); }
operator const char*() const { return m_dest.GetPtr(); }
const wxString GetString() const;
//operator wxString() const;
@ -182,9 +182,8 @@ public:
class FastFormatUnicode
{
protected:
ScopedAlignedAlloc<char,16>* m_dest;
bool m_deleteDest;
uint m_Length;
CharBufferType m_dest;
uint m_Length;
public:
FastFormatUnicode();
@ -203,9 +202,9 @@ public:
FastFormatUnicode& ToUpper();
FastFormatUnicode& ToLower();
const wxChar* c_str() const { return (const wxChar*)m_dest->GetPtr(); }
operator const wxChar*() const { return (const wxChar*)m_dest->GetPtr(); }
operator wxString() const { return (const wxChar*)m_dest->GetPtr(); }
const wxChar* c_str() const { return (const wxChar*)m_dest.GetPtr(); }
operator const wxChar*() const { return (const wxChar*)m_dest.GetPtr(); }
operator wxString() const { return (const wxChar*)m_dest.GetPtr(); }
FastFormatUnicode& operator+=(const wxString& s)
{

View File

@ -14,13 +14,9 @@
*/
#include "PrecompiledHeader.h"
#include "Threading.h"
#include "TlsVariable.inl"
#include "SafeArray.inl"
using namespace Threading;
// Implement some very commonly used SafeArray types here
// (done here for lack of a better place)
@ -37,119 +33,6 @@ template class SafeAlignedArray<u8,16>;
// system deadlock.
static const int MaxFormattedStringLength = 0x80000;
typedef ScopedAlignedAlloc<char,16> CharBufferType;
// --------------------------------------------------------------------------------------
// FastFormatBuffers
// --------------------------------------------------------------------------------------
// This class provides a series of pre-allocated thread-local buffers for use by string
// formatting tools. These buffers are handed out in round-robin style and require *no*
// thread sync objects and avoid multi-thread contention completely -- allowing multiple
// threads to format complicated strings concurrently with maximum efficiency.
//
class FastFormatBuffers
{
DeclareNoncopyableObject(FastFormatBuffers);
protected:
typedef char CharType;
typedef CharBufferType BufferType;
static const uint BufferCount = 6;
BufferType m_buffers[BufferCount];
uint m_curslot;
public:
FastFormatBuffers()
{
// This protects against potential recursive calls to our formatter, by forcing those
// calls to use a dynamic buffer for formatting.
m_curslot = BufferCount;
for (uint i=0; i<BufferCount; ++i)
{
m_buffers[i].Alloc(512);
}
m_curslot = 0;
}
virtual ~FastFormatBuffers() throw()
{
pxAssumeDev(m_curslot==0,
wxsFormat(L"Dangling %s formatting buffer detected!",
(sizeof(CharType)==1) ? L"UTF8/Ascii" : L"Wide-char"
)
);
}
bool HasFreeBuffer() const
{
return m_curslot < BufferCount-1;
}
BufferType& GrabBuffer()
{
++m_curslot;
pxAssert(m_curslot < BufferCount);
return m_buffers[m_curslot];
}
void ReleaseBuffer()
{
--m_curslot;
pxAssert(m_curslot < BufferCount);
}
BufferType& operator[](uint i)
{
IndexBoundsAssume( ((sizeof(CharType)==1) ? L"Ascii Formatting Buffer" : L"Unicode Formatting Buffer"), i, BufferCount );
return m_buffers[i];
}
};
// --------------------------------------------------------------------------------------
// GlobalBufferManager
// --------------------------------------------------------------------------------------
// This local-scope class is needed in order to safely deal with C++ initializing and destroying
// global objects in arbitrary order. The initbit is updated by the object when constructed and
// destroyed; code using this class provides its own statically-initialized boolean (which MUST
// default to false!) and then sets the boolean to true to indicate the object is ready for use.
//
template< typename T >
class GlobalBufferManager
{
public:
bool& initbit;
T instance;
GlobalBufferManager( bool& globalBoolean )
: initbit( globalBoolean )
{
initbit = true;
}
~GlobalBufferManager() throw()
{
initbit = false;
instance.Dispose();
}
T& Get()
{
return instance;
}
operator T&()
{
return instance;
}
};
static bool buffer_is_avail = false;
static GlobalBufferManager< BaseTlsVariable< FastFormatBuffers > > m_buffer_tls(buffer_is_avail);
static
#ifndef __linux__
__ri
@ -230,20 +113,6 @@ uint format_that_unicode_mess( CharBufferType& buffer, uint writepos, const wxCh
return 0; // unreachable.
}
CharBufferType* GetFormatBuffer( bool& deleteDest )
{
deleteDest = false;
if (buffer_is_avail)
{
if (m_buffer_tls.Get()->HasFreeBuffer())
return &m_buffer_tls.Get()->GrabBuffer();
}
deleteDest = true;
return new CharBufferType(2048);
}
// --------------------------------------------------------------------------------------
// FastFormatUnicode (implementations)
// --------------------------------------------------------------------------------------
@ -254,26 +123,19 @@ CharBufferType* GetFormatBuffer( bool& deleteDest )
// this class nicely in its current state. --air
FastFormatUnicode::FastFormatUnicode()
: m_dest(2048)
{
m_dest = GetFormatBuffer(m_deleteDest);
Clear();
}
FastFormatUnicode::~FastFormatUnicode() throw()
{
try {
if (m_deleteDest)
delete m_dest;
else
m_buffer_tls.Get()->ReleaseBuffer();
}
DESTRUCTOR_CATCHALL
}
void FastFormatUnicode::Clear()
{
m_Length = 0;
((wxChar*)m_dest->GetPtr())[0] = 0;
((wxChar*)m_dest.GetPtr())[0] = 0;
}
FastFormatUnicode& FastFormatUnicode::WriteV( const char* fmt, va_list argptr )
@ -282,8 +144,8 @@ FastFormatUnicode& FastFormatUnicode::WriteV( const char* fmt, va_list argptr )
const uint inspos = m_Length;
const uint convLen = converted.Length();
m_dest->MakeRoomFor((inspos + convLen + 64) * sizeof(wxChar));
memcpy( &((wxChar*)m_dest->GetPtr())[inspos], converted.wc_str(), (convLen+1)*sizeof(wxChar) );
m_dest.MakeRoomFor((inspos + convLen + 64) * sizeof(wxChar));
memcpy( &((wxChar*)m_dest.GetPtr())[inspos], converted.wc_str(), (convLen+1)*sizeof(wxChar) );
m_Length += convLen;
return *this;
@ -291,7 +153,7 @@ FastFormatUnicode& FastFormatUnicode::WriteV( const char* fmt, va_list argptr )
FastFormatUnicode& FastFormatUnicode::WriteV( const wxChar* fmt, va_list argptr )
{
m_Length = format_that_unicode_mess( *m_dest, m_Length, fmt, argptr );
m_Length = format_that_unicode_mess( m_dest, m_Length, fmt, argptr );
return *this;
}
@ -324,12 +186,12 @@ FastFormatUnicode& FastFormatUnicode::Write( const wxString fmt, ... )
bool FastFormatUnicode::IsEmpty() const
{
return ((wxChar&)(*m_dest)[0]) == 0;
return ((wxChar&)m_dest[0]) == 0;
}
FastFormatUnicode& FastFormatUnicode::ToUpper()
{
wxChar* ch = (wxChar*)m_dest->GetPtr();
wxChar* ch = (wxChar*)m_dest.GetPtr();
for ( uint i=0; i<m_Length; ++i, ++ch )
*ch = (wxChar)wxToupper(*ch);
@ -338,7 +200,7 @@ FastFormatUnicode& FastFormatUnicode::ToUpper()
FastFormatUnicode& FastFormatUnicode::ToLower()
{
wxChar* ch = (wxChar*)m_dest->GetPtr();
wxChar* ch = (wxChar*)m_dest.GetPtr();
for ( uint i=0; i<m_Length; ++i, ++ch )
*ch = (wxChar)wxTolower(*ch);
@ -390,35 +252,28 @@ wxString operator+(const FastFormatUnicode& str1, const wxChar* str2)
// FastFormatAscii (implementations)
// --------------------------------------------------------------------------------------
FastFormatAscii::FastFormatAscii()
: m_dest(2048)
{
m_dest = GetFormatBuffer(m_deleteDest);
Clear();
}
FastFormatAscii::~FastFormatAscii() throw()
{
try {
if (m_deleteDest)
delete m_dest;
else
m_buffer_tls.Get()->ReleaseBuffer();
}
DESTRUCTOR_CATCHALL
}
void FastFormatAscii::Clear()
{
m_dest->GetPtr()[0] = 0;
m_dest.GetPtr()[0] = 0;
}
const wxString FastFormatAscii::GetString() const
{
return fromAscii(m_dest->GetPtr());
return fromAscii(m_dest.GetPtr());
}
FastFormatAscii& FastFormatAscii::WriteV( const char* fmt, va_list argptr )
{
format_that_ascii_mess( *m_dest, strlen(m_dest->GetPtr()), fmt, argptr );
format_that_ascii_mess( m_dest, strlen(m_dest.GetPtr()), fmt, argptr );
return *this;
}
@ -434,5 +289,5 @@ FastFormatAscii& FastFormatAscii::Write( const char* fmt, ... )
bool FastFormatAscii::IsEmpty() const
{
return (*m_dest)[0] == 0;
return m_dest[0] == 0;
}