[2 days left] What’s wrong with your cloud strategy? Learn why multicloud solutions matter with Nimble Storage.Register Now

x
?
Solved

LOCK CMutex, CSingleLock, CMultiLock, ?????

Posted on 2004-10-22
9
Medium Priority
?
4,002 Views
Last Modified: 2013-11-20
I have a multithreaded application, and I'm trying to set it up so that only one thread can pass through a paticular function at one time.

Example:
void FileWatcher::AddFileName(const CString& NewFile)
{
      m_lockingobj.Lock(); //If already lock, then waith until unlock
      m_ListFilesNeedProcessing.Add(NewFile);
      m_lockingobj.Unlock();
}

So what I bascially want is when the Lock member function is called, if another thread has it locked already, then the current thread should be on hold until the other thread unlocks it.

When I run the above code, I get an ASSERT error at runtime in the following MFC CSingleLock member function:

BOOL CSingleLock::Lock(DWORD dwTimeOut /* = INFINITE */)
{
      ASSERT(m_pObject != NULL || m_hObject != NULL);
      ASSERT(!m_bAcquired); // <--- <--- *** Error here ***

      m_bAcquired = m_pObject->Lock(dwTimeOut);
      return m_bAcquired;
}

Is CSingleLock the right class for the functionallity I'm looking for, should I be using something else.

This is pretty simple to do in UNIX with POSIX functions, but I can't seem to figure out the correct method using MFC.
0
Comment
Question by:Axter
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 5
  • 2
  • 2
9 Comments
 
LVL 86

Expert Comment

by:jkr
ID: 12384329
You can do that quite easily there, too. I I have to admit that I *never* used these MFC sync objects, since I always considered them superfluous. Whet you want can be achieved by using e.g.

class CLockingObj {

public:

    CLockingObj () { InitializeCriticalSection(&m_cs);}
    ~CLockingObj () { DeleteCriticalSection(&m_cs);}

    void Lock() { EnterCriticalSection(&m_cs);}
    void Unock() { LeaveCriticalSection(&m_cs);}

protected:

    CRITICAL_SECTION m_cs;
};
0
 
LVL 30

Author Comment

by:Axter
ID: 12384429
I just tried it, but it doesn't seem to be working.

To test it out, I call Lock twice.

      m_WaitUntilInQue.Lock();
      m_WaitUntilInQue.Lock();//Code should never reach this point

When I debug the above code, it does reach the secon Lock.

FYI:  You have a typo in above code (Unock instead of Unlock)
0
 
LVL 86

Assisted Solution

by:jkr
jkr earned 1000 total points
ID: 12384494
>> I just tried it, but it doesn't seem to be working

That's not a bug, that's a feature:

