Skip to content

fix(AgentBase): Optimize AgentBase's lock management to resolve concurrency conflicts.#733

Open
wuji1428 wants to merge 1 commit intoagentscope-ai:mainfrom
wuji1428:fix-agentbase-concurrent-conflict
Open

fix(AgentBase): Optimize AgentBase's lock management to resolve concurrency conflicts.#733
wuji1428 wants to merge 1 commit intoagentscope-ai:mainfrom
wuji1428:fix-agentbase-concurrent-conflict

Conversation

@wuji1428
Copy link
Contributor

@wuji1428 wuji1428 commented Feb 5, 2026

AgentScope-Java Version

[The version of AgentScope-Java you are working on, e.g. 1.0.8, check your pom.xml dependency version or run mvn dependency:tree | grep agentscope-parent:pom(only mac/linux)]

Description

Ref Issue #732

Replace the doFinally structure with Mono.using and set the eager parameter to true. This ensures that the state bit is forcibly reset before the onComplete or onError signals reach the downstream subscriber, thereby eliminating the race condition.

Checklist

Please check the following items before code is ready to be reviewed.

  • Code has been formatted with mvn spotless:apply
  • All tests are passing (mvn test)
  • Javadoc comments are complete and follow project conventions
  • Related documentation has been updated (e.g. links, examples, etc.)
  • Code is ready for review

…ource management

Two new test methods were added to verify the agent's behavior under concurrent invocation, and resource management and exception handling mechanisms in the `AgentBase` class were improved. Related dependencies and import statements were also updated.
@wuji1428 wuji1428 requested a review from a team February 5, 2026 07:12
@gemini-code-assist
Copy link

Summary of Changes

Hello @wuji1428, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request resolves a critical concurrency conflict within the AgentBase class by refactoring its reactive call methods. The change replaces the doFinally block with Mono.using and sets an eager release strategy, ensuring that the agent's internal running state is consistently and promptly reset. This prevents IllegalStateException errors that could occur when an agent was called multiple times in quick succession due to an improperly managed lock.

Highlights

  • Concurrency Fix in AgentBase: The doFinally operator in AgentBase's call methods has been replaced with Mono.using to ensure proper lock management and prevent race conditions.
  • Eager Resource Release: The Mono.using operator is configured with eager=true, guaranteeing that the running state is reset immediately before downstream signals (onComplete/onError) are processed.
  • New Concurrency Tests: Dedicated unit tests (testConcurrencyConflict and testConcurrencyConflictStructuredOutput) have been added to AgentBaseTest and ReActAgentStructuredOutputTest to validate the fix and prevent future regressions.
Changelog
  • agentscope-core/src/main/java/io/agentscope/core/agent/AgentBase.java
    • Refactored the call methods to use Mono.using instead of doFinally for managing the running state.
    • Moved the running state check and resetInterruptFlag into the resource acquisition function of Mono.using.
    • Ensured the running state is reset by passing true as the eager parameter to Mono.using.
  • agentscope-core/src/test/java/io/agentscope/core/agent/AgentBaseTest.java
    • Added import reactor.core.scheduler.Schedulers;.
    • Implemented testConcurrencyConflict to verify that concurrent calls to agent.call() do not result in IllegalStateException.
  • agentscope-core/src/test/java/io/agentscope/core/agent/ReActAgentStructuredOutputTest.java
    • Added necessary imports for TestConstants, Duration, and Schedulers.
    • Introduced testConcurrencyConflictStructuredOutput to confirm the concurrency fix also applies to structured output calls.
Activity
  • The pull request was opened by wuji1428 to address Issue [Bug]: Concurrency Issue in AgentBase.call() #732.
  • The author has completed all items in the checklist, including code formatting, passing tests, Javadoc comments, and documentation updates.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request effectively resolves a concurrency issue in AgentBase by replacing doFinally with an eager Mono.using. This change correctly ensures that the agent's running state is reset before the operation completes, preventing race conditions. The added tests in AgentBaseTest and ReActAgentStructuredOutputTest validate this fix for different call method overloads. I've included one suggestion to refactor the call methods in AgentBase to reduce code duplication and improve maintainability.

Comment on lines +163 to +185
return Mono.using(
() -> {
if (checkRunning && !running.compareAndSet(false, true)) {
throw new IllegalStateException(
"Agent is still running, please wait for it to finish");
}
resetInterruptFlag();
return this;
},
resource ->
TracerRegistry.get()
.callAgent(
this,
msgs,
() ->
notifyPreCall(msgs)
.flatMap(this::doCall)
.flatMap(this::notifyPostCall)
.onErrorResume(
createErrorHandler(
msgs.toArray(new Msg[0])))),
resource -> running.set(false),
true);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The three overloaded call methods (call(List<Msg>), call(List<Msg>, Class<?>), and call(List<Msg>, JsonNode)) share nearly identical logic for handling the agent's running state using Mono.using. This code duplication can be eliminated by extracting the common logic into a private helper method. This would improve maintainability, as future changes to the lock management would only need to be made in one place.

Here is a suggested refactoring that would replace all three call methods and add a new private executeCall method:

    @Override
    public final Mono<Msg> call(List<Msg> msgs) {
        return executeCall(msgs, this::doCall);
    }

    @Override
    public final Mono<Msg> call(List<Msg> msgs, Class<?> structuredOutputClass) {
        return executeCall(msgs, m -> doCall(m, structuredOutputClass));
    }

    @Override
    public final Mono<Msg> call(List<Msg> msgs, JsonNode schema) {
        return executeCall(msgs, m -> doCall(m, schema));
    }

    private Mono<Msg> executeCall(List<Msg> msgs, Function<List<Msg>, Mono<Msg>> doCallLogic) {
        return Mono.using(
                () -> {
                    if (checkRunning && !running.compareAndSet(false, true)) {
                        throw new IllegalStateException(
                                "Agent is still running, please wait for it to finish");
                    }
                    resetInterruptFlag();
                    return this;
                },
                resource ->
                        TracerRegistry.get()
                                .callAgent(
                                        this,
                                        msgs,
                                        () ->
                                                notifyPreCall(msgs)
                                                        .flatMap(doCallLogic)
                                                        .flatMap(this::notifyPostCall)
                                                        .onErrorResume(
                                                                createErrorHandler(
                                                                        msgs.toArray(new Msg[0])))),
                resource -> running.set(false),
                true);
    }

Since a single code suggestion cannot span all three methods across different parts of the file, I'm providing the full refactoring here for your consideration.

@codecov
Copy link

codecov bot commented Feb 5, 2026

Codecov Report

❌ Patch coverage is 90.69767% with 4 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
.../main/java/io/agentscope/core/agent/AgentBase.java 90.69% 2 Missing and 2 partials ⚠️

📢 Thoughts on this report? Let us know!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant