Windows CE Networking Team WebLog

Windows CE Networking - from NDIS to TCP to SOAP to VOIP and everything in between.

How I save myself weeks of horrible debugging each year

Being in networking and middleware, I have to deal with locking / critical sections a lot.  Multi-threaded code is inherently tricky to get right.  You have problems with deadlock - which are straightforward to debug usually assuming you have a good debugger and you hit it in your office and not the field :).  The harder ones are race conditions, where two threads are blasting the same data at the same time.

I've saved myself countless problems by using the SVSSynch class, which is part of the Windows CE utility library svsutil.hxx.  In RETAIL builds, it is a thin wrapper around the standard CRITICAL_SECTION functions.  On DEBUG builds, though, it has the ability to do extra checks to make sure the lock is in the state you want it. 

So let's say I have a function that I assume has some lock held when it gets called.  The lame way to do this is

void MYFunction() {
 // MyLock had better be held!
 ...
}

The better way to handle this is using the SVSSynch.IsLocked and an ASSERT.  This documents my assumption (no need to duplicate the comment) and will break if my assumption was wrong.  This is 100 times easier to deal with than having the variables in MyFunction mysteriously changing when 2 threads access it simultaneously.

void MYFunction() {
 DEBUGCHK(MyLock.IsLocked());
 ...
}

A related situation is where you're going to go and block for a long period of time, say waiting for data off the network.  You almost always want to release any locks you may hold before doing this.  Again, don't just trust yourself but check.

void MyGetDataFromNetwork() {
 DEBUGCHK(! MyLock.IsLocked());
 select(...);
 ...
}

In years where I do lots of active development, I figure that using this class and the various DEBUGCHKs its given me has saved weeks of horribly painful debugging.  And as if being lazy isn't good enough :), I've shipped a higher quality product to our customers.

svsutil.hxx should be available in all later PocketPC SDKs but not earlier ones.  Since I'm a nice guy I'll include it here.  As our standard disclaimer says, consider source code we post AS-IS.

#ifdef DEBUG
#define SVSUTIL_DEBUG_MT 1
#endif

class SVSSynch {
protected:
 CRITICAL_SECTION cs;

#if defined (SVSUTIL_DEBUG_MT)
 int     iRef;
 DWORD    dwLockingThreadId;
#endif

public:
 SVSSynch (void) {
  InitializeCriticalSection (&cs);

#if defined (SVSUTIL_DEBUG_MT)
  iRef        = 0;
  dwLockingThreadId = 0;
#endif
 }
 ~SVSSynch (void) {
#if defined (SVSUTIL_DEBUG_MT)
  SVSUTIL_ASSERT ((iRef == 0) && (dwLockingThreadId == 0));
#endif

  DeleteCriticalSection (&cs);
 }

public:
 void Lock (void) {
  EnterCriticalSection (&cs);

#if defined (SVSUTIL_DEBUG_MT)
  DWORD dwCurrentThreadId = GetCurrentThreadId();
  SVSUTIL_ASSERT (((iRef == 0) && (dwLockingThreadId == 0)) || ((iRef > 0) && (dwLockingThreadId == dwCurrentThreadId)));
  dwLockingThreadId = dwCurrentThreadId;
  iRef++;
#endif
 }

 void Unlock (void) {
#if defined (SVSUTIL_DEBUG_MT)
  DWORD dwCurrentThreadId = GetCurrentThreadId ();
  SVSUTIL_ASSERT ((iRef > 0) && (dwLockingThreadId == dwCurrentThreadId));
  if (--iRef == 0)
   dwLockingThreadId = 0;
#endif

  LeaveCriticalSection (&cs);
 }

 int IsLocked (void) {
#if defined (SVSUTIL_DEBUG_MT)
  DWORD dwCurrentThreadId = GetCurrentThreadId();
  return (iRef > 0) && (dwLockingThreadId == dwCurrentThreadId);
#else
  return TRUE;
#endif
 }
};

[Author: John Spaith]

Published Friday, October 07, 2005 9:50 AM by cenet
Filed under:

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

No Comments

Leave a Comment

(required) 
(optional)
(required) 

  
Enter Code Here: Required
Submit

© 2009 Microsoft Corporation. All rights reserved. Terms of Use  |  Trademarks  |  Privacy Statement
Microsoft
Page view tracker