"Once a thread has ownership of a critical section, it can make additional calls to EnterCriticalSection or TryEnterCriticalSection without blocking its execution. This prevents a thread from deadlocking itself while waiting for a critical section that it already owns." (from: http://msdn.microsoft.com/library/en-us/dllproc/base/entercriticalsection.asp)

>>FYI:  You have a typo in above code

Well, I shouldn't type code into a browser window :o)

0
Will your db performance match your db growth?

In Percona’s white paper “Performance at Scale: Keeping Your Database on Its Toes,” we take a high-level approach to what you need to think about when planning for database scalability.

 
LVL 19

Expert Comment

by:drichards
ID: 12386471
jkr, you have reinvented the MFC CCriticalSection class with your CLockingObj.

As for CSingleLock, it is meant as a local wrapper for one of the basic sync object types.  It's only useful feature is that in it's destructor it releases the sync object so you don't have to have explicit calls to Unlock in your code.  This can be useful if your code throws exceptions not caught in the throwing function.

You use it like this, assuming m_lockingObj is a CCriticalSection (or maybe CMutex) member of the class:

    void FileWatcher::AddFileName(const CString& NewFile)
    {
         CSingleLock lock(&m_lockingObj);
         lock.Lock();  //If already lock, then waith until unlock
         m_ListFilesNeedProcessing.Add(NewFile);
         lock.Unlock();  // Optional with CSingleLock
    }

You could just as easily use the CCriticalSection directly if you are not worried about abnormal execution - just change your m_lockingObj from CSingleLock to CCriticalSection.

As jkr notes, putting two calls to a critical section or mutex in succession will not cause a wait at the second one since a thread is allowed to acquire a sync bject multiple times.  This allows you to recurse safely.  Note, however, that the thread must do a parallel release of the sync object for each acquisition.
0
 
LVL 30

Author Comment

by:Axter
ID: 12387679
>>"Once a thread has ownership of a critical section, it can make additional calls to EnterCriticalSection or TryEnterCriticalSection without blocking its execution.
That explains a lot of the problems I've been having with my code.
But I'm not sure what's an easy work around, without having to completely change my code.


>> that the thread must do a parallel release of the sync object for each acquisition.

Not sure what you mean by this.  Can you give more details, or different explanation?

0
 
LVL 30

Author Comment

by:Axter
ID: 12387701
My main problem here is that I want a thread to create FileWatcher object, and then when it calls GetFileName member function, I want it to block until there's a valid filename to retrieve.

FileWatcher  filewatcher;

CString NewFile = filewatcher.GetFileName(); //Should block here


To accomplish this, I had a member function called m_WaitUntilInQue, which in the constructor I called the lock.

FileWatcher ::FileWatcher()
{
   m_WaitUntilInQue.Lock();
}

Then in GetFileName, I made another call to Lock.

CString FileWatcher::GetFileName()
{
   m_WaitUntilInQue.Lock();//This should block until another process Unlocks
   //Now get data in container and return it
   

Unfortunately, with this logic, both Locks are in the same thread.


I'm not sure what would be a good work around.

For now, I removed the lock code, and I have GetFileName do a check on the size of the container.  If the container is empty, I do a Sleep(1000), and then check again.
But I hate this method, since it takes more resources, and there's a one second delay when valid data arrives.
0
 
LVL 19

Accepted Solution

by:
drichards earned 1000 total points
ID: 12388394
>> Not sure what you mean by this.  Can you give more details, or different explanation?
This just means that if your thread locks an object n times, it must also unlock it n times in order to free the sync object for other threads.

It sounds like you want to use an event rather than a critical section or mutex.  Events are a little different in that you don't acquire them.  You just signal them in one hread while other threads are waiting for the signal.  In your case, you want the GeFileName thread to wait for something to be placed in a queue somewhere.  So you have GetFileName wait on the event and code somewhere else willset the event.

Events can be auto-reset or manual reset.  Auto reset events will release exactly one waiting thread and will remain set until a thread tries to wait on it.  A manula reset event, on the other hand, remains set until you expliciitly reset it.  Thus, the manual reset event will release ALL threads waiting on it until it is reset.  The code here uses the MFC CEvent.  If you don't plan on using CSingleLock, you could use the raw Windows event object.  In that case, m_WaitUntilInQue becomes a HANDLE and you use the CreateEvent, SetEvent(HANDLE hEvent), and WaitForSingleObject Win32 API functions (shown as comments in the example below).

-----------------------------------------
    class Filewatcher
    {
    ...
    private:
        CEvent m_WaitUntilInQue; // HANDLE m_WaitUntilInQueue;
    };

    // Create an auto-reset event
    FileWatcher::FileWatcher()
    :m_WaitUntilInQue() // m_WaitUntilInQueue(CreateEvent(NULL, FALSE, FALSE, NULL))
    {}

    CString FileWatcher::GetFileName()
    {
       m_WaitUntilInQue.Lock();// WaitForSingleObject(m_WaitUntilInQue, INFINITE);
       //Now get data in container and return it
    }

Then, wherever your code is that puts something in the queue:

     m_WaitUntilInQue.SetEvent(); // SetEvent(m_WaitUntilInQue);
--------------------------------------------------

The other aspect of this is that the wait is infinite in GetFileName.  If you want to end the program, you need an additional signal to break the wait or time out the wait periodically and check for exit conditions.  You could also just terminate the waiting thread, but then your program must keep a handle to the thread in order to kill it later. One way to manage thread shutdown is to have a global manual reset event that is given to all synchronized classes via the constructor or an initialization method.  This code checks every second to see if it should exit.  You could just as easily use a simple flag rather than an event as the exit signal.  The advantage of the event is that in some cases you can save a bit of code by using WaitForMultipleObjects.


    CString FileWatcher::GetFileName()
    {
        CSyncObject *so[] = { &m_WaitUntilInQue, &m_exitEvent }; // Don't need with Win32 version
        CMultiLock waitObjects(so, 2);  // HANDLE waitObjects[] = { m_WaitUntilInQue, m_exitEvent };
        if (waitObjects.Lock(INFINITE, FALSE) == WAIT_OBJECT_0) // if ( WaitForMultipleObjects(2, waitObjects, FALSE, INFINITE) == WAIT_OBJECT_0 )
        {
            // Only get here if queue event is signaled.
            //Now get data in container and return it
         }
    }

Elsewhere the thread will have to examine the exit event to see if it should exit its thread function.

Depending on what the program is doing, it may be OK to just terminate the thread, but that's usually bad practice.
0
 
LVL 30

Author Comment

by:Axter
ID: 12388540
CEvent sounds like what I need.

I'll have to wait until Monday before I can test this out.

Thanks
0
 
LVL 30

Author Comment

by:Axter
ID: 12409179
Thank you both very much.

There were two main problems with my code.
First problems, was that I didn't realize CSyncObjects could not lock itself on the same thread.
My second main problem, is that I was trying to use one class for all my multithreading needs.

I'm now using CEvent for the GetFileName and AddFileName.
And I'm also using CMutex to lock and unlock before modifying or using my m_ListFilesNeedProcessing container.

I'm also using CSemaphore to limit the number of threads created.

Now that I understand the difference between CSemaphore, CMutex, and CCriticalSection I feel much more confident in using them.
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.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Introduction: The undo support, implementing a stack. Continuing from the eigth article about sudoku.   We need a mechanism to keep track of the digits entered so as to implement an undo mechanism.  This should be a ‘Last In First Out’ collec…
If you use Adobe Reader X it is possible you can't open OLE PDF documents in the standard. The reason is the 'save box mode' in adobe reader X. Many people think the protected Mode of adobe reader x is only to stop the write access. But this fe…
This video will show you how to get GIT to work in Eclipse.   It will walk you through how to install the EGit plugin in eclipse and how to checkout an existing repository.
This tutorial will teach you the special effect of super speed similar to the fictional character Wally West aka "The Flash" After Shake : http://www.videocopilot.net/presets/after_shake/ All lightning effects with instructions : http://www.mediaf…

656 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question