2

Good evening fellow stackers, I have a mutex and threaded related question about

// ¦ = Run one or the other per call to routine

|GROUP A|   GROUP B  |
| A & B GROUP MUTEX  |
|=======|============|
|A1 & A2¦ B1 & |B2|B3|
               |MUTEX|
               |==|==|
               |B2¦B3|

Situation

Is this situation safe or viable?

I have 5 sub routines in VB.NET and I can run them in 2 groups comprising of seperate subsets in an asynchronous threaded manner.

However, as the horribly rough text-o-graph above shows, the mutex's work on the subset B as well.

Logic

Group A and B have to wait for the eachother if the jobs have started on either - A & B GROUP MUTEX helps keep this in check.

Inside Group A both can routines begin concurrently (B would have to wait for both A1 and A2 to be complete).

Group B, with B1, B2 and B3 can run B1 at the same time as either B2 and B3 but B2 and B3 cannot run concurrently, hence the subset MUTEX check.

Update: @pstrjds made a good point with using Sync-locking instead: The reason I am using Mutex's and not Sync Lock is due to the fact that I need to make this process multi user safe as within the sub routines are a lot of SQL and data based operations which required the program to be locked whilst multiple SQL-Transactions are carried out on different databases across 3 different servers to update them concurrently and safely - transaction handling in SQL is done and working well hence the broken down psuedo-code. @pstrjds is correct in saying that this is using threads spawned off the one class, however within the subroutines it calls to a web service (part of Group B's work in B2 and B3) which updates a separate 3rd party server. </Wall-o-Text>

Psuedo-code

Unfortunately the exact code is rather long but I am using this structure:

Sub AGroup_Method()

    Dim bln_FirstInstance As Boolean

    Using objABMutex As New Mutex(True, "Global\AB_MutexLock", blnFirstInstance)
        If bln_FirstInstance Then
            //Start Threads for subroutine A1 And then A2
            StartThread_A1()
            StartThread_A2()
        Else
           //Post that Group A subroutine needs to wait for Group B
        End If
    End Using
End Sub

Sub BGroup_Method(Byval p_blnRunBTwo as Boolean)

    Dim bln_FirstInstance As Boolean
    Dim blnBGroup_FirstInstance As Boolean

    Using objABMutex As New Mutex(True, "Global\AB_MutexLock", bln_FirstInstance)
        If bln_FirstInstance Then
            //Do subroutine group B

            //Start B1
            StartThread_B1()

            Using objBGroupMutex As New Mutex(True, "Global\BGroup_MutexLock", blnBGroup_FirstInstance)
                If p_blnRunBTwo 
                    If blnBGroup_FirstInstance Then
                        //Wait for mutex from B3 and then run B2
                        StartThread_B2
                    End If
                Else 
                    If blnBGroup_FirstInstance Then
                        StartThread_B3
                    End If
                End If   
            End Using                     
        Else
           //Post that Group B subroutine needs to wait for Group A

        End If

    End Using

End Sub

Help!

What I would like to ask is if the mutex nesting is a possible problem, if it is bad practice whatnots and is there a better way of implementing this using a threaded system.

Tom 'Blue' Piddock
  • 2,131
  • 1
  • 21
  • 36
  • 1
    Unless you need these mutexes to be inter-process, you would not need to use named mutexes, and you only need "Global\" if you are using a named mutex in a terminal services environment where you need to protect something from multiple users on the same system (i.e. writing to a log file or something of that nature) – pstrjds Nov 17 '10 at 13:36
  • @pstrjds You are bang on with the inter-process, the original reason for the MUTEX was to allow it to run simultaneous by each user on the system, with a flag that signifies Group A, B2 or B3 option. Writing logs is also username and session dependant so they are safely named without a problem with locking. – Tom 'Blue' Piddock Nov 17 '10 at 14:05
  • Just a heads up on the global named mutex then, you can run into permissions issues for users that will not allow them to create the mutex, you also need to create it with proper MutexAccessRule for your situation so that users other than the creator are able to access it. – pstrjds Nov 17 '10 at 14:38
  • Ok duely noted, useful information! Add it to your answer. – Tom 'Blue' Piddock Nov 17 '10 at 15:07
  • Added with example (sorry its in C#, but should be easy to translate) – pstrjds Nov 17 '10 at 15:37
  • All good, can read C# easily, thank you for the update, I'll check it out and see how it works with the B group :) – Tom 'Blue' Piddock Nov 17 '10 at 17:45

2 Answers2

1

I don't think you are off the wall using a Mutex here, but if all of the code is in the same class and you are just spawning worker threads in that class (I am basing this off the pseudo-code you presented) you could get away with just declaring two objects and using SyncLock to handle the access. That should be more performative than using the Mutex (again assuming that all of this code is in the same class and the threads are launched from the class and you don't need inter-process serialization)

