From: Debajyoti Bera <dbera.web@gmail.com>

Lucene uses lockfiles for sharing index files across multiple processes.  Stock
lucene.net implementation creates the lockfile incorrectly. 
01_obtain-lock-fix.patch fixes that but leaves a small race window. This 
patch removes the race window. It uses native open() syscall instead of 
System.IO File operations as the mono implementations for File.Open() turned 
out to be buggy (giving wrong sharing violation errors).

Updated by: Joe Shaw <joeshaw@novell.com>

My update to this patch redoes the way the locking PID was being written out
to disk.  We were doing it previously using a UnixStream and StreamWriter,
but that turned out to be very slow and put a noticable dent in indexing
performance.  This new version uses POSIX I/O calls directly to write out
the PID and it resolves the performance issue.

--- Store/FSDirectory.cs	2006-08-18 17:56:52.000000000 -0400
+++ Store/FSDirectory.cs.joe	2006-08-18 17:55:48.000000000 -0400
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 using System;
+using Mono.Unix.Native;
 using Constants = Lucene.Net.Util.Constants;
 namespace Lucene.Net.Store
 {
@@ -52,6 +53,7 @@
 			}
 			public override bool Obtain()
 			{
+				Log ("Trying to obtain lock " + lockFile.FullName);
 				if (Lucene.Net.Store.FSDirectory.DISABLE_LOCKS || Enclosing_Instance.DisableLocks)
 					return true;
 				
@@ -62,36 +64,72 @@
 					tmpBool = System.IO.Directory.Exists(Enclosing_Instance.lockDir.FullName);
 				if (!tmpBool)
 				{
-                    try
-                    {
-                        System.IO.Directory.CreateDirectory(Enclosing_Instance.lockDir.FullName);
-                    }
-                    catch (Exception)
+					try
+                    			{
+						System.IO.Directory.CreateDirectory(Enclosing_Instance.lockDir.FullName);
+                    			}
+                    			catch (Exception)
 					{
 						throw new System.IO.IOException("Cannot create lock directory: " + Enclosing_Instance.lockDir);
 					}
 				}
 
-				bool obtainedLock = false;
-
-				if (! System.IO.File.Exists(lockFile.FullName)) {
-                	try
-	                {
-    	                System.IO.FileStream createdFile = lockFile.Create();
-						System.IO.StreamWriter writer = new System.IO.StreamWriter (createdFile);
-						writer.WriteLine (System.Diagnostics.Process.GetCurrentProcess().Id);
-						writer.Close ();
-                    	createdFile.Close();
-	                    obtainedLock = true;
-    	            }
-        	        catch (Exception e)
-            	    {
-                	    // Fall through
-	                }
-				}
+                try
+                {
+		    int fd = Mono.Unix.Native.Syscall.open (
+				lockFile.FullName,
+				Mono.Unix.Native.OpenFlags.O_RDWR | 
+				Mono.Unix.Native.OpenFlags.O_CREAT |
+				Mono.Unix.Native.OpenFlags.O_EXCL,
+				Mono.Unix.Native.FilePermissions.S_IRUSR);
+		    if (fd == -1)
+			    throw new System.IO.IOException ("Could not create lock file: "
+				    + Mono.Unix.Native.Stdlib.strerror (
+					    Mono.Unix.Native.Stdlib.GetLastError ()
+				    ));
+
+		    // This code replaces the commented-out code below because
+		    // it ends up being much faster.  The reason for this is
+		    // that closing a UnixStream causes Syscall.fsync() to be
+		    // called, and that apparently is extremely slow!
+		    //
+		    // Time(ms) Count   P/call(ms) Method name
+		    // 1563.926      68   22.999   Mono.Unix.Native.Syscall::fsync(int)
+		    //
+		    // Since the lock file is written out very often, this time
+		    // adds up and noticably slows down indexing.
+		    IntPtr ptr = IntPtr.Zero;
+		    long ret;
+
+		    try {
+			    string s = System.Diagnostics.Process.GetCurrentProcess ().Id.ToString () + "\n";
+			    ptr = Mono.Unix.UnixMarshal.StringToHeap (s);
+
+			    do {
+				    ret = Mono.Unix.Native.Syscall.write (fd, ptr, (ulong) s.Length);
+			    } while (Mono.Unix.UnixMarshal.ShouldRetrySyscall ((int) ret));
+			    Mono.Unix.UnixMarshal.ThrowExceptionForLastErrorIf ((int) ret);
+		    } finally {
+			    Mono.Unix.UnixMarshal.FreeHeap (ptr);
+
+			    do {
+				    ret = Mono.Unix.Native.Syscall.close (fd);
+			    } while (Mono.Unix.UnixMarshal.ShouldRetrySyscall ((int) ret));
+			    Mono.Unix.UnixMarshal.ThrowExceptionForLastErrorIf ((int) ret);
+		    }
+
+		    //System.IO.StreamWriter w = new System.IO.StreamWriter (new Mono.Unix.UnixStream (fd, true));
+		    //w.WriteLine (System.Diagnostics.Process.GetCurrentProcess ().Id);
+		    //w.Close ();
 
-				Log ("{0} lock {1}", obtainedLock ? "Obtained" : "Could not obtain", lockFile.FullName);
-				return obtainedLock;
+		    Log ("Obtained lock " + lockFile.FullName);
+                    return true;
+                }
+                catch (Exception e)
+                {
+		    Log ("Exception in CreateNew for file:" + lockFile.FullName + ":" + e);
+                    return false;
+                }
 			}
 			public override void  Release()
 			{
@@ -110,6 +148,10 @@
 				}
 				else
 					tmpBool = false;
+				if (System.IO.File.Exists(lockFile.FullName)) {
+					Log ("Release didnt delete lockfile {0}.", lockFile.FullName);
+					tmpBool = false;
+				}
 				bool generatedAux = tmpBool;
 				if (tmpBool)
 					Log ("Released lock {0}", lockFile.FullName);
