.net memory leaking with Thread.Abort() and using statement - c#

Edit:I am trying to use Constrained Execution Regions as a new weapon against Abort(). I am still running test, I hope it work. Abort() is really a bad thing. I will report later.. If anyone has any argument against CER please note that.
I am having a problem about .NET memory leaking.
I have a project, quite complex, threads in threads in threads, makes it hard to debug.
I know Abort() is depreciated, but I have my reasons to use them:
My method is a long method, I/O, network related. There's no explicit time-consuming operation that I can put a flag inside. If I put flags everywhere the code will be a mess.
When needed, the thread has to be terminated at once, sooner is better. All the work inside is no longer needed.
When I run the program normally, by which threads finish their work and die naturally one by one, everything is ok (I have run my program for 2 years, no memory leaking).
But if I try to create new thread, Start() and Abort() them frequently (1-2 threads/second, just a bit faster than the case I don't abort()), the memory leaks.
And the more wired thing is that, after the operation is finished, the memory occupied will stay high for a few time, like minutes or 10 minutes, but it will finally return to normal level, like nothing happened.
In debug mode, I can see no active thread, but memory leaked.
So I used .NET memory profiler to trace. Most instances occupied the memory are many byte[] referenced by MemoryStream.
Yes, I do use MemoryStream, but ALL of my MemoryStreams are inside using blocks, no exception. Everything should be in using block correctly.
As far as I know, Thread.Abort() is throwing an exception to force it to close, but as I confirmed in debug mode, all the threads are closed expectedly. Why there are still reference? Why the memory is released after some time? (But still much longer than the situation when I don't abort the thread and let it finish the work.)
IMO using blocks can guarantee that even the ThreadAbortException is thrown inside the Dispose() can be executed correctly.
Edit:
public static byte[] Recover(byte[] png)
{
using (MemoryStream pngStream = new MemoryStream(png))
{
using (System.Drawing.Image image = System.Drawing.Image.FromStream(pngStream))
{
using (MemoryStream bmpStream = new MemoryStream())
{
image.Save(bmpStream, System.Drawing.Imaging.ImageFormat.Bmp);
bmpStream.Seek(0, System.IO.SeekOrigin.Begin);
byte[] BMP = new byte[bmpStream.Length];
bmpStream.Read(BMP, 0, (int)bmpStream.Length);
return BMP;
}
}
}
}
public xxxxMission(byte[] png, Server server, int no) //constructor
{
byte[] bmp = xxxBMP.Recover(png); //png is generated by getBlankBMP();
//....
}

Related

How do I properly dispose and free the memory used for V8.Net.V8Engine instances?

I'm running into an issue when using my V8Engine instance, it appears to have a small memory leak, and disposing of it, as well as forcing the garbage collection doesn't seem to help much. It will eventually throw an AccessViolationException on V8Enging local_m_negine = new V8Engine() claiming a Fatal error in heap setup, Allocation failed - process out of memory and Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
Monitoring the program's memory usage through Task manager whilst running confirms that it is leaking memory, around 1000 KB every couple of seconds I think. I suspect it is the variables being declared within the executed script not being collected, or something to do with the GlobalObject.SetProperty method. Calling V8Engine.ForceV8GarbageCollection(), V8Engine.Dispose() and even GC.WaitForPendingFinalizers() & GC.Collect() doesn't prevent this memory being leaked (Although it is worth noting that it seems to leak it slower with these commands in place, and I know I shouldn't use GC but it was there as a last resort to see if it would fix the issue.)
A tangential issue that could also provide a solution is the inability to clear the execution context for V8Engine. I am required to dispose and re-instantiate the engine for each script, which I believe is where the memory leak is happening, otherwise I run into issues where variables have already been declared, causing V8Engine.Execute() to throw an exception saying such.
I can definitely confirm that the memory leak is something to do with the V8Engine Implementation, as running the older version of this program that uses Microsoft.JScript has no such memory leak, and the memory used remains consistent.
The affected code is as follows;
//Create the V8Engine and dispose when done
using (V8Engine local_m_engine = new V8Engine())
{
//Set the Lookup instance as a global object so that the JS code in the V8.Net wrapper can access it
local_m_engine.GlobalObject.SetProperty("Lookup", m_lookup, null, true, ScriptMemberSecurity.ReadOnly);
//Execute the script
result = local_m_engine.Execute(script);
//Please just clear everything I can't cope.
local_m_engine.ForceV8GarbageCollection();
local_m_engine.GlobalObject.Dispose();
}
EDIT:
Not sure how useful this will be but I've been running some memory profiling tools on it and have learnt that after running an isolated version of the original code, My software ends up with a large amount of instances of IndexedObjectList's full of null values (see here: http://imgur.com/a/bll5K). It appears to have one instance of each class for each V8Engine instance that is made, but they aren't being disposed or freed. I cant help but feel like I'm missing a command or something here.
The code I'm using to test and recreate the memory leak that the above implementation causes is as follows:
using System;
using V8.Net;
namespace V8DotNetMemoryTest
{
class Program
{
static void Main(string[] args)
{
string script = #" var math1 = 5;
var math2 = 10;
result = 5 + 10;";
Handle result;
int i = 0;
V8Engine local_m_engine;
while (true)
{
//Create the V8Engine and dispose when done
local_m_engine = new V8Engine();
//Set the Lookup instance as a global object so that the JS code in the V8.Net wrapper can access it
//local_m_engine.GlobalObject.SetProperty("Lookup", m_lookup, null, true, ScriptMemberSecurity.ReadOnly);
//Execute the script
result = local_m_engine.Execute(script);
Console.WriteLine(i++);
result.ReleaseManagedObject();
result.Dispose();
local_m_engine.Dispose();
GC.WaitForPendingFinalizers();
GC.Collect();
local_m_engine = null;
}
}
}
}
Sorry, I had no idea this question existed. Make sure to use the v8.net tag.
Your problem is this line:
result = local_m_engine.Execute(script);
The result returned is never disposed. ;) You are responsible for returned handles. Those handles are struct values, not class objects.
You could also do using (result = local_m_engine.Execute(script)) { ... }
There is a new version released. I am finally resurrecting this project again as I will need it for the FlowScript VPL project - and it now supports .Net Standard as well for cross-platform support!

Why do bitmaps stay in memory unless I call GC.Collect?

I'm developing an application that connects to a GigEVision camera, and pulls images from it. I'm currently using the Pleora eBus SDK with C#.NET.
The code below is just a test application for the camera connection - it can stream images, but rapidly runs out of memory unless I call GC.Collect();
It's worth noting the images being streamed are large (4096x3072), so the crash happens fairly quickly.
I suspected at first that not calling Dispose() was the issue. However, I can call Dispose() on each image right before getting rid of the reference to it, and that did not fix the issue.
I've also tried explicitly releasing the buffers that come into the display thread callback, but that had no effect.
Can I get my memory back in a more elegant way?
using System;
using System.Windows.Forms;
using PvDotNet;
using PvGUIDotNet;
using System.Drawing;
namespace eBus_Connection
{
public partial class MainForm : Form
{
PvDeviceGEV camera;
PvStreamGEV stream;
PvPipeline pipeline;
PvDisplayThread thread;
bool updating = false;
public MainForm()
{
InitializeComponent();
}
private void MainForm_Shown(object sender, EventArgs e)
{
PvDeviceInfo info;
PvDeviceFinderForm form = new PvDeviceFinderForm();
form.ShowDialog();
info = form.Selected;
camera = PvDeviceGEV.CreateAndConnect(info) as PvDeviceGEV;
stream = PvStreamGEV.CreateAndOpen(info.ConnectionID) as PvStreamGEV;
pipeline = new PvPipeline(stream);
if (camera == null || stream == null)
throw new Exception("Camera or stream could not be created.");
camera.NegotiatePacketSize();
camera.SetStreamDestination(stream.LocalIPAddress, stream.LocalPort);
camera.StreamEnable();
camera.Parameters.ExecuteCommand("AcquisitionStart");
pipeline.Start();
thread = new PvDisplayThread();
thread.OnBufferDisplay += thread_OnBufferDisplay;
thread.Start(pipeline, camera.Parameters);
status.DisplayThread = thread;
status.Stream = stream;
}
void thread_OnBufferDisplay(PvDisplayThread aDisplayThread, PvBuffer aBuffer)
{
Bitmap b = new Bitmap((int)aBuffer.Image.Width, (int)aBuffer.Image.Height);
aBuffer.Image.CopyToBitmap(b);
BeginInvoke(new Action<Bitmap>(ChangeImage), b);
}
void ChangeImage(Bitmap b)
{
if (PictureBox.Image != null)
PictureBox.Dispose();
PictureBox.Image = b;
GC.Collect();//taking this away causes memory to leak rapidly.
}
}
}
It's very likely that somewhere in your code an Image such as a Bitmap is not being disposed. Bitmap extends Image which implements IDisposable which means you need to call Dispose() on it when you're done with it (often by wrapping it with a using statement). You aren't disposing the Bitmap or Image somewhere so the GC finalizes it when it can (or in this case when you explicitly invoke the GC).
Once the GC determines that a class is no longer referenced, it becomes available to clean up... Before cleaning it up, it checks for a finalizer. If a finalizer exists, the class is placed in a special GC finalizer queue that will run the finalizer before cleaning up the resources/memory. Most IDisposable classes have finalizers which allow the GC to do the Dispose() call work in case you forgot to manually dispose the class yourself. It seems that this is what is happening to your code, but without seeing ALL classes I can only guess what isn't disposed (and no idea where).
EDIT: I do have a guess though. I bet the PictureBox.Dispose() call does not dispose PictureBox.Image
If an object implements IDisposable then you should absolutely call Dispose on it but disposing an object doesn't release the memory it occupies. It releases things like, in this case, an image handle. Such resources have to be released first, before memory can be reclaimed, so disposing still helps.
When the GC runs, if an object hasn't been disposed then it must first finalise it, meaning that it must wait longer to reclaim the memory. If the object has been disposed, memory is reclaimed as soon as the GC runs.
The GC runs in the background though. If your application is busy allocating more and more memory then the GC never gets a chance to run and reclaim it, regardless of whether you dispose objects or not. In such cases, you need to invoke the GC explicitly from time to time. Creating multiple images is the most common scenario that requires explicit GC invocation.
It's worth noting that ALL objects remain in memory until the GC runs and cleans them up, whether the object implements IDisposable or not. You normally don't notice it though, because most apps have enough down-time to allow the GC to run implicitly and reclaim that memory. There's nothing special about your Bitmap objects in this regard.
You are disposing the picture box instead of the image. Even if that will dispose the image in the picture box, it will only do that the first time. After that the picture box is in a disposed state and calling Dispose again will do nothing.
You should get the image reference from the picture box and dispose it once it's not used any more:
void ChangeImage(Bitmap b) {
Image oldImage = PictureBox.Image;
PictureBox.Image = b;
if (oldImage != null) {
oldImage.Dispose();
}
}
A bitmap that isn't disposed correctly have to be finalized before it can be collected. There is a background thread that finalizes objects that needs to be collected, but if you abandon objects faster than that thread can take care of them, you will run out of memory.
When the bitmap is disposed correctly, it becomes a regular managed object that can be collected right away whenever the garbage collector wants to.

out of memory exception when use control.BackgroundImage = Image.FromStream(memStream);

I write a code that read a png image from file and show with control.
I want read image from stream and set
control.BackgroundImage = Image.FromStream(memStream);
but when use this code , occur "out of memory" exception. but when use
control.Image = Image.FromStream(memStream);
or
control.BackgroundImage = Image.FromFile(fileSource);
, that is work.
image file size is 5KB.
if (System.IO.File.Exists(imgSource))
{
using (FileStream localFileStream = new FileStream(imgSource, FileMode.Open))
{
using (MemoryStream memStream = new MemoryStream())
{
int bytesRead;
byte[] buffer = new byte[1024];
while ((bytesRead = localFileStream.Read(buffer, 0, buffer.Length)) > 0)
{
memStream.Write(buffer, 0, bytesRead);
}
retIMG = Image.FromStream(memStream);
pictureBox1.Image = retIMG; // is work
label1.Image = retIMG; // is work
button1.Image = retIMG; // is work
button1.BackgroundImage = retIMG; // don't work
groupBox1.BackgroundImage = retIMG; // don't work
panel1.BackgroundImage = retIMG; // don't work
}
}
}
I think a bug in .net framework.
please you help me?
Read the remarks on Image.FromStream on MSDN:
You must keep the stream open for the lifetime of the Image.
So if you remove the using around the creation of your MemoryStream your code works fine.
Of course you should preferrably dispose the MemoryStream once you no longer need the Image you created, although there is likely no harm in this case in not calling Dispose() and leaving it up to the GC to collect it once unused.
The fact that it seems to work with some of your code is likely pure luck and should not be considered a working solution. Always read the documentation to find out about quirks like this.
Giving some background to add to DeCaf's correct answer. GDI+ tries very hard to avoid copying the pixels of a bitmap. That's expensive, bitmaps taking dozens of megabytes is not unusual. When you load a bitmap from a file with the Bitmap constructor or Image.FromFile() then GDI+ creates a memory-mapped file. The pixels are paged-in on demand, only when needed. Very efficient but it puts a lock on the file. Clearly you were trying to avoid that in lock in this code.
You indeed avoid that lock by loading the bytes into memory yourself with a MemoryStream. But the same principle still applies, GDI+ still doesn't copy the pixels and only reads from the stream when it needs to. This goes wrong when you Dispose() the stream. Very hard to diagnose because the exception occurs later, typically when the bitmap needs to be drawn. It bombs in the painting code, you don't have any code to look at but Application.Run(). With a crappy exception message, GDI+ only has a handful of error codes. You are not out of memory, it only looks that way to GDI+, it cannot otherwise figure out why the stream suddenly isn't readable anymore.
At least part of the problem is caused by the very awkward implementation of MemoryStream.Dispose(). Dispose is meant to release unmanaged resources. A memory stream doesn't have any, it only owns memory. That's already taken care of by the garbage collector. Unfortunately they implemented it anyway. Not by actually disposing anything, since there's nothing to dispose, but by marking the MemoryStream unreadable. Which triggers the error in GDI+ when it tries to read while drawing the bitmap.
So simply remove the using statement to avoid disposing the MemoryStream to solve your problem. And don't fret about disposing it later when the bitmap is no longer in use. There's nothing to dispose, the garbage collector automatically frees the memory.
Two things which together resolved this intermittent issue, which has nothing to do with image size.
First, ensure that the image is in RGB mode and definitely not CMYK mode. In our experience, the RGB rendering is actually larger.
Second, erase (and dispose of if possible) any previous image in the image container before loading the new image, such as
Control.Image = Nothing

New FileStream already closed/disposed?

I open a FileStream with FileMode.Open and FileAccess.Read. Shortly after that I call a function to handle the file's contents. I use Invoke to make the call because the call comes from a Thread and the function has to put the results on a Form. The function accepts any kind of Stream (I call it with MemoryStreams too without a problem) and uses XmlTextReader to read the XML in the FileStream, but on rare occasions for unknown reasons even the first Read() throws an ObjectDisposedException and the stream's CanRead property returns false if the stream was already closed.
In the Thread the FileStream is a local using variable, so I don't think another threads should be able to close it, and I don't close it until the Invoke returned. There are no Exceptions thrown so the file is definetly there (since there is no FileNotFoundException) and should be accessed properly (since there is no UnauthorizedAccessException and IOException).
How could my FileStream still look closed sometimes just after opened?
(It might matter that I'm running my code on a Windows CE 5 device with Compact Framework 3.5 and I wasn't able to reproduce the same behaviour on my desktop PC with XP yet.)
EDIT:
I know, that this Invoke is ugly but that alone can't be a reason to fail, can it? (And, in most of the cases it doesn't fail at all.)
//the code in the thread
//...
using (FileStream fs = File.Open(assemblyPath + "\\white.xml", FileMode.Open, FileAccess.Read))
{
mainForm.Instance.Invoke(new DataHandler(mainForm.Instance.handleData), new object[] { fs });
}
//...
//and the handler
public void handleData(Stream stream)
{
infoPanel.SuspendLayout();
try
{
using (XmlTextReader xml = new XmlTextReader(stream))
{
//it doesn't matter what is here
}
}
catch{}
}
There's one reason I can think of: the worker thread got aborted. This will run the finally block generated by the using statement and close the file. How it could be aborted is a secondary question. Is the thread's IsBackground property set to true? Is the program bombing on an unhandled exception elsewhere and shutting down? Just guesses of course.
Sure, this is expected behavior. You call Invoke, which marshals the call to another thread. The calling thread then continues to run and the using block exits, calling Dispose on the stream. This Dispose is happening before you are done (and maybe before you start) using the stream in the UI thread. The exact timing of these actions is going to depend on processor load and some other factors, but it's certainly unsafe.
Either don't put the stream in a using block or better yet have the thread do the read and pass the results to the UI via Invoke.
EDIT
As Hans points out in the comment, the above explanation should be for a BeginInvoke call, which underneath calls PostMessage. Invoke, on the other hand, uses SendMessage. Both propbably uses some WM_COPYDATA shenanigans (I've not looked to see) to marshal the data.
The Invoke call should be executing the entire handler you have posted, though the behavior you see indicates otherwise. From the code you posted there's no real way for us to determine what is closing the stream.
I would still refactor what you've done here because right now you're tying up both the UI and worker threads with the reader operation. I'd do the read work in the worker thread and then pass the results to the UI. This would decrease the odds of the reader work causing UI choppiness and would eliminate the possibility of the stream getting closed while you're reading from it.
I saw the same issue on some embedded board (ARM) I'm working on. Then I created a little test.
The following code (not involving any Threads!) crashes:
using (var w = new StreamWriter(File.Create("file.txt"), System.Text.Encoding.UTF8))
{
for (int i = 0; i < 1000; i++)
{
w.WriteLine("Test");
}
}
This code however does not crash:
using (var w = File.CreateText("file.txt"))
{
for (int i = 0; i < 1000; i++)
{
w.WriteLine("Test");
}
}
So, my guess can only be that the underlying native code treats text files differently than when you open the file using File.Create(). Both files are then written in UTF-8, so there is no difference about the encoding.
BTW: sorry I'm one year late on the answer, but I hope it'll help somebody

C# - Locking issues with Mutex

I've got a web application that controls which web applications get served traffic from our load balancer. The web application runs on each individual server.
It keeps track of the "in or out" state for each application in an object in the ASP.NET application state, and the object is serialized to a file on the disk whenever the state is changed. The state is deserialized from the file when the web application starts.
While the site itself only gets a couple requests a second tops, and the file it rarely accessed, I've found that it was extremely easy for some reason to get collisions while attempting to read from or write to the file. This mechanism needs to be extremely reliable, because we have an automated system that regularly does rolling deployments to the server.
Before anyone makes any comments questioning the prudence of any of the above, allow me to simply say that explaining the reasoning behind it would make this post much longer than it already is, so I'd like to avoid moving mountains.
That said, the code that I use to control access to the file looks like this:
internal static Mutex _lock = null;
/// <summary>Executes the specified <see cref="Func{FileStream, Object}" /> delegate on
/// the filesystem copy of the <see cref="ServerState" />.
/// The work done on the file is wrapped in a lock statement to ensure there are no
/// locking collisions caused by attempting to save and load the file simultaneously
/// from separate requests.
/// </summary>
/// <param name="action">The logic to be executed on the
/// <see cref="ServerState" /> file.</param>
/// <returns>An object containing any result data returned by <param name="func" />.
///</returns>
private static Boolean InvokeOnFile(Func<FileStream, Object> func, out Object result)
{
var l = new Logger();
if (ServerState._lock.WaitOne(1500, false))
{
l.LogInformation( "Got lock to read/write file-based server state."
, (Int32)VipEvent.GotStateLock);
var fileStream = File.Open( ServerState.PATH, FileMode.OpenOrCreate
, FileAccess.ReadWrite, FileShare.None);
result = func.Invoke(fileStream);
fileStream.Close();
fileStream.Dispose();
fileStream = null;
ServerState._lock.ReleaseMutex();
l.LogInformation( "Released state file lock."
, (Int32)VipEvent.ReleasedStateLock);
return true;
}
else
{
l.LogWarning( "Could not get a lock to access the file-based server state."
, (Int32)VipEvent.CouldNotGetStateLock);
result = null;
return false;
}
}
This usually works, but occasionally I cannot get access to the mutex (I see the "Could not get a lock" event in the log). I cannot reproduce this locally - it only happens on my production servers (Win Server 2k3/IIS 6). If I remove the timeout, the application hangs indefinitely (race condition??), including on subsequent requests.
When I do get the errors, looking at the event log tells me that the mutex lock was achieved and released by the previous request before the error was logged.
The mutex is instantiated in the Application_Start event. I get the same results when it is instantiated statically in the declaration.
Excuses, excuses: threading/locking is not my forté, as I generally don't have to worry about it.
Any suggestions as to why it randomly would fail to get a signal?
Update:
I've added proper error handling (how embarrassing!), but I am still getting the same errors - and for the record, unhandled exceptions were never the problem.
Only one process would ever be accessing the file - I don't use a web garden for this application's web pool, and no other applications use the file. The only exception I can think of would be when the app pool recycles, and the old WP is still open when the new one is created - but I can tell from watching the task manager that the issue occurs while there is only one worker process.
#mmr: How is using Monitor any different from using a Mutex? Based on the MSDN documentation, it looks like it is effectively doing the same thing - if and I can't get the lock with my Mutex, it does fail gracefully by just returning false.
Another thing to note: The issues I'm having seem to be completely random - if it fails on one request, it might work fine on the next. There doesn't seem to be a pattern, either (certainly no every other, at least).
Update 2:
This lock is not used for any other call. The only time _lock is referenced outside the InvokeOnFile method is when it is instantiated.
The Func that is invoked is either reading from the file and deserializing into an object, or serializing an object and writing it to the file. Neither operation is done on a separate thread.
ServerState.PATH is a static readonly field, which I don't expect would cause any concurrency problems.
I'd also like to re-iterate my earlier point that I cannot reproduce this locally (in Cassini).
Lessons learned:
Use proper error handling (duh!)
Use the right tool for the job (and have a basic understanding of what/how that tool does). As sambo points out, using a Mutex apparently has a lot of overhead, which was causing issues in my application, whereas Monitor is designed specifically for .NET.
You should only be using Mutexes if you need cross-process synchronization.
Although a mutex can be used for
intra-process thread synchronization,
using Monitor is generally preferred,
because monitors were designed
specifically for the .NET Framework
and therefore make better use of
resources. In contrast, the Mutex
class is a wrapper to a Win32
construct. While it is more powerful
than a monitor, a mutex requires
interop transitions that are more
computationally expensive than those
required by the Monitor class.
If you need to support inter-process locking you need a Global mutex.
The pattern being used is incredibly fragile, there is no exception handling and you are not ensuring that your Mutex is released. That is really risky code and most likely the reason you see these hangs when there is no timeout.
Also, if your file operation ever takes longer than 1.5 seconds then there is a chance concurrent Mutexes will not be able to grab it. I would recommend getting the locking right and avoiding the timeout.
I think its best to re-write this to use a lock. Also, it looks like you are calling out to another method, if this take forever, the lock will be held forever. That's pretty risky.
This is both shorter and much safer:
// if you want timeout support use
// try{var success=Monitor.TryEnter(m_syncObj, 2000);}
// finally{Monitor.Exit(m_syncObj)}
lock(m_syncObj)
{
l.LogInformation( "Got lock to read/write file-based server state."
, (Int32)VipEvent.GotStateLock);
using (var fileStream = File.Open( ServerState.PATH, FileMode.OpenOrCreate
, FileAccess.ReadWrite, FileShare.None))
{
// the line below is risky, what will happen if the call to invoke
// never returns?
result = func.Invoke(fileStream);
}
}
l.LogInformation("Released state file lock.", (Int32)VipEvent.ReleasedStateLock);
return true;
// note exceptions may leak out of this method. either handle them here.
// or in the calling method.
// For example the file access may fail of func.Invoke may fail
If some of the file operations fail, the lock will not be released. Most probably that is the case. Put the file operations in try/catch block, and release the lock in the finally block.
Anyway, if you read the file in your Global.asax Application_Start method, this will ensure that noone else is working on it (you said that the file is read on application start, right?). To avoid collisions on application pool restaring, etc., you just can try to read the file (assuming that the write operation takes an exclusive lock), and then wait 1 second and retry if exception is thrown.
Now, you have the problem of synchronizing the writes. Whatever method decides to change the file should take care to not invoke a write operation if another one is in progress with simple lock statement.
I see a couple of potential issues here.
Edit for Update 2: If the function is a simple serialize/deserialize combination, I'd separate the two out into two different functions, one into a 'serialize' function, and one into a 'deserialize' function. They really are two different tasks. You can then have different, lock-specific tasks. Invoke is nifty, but I've gotten into lots of trouble myself going for 'nifty' over 'working'.
1) Is your LogInformation function locking? Because you call it inside the mutex first, and then once you release the mutex. So if there's a lock to write to the log file/structure, then you can end up with your race condition there. To avoid that, put the log inside the lock.
2) Check out using the Monitor class, which I know works in C# and I'd assume works in ASP.NET. For that, you can just simply try to get the lock, and fail gracefully otherwise. One way to use this is to just keep trying to get the lock. (Edit for why: see here; basically, a mutex is across processes, the Monitor is in just one process, but was designed for .NET and so is preferred. No other real explanation is given by the docs.)
3) What happens if the filestream opening fails, because someone else has the lock? That would throw an exception, and that could cause this code to behave badly (ie, the lock is still held by the thread that has the exception, and another thread can get at it).
4) What about the func itself? Does that start another thread, or is it entirely within the one thread? What about accessing ServerState.PATH?
5) What other functions can access ServerState._lock? I prefer to have each function that requires a lock get its own lock, to avoid race/deadlock conditions. If you have many many threads, and each of them try to lock on the same object but for totally different tasks, then you could end up with deadlocks and races without any really easily understandable reason. I've changed to code to reflect that idea, rather than using some global lock. (I realize other people suggest a global lock; I really don't like that idea, because of the possibility of other things grabbing it for some task that is not this task).
Object MyLock = new Object();
private static Boolean InvokeOnFile(Func<FileStream, Object> func, out Object result)
{
var l = null;
var filestream = null;
Boolean success = false;
if (Monitor.TryEnter(MyLock, 1500))
try {
l = new Logger();
l.LogInformation("Got lock to read/write file-based server state.", (Int32)VipEvent.GotStateLock);
using (fileStream = File.Open(ServerState.PATH, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None)){
result = func.Invoke(fileStream);
} //'using' means avoiding the dispose/close requirements
success = true;
}
catch {//your filestream access failed
l.LogInformation("File access failed.", (Int32)VipEvent.ReleasedStateLock);
} finally {
l.LogInformation("About to released state file lock.", (Int32)VipEvent.ReleasedStateLock);
Monitor.Exit(MyLock);//gets you out of the lock you've got
}
} else {
result = null;
//l.LogWarning("Could not get a lock to access the file-based server state.", (Int32)VipEvent.CouldNotGetStateLock);//if the lock doesn't show in the log, then it wasn't gotten; again, if your logger is locking, then you could have some issues here
}
return Success;
}

Categories