Title: [698] trunk/rails-integration/src: Fix for deadlock situation caused when calling addObject from a thread whichs holds a lock on the servlet context, while the pool manager is also add objects to the pool.
Revision
698
Author
tantalon
Date
2007-08-23 00:40:12 -0400 (Thu, 23 Aug 2007)

Log Message

Fix for deadlock situation caused when calling addObject from a thread whichs holds a lock on the servlet context, while the pool manager is also add objects to the pool.

Modified Paths

Diff

Modified: trunk/rails-integration/src/main/java/org/jruby/webapp/RailsContextListener.java (697 => 698)


--- trunk/rails-integration/src/main/java/org/jruby/webapp/RailsContextListener.java	2007-08-23 01:09:51 UTC (rev 697)
+++ trunk/rails-integration/src/main/java/org/jruby/webapp/RailsContextListener.java	2007-08-23 04:40:12 UTC (rev 698)
@@ -93,15 +93,9 @@
 	protected ObjectPool createObjectPool(RailsFactory railsFactory) {
 		int maxObjects = getIntegerProperty("jruby.pool.maxActive", 4);
 		int minIdle = getIntegerProperty("jruby.pool.minIdle", 2);
-		int initialSize = getIntegerProperty("jruby.pool.initialSize", minIdle);
 		int checkInterval = getIntegerProperty("jruby.pool.checkInterval", 1000);
 		CustomObjectPool pool = new CustomObjectPool(railsFactory, maxObjects, minIdle, checkInterval);
 		pool.setMaxWait(getIntegerProperty("jruby.pool.maxWait", 30000));
-		try {
-			pool.addObjects(initialSize);
-		} catch (Exception e) {
-			// ignore
-		}
 		return pool;
 	}
 

Modified: trunk/rails-integration/src/main/java/org/jruby/webapp/RailsFactory.java (697 => 698)


--- trunk/rails-integration/src/main/java/org/jruby/webapp/RailsFactory.java	2007-08-23 01:09:51 UTC (rev 697)
+++ trunk/rails-integration/src/main/java/org/jruby/webapp/RailsFactory.java	2007-08-23 04:40:12 UTC (rev 698)
@@ -114,6 +114,9 @@
 		}
 
 		// make servlet context accessible
+		// WARNING: this locks the servlet context, watch out for deadlock!
+		// We must ensure that nothing that holds a lock on the context will also try to create rails objects.
+		// This is done in CustomObjectPool by forcing all object creation though a single thread.
 		runtime.defineReadonlyVariable("$servlet_context", JavaEmbedUtils.javaToRuby(runtime, context));
 
 		// test Gems

Modified: trunk/rails-integration/src/main/java/org/jruby/webapp/util/CustomObjectPool.java (697 => 698)


--- trunk/rails-integration/src/main/java/org/jruby/webapp/util/CustomObjectPool.java	2007-08-23 01:09:51 UTC (rev 697)
+++ trunk/rails-integration/src/main/java/org/jruby/webapp/util/CustomObjectPool.java	2007-08-23 04:40:12 UTC (rev 698)
@@ -4,6 +4,7 @@
 import edu.emory.mathcs.backport.java.util.concurrent.BlockingQueue;
 import edu.emory.mathcs.backport.java.util.concurrent.ArrayBlockingQueue;
 import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit;
+import edu.emory.mathcs.backport.java.util.concurrent.Semaphore;
 import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicInteger;
 import java.util.NoSuchElementException;
 /**
@@ -24,6 +25,7 @@
 	private boolean open;
 	private int checkInterval;
 	private int maxWait;
+	private PoolSizeManager poolManager;
 
 	public CustomObjectPool(PoolableObjectFactory factory, int maxObjects, int minIdle) {
 		this(factory, maxObjects, minIdle, DEFAULT_CHECK_INTERVAL);
@@ -40,9 +42,13 @@
 		this.numObjects = new AtomicInteger();
 		this.numActive = new AtomicInteger();
 
-		if (checkInterval != 0) {
-			new Thread(new PoolSizeManager(), "ObjectPoolManager").start();
+		if (checkInterval == 0) {
+			// only add items when addObject is explicitly called
+			checkInterval = Integer.MAX_VALUE;
 		}
+
+		poolManager = new PoolSizeManager();
+		poolManager.start();
 	}
 
 	public Object borrowObject() throws Exception {
@@ -86,7 +92,20 @@
 		}
 	}
 
-	public synchronized void addObject() throws Exception {
+	public void addObject() throws Exception {
+		poolManager.waitForObjectAddition();
+	}
+
+	public void addObjects(int count) throws Exception {
+		for(int i=0; i<count; i++) {
+			addObject();
+		}
+	}
+
+	/**
+	 * Add an object if needed, this must only be called from the pool manager.
+	 */
+	private void addObjectInternal() throws Exception {
 		// enforce the constraints, so this only happens if it makes sense to
 		if (open && getNumObjects() < maxObjects && getNumIdle() < minIdle) {
 			Object object = factory.makeObject();
@@ -100,12 +119,6 @@
 		}
 	}
 
-	public void addObjects(int count) throws Exception {
-		for(int i=0; i<count; i++) {
-			addObject();
-		}
-	}
-
 	public int getNumIdle() {
 		return getNumObjects() - getNumActive();
 	}
@@ -152,22 +165,38 @@
 		throw new UnsupportedOperationException("You cannot change the factory after the pool is created");
 	}
 
-	private class PoolSizeManager implements Runnable {
+	private class PoolSizeManager extends Thread {
+		private Semaphore objectBarrier;
+
+		public PoolSizeManager() {
+			super("ObjectPoolManager");
+			objectBarrier = new Semaphore(0);
+		}
+
+		public void waitForObjectAddition() {
+			interrupt();
+			objectBarrier.acquireUninterruptibly();
+		}
+
 		public void run() {
 			while (open) {
+				// wait a bit between additions
+				try {
+					Thread.sleep(checkInterval);
+				} catch (InterruptedException e) {
+					// ignore
+				}
+				// start fresh each time
+				objectBarrier.drainPermits();
 				// try to add an object if it's needed
 				try {
-					addObject();
+					addObjectInternal();
 				} catch (Exception e) {
 					// add object failed
 					e.printStackTrace();
 				}
-				// wait a bit
-				try {
-					Thread.sleep(checkInterval);
-				} catch (InterruptedException e) {
-					// ignore
-				}
+				// we've added a new object
+				objectBarrier.release();
 			}
 		}
 	}

Modified: trunk/rails-integration/src/test/java/org/jruby/webapp/util/CustomObjectPoolTest.java (697 => 698)


--- trunk/rails-integration/src/test/java/org/jruby/webapp/util/CustomObjectPoolTest.java	2007-08-23 01:09:51 UTC (rev 697)
+++ trunk/rails-integration/src/test/java/org/jruby/webapp/util/CustomObjectPoolTest.java	2007-08-23 04:40:12 UTC (rev 698)
@@ -59,11 +59,11 @@
 		factory.destroyObject("2");
 		factoryControl.replay();
 
-		CustomObjectPool pool = new CustomObjectPool(factory, 5, 1, 0);
-		pool.addObjects(10);
+		CustomObjectPool pool = new CustomObjectPool(factory, 1, 1, 0);
+		Thread.sleep(200); // let min idle catch up
 		Object object = pool.borrowObject();
 		pool.invalidateObject(object);
-		pool.addObjects(10);
+		Thread.sleep(200); // let min idle catch up
 		pool.close();
 
 		factoryControl.verify();
@@ -94,14 +94,17 @@
 	public void testAddDuringRequest() throws Exception {
 		factoryControl.expectAndReturn(factory.makeObject(), "1");
 		factory.activateObject("1");
+		factoryControl.expectAndReturn(factory.makeObject(), "2");
 		factory.passivateObject("1");
+		factory.destroyObject("2");
 		factory.destroyObject("1");
 		factoryControl.replay();
 
-		CustomObjectPool pool = new CustomObjectPool(factory, 3, 1, 0);
+		CustomObjectPool pool = new CustomObjectPool(factory, 3, 1, 100);
 		pool.setMaxWait(200);
-		addSoon(pool);
+
 		Object object1 = pool.borrowObject();
+		Thread.sleep(200);
 		pool.returnObject(object1);
 		pool.close();
 
@@ -117,13 +120,12 @@
 		factory.destroyObject("1");
 		factoryControl.replay();
 
-		CustomObjectPool pool = new CustomObjectPool(factory, 3, 1, 0);
-		pool.setMaxWait(200);
+		CustomObjectPool pool = new CustomObjectPool(factory, 3, 1, 1000);
+		pool.setMaxWait(0);
 		pool.addObject();
 
 		Object object1 = pool.borrowObject();
-		returnSoon(pool, object1);
-		addSoon(pool);
+		pool.returnObject(object1);
 
 		Object object2 = pool.borrowObject();
 		pool.returnObject(object2);
@@ -150,30 +152,4 @@
 		factoryControl.verify();
 	}
 
-	private void addSoon(final CustomObjectPool pool) {
-		new Thread() {
-			public void run() {
-				try {
-					Thread.sleep(100);
-					pool.addObject();
-				} catch (Exception e) {
-					e.printStackTrace();
-				}
-			}
-		}.start();
-	}
-
-	private void returnSoon(final CustomObjectPool pool, final Object object) {
-		new Thread() {
-			public void run() {
-				try {
-					Thread.sleep(50);
-					pool.returnObject(object);
-				} catch (Exception e) {
-					e.printStackTrace();
-				}
-			}
-		}.start();
-	}
-
 }
\ No newline at end of file
_______________________________________________
Jruby-extras-devel mailing list
[email protected]
http://rubyforge.org/mailman/listinfo/jruby-extras-devel

Reply via email to