Dim abLock as Object = new Object()
Dim bLock as Object = new Object()

Sub AMethod()
    SyncLock abLock
    ' Do stuff here
    End SyncLock
End Sub

Sub BMethod(ByVal pInBTwo As Boolean)
    SyncLock abLock
        StartB1Thread()

        SyncLock bLock
            ' Do B2 or B3 stuff
        End SyncLock
    End SyncLock
End Sub

Edit: Added information about named mutex as per suggestion: If you are using a globally named mutex you can run into permissions issues if the user running the code has limited access (take for example running as a guest user). Another issue that comes into play is creating it with the proper MutexAccessRule so that another user can access it after it is created. Code sample in C#:

Mutex mutex = null;
bool exists = true;

// Using try catch since as far as I know there is no other way to check if
// a mutex exists, but to open it and catch the exception, this may be changed
// in .Net 4.0, I originally wrote this targeting 2.0
try
{
     mutex = Mutex.OpenExists(@"Global\MyMutexName");
}
catch
{
     exists = false;
}

if (!exists)
{
     SecurityIdentifer sid = new SecurityIdentifier(WellKnownSidType.WorldSid, null);
     MutexAccessRule rule = new MutexAccessRule(sid, MutexRights.FullControl,
         AccessControlType.Allow);
     MutexSecurity security = new MutexSecurity();
     security.AddAccessRule(rule);

     bool createdNew = false;
     mutex = new Mutex(false, @"Global\MyMutexName", out createdNew, security);
}

The above code will create a mutex that is not restricted to the user that created it, this is important if you are in a terminal services environment with multiple people running the same program at the same time. You may be okay restricting some of the permissions more than I did there (really I create it with almost no restrictions), but without that you can have person A create the mutex and person B throw an exception because they can't access the mutex created by person A.

pstrjds
  • 16,840
  • 6
  • 52
  • 61
  • Voted up for use of Sync Lock which would be a good use if this wasn't inter-process. I will clarify in my question the interprocess situation! A valid and useful answer none the less. – Tom 'Blue' Piddock Nov 17 '10 at 14:07
  • Well it being interprocess also throws out my other thought that you might want a series of event handles. I have to agree with @Hans that it is unclear as to what exactly is going on. If these are running in other processes than you may want to look at passing messages back and forth to get the synchronization. – pstrjds Nov 17 '10 at 14:16
  • When testing, the mutex lock didn't have issues within the one user but after changing to this style of Mutex the process works securely across multiple users and does not dead lock - Accepted as answer. – Tom 'Blue' Piddock Nov 18 '10 at 09:12
  • Great, glad it is working. I ended up having to do this more advanced Mutex creation when using multiple users in a terminal services environment. If I didn't then the first user could create and lock the mutex, but all other users ran into permissions issues with it and rather than locking threw exceptions. – pstrjds Nov 18 '10 at 13:25
1

Yes, it is a bad practice. I scanned the question a few times but I don't exactly feel informed well enough to give a good answer. That's a problem, if you can't easily explain your locking model and you've got trouble coming up with a good picture that makes it clear then it also becomes very difficult to reason about the number of possible state permutations.

Not being able to reason about all possible state permutations has a potentially very nasty runtime penalty. Deadlock.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536