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