[Swift-commit] cog r3445

swift at ci.uchicago.edu swift at ci.uchicago.edu
Mon Aug 6 23:35:03 CDT 2012


------------------------------------------------------------------------
r3445 | hategan | 2012-08-06 23:33:58 -0500 (Mon, 06 Aug 2012) | 1 line

added a file locking class based on the bakery algorithm (so it doesn't need support from the FS -- in theory)
------------------------------------------------------------------------
Index: modules/util/src/org/globus/cog/util/concurrent/FileLock.java
===================================================================
--- modules/util/src/org/globus/cog/util/concurrent/FileLock.java	(revision 0)
+++ modules/util/src/org/globus/cog/util/concurrent/FileLock.java	(revision 3445)
@@ -0,0 +1,190 @@
+//----------------------------------------------------------------------
+//This code is developed as part of the Java CoG Kit project
+//The terms of the license can be found at http://www.cogkit.org/license
+//This message may not be removed or altered.
+//----------------------------------------------------------------------
+
+/*
+ * Created on Aug 5, 2012
+ */
+package org.globus.cog.util.concurrent;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileFilter;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.security.SecureRandom;
+import java.util.Random;
+
+import org.apache.log4j.Logger;
+
+/**
+ * An implementation of file locks using Lamport's Bakery algorithm.
+ * It tries to use the PID as thread identifier with fallback to random numbers.
+ *
+ * The Entering and Number arrays are implemented as sparse arrays of
+ * files in some directory.
+ */
+public class FileLock {    
+    public static final Logger logger = Logger.getLogger(FileLock.class);
+    
+    public static final String NUMBER = "locking.number";
+    public static final String ENTERING = "locking.entering";
+    
+    private int myId, myN;
+    private File dir;
+    
+    public FileLock(String dir) {
+        this(new File(dir));
+    }
+    
+    public FileLock(File dir) {
+        this.dir = dir;
+        dir.mkdirs();
+        this.myId = getId();
+    }
+
+    private int getId() {
+        try {
+            return Integer.parseInt(new File("/proc/self").getCanonicalFile().getName());
+        }
+        catch (Exception e) {
+            logger.warn("Failed to get PID of current process", e);
+            try {
+                SecureRandom rnd = SecureRandom.getInstance("SHA1PRNG");
+                return rnd.nextInt() & 0x7fffffff;
+            }
+            catch (Exception ee) {
+                logger.warn("Failed to get instance of SHA1PRNG", ee);
+                return new Random().nextInt() & 0x7fffffff;
+            }
+        }
+    }
+    
+    public void lock() throws IOException, InterruptedException {
+        write(ENTERING, myId, 1);
+        write(NUMBER, myId, myN = 1 + maxNumber());
+        write(ENTERING, myId, 0);
+        waitOther();
+    }
+    
+    private void waitOther() throws InterruptedException {
+        int last = -1;
+        while (true) {
+            int minIndex = getMinIndex(last);
+            
+            if (minIndex == Integer.MAX_VALUE) {
+                // all remaining NUMBER[j] and ENTERING[j] are 0
+                break;
+            }
+            File e = makeFile(ENTERING, minIndex);
+            
+            while (e.exists()) {
+                Thread.sleep(100);
+            }
+            
+            File n = makeFile(NUMBER, minIndex);
+            
+            while (n.exists()) {
+                int nj = read(n);
+                if (nj > myN || ((nj == myN) && (minIndex >= myId))) {
+                    break;
+                }
+                Thread.sleep(100);
+            }
+            last = minIndex;
+        }
+    }
+
+    private File makeFile(String prefix, int index) {
+        return new File(dir, prefix + "." + index);
+    }
+
+    private int getMinIndex(final int greaterThan) {
+        File[] numbers = dir.listFiles(new FileFilter() {
+            @Override
+            public boolean accept(File f) {
+                String name = f.getName();
+                return f.isFile() && (name.startsWith(NUMBER) || name.startsWith(ENTERING)) && (getIndex(f) > greaterThan);
+            }
+        });
+        int min = Integer.MAX_VALUE;
+        for (File n : numbers) {
+            int in = getIndex(n);
+            if (in < min) {
+                min = in;
+            }
+        }
+        return min;
+    }
+
+    private int getIndex(File n) {
+        try {
+            return Integer.parseInt(n.getName().substring(n.getName().lastIndexOf('.') + 1));
+        }
+        catch (Exception e) {
+            throw new IllegalArgumentException("A file is conflicting with directory locking: " + n, e);
+        }
+    }
+
+    private int maxNumber() {
+        File[] numbers = dir.listFiles(new FileFilter() {
+            @Override
+            public boolean accept(File f) {
+                return f.isFile() && f.getName().startsWith(NUMBER);
+            }
+        });
+        
+        int max = 0;
+        for (File n : numbers) {
+            int in = read(n);
+            if (in > max) {
+                max = in;
+            }
+        }
+        return max;
+    }
+
+    private int read(File n) {
+        try {
+            BufferedReader br = new BufferedReader(new FileReader(n));
+            try {
+                return Integer.parseInt(br.readLine());
+            }
+            finally {
+                br.close();
+            }
+        }
+        catch (Exception e) {
+            // nothing. The algorithm tolerates incorrect reads
+        }
+        return 0;
+    }
+
+    private void write(String prefix, int id, int value) throws IOException {
+        File f = new File(dir, prefix + "." + id);
+        if (value == 0) {
+            if (!f.delete()) {
+                f.deleteOnExit();
+                throw new IOException("Failed to delete " + f);
+            }
+        }
+        else {
+            BufferedWriter br = new BufferedWriter(new FileWriter(f));
+            try {
+                br.write(String.valueOf(value));
+            }
+            finally {
+                br.close();
+            }
+            f.deleteOnExit();
+        }
+    }
+
+    public void unlock() throws IOException {
+        write("locking.number", myId, 0);
+    }
+}



More information about the Swift-commit mailing list