1

I am trying to do the following:

var path = Server.MapPath("File.js"));

// Create the file if it doesn't exist or if the application has been restarted
// and the file was created before the application restarted
if (!File.Exists(path) || ApplicationStartTime > File.GetLastWriteTimeUtc(path)) {
    var script = "...";

    using (var sw = File.CreateText(path)) {
        sw.Write(script);
    }
}

However occasionally the following error is sometimes thrown:

The process cannot access the file '...\File.js' because it is being used by another process

I have looked on here for similar questions however mine seems slightly different from the others. Also I cannot replicate it until the server is under heavy load and therefore I wish to make sure it is correct before I upload the fix.

I'd appreciate it if someone could show me how to fix this.

Thanks

nfplee
  • 7,643
  • 12
  • 63
  • 124
  • Is your application run on demand / multiple times? You may need a mutex to protect file access. – helb Jan 15 '15 at 09:11
  • 1
    If two requests arrive at roughly the same time, the two IIS threads can try to write the file at the same time and step on each other's toes. You could implement some kind of locking, but I would advise against that strategy -- maybe you can write a handler to generate the script on the fly instead of writing it to disk every once in a while? – Frédéric Hamidi Jan 15 '15 at 09:12
  • @helb it's a web application and this code is trigger on every request. – nfplee Jan 15 '15 at 09:12
  • Have you tried sw.Close(); ? I know that the using statement is closing the file auto. but just to be sure. – Luca Jan 15 '15 at 09:14
  • You must guard mutual access since your application may run multiple times in parallel. – helb Jan 15 '15 at 09:14

1 Answers1

2

It sounds like two requests are running on your server at the same time, and they're both trying to write to that file at the same time.

You'll want to add in some sort of locking behavior, or else write a more robust architecture. Without knowing more about what specifically you're actually trying to accomplish with this file-writing procedure, the best I can suggest is locking. I'm generally not a fan of locking like this on web servers, since it makes requests depend on each other, but this would solve the problem.


Edit: Dirk pointed out below that this may or may not actually work. Depending on your web server configuration, static instances may not be shared, and the same result could occur. I've offered this as a proof of concept, but you should most definitely address the underlying problem.


private static object lockObj = new object();

private void YourMethod()
{
    var path = Server.MapPath("File.js"));

    lock (lockObj)
    {
        // Create the file if it doesn't exist or if the application has been restarted
        // and the file was created before the application restarted
        if (!File.Exists(path) || ApplicationStartTime > File.GetLastWriteTimeUtc(path))
        {
            var script = "...";

            using (var sw = File.CreateText(path))
            {
                sw.Write(script);
            }
        }
    }
}

But, again, I'd be tempted to reconsider what you're actually trying to accomplish with this. Perhaps you could build this file in the Application_Start method, or even just a static constructor. Doing it for every request is a messy approach that will be likely to cause issues. Particularly under heavy load, where every request will be forced to run synchronously.

Matthew Haugen
  • 12,916
  • 5
  • 38
  • 54
  • 1
    I'm not an expert on web servers, but if the server uses multiple app domains then this won't work as each app domain has its own static instances. – Dirk Jan 15 '15 at 09:17
  • @Dirk Ah, interesting. I hadn't thought of that, that's a good point. As I said, I'd never actually do this, it's more of just a proof of concept. But yes, if that's true (and I believe it), it's definitely worth bringing this code out into something that only runs once. – Matthew Haugen Jan 15 '15 at 09:18
  • 1
    You could use a named mutex instead of the standard lock. That even works across process boundaries. – Dirk Jan 15 '15 at 09:22
  • 1
    Thanks I took your advice and moved the code to the Application_Start event. – nfplee Jan 16 '15 at 09:52