|
1 | 1 | /*
|
2 |
| - * Copyright (c) 2020, 2023 Oracle and/or its affiliates. |
| 2 | + * Copyright (c) 2020, 2024 Oracle and/or its affiliates. |
3 | 3 | *
|
4 | 4 | * Licensed under the Apache License, Version 2.0 (the "License");
|
5 | 5 | * you may not use this file except in compliance with the License.
|
|
18 | 18 | import java.lang.reflect.Method;
|
19 | 19 | import java.time.Duration;
|
20 | 20 | import java.util.Arrays;
|
| 21 | +import java.util.HashMap; |
| 22 | +import java.util.Map; |
21 | 23 | import java.util.Objects;
|
22 | 24 | import java.util.concurrent.CancellationException;
|
23 | 25 | import java.util.concurrent.CompletableFuture;
|
24 | 26 | import java.util.concurrent.CompletionStage;
|
25 |
| -import java.util.concurrent.ConcurrentHashMap; |
26 | 27 | import java.util.concurrent.ExecutionException;
|
27 | 28 | import java.util.concurrent.Future;
|
28 | 29 | import java.util.concurrent.atomic.AtomicBoolean;
|
29 | 30 | import java.util.concurrent.locks.ReentrantLock;
|
| 31 | +import java.util.function.Function; |
30 | 32 | import java.util.function.Supplier;
|
31 | 33 |
|
32 | 34 | import io.helidon.common.context.Context;
|
@@ -86,7 +88,8 @@ class MethodInvoker implements FtSupplier<Object> {
|
86 | 88 | * caches the FT handler as well as some additional variables. This mapping must
|
87 | 89 | * be shared by all instances of this class.
|
88 | 90 | */
|
89 |
| - private static final ConcurrentHashMap<MethodStateKey, MethodState> METHOD_STATES = new ConcurrentHashMap<>(); |
| 91 | + private static final MethodStateCache METHOD_STATES = new MethodStateCache(); |
| 92 | + |
90 | 93 | /**
|
91 | 94 | * The method being intercepted.
|
92 | 95 | */
|
@@ -693,7 +696,7 @@ private static class MethodState {
|
693 | 696 | /**
|
694 | 697 | * A key used to lookup {@code MethodState} instances, which include FT handlers.
|
695 | 698 | * A class loader is necessary to support multiple applications as seen in the TCKs.
|
696 |
| - * The method class in necessary given that the same method can inherited by different |
| 699 | + * The method class in necessary given that the same method can inherit by different |
697 | 700 | * classes with different FT annotations and should not share handlers. Finally, the
|
698 | 701 | * method is main part of the key.
|
699 | 702 | */
|
@@ -727,4 +730,34 @@ public int hashCode() {
|
727 | 730 | return Objects.hash(classLoader, methodClass, method);
|
728 | 731 | }
|
729 | 732 | }
|
| 733 | + |
| 734 | + /** |
| 735 | + * Used instead of a {@link java.util.concurrent.ConcurrentHashMap} to avoid some |
| 736 | + * locking problems. |
| 737 | + */ |
| 738 | + private static class MethodStateCache { |
| 739 | + |
| 740 | + private final ReentrantLock lock = new ReentrantLock(); |
| 741 | + private final Map<MethodStateKey, MethodState> cache = new HashMap<>(); |
| 742 | + |
| 743 | + MethodState computeIfAbsent(MethodStateKey key, Function<MethodStateKey, MethodState> function) { |
| 744 | + lock.lock(); |
| 745 | + try { |
| 746 | + MethodState methodState = cache.get(key); |
| 747 | + if (methodState != null) { |
| 748 | + return methodState; |
| 749 | + } |
| 750 | + MethodState newMethodState = function.apply(key); |
| 751 | + Objects.requireNonNull(newMethodState); |
| 752 | + cache.put(key, newMethodState); |
| 753 | + return newMethodState; |
| 754 | + } finally { |
| 755 | + lock.unlock(); |
| 756 | + } |
| 757 | + } |
| 758 | + |
| 759 | + void clear() { |
| 760 | + cache.clear(); |
| 761 | + } |
| 762 | + } |
730 | 763 | }
|
0 commit comments