• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 254
  • Last Modified:

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
0
mrwad99
Asked:
mrwad99
  • 6
  • 5
  • 2
  • +2
3 Solutions
 
AndyAinscowCommented:
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)
0
 
mahesh1402Commented:
>>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
0
 
mrwad99Author Commented:
>> 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.
0
What does it mean to be "Always On"?

Is your cloud always on? With an Always On cloud you won't have to worry about downtime for maintenance or software application code updates, ensuring that your bottom line isn't affected.

 
AlexFMCommented:
You can always get ID of current thread using GetCurrentThreadId function

I understand that you implemend non-standard logic, when thread which needs to access some resource, doesn't wait for it. It only checks resource state and do something else if it is busy. You can implement this using CRITICAL_SECTION, TryEnterCriticalSection and LeaveCriticalSection functions.

TryEnterCriticalSection behaves exactly as you need: tries and returns immidiately. Good news are that another problem: "one of these functions may call into another of these functions" has solution. When TryEnterCriticalSection is called from the same thread, it always succeeds. Remember that for every successful call to TryEnterCriticalSection you nust call LeaveCriticalSection.
0
 
mrwad99Author Commented:
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
0
 
AlexFMCommented:
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?
0
 
waysideCommented:
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
}


0
 
AlexFMCommented:
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.
0
 
mrwad99Author Commented:
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.
0
 
waysideCommented:
If FuncA() successfully entered the critical section and called FuncB(), how is FuncB() ever able to do more than return TIMEOUT?

Conversely, if FuncA() is unable to enter the critical section it will never call FuncB(). I don't see how this scheme will do want you want, or ever lead to the leave being called twice.

What I am suggesting is to split the critical section stuff from the work:

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

     // entered the critical section, now do something
     MY_RETURN_TYPE result = FuncA_work();

     // leave the critical section
     SetBusy(FALSE);
     return result;
}

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

     // entered the critical section, now do something
     MY_RETURN_TYPE result = FuncB_work();

     // leave the critical section
     SetBusy(FALSE);

     return result;
}

MY_RETURN_TYPE CMyClass::FuncA_work() {
   // do some work
   ..
   // call funcB's work function
   MY_RETURN_TYPE result = FuncB_work();

   if (!result) {
      // do something
   }
   else {
      // do something else
   }

   return result;
}

MY_RETURN_TYPE CMyClass::FuncB_work() {
   return some_other_call();
}

The idea is that none of the functions that do the work deal with the critical section, this is handled one step up the chain. So when you call FuncA() and enter the critical section, all subsequent calls do not try to enter or leave the critical section. This avoids the double-leave.


0
 
AlexFMCommented:
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.
0
 
mrwad99Author Commented:
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.
0
 
AndyAinscowCommented:
I don't think you read the comment by Alex closely enough.

You ONLY call LeaveCriticalSection when the TryEnterCriticalSection has SUCCEEDED.
0
 
AlexFMCommented:
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.
0
 
AlexFMCommented:
Your ClassA::Func1 is OK. How does this happen that there is odd LeaveCriticalSection? Check all calls to SetBusy in your program.
0
 
mrwad99Author Commented:
>> How does this happen that there is odd LeaveCriticalSection

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

Thanks all :)
0

Featured Post

Concerto Cloud for Software Providers & ISVs

Can Concerto Cloud Services help you focus on evolving your application offerings, while delivering the best cloud experience to your customers? From DevOps to revenue models and customer support, the answer is yes!

Learn how Concerto can help you.

  • 6
  • 5
  • 2
  • +2
Tackle projects and never again get stuck behind a technical roadblock.
Join Now