4

I'm writing an ASP.NET web application using C# that may read and write a text file from the IIS server (using System.IO.FileStream) and I'm wondering how do I implement a global lock on this operation?

c00000fd
  • 18,074
  • 19
  • 132
  • 318

5 Answers5

3

For global lock you need mutex

    // The key can be part of the file name - 
    //   be careful not all characters are valid
    var mut = new Mutex(true, key);

    try
    {   
        // Wait until it is safe to enter.
        mut.WaitOne();

        // here you manipulate your file
    }
    finally
    {
        // Release the Mutex.
        mut.ReleaseMutex();
    }   
Aristos
  • 63,580
  • 14
  • 112
  • 146
  • Thanks. A follow-up question. Do I have to make the mutex name in your 'key' variable as `Global\key` for it to be read on a global scale? – c00000fd Mar 03 '13 at 00:47
  • @c00000fd The key, what ever the name is, is global for the computer. – Aristos Mar 03 '13 at 00:49
  • OK. I'm basing it on WinAPI named mutexes (which I believe this one is "on the inside"). Read description for the `lpName` parameter: http://msdn.microsoft.com/en-us/library/windows/desktop/ms682411(v=vs.85).aspx – c00000fd Mar 03 '13 at 00:59
  • @c00000fd I do not understand you. Can you tell direct your point? – Aristos Mar 03 '13 at 01:01
  • @c00000fd I use it on my program with out set any Global\ at the front and is lock globally. Now I do not know how more global can be and what is this global in front is mean/used. – Aristos Mar 03 '13 at 01:07
  • It the unmanaged world the `Global\ ` prefix on the name will make that synchronization object global vs. local (`global` meaning that a process running in one session will see this object from another session.) I was thinking to use it because I'm not sure from how many sessions my ASP.NET web app may be running on the IIS? It'd be nice if someone could confirm or deny it? – c00000fd Mar 03 '13 at 01:13
  • @c00000fd I do not know to answer what this global means and how is used. I use the mutex on my program that is web garden with 5 pools, many threads each pool, with out set any global there, and the mutex works as global. The mutex is global for the computer. – Aristos Mar 03 '13 at 01:16
  • 1
    Check this one: http://stackoverflow.com/questions/229565/what-is-a-good-pattern-for-using-a-global-mutex-in-c – c00000fd Mar 03 '13 at 01:19
  • @c00000fd Yes, ok I will check it with details on my program. – Aristos Mar 03 '13 at 01:22
  • Aren't named mutex system-wide? If yes it's quite expensive. And is not necessary for operations inside a single process. Isn't it? – abatishchev Mar 03 '13 at 02:09
  • @abatishchev: Are you sure that your web app will run in a single process? – c00000fd Mar 03 '13 at 06:50
  • @c00000fd: One application pool - one process, usually. [You can change that](http://stackoverflow.com/questions/2659571/how-does-application-pool-work-in-iis) however it's not recommended or better to say not used. – abatishchev Mar 03 '13 at 06:53
  • @abatishchev: So if my web app is opened through several web browsers connecting from different IPs, it will run in a single process, correct? – c00000fd Mar 03 '13 at 07:03
  • @c00000fd: On the server, yes. You can get corresponding PID (process id) [easily](http://stackoverflow.com/questions/748927/iis-application-pool-pid). – abatishchev Mar 03 '13 at 07:07
  • You will get permissions issues if you're using this code in multiple Application Pools. In that case, http://stackoverflow.com/questions/229565/what-is-a-good-pattern-for-using-a-global-mutex-in-c is a better solution -- the errors will look something like: ("Access to the path 'myMutexName' is denied.") – GlennG Jan 30 '15 at 10:31
  • @GlennG Thank you for the note, this is an idea, not a fully class that can handle all the cases. Now about the security issues, up to now I didnt have any issue, but thank you for the note. Please also note, here we have asp.net pool, runs under the same account, we do not have multiple accounts) – Aristos Jan 30 '15 at 10:40
1

The easiest solution would be to create a new object in the Cache or Application object, preferably in the Application_Startup within the global.asax file. Such as:

Cache["myLocker"] = new object();

Then you can use the standard "lock" syntax.

lock(Cache["myLocker"]) 
{
  // do file access here...
}
Tim P.
  • 2,805
  • 21
  • 26
  • `Application` is first thing coming to mind. But I was told once and I like this idea that this class was provided for Classic ASP compatibility and you don't need it actually because you can use static classes instead which are much faster. – abatishchev Mar 03 '13 at 02:17
1

From what @Aristos suggested and from this post, I came up with this class:

using System.Threading;
using System.Security.AccessControl;
using System.Security.Principal;

using System.Runtime.InteropServices;   //GuidAttribute
using System.Reflection;                //Assembly

namespace ITXClimateSaverWebApp
{
    public class GlobalNamedLock
    {
        private Mutex mtx;

        public GlobalNamedLock(string strLockName)
        {
        //Name must be provided!
            if(string.IsNullOrWhiteSpace(strLockName))
            {
                //Use default name
                strLockName = ((GuidAttribute)Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(GuidAttribute), false).GetValue(0)).Value.ToString();
            }

            //Create security permissions for everyone
            //It is needed in case the mutex is used by a process with
            //different set of privileges than the one that created it
            //Setting it will avoid access_denied errors.
            MutexSecurity mSec = new MutexSecurity();
            mSec.AddAccessRule(new MutexAccessRule(new SecurityIdentifier(WellKnownSidType.WorldSid, null),
                MutexRights.FullControl, AccessControlType.Allow));

            //Create the global mutex
            bool bCreatedNew;
            mtx = new Mutex(false, @"Global\" + strLockName, out bCreatedNew, mSec);
        }

        public bool enterCRITICAL_SECTION()
        {
            //Enter critical section
            //INFO: May throw an exception!
            //RETURN:
            //      = 'true' if successfully entered
            //      = 'false' if failed (DO NOT continue!)

            //Wait
            return mtx.WaitOne();
        }

        public void leaveCRITICAL_SECTION()
        {
            //Leave critical section
            //INFO: May throw an exception!

            //Release it
            mtx.ReleaseMutex();
        }
    }
}

and then the way to call it for a global lock:

try
{
    GlobalNamedLock gl = new GlobalNamedLock("MyLockName");

    try
    {
        if (gl.enterCRITICAL_SECTION())
        {
            //Use the global resource now
        }
    }
    finally
    {
        gl.leaveCRITICAL_SECTION();
    }
}
catch (Exception ex)
{
    //Failed -- log it
}

So this seems to do the job. What do you think?

Community
  • 1
  • 1
c00000fd
  • 18,074
  • 19
  • 132
  • 318
0

Why don't just use global sync root object?

internal static class Lock
{
    public static object SyncRoot = new object{};
}

Usage:

lock (Lock.SyncRoot)
{
}
abatishchev
  • 92,232
  • 78
  • 284
  • 421
  • Is it because this static lock doesn't really work with multiple worker processes? – Shiroy Apr 18 '17 at 22:57
  • This answer is wrong actually, and wont work in cases with multiple worker processes. – Ahmad Aug 18 '19 at 20:48
  • @Ahmad: the question mentions nothing about "multiple worker processes", neither all other answers, basically. So what are you talking about exactly? – abatishchev Aug 19 '19 at 03:46
  • @abatishchev It is talking about a global lock, and the way you provide will not do that in some cases that will not be rare. That's my point. – Ahmad Aug 20 '19 at 06:07
  • @Ahmad: which of the answers above *would work* with multiple worker processes? – abatishchev Aug 20 '19 at 06:26
  • @abatishchev The answer by @Aristos that uses inter-process `Mutex`. – Ahmad Aug 21 '19 at 07:17
  • @Ahmad: Fair enough. However, the question itself mentions nothing of that in the initial requirements. – abatishchev Aug 21 '19 at 18:56
0

Named mutexes are systems objects. Unlike lock keywords, they work even across process boundaries. Lock is only useful in the context of synchronizing threads in same process.