Link to home
Start Free TrialLog in
Avatar of mrwad99
mrwad99Flag for United Kingdom of Great Britain and Northern Ireland

asked on

More thread ID problems

I have a multithreaded app.  In this app are several functions, only one of which can ever be called at one time.  I control access to these functions via a variable in a containing class called "m_bBusy".  Access to this variable is protected by a CCriticalSection.

Now, when one of these functions is running, m_bBusy gets set to TRUE.  If any other thread tries to access one of these functions and m_bBusy is TRUE, it fails.  No blocking, just a simple fail with a return code something along the lines of a custom enum type TIMEOUT.  When a function ends, m_bBusy gets set to FALSE.

So all that is fairly cool.

However.  I am at the stage now where one of these functions may call into another of these functions.  The second call will always fail however as the first function call has set m_bBusy to TRUE.  So I am failing when what I have done in theory is fine.

Now, I have asked a previous question on how to get the ID of a thread, the idea being that when testing m_bBusy, I should see if the ID of the thread that set the busy state is the same as the one that is trying to set it again.  If it is, then it is safe to carry on.

Like this:

First_function()
{
  //Test m_bBusy, it is FALSE so Set m_bBusy to TRUE

  //.. do some work

  // Call second function

  //.. do some more work

  // Set m_bBusy to FALSE
}


Second_function()
{
  // Test m_bBusy: it is TRUE, but instead of quitting, test the ID of the thread that set the state.  If it is the same as this thread's ID, carry on.  Otherwise, return a timeout.
}

See what I am trying to do ?  Now, AlexFM kindly advised me in my last question that I can get the ID of a thread when I create one via AfxBeginThread through CWinThread::m_nThreadID.  I need this value to be available in all function calls I make in this thread: but how ?  

I could pass it as a member of a data structure I pass to my thread function, but then every other function I call within my thread function has to have provision for this too.  And this is impractical.  Besides, what if someone calls one of these functions in the *main* thread, whilst a worker thread created with AfxBeginThread is doing something ?  How do I get the ID of the main thread in this case ?

TIA
Avatar of AndyAinscow
AndyAinscow
Flag of Switzerland image

For the ID of the maint thread - global variable available to all ?  (The way one shares variables in C code between functions / .cpp files)

Alternative - a static member of a class proving info to all threads (so it just needs to be set once and all instances of the class have access to it)
>>How do I get the ID of the main thread in this case ?

Get the ID of the main thread before starting up any child threads. Store it in a global variable, and in the child thread(s) do a comparison between the ID of the current thread and the stored "main" thread ID.

MAHESH
Avatar of mrwad99

ASKER

>> For the ID of the maint thread - global variable available to all ?  

OK, that's a possibility.  Since my project is a DLL, and these functions are exported, how do I get the ID of the thread ?  And if it could be a global variable, that means it is accesible via some function, so why not scrap the global and call the function in each of these 'protected' functions that my class exports ?

Thanks.
ASKER CERTIFIED SOLUTION
Avatar of AlexFM
AlexFM

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of mrwad99

ASKER

AlexFM:  

Fantastic, just the kind of thing I was looking for.  What you mention about

>>Remember that for every successful call to TryEnterCriticalSection you nust call LeaveCriticalSection

is presenting quite an issue however.  It appears that with the following sequence of calls:

TryEnterCriticalSection
LeaveCriticalSection
LeaveCriticalSection

I cannot aquire the CRITICAL_SECTION again.  This is apparently due to the extra LeaveCriticalSection.  I take it this is by design; even so it is a pain in the rear.

What I am thinking is to have some count that keeps note of how many times we have aquired the CRITICAL_SECTION: decrementing it with each call to LeaveCriticalSection, incrementing it with each call to TryEnterCriticalSection.  I could this via one of the InterlockedXXX functions probably.  If I have a count of 0, do not execute LeaveCriticalSection.

But this is another layer of complexity.

Is there any way around this other than that mentioned above ?

TIA
Avatar of AlexFM
AlexFM

Number of LeaveCriticalSection must be equal to number of TryEnterCriticalSection calls which returnes TRUE. Your code must be straightforward:

if ( TryEnterCriticalSection() )
{
   ...

   LeaveCriticalSection(...);
}
else
{
    ...
}

Where is the place for error?
Why not split your function into two functions, one which does the work, and one which does the busy check and then calls the work function if it can proceed.

Then call the appropriate function when needed. Something like:

void first_function_work() {
  // do some work here

  // Call second function
  second_function_work();

  //.. do some more work
}

void first_function()
{
  //Test m_bBusy, it is FALSE so Set m_bBusy to TRUE

  // call the work function
  first_function_work();

  // Set m_bBusy to FALSE
}


BTW, additional call to LeaveCriticalSection can set critical section reference cointer to -1, which is 0xFFFF, maybe this is the problem :) Post this to Microsoft.
Avatar of mrwad99

ASKER

AlexFM:

>> Where is the place for error?

OK, let me clarify the problem.  As I said above, I can have functions which call other functions.  Consider this:

I have a function which sets the busy state to TRUE.  This simply enters the CRITICAL_SECTION if possible and returns TRUE; if not possible, it returns FALSE.  Like this:

BOOL CMyClass::SetBusy(BOOL bBusy /* = TRUE */)
{
      if (bBusy)
      {
            if (TryEnterCriticalSection( &m_cs ) )
            {
                  TRACE(_T("Thread %i entered the CRITICAL_SECTION"), GetCurrentThreadId() );
                  return TRUE;
            }
            else
            {
                  TRACE(_T("Thread %i failed to enter the CRITICAL_SECTION"), GetCurrentThreadId() );
                  return FALSE;
            }
      }
      else
      {
            TRACE(_T("Thread %i leaving the CRITICAL_SECTION..."), GetCurrentThreadId() );
            LeaveCriticalSection( &m_cs );
            return TRUE;
      }
}

ASsume the member variable

CRITICAL_SECTION m_cs;

MY_RETURN_TYPE CMyClass::FuncA()
{
      // Set busy state to TRUE.  
      if (!SetBusy()) return TIMEOUT;

      // Do something
      if (!FuncB())
      {
            SetBusy(FALSE);            // LINE A
            return FAILED;
      }

      SetBusy(FALSE);
      return SUCCESS;
}

MY_RETURN_TYPE CMyClass::FuncB()
{
      // Set busy state to TRUE.  
      if (!SetBusy()) return TIMEOUT;

      if (!<SOME CALL>)
      {
            SetBusy(FALSE);            // LINE B
            return FAILED;
      }

      SetBusy(FALSE);

      return SUCCESS;
}

Note line A:  We jump to FuncB(), and, if FuncB() fails, set the busy state to FALSE (as we have finished the work) and return failure.  Back in FuncA(), the return of FuncB() is tested, and, if it is failure, it too returns failure.  However, it also sets busy to FALSE.  This is where I can have two calls to what boils down to LeaveCriticalSection.

Note line B: I need the call to SetBusy(FALSE) as a client caller of my code might call FuncB() outside of FuncA()

Even if no error occurs, I still end up with two calls to SetBusy(FALSE); one when FuncB() ends, the other when FuncA() ends.

There is a possibility that I could carefully determine each and every path into and out of all my functions to put the calls to SetBusy(FALSE) in precisely the right places so as to try to guarantee that there will not be two consequetive calls to SetBusy(), but that is such bad practice and would make maintenance a nightmare.  So it reallyis not an option.

So I need some way to ensure I don't call LeaveCriticalSection() too many times.  Hence my suggestion.

Any way around this that you can see ?

>> Wayside:

Not sure how that helps.  With my new description above, can you clarify ?

Thanks all.
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
You don't need SetBusy at all. Successful call to TryEnterCriticalSection does what you need. Try to forget about busy flag and SetBusy function, just do this with CRITICAL_SECTION, which replaced busy flag.
Avatar of mrwad99

ASKER

Wayside

Yes, I see that.  It is a working solution, and for that I thank you.  Let me clarify one thing however:

>> If FuncA() successfully entered the critical section and called FuncB(), how is FuncB() ever able to do more than return TIMEOUT?

As AlexFM stated, a CRITICAL_SECTION can be re-entered by the same thread that has already entered it.  So, two consequtive TryEnterCriticalSection calls by the same thread are fine, provided that precisely two LeaveCriticalSection calls follow.  So, FuncA() calling FuncB() within the same thread will work.

AlexFM:

>> You don't need SetBusy at all. Successful call to TryEnterCriticalSection does what you need

Hmm. Sort of.  That would work if I only had one class.  I was brief on the details earlier as I was hoping that they would not be required, but I guess they are.  Here we go (as short as possible).

I am using an SDK.  This SDK has several functions, no two of which can be called at the same time by different threads.  If I attempt this, the second thread simply blocks until the SDK call in the first thread has ended.  This is bad for obvious reasons.  I have several classes containing functions that are essentially a wrapper around these SDK calls.  Each class wraps a group of SDK functions with similar functionality.

// Deals with one aspect of SDK functionality
class A
{
public:
      MY_RETURN_TYPE Func1();
      MY_RETURN_TYPE Func2();
};

// Deals with another aspect of SDK functionality
class B
{
public:
      MY_RETURN_TYPE Func1();
      MY_RETURN_TYPE Func2();
};

Now, the real stinker is that as a consequence of no two SDK calls being able to be called at the same time, no two functions from either class A or class B can be called at the same time.  So, the way to get around this that I decided was to have a class that controlled access to all functions:

class Controller
{
public:
      SetBusy(BOOL bBusy = TRUE);
      static Controller& Instance();
private:
      CRITICAL_SECTION m_cs;
};


with

BOOL Controller::SetBusy(BOOL bBusy /* = TRUE */)
{
     if (bBusy)
     {
          if (TryEnterCriticalSection( &m_cs ) )
          {
               TRACE(_T("Thread %i entered the CRITICAL_SECTION"), GetCurrentThreadId() );
               return TRUE;
          }
          else
          {
               TRACE(_T("Thread %i failed to enter the CRITICAL_SECTION"), GetCurrentThreadId() );
               return FALSE;
          }
     }
     else
     {
          TRACE(_T("Thread %i leaving the CRITICAL_SECTION..."), GetCurrentThreadId() );
          LeaveCriticalSection( &m_cs );
          return TRUE;
     }
}

as you can see this is a singleton class.

So, I can say things like

MY_RETURN_TYPE ClassA::Func1()
{
      if (!Controller::Instance().SetBusy())
            return FALSE;
      
      // It is safe to do some work

      Controller::Instance().SetBusy(FALSE);
}

and similarly for the other functions.  Without the SetBusy() function, I would essentially have to have a CRITICAL_SECTION in each class, but ***that does not guarantee synchronized access amongst different class functions***.


Now, can you please re-read the comment I made on 04/05/2006 03:31PM BST: hopefully that will make more sense.

Points at 500

Thanks very much.
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
BOOL Controller::SetBusy(BOOL bBusy /* = TRUE */)

Well, this wraps critical section. Now your code should be:

if ( Controller::Instance().SetBusy() )
{
   ...

   Controller::Instance().SetBusy(FALSE) ;
}
else
{
    ...
}

which is actually equal to code fragment from my previous post. Again, there is no place for odd LeaveCriticalSection.
Your ClassA::Func1 is OK. How does this happen that there is odd LeaveCriticalSection? Check all calls to SetBusy in your program.
Avatar of mrwad99

ASKER

>> How does this happen that there is odd LeaveCriticalSection

It does not happen.  I was misreading my own code.

Thanks all :)