Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Automatic Semaphore release on process Exit

I am using Semaphore to limit the number of concurrent instances my application can run.

There are many ways a process can terminate. Can the Semaphore be created so it automatically releases upon process exit?

EDIT:

I would like some magic to automatically clean up the semaphore 'raised' state for the process owning it upon exit or crash. Just to be sure that it is cleared, no matter what.

MORE:

I am looking for any viable option for it, considering:

  • it would be great that NO external application is required to get a hold on every instance of the protected application
  • it doesn't have to be Semaphore - any synchronization object that has COUNTER and is AUTOMATICALLY released upon death of a owner process will be fine, even if it's cheating
  • I am using .NET 2.0, can't move to newer version on this project, but can use c/c++ and inter-op to leverage something if there is something
like image 916
Daniel Mošmondor Avatar asked Mar 08 '11 11:03

Daniel Mošmondor


3 Answers

You can hook into the AppDomain.ProcessExit event to perform any cleanup operations like releasing the semaphore.

Generally, named semaphores are designed to coordinate resources across processes without taking particular process life-time into account. Semaphores in .NET are backed by native Windows semaphore objects, and the MSDN says:

The semaphore object is destroyed when its last handle has been closed. Closing the handle does not affect the semaphore count; therefore, be sure to call ReleaseSemaphore before closing the handle or before the process terminates.

Hence the right approach is explicit handling before process termination.


Update — Other options to consider:

  1. In case it's not feasible to handle “emergency” release manually in the AppDomain.ProcessExit event, consider creating an IDisposable wrapper that would acquire the semaphore in its constructor and release it in the Dispose method.
  2. Another question is: is a Semaphore the right synchronization object for this case? Wouldn't a simple (named) mutex work better?

Update — In case of an application crash or forced termination (i.e. via Task Manager) ProcessExit won't have a chance to be handled. Hence any unmanaged resources shared between multiple processes may not be finalized / disposed / handled correctly. See this article for further details.

A viable option may be creating a named pipe. The advantage of named pipes is they cease to exit once the creating process is terminated. According to MSDN:

Note that an instance of a named pipe may have more than one handle associated with it. An instance of a named pipe is always deleted when the last handle to the instance of the named pipe is closed.

There are two options to limit the number of pipe instances:

  1. Just one instance: By specifying the FILE_FLAG_FIRST_PIPE_INSTANCE flag in the dwOpenMode argument it is possible to prohibit creation of multiple instances of the pipe. Then, the second process attempting to create the pipe will receive an error.
  2. More instances: By specifying the number of allowed instances in the nMaxInstances argument. When N are allowed, the N+1st process will receive an error.
like image 83
Ondrej Tucny Avatar answered Nov 11 '22 04:11

Ondrej Tucny


The appropriate answer is to implement a 'Critical Finalizer' around your semaphore to ensure proper clean-up in all cases. ProcessExit is not guaranteed to execute in a failure scenario, such as a forced appdomain unload due to non-trappable exceptions (StackOverflowException and InvalidProgramException being two good examples.)

More info @ http://msdn.microsoft.com/en-us/library/system.runtime.constrainedexecution.criticalfinalizerobject.aspx, to quote: "the common language runtime (CLR) guarantees that all critical finalization code will be given the opportunity to execute, provided the finalizer follows the rules for a CER, even in situations where the CLR forcibly unloads an application domain or aborts a thread."

like image 24
Shaun Wilson Avatar answered Nov 11 '22 03:11

Shaun Wilson


If, unlike Daniel, you can upgrade to .Net 3.5 a NamedPipeServerStream class is available to create your pipe.

  NamedPipeServerStream pipe;
  try
  {
    pipe = new NamedPipeServerStream(name, PipeDirection.InOut, 3);
  }
  catch (IOException)
  {
    //Maximum number of instances reached (3).
  }

I advice you to keep a static reference on the pipe to avoid Finalization before exiting the process. The drawback of pipe is that you can't wait until an instance is available without polling.

like image 2
Guillaume Avatar answered Nov 11 '22 04:11

Guillaume