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

Code before:
                .getBeanFactory()
                .getBean(FrameworkExecutorRepository.class)
                .getConnectivityScheduledExecutor();
        Configuration configuration = ConfigurationUtils.getGlobalConfiguration(url.getOrDefaultModuleModel());
        this.reconnectTaskTryCount = configuration.getInt(RECONNECT_TASK_TRY_COUNT, DEFAULT_RECONNECT_TASK_TRY_COUNT);
        this.reconnectTaskPeriod = configuration.getInt(RECONNECT_TASK_PERIOD, DEFAULT_RECONNECT_TASK_PERIOD);
        setRouterChain(routerChain);
    }

    @Override
    public List<Invoker<T>> list(Invocation invocation) throws RpcException {
        if (destroyed) {
            throw new RpcException(
                    "Directory of type " + this.getClass().getSimpleName() + " already destroyed for service "
                            + getConsumerUrl().getServiceKey() + " from registry " + getUrl());
        }

        BitList<Invoker<T>> availableInvokers;
        SingleRouterChain<T> singleChain = null;
        try {
            if (routerChain != null) {
                routerChain.getLock().readLock().lock();
            }
            try {
                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",

Review comment (bug):
The error message "Failed to acquire invokerRefreshLock for reading invokers" is slightly misleading. It suggests the lock is specifically for "reading invokers", but this is a read lock on invokerRefreshLock. Consider clarifying this is about acquiring the read lock, for example: "Failed to acquire read lock on invokerRefresh within timeout".
```suggestion
                    throw new RpcException("Failed to acquire read lock on invokerRefreshLock within timeout");
```

Code after:
                .getBeanFactory()
                .getBean(FrameworkExecutorRepository.class)
                .getConnectivityScheduledExecutor();
        Configuration configuration = ConfigurationUtils.getGlobalConfiguration(url.getOrDefaultModuleModel());
        this.reconnectTaskTryCount = configuration.getInt(RECONNECT_TASK_TRY_COUNT, DEFAULT_RECONNECT_TASK_TRY_COUNT);
        this.reconnectTaskPeriod = configuration.getInt(RECONNECT_TASK_PERIOD, DEFAULT_RECONNECT_TASK_PERIOD);
        setRouterChain(routerChain);
    }

    @Override
    public List<Invoker<T>> list(Invocation invocation) throws RpcException {
        if (destroyed) {
            throw new RpcException(
                    "Directory of type " + this.getClass().getSimpleName() + " already destroyed for service "
                            + getConsumerUrl().getServiceKey() + " from registry " + getUrl());
        }

        BitList<Invoker<T>> availableInvokers;
        SingleRouterChain<T> singleChain = null;
        try {
            if (routerChain != null) {
                routerChain.getLock().readLock().lock();
            }
            boolean lockAcquired = false;
            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();
                }
            }