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); + } +}