This is an automated email from the ASF dual-hosted git repository.

ggregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-pool.git

commit 890b68c2c4abb5d9ff98833bbc27b5ccdad63bb6
Author: Gary Gregory <garydgreg...@gmail.com>
AuthorDate: Fri Oct 7 17:36:32 2022 -0400

    [POOL-407] Threads get stuck when idleObjects list is empty
    
    Add working test case.
---
 .../apache/commons/pool2/pool407/KeyedPool407.java | 27 ++++++++
 .../commons/pool2/pool407/KeyedPool407Config.java  | 31 +++++++++
 .../commons/pool2/pool407/KeyedPool407Factory.java | 53 +++++++++++++++
 .../commons/pool2/pool407/KeyedPool407Fixture.java | 22 +++++++
 .../commons/pool2/pool407/KeyedPool407Test.java    | 76 ++++++++++++++++++++++
 5 files changed, 209 insertions(+)

diff --git a/src/test/java/org/apache/commons/pool2/pool407/KeyedPool407.java 
b/src/test/java/org/apache/commons/pool2/pool407/KeyedPool407.java
new file mode 100644
index 00000000..1a65b0b7
--- /dev/null
+++ b/src/test/java/org/apache/commons/pool2/pool407/KeyedPool407.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.pool2.pool407;
+
+import org.apache.commons.pool2.impl.GenericKeyedObjectPool;
+
+public final class KeyedPool407 extends GenericKeyedObjectPool<String, 
KeyedPool407Fixture, RuntimeException> {
+
+    public KeyedPool407(final KeyedPool407Fixture fixture) {
+        super(new KeyedPool407Factory(fixture), new KeyedPool407Config());
+    }
+}
diff --git 
a/src/test/java/org/apache/commons/pool2/pool407/KeyedPool407Config.java 
b/src/test/java/org/apache/commons/pool2/pool407/KeyedPool407Config.java
new file mode 100644
index 00000000..903247bd
--- /dev/null
+++ b/src/test/java/org/apache/commons/pool2/pool407/KeyedPool407Config.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.pool2.pool407;
+
+import java.time.Duration;
+
+import org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig;
+
+public final class KeyedPool407Config extends 
GenericKeyedObjectPoolConfig<KeyedPool407Fixture> {
+
+    public KeyedPool407Config() {
+        setBlockWhenExhausted(true);
+        setMaxTotalPerKey(1);
+        setMaxWait(Duration.ofMillis(-1));
+    }
+}
diff --git 
a/src/test/java/org/apache/commons/pool2/pool407/KeyedPool407Factory.java 
b/src/test/java/org/apache/commons/pool2/pool407/KeyedPool407Factory.java
new file mode 100644
index 00000000..07db0a88
--- /dev/null
+++ b/src/test/java/org/apache/commons/pool2/pool407/KeyedPool407Factory.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.pool2.pool407;
+
+import java.util.Objects;
+
+import org.apache.commons.pool2.BaseKeyedPooledObjectFactory;
+import org.apache.commons.pool2.PooledObject;
+import org.apache.commons.pool2.impl.DefaultPooledObject;
+
+public final class KeyedPool407Factory extends 
BaseKeyedPooledObjectFactory<String, KeyedPool407Fixture, RuntimeException> {
+
+    private final KeyedPool407Fixture fixture;
+
+    KeyedPool407Factory(final KeyedPool407Fixture fixture) {
+        this.fixture = fixture;
+    }
+
+    @Override
+    public KeyedPool407Fixture create(final String key) {
+        // This is key to the test, creation failed and returns null for 
instance see
+        // 
https://github.com/openhab/openhab-core/blob/main/bundles/org.openhab.core.io.transport.modbus/src/main/java/org/openhab/core/io/transport/modbus/internal/pooling/ModbusSlaveConnectionFactoryImpl.java#L163
+        // the test passes when this returns new Pool407Fixture();
+        return fixture;
+    }
+
+    @Override
+    public boolean validateObject(final String key, final 
PooledObject<KeyedPool407Fixture> p) {
+        // TODO Should this be enough even if wrap() does throw and returns a 
DefaultPooledObject wrapping a null?
+        return p.getObject() != null;
+    }
+
+    @Override
+    public PooledObject<KeyedPool407Fixture> wrap(final KeyedPool407Fixture 
value) {
+        // Require a non-null value.
+        return new DefaultPooledObject<>(Objects.requireNonNull(value, 
"value"));
+    }
+}
diff --git 
a/src/test/java/org/apache/commons/pool2/pool407/KeyedPool407Fixture.java 
b/src/test/java/org/apache/commons/pool2/pool407/KeyedPool407Fixture.java
new file mode 100644
index 00000000..48f3e613
--- /dev/null
+++ b/src/test/java/org/apache/commons/pool2/pool407/KeyedPool407Fixture.java
@@ -0,0 +1,22 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.pool2.pool407;
+
+public final class KeyedPool407Fixture {
+    // empty
+}
diff --git 
a/src/test/java/org/apache/commons/pool2/pool407/KeyedPool407Test.java 
b/src/test/java/org/apache/commons/pool2/pool407/KeyedPool407Test.java
new file mode 100644
index 00000000..dc54d027
--- /dev/null
+++ b/src/test/java/org/apache/commons/pool2/pool407/KeyedPool407Test.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.pool2.pool407;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+import org.junit.jupiter.api.Test;
+
+public class KeyedPool407Test {
+
+    private static class KeyedPool407Borrower implements Runnable {
+        private final KeyedPool407 pool;
+
+        public KeyedPool407Borrower(final KeyedPool407 pool) {
+            this.pool = pool;
+        }
+
+        @Override
+        public void run() {
+            try {
+                final String key = "key";
+                final KeyedPool407Fixture foo = pool.borrowObject(key);
+                if (foo != null) {
+                    pool.returnObject(key, foo);
+                }
+            } catch (final Exception e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+    private static final int POOL_SIZE = 3;
+
+    private void test(final KeyedPool407Fixture fixture, final int poolSize) 
throws InterruptedException {
+        final ExecutorService executor = 
Executors.newFixedThreadPool(poolSize);
+        final KeyedPool407 pool = new KeyedPool407(fixture);
+
+        // start 'poolSize' threads that try to borrow a Pool407Fixture with 
the same key
+        for (int i = 0; i < poolSize; i++) {
+            executor.execute(new KeyedPool407Borrower(pool));
+        }
+
+        // this never finishes because two threads are stuck forever
+        executor.shutdown();
+        assertTrue(executor.awaitTermination(10, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void testFail() throws InterruptedException {
+        test(null, POOL_SIZE);
+    }
+
+    @Test
+    public void testPass() throws InterruptedException {
+        test(new KeyedPool407Fixture(), 3);
+    }
+}

Reply via email to