Ranting and Roaring

2004/06/28

Patching wxMutexInternal

Last week I was having a pretty serious problem with wxPython on the Macintosh. The following block of code in wxPython is just plain wrong and after a few hours of operations the m_waiters array would be corrupted because of multiple threads contending for the memory.

If you’re running wxPython 2.5.1.5, seriously considering patching your library as per these instructions.

 while ( m_owner != kNoThreadID && m_owner != current) { 	m_waiters.Add(current); 	::SetThreadStateEndCritical(kCurrentThreadID, kStoppedThreadState, m_owner); 	::ThreadBeginCritical(); } 

It should read (as far as I can tell)

 while ( m_owner != kNoThreadID && m_owner != current) { 	::SetThreadStateEndCritical(kCurrentThreadID, kStoppedThreadState, m_owner); 	m_waiters.Add(current); 	::ThreadBeginCritical(); } 

I knew that rebuilding wxPython libraries from scratch could end up being more trouble than it’s worth, so I decided to take a different approach. Last Friday night I did an extending hacking session on wxPython’s libraries. I downloaded Apple’s Xcode developer tools and disassembled (using objdump) the broken module. Looking at the PowerPC opcodes, I figured one little tweak would do the trick. I needed to change this:

 675f8:   38 a0 00 01     li      r5,1 675fc:   4b fb 17 39     bl      [wxBaseArrayLong::Add]  67600:   38 60 00 01     li      r3,1 67604:   38 80 00 01     li      r4,1 67608:   80 be 00 00     lwz     r5,0(r30) 6760c:   48 01 30 69     bl      [::SetThreadStateEndCritical]  67610:   48 01 30 a5     bl      [::ThreadBeginCritical] 

to this:

 67610:   48 01 30 a5     bl      [::ThreadBeginCritical]  675f8:   38 a0 00 01     li      r5,1 675fc:   4b fb 17 39     bl      [wxBaseArrayLong::Add]  67600:   38 60 00 01     li      r3,1 67604:   38 80 00 01     li      r4,1 67608:   80 be 00 00     lwz     r5,0(r30) 6760c:   48 01 30 69     bl      [::SetThreadStateEndCritical] 

Note that I had to figure out those symbolic names — the objdump program didn’t do that for me. Here’s a Python program to make the fix:

 old_data = [ 	0x38, 0xa0, 0x00, 0x01,	 	0x4b, 0xfb, 0x17, 0x39,		# wxArrayLong::Add 	0x38, 0x60, 0x00, 0x01, 	0x38, 0x80, 0x00, 0x01, 	0x80, 0xbe, 0x00, 0x00, 	0x48, 0x01, 0x30, 0x69,		# ::SetThreadStateEndCritical 	0x48, 0x01, 0x30, 0xa5,		# ::ThreadBeginCritical ]  new_data = [ 	0x48, 0x01, 0x30, 0xa5,		# ::ThreadBeginCritical 	0x38, 0xa0, 0x00, 0x01,	 	0x4b, 0xfb, 0x17, 0x39,		# wxArrayLong::Add 	0x38, 0x60, 0x00, 0x01, 	0x38, 0x80, 0x00, 0x01, 	0x80, 0xbe, 0x00, 0x00, 	0x48, 0x01, 0x30, 0x69,		# ::SetThreadStateEndCritical ]  fio = open('build/Jaeger.app/Contents/Frameworks/libwx_base_carbond-2.5.1.0.0.dylib', 'rb+') fio.seek(start)  f_in = fio.read(len(old_data))  for i in xrange(len(old_data)): 	if old_data[i] != ord(f_in[i]): 		print >> sys.stderr, "data did not match" 		print >> sys.stderr, "%d: %02x %02x" % ( i, old_data[i], ord(f_in[i]) ) 		sys.exit(1)  fio.seek(start)  for i in xrange(len(new_data)): 	fio.write(chr(new_data[i])) 	 fio.close() 

There was one other piece of code causing a crash in Jäger. My advice — never never ever try to modify wxPython objects from outside their thread: it ain’t ever going to work.

Powered by WordPress