Skip to content

Commit 0ebbda7

Browse files
fix: expand ReentrantLock scope in MonitorThreadContainer for better … (#601)
1 parent a970aec commit 0ebbda7

File tree

3 files changed

+84
-22
lines changed

3 files changed

+84
-22
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
77
### :magic_wand: Added
88
- Host Availability Strategy to help keep host health status up to date ([PR #530](https://github.com/awslabs/aws-advanced-jdbc-wrapper/pull/530)).
99

10+
### :bug: Fixed
11+
- Race condition issues between `MonitorThreadContainer#getInstance()` and `MonitorThreadContainer#releaseInstance()` ([PR #601](https://github.com/awslabs/aws-advanced-jdbc-wrapper/pull/601)).
12+
1013
### :crab: Changed
1114
- Dynamically sets the default host list provider based on the dialect used. User applications no longer need to manually set the AuroraHostListProvider when connecting to Aurora Postgres or Aurora MySQL databases.
1215
- Deprecated AuroraHostListConnectionPlugin.

wrapper/src/main/java/software/amazon/jdbc/plugin/efm/MonitorThreadContainer.java

Lines changed: 19 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -54,19 +54,19 @@ public static MonitorThreadContainer getInstance() {
5454
}
5555

5656
static MonitorThreadContainer getInstance(final ExecutorServiceInitializer executorServiceInitializer) {
57-
if (singleton == null) {
58-
LOCK_OBJECT.lock();
59-
try {
60-
if (singleton == null) {
61-
singleton = new MonitorThreadContainer(executorServiceInitializer);
62-
CLASS_USAGE_COUNT.set(0);
63-
}
64-
} finally {
65-
LOCK_OBJECT.unlock();
57+
MonitorThreadContainer singletonToReturn;
58+
LOCK_OBJECT.lock();
59+
try {
60+
if (singleton == null) {
61+
singleton = new MonitorThreadContainer(executorServiceInitializer);
62+
CLASS_USAGE_COUNT.set(0);
6663
}
64+
singletonToReturn = singleton;
65+
CLASS_USAGE_COUNT.getAndIncrement();
66+
} finally {
67+
LOCK_OBJECT.unlock();
6768
}
68-
CLASS_USAGE_COUNT.getAndIncrement();
69-
return singleton;
69+
return singletonToReturn;
7070
}
7171

7272
/**
@@ -77,18 +77,15 @@ public static void releaseInstance() {
7777
if (singleton == null) {
7878
return;
7979
}
80-
81-
if (CLASS_USAGE_COUNT.decrementAndGet() <= 0) {
82-
LOCK_OBJECT.lock();
83-
try {
84-
if (singleton != null) {
85-
singleton.releaseResources();
86-
singleton = null;
87-
CLASS_USAGE_COUNT.set(0);
88-
}
89-
} finally {
90-
LOCK_OBJECT.unlock();
80+
LOCK_OBJECT.lock();
81+
try {
82+
if (singleton != null && CLASS_USAGE_COUNT.decrementAndGet() <= 0) {
83+
singleton.releaseResources();
84+
singleton = null;
85+
CLASS_USAGE_COUNT.set(0);
9186
}
87+
} finally {
88+
LOCK_OBJECT.unlock();
9289
}
9390
}
9491

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package software.amazon.jdbc.plugin.efm;
18+
19+
import static org.mockito.Mockito.times;
20+
import static org.mockito.Mockito.verify;
21+
import static org.mockito.Mockito.when;
22+
23+
import java.util.concurrent.ExecutorService;
24+
import java.util.concurrent.Executors;
25+
import org.junit.jupiter.api.AfterEach;
26+
import org.junit.jupiter.api.BeforeEach;
27+
import org.junit.jupiter.api.RepeatedTest;
28+
import org.mockito.Mock;
29+
import org.mockito.MockitoAnnotations;
30+
31+
public class MultiThreadedMonitorThreadContainerTest {
32+
33+
@Mock ExecutorServiceInitializer mockExecutorServiceInitializer;
34+
@Mock ExecutorService mockExecutorService;
35+
36+
private AutoCloseable closeable;
37+
38+
@BeforeEach
39+
void init() {
40+
closeable = MockitoAnnotations.openMocks(this);
41+
when(mockExecutorServiceInitializer.createExecutorService()).thenReturn(mockExecutorService);
42+
}
43+
44+
@AfterEach
45+
void cleanup() throws Exception {
46+
closeable.close();
47+
MonitorThreadContainer.releaseInstance();
48+
}
49+
50+
@RepeatedTest(value = 1000, name = "MonitorThreadContainer ThreadPoolExecutor is not closed prematurely")
51+
void testThreadPoolExecutorNotClosedPrematurely() throws InterruptedException {
52+
MonitorThreadContainer.getInstance(mockExecutorServiceInitializer);
53+
54+
ExecutorService executorService = Executors.newCachedThreadPool();
55+
executorService.execute(() -> MonitorThreadContainer.getInstance(mockExecutorServiceInitializer));
56+
Thread.sleep(3);
57+
executorService.execute(MonitorThreadContainer::releaseInstance);
58+
executorService.shutdown();
59+
60+
verify(mockExecutorService, times(0)).shutdownNow();
61+
}
62+
}

0 commit comments

Comments
 (0)