Repository: apache/dubbo
File: dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/directory/AbstractDirectory.java (Java)

Code before:
                if (!invokerRefreshReadLock.tryLock(LockUtils.DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS)) {
                    throw new RpcException("Failed to acquire invokerRefreshLock for reading invokers");
                }
                // use clone to avoid being modified at doList().
                if (invokersInitialized) {
                    availableInvokers = validInvokers.clone();
                } else {
                    availableInvokers = invokers.clone();
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                throw new RpcException("Failed to refresh invokers, cause: " + e.getMessage(), e);
            } finally {
                invokerRefreshReadLock.unlock();
            }

            if (routerChain != null) {
                singleChain = routerChain.getSingleChain(getConsumerUrl(), availableInvokers, invocation);
                singleChain.getLock().readLock().lock();
            }
            List<Invoker<T>> routedResult = doList(singleChain, availableInvokers, invocation);
            if (routedResult.isEmpty()) {
                // 2-2 - No provider available.

                logger.warn(
                        CLUSTER_NO_VALID_PROVIDER,
                        "provider server or registry center crashed",
                        "",
                        "No provider available after connectivity filter for the service "
                                + getConsumerUrl().getServiceKey()
                                + " All routed invokers' size: " + routedResult.size()
                                + " from registry " + this
                                + " on the consumer " + NetUtils.getLocalHost()
                                + " using the dubbo version " + Version.getVersion() + ".");
            }
            return Collections.unmodifiableList(routedResult);
        } finally {
            if (singleChain != null) {
                singleChain.getLock().readLock().unlock();
            }
            if (routerChain != null) {
                routerChain.getLock().readLock().unlock();
            }
        }
    }

    @Override
    public URL getUrl() {
        return url;
    }


Review comment (bug):
There is a critical bug: invokerRefreshReadLock.unlock() is called unconditionally in the finally block, but if tryLock() throws an InterruptedException, the lock was never acquired. Calling unlock() on a lock that isn't held will throw IllegalMonitorStateException, which will mask the original InterruptedException.

The fix should track whether the lock was successfully acquired before attempting to unlock it. Consider using a boolean flag to track lock acquisition status.

Code after:
            try {
                if (!invokerRefreshReadLock.tryLock(LockUtils.DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS)) {
                    throw new RpcException(
                            "Failed to acquire read lock on invokerRefreshLock within timeout. " + "Timeout: "
                                    + LockUtils.DEFAULT_TIMEOUT + "ms, " + "Lock state: [readLockHeld="
                                    + invokerRefreshLock.getReadLockCount() + ", writeLockHeld="
                                    + invokerRefreshLock.isWriteLocked() + ", writeLockHeldByCurrentThread="
                                    + invokerRefreshLock.isWriteLockedByCurrentThread() + "], Service: "
                                    + getConsumerUrl().getServiceKey());
                }
                lockAcquired = true;
                // use clone to avoid being modified at doList().
                if (invokersInitialized) {
                    availableInvokers = validInvokers.clone();
                } else {
                    availableInvokers = invokers.clone();
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                throw new RpcException(
                        "Interrupted while acquiring read lock for invoker access, cause: " + e.getMessage(), e);
            } finally {
                if (lockAcquired) {
                    invokerRefreshReadLock.unlock();
                }
            }

            if (routerChain != null) {
                singleChain = routerChain.getSingleChain(getConsumerUrl(), availableInvokers, invocation);
                singleChain.getLock().readLock().lock();
            }
            List<Invoker<T>> routedResult = doList(singleChain, availableInvokers, invocation);
            if (routedResult.isEmpty()) {
                // 2-2 - No provider available.

                logger.warn(
                        CLUSTER_NO_VALID_PROVIDER,
                        "provider server or registry center crashed",
                        "",
                        "No provider available after connectivity filter for the service "
                                + getConsumerUrl().getServiceKey()
                                + " All routed invokers' size: " + routedResult.size()
                                + " from registry " + this
                                + " on the consumer " + NetUtils.getLocalHost()
                                + " using the dubbo version " + Version.getVersion() + ".");
            }
            return Collections.unmodifiableList(routedResult);
        } finally {
            if (singleChain != null) {
                singleChain.getLock().readLock().unlock();
            }