ColinM
ColinM

Reputation: 204

Locking multiple threads

I have a C++ program in a Win32 device. The code has function X that should block other calls to X. That's simple enough, I can use a mutex to do that.

Function X, however, creates and launches a thread, Y, that will monitor things after X has finished. I need to ensure that X cannot run again until Y is satisfied that everything is finished properly.

As I understand it, a mutex can only be acquired and released on the same thread. What I'd like to do is to hand over the 'locked-ness' of the mutex from X to Y.

If it's easier to picture this in terms of what actually happens, X is there to print something, Y is there to check that the print job completes without running out of paper. Once Y is satisfied that the job has completed and the paper has not run out then it can let X print something else. We want X to finish ASAP so that the device can get on with other work (which will usually not involve printing, and therefore should not be held up while the printer finishes.)

So... is there a standard cross-thread locking pattern that will do what I want to do?

I can't use boost or any other third party libraries, only Windows built-in operations.

Upvotes: 1

Views: 655

Answers (3)

ColinM
ColinM

Reputation: 204

I marked Harry Johnston's answer as accepted because it sounds like it will do the job fine, and seems a good generic solution. And I may even use it.

What I have done for now is to replace the single mutex with two critical section objects, let's calls them CSx and CSy. Entry to X depends on entering CSx. Once that is done, X also enters CSy. It then immediately leaves CSy, and this is because when thread Y is launched it will enter CSy for the duration of its life. The only reason X enters CSy (and leaves it immediately) is because that's how it knows that thread Y is not running from a previous call to X.

This is similar to Erik's comment, above, but instead of tracking a single thread handle it allows multiple X and Y to queue up.

The risk here is that thread Y may not enter CSy before function X leaves CSx, so there is scope for another X to get in first, though the circumstances of the device and what it is doing means that this won't actually happen.

Even so, Harry's solution has the benefit of a single locking object and the simplicity and elegance that this brings.

Upvotes: 0

Harry Johnston
Harry Johnston

Reputation: 36348

That's a simple enough scenario. All you need to do is replace the mutex with an auto-reset event, initially signaled.

An auto-reset event can be used in the same way as a mutex (subject to some provisos, see below) but can be released by any thread:

  • The event starts out signaled ("unowned").
  • To enter function X, wait on the event. Only one thread will be allowed in, and the event will be cleared automatically.
  • Function X starts thread Y, then exits without signaling the event.
  • At this point no thread can enter function X, not even the same thread that made the previous call.
  • When thread Y has completed its work, it signals the event. This will allow exactly one thread to enter function X.

There are a few differences between mutex objects and event objects that you should be aware of:

  • Unlike a mutex, an auto-reset event does not allow recursive entry by the same thread. So if function X calls itself you'll need to rearrange the code slightly, so that acquiring the lock happens outside of the recursion.

  • Unlike a mutex, the API will not generate an error if the thread that "owns" an event exits unexpectedly. So if Y were to exit without signaling the event, the application will deadlock. If this is a problem, you will need to monitor the status of thread Y yourself. (And, of course, the same reasoning applies to the thread calling X, if it exits before launching Y.)

Upvotes: 2

Serge Ballesta
Serge Ballesta

Reputation: 149155

I would reverse the order of threads :

  • thread Y takes the mutex
  • thread Y starts thread X to do the printing
    • thread X does it printing and returns
    • thread Y waits for its condition and monitors printing
  • thread Y releases the mutex

That way allows you to lock and release the mutex in same thread. In addition you get a nicer separation of concerns :

  • X only deals with printing and does not even know of Y
  • Y is concerned by synchronization and proper printing end

Upvotes: 0

Related Questions