Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use Locks instead of synchronised keyword #3077

Merged

Conversation

krmahadevan
Copy link
Member

@krmahadevan krmahadevan commented Feb 26, 2024

Closes #3040

Fixes #3040 .

Did you remember to?

  • Add test case(s)
  • Update CHANGES.txt
  • Auto applied styling via ./gradlew autostyleApply

We encourage pull requests that:

  • Add new features to TestNG (or)
  • Fix bugs in TestNG

If your pull request involves fixing SonarQube issues then we would suggest that you please discuss this with the
TestNG-dev before you spend time working on it.

Note: For more information on contribution guidelines please make sure you refer our Contributing section for detailed set of steps.

Summary by CodeRabbit

  • New Features
    • Users can now define the order of TestNG listeners for more controlled execution flows.
  • Bug Fixes
    • Improved thread safety across the framework by replacing synchronized blocks with AutoCloseableLock and KeyAwareAutoCloseableLock for better concurrency control.
    • Aligned TestNG 7.x DataProvider behavior with TestNG 6.x for consistent test retrying.
    • Enabled dynamic adjustment of TestNG threads post-deprecation of IExecutorFactory.
  • Refactor
    • Enhanced thread safety in logging and output retrieval by introducing AutoCloseableLock in the Reporter.java.
    • Synchronized critical sections in various components, including SkipException, SuiteRunner, and test classes, with AutoCloseableLock and KeyAwareAutoCloseableLock for improved concurrency management.
    • Modified listener and retry analyzer implementations for thread safety and concurrency improvements.

Copy link

coderabbitai bot commented Feb 26, 2024

Walkthrough

The recent updates to TestNG focus on enhancing thread safety and concurrency control by replacing the synchronized keyword with ReentrantLock and introducing AutoCloseableLock for better resource management. This shift aligns with the move towards Virtual Threads, improving the framework's efficiency and readiness for future Java versions.

Changes

Files Change Summary
.../internal/AutoCloseableLock.java, .../internal/KeyAwareAutoCloseableLock.java Introduced AutoCloseableLock and KeyAwareAutoCloseableLock for better locking mechanisms.
.../java/org/testng/Reporter.java, .../SuiteRunner.java, .../ConfigurationGroupMethods.java, .../invokers/ConfigInvoker.java, .../invokers/TestInvoker.java, .../invokers/TestMethodWorker.java, .../thread/graph/GraphOrchestrator.java, .../reporters/JUnitXMLReporter.java, .../xml/XMLParser.java Replaced synchronized blocks with try-with-resources statements using AutoCloseableLock.
.../SkipException.java, .../test/java/test/... (various test files) Utilized AutoCloseableLock for thread safety in exception handling and test scenarios.
.../test/java/test/listeners/factory/ExampleListener.java, .../test/java/test/listeners/issue2043/listeners/FailFastListener.java Enhanced concurrency control with AtomicBoolean and ConcurrentHashMap.newKeySet().

Assessment against linked issues

Objective Addressed Explanation
replace the usages of synchronized with ReentrantLock (#3040)

Poem

In the land of code, where threads intertwine,
A rabbit hopped forth, with a plan so divine.
"No more 'synchronized', let's lock with care,
With AutoCloseableLock, thread safety we declare!"
🐇 With every hop and every leap,
TestNG grows strong, its future to keep.
"To concurrency's future, we proudly hop!"
🌟 Said the wise CodeRabbit, at the mountain top. 🌄

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

Share

Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>.
    • Generate unit-tests for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit tests for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai generate interesting stats about this repository and render them as a table.
    • @coderabbitai show all the console.log statements in this repository.
    • @coderabbitai read src/utils.ts and generate unit tests.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (invoked as PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger a review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai help to get help.

Additionally, you can add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.

CodeRabbit Configration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • The JSON schema for the configuration file is available here.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/coderabbit-overrides.v2.json

CodeRabbit Discord Community

Join our Discord Community to get help, request features, and share feedback.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Review Status

Actionable comments generated: 2

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between 823799c and 02132bd.
Files selected for processing (23)
  • CHANGES.txt (1 hunks)
  • testng-core-api/src/main/java/org/testng/Reporter.java (3 hunks)
  • testng-core-api/src/main/java/org/testng/internal/AutoCloseableLock.java (1 hunks)
  • testng-core-api/src/main/java/org/testng/internal/KeyAwareAutoCloseableLock.java (1 hunks)
  • testng-core/src/main/java/org/testng/SkipException.java (3 hunks)
  • testng-core/src/main/java/org/testng/SuiteRunner.java (1 hunks)
  • testng-core/src/main/java/org/testng/internal/ConfigurationGroupMethods.java (4 hunks)
  • testng-core/src/main/java/org/testng/internal/invokers/ConfigInvoker.java (2 hunks)
  • testng-core/src/main/java/org/testng/internal/invokers/TestInvoker.java (1 hunks)
  • testng-core/src/main/java/org/testng/internal/invokers/TestMethodWorker.java (3 hunks)
  • testng-core/src/main/java/org/testng/internal/thread/graph/GraphOrchestrator.java (5 hunks)
  • testng-core/src/main/java/org/testng/reporters/JUnitXMLReporter.java (2 hunks)
  • testng-core/src/main/java/org/testng/xml/XMLParser.java (2 hunks)
  • testng-core/src/test/java/test/configuration/ConfigurationGroupBothSampleTest.java (1 hunks)
  • testng-core/src/test/java/test/inheritance/issue2489/tests/BaseClassA.java (2 hunks)
  • testng-core/src/test/java/test/junitreports/TestClassContainerForGithubIssue1265.java (1 hunks)
  • testng-core/src/test/java/test/listeners/factory/ExampleListener.java (1 hunks)
  • testng-core/src/test/java/test/listeners/issue2043/listeners/FailFastListener.java (2 hunks)
  • testng-core/src/test/java/test/retryAnalyzer/issue2798/HashCodeAwareRetryAnalyzer.java (1 hunks)
  • testng-core/src/test/java/test/testng1232/TestListenerFor1232.java (1 hunks)
  • testng-core/src/test/java/test/thread/BaseThreadTest.java (2 hunks)
  • testng-core/src/test/java/test/thread/MultiThreadedDependentSampleTest.java (2 hunks)
  • testng-core/src/test/java/test/thread/MultiThreadedDependentTest.java (2 hunks)
Additional comments: 24
testng-core-api/src/main/java/org/testng/internal/AutoCloseableLock.java (1)
  • 1-23: The implementation of AutoCloseableLock is well-designed and follows best practices for managing locks in Java. Using AutoCloseableLock in a try-with-resources statement ensures that the lock is always properly released, even in the case of an exception. This is a significant improvement in terms of code maintainability and safety.
testng-core/src/test/java/test/retryAnalyzer/issue2798/HashCodeAwareRetryAnalyzer.java (1)
  • 19-23: The use of AutoCloseableLock within the retry method is correctly implemented to ensure thread safety when accessing and modifying shared resources. This change enhances the concurrency control in the retry logic, ensuring that the hashCodes list and the cnt variable are accessed in a thread-safe manner. Good job on implementing this pattern.
testng-core/src/test/java/test/junitreports/TestClassContainerForGithubIssue1265.java (1)
  • 13-16: The modifications mentioned involve removing the synchronized keyword from the startEverything and shutdownContainer methods without showing the addition of alternative synchronization mechanisms like ReentrantLock. If these methods do not access shared resources or if there's no need for synchronization, this change could be justified. However, it's important to ensure that removing synchronized does not introduce concurrency issues. Can you clarify if these methods require synchronization based on their internal logic or accessed resources?
testng-core-api/src/main/java/org/testng/Reporter.java (4)
  • 71-77: The introduction of AutoCloseableLock for synchronized logging in the log method is a good practice for ensuring thread safety. However, it's important to ensure that all logging operations throughout the codebase are updated to use this new mechanism to maintain consistency.
  • 79-79: The logToReports method correctly handles HTML escaping based on the m_escapeHtml flag and manages orphaned output. This method improves modularity by separating the concerns of logging and managing output storage.
  • 169-175: Using AutoCloseableLock in the getOutput method for synchronized output retrieval is a positive change for thread safety. Similar to the logging changes, ensure that all output retrieval operations are updated to use this new locking mechanism.
  • 177-177: The getOutputFromResult method is well-implemented for retrieving test outputs in a thread-safe manner. It correctly handles the case where the test result might be null, preventing potential NullPointerExceptions.
testng-core/src/main/java/org/testng/internal/ConfigurationGroupMethods.java (3)
  • 31-35: The introduction of AutoCloseableLock instances for managing synchronization around beforeGroups and afterGroups is a significant improvement for thread safety. This change ensures that group-related operations are performed atomically, reducing the risk of concurrency issues.
  • 70-70: The use of AutoCloseableLock in getBeforeGroupMethodsForGroup ensures that the retrieval and processing of before group methods are thread-safe. This is crucial in a multi-threaded environment to prevent inconsistent states.
  • 85-85: Similarly, employing AutoCloseableLock in getAfterGroupMethods for after group methods retrieval enhances thread safety. It's important to verify that all group-related operations across the codebase consistently use these locks.
testng-core/src/main/java/org/testng/reporters/JUnitXMLReporter.java (2)
  • 150-157: The introduction of AutoCloseableLock for thread-safe XML document creation in createElementFromTestResults is a prudent change. This ensures that the document creation process is protected from concurrent modifications, which is essential for maintaining data integrity.
  • 161-169: Using AutoCloseableLock in createElementFromIgnoredTests to ensure thread safety during the creation of XML elements for ignored tests is a good practice. This change helps prevent potential issues arising from concurrent access to shared resources.
testng-core/src/main/java/org/testng/internal/invokers/TestMethodWorker.java (1)
  • 121-129: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [105-135]

The introduction of KeyAwareAutoCloseableLock and its usage in TestMethodWorker for synchronizing the invocation of beforeClass and afterClass methods is a significant improvement for thread safety. This change ensures that class-level setup and teardown operations are performed atomically, reducing the risk of concurrency issues. It's important to ensure that all class-level operations across the codebase consistently use this locking mechanism.

testng-core/src/main/java/org/testng/SuiteRunner.java (1)
  • 418-418: The introduction of AutoCloseableLock named suiteResultsLock is a good practice for managing locks in a safer and more readable manner, especially when used in a try-with-resources statement. This approach ensures that the lock is always released, even if an exception occurs, which is crucial for avoiding deadlocks.
testng-core/src/main/java/org/testng/internal/invokers/ConfigInvoker.java (3)
  • 31-31: The import of org.testng.internal.AutoCloseableLock is correctly added to utilize the AutoCloseableLock class for lock management. This change aligns with the PR's objective to replace synchronized blocks with ReentrantLock mechanisms.
  • 560-560: The addition of the private final AutoCloseableLock internalLock = new AutoCloseableLock(); field introduces a lock that can be used across the ConfigInvoker class to manage concurrency more effectively than the previous synchronized approach. This change is crucial for the intended improvements in concurrency management.
  • 563-563: The replacement of a synchronized block with a try-with-resources statement using internalLock.lock() in the setClassInvocationFailure method is a significant improvement. This change leverages the AutoCloseable feature of the AutoCloseableLock, ensuring that the lock is automatically released when the try block is exited, which is a best practice for managing locks and preventing potential deadlocks.
testng-core/src/main/java/org/testng/internal/invokers/TestInvoker.java (3)
  • 851-851: The introduction of AutoCloseableLock as a static final variable is a good practice for lock objects, ensuring that the lock is shared across all instances of TestInvoker. This aligns with the objective of replacing synchronized blocks with more flexible locking mechanisms.
  • 858-860: The use of AutoCloseableLock within a try-with-resources statement is a correct implementation for replacing a synchronized block. This ensures that the lock is automatically released when the block is exited, either normally or via an exception, which is a key advantage of AutoCloseableLock.
  • 863-902: Splitting the considerExceptions method into considerExceptions and considerExceptionsInternal improves readability and maintainability by separating the locking mechanism from the business logic. This change also adheres to the Single Responsibility Principle, making the code easier to understand and test. However, ensure that all calls to considerExceptions have been updated to reflect this change, avoiding any potential issues with method invocation.
CHANGES.txt (4)
  • 2-2: The summary correctly highlights the replacement of synchronized with ReentrantLock as a significant change aimed at enhancing concurrency management in TestNG. This change aligns with modern Java concurrency practices and is expected to improve performance and thread safety.
  • 2-2: The update to TestNG's DataProvider behavior to align with version 6.x when retrying tests addresses a critical functionality for test execution consistency. Ensuring that the behavior matches across versions is crucial for users upgrading TestNG without altering their tests' expected outcomes.
  • 2-2: The adjustment for dynamically managing TestNG threads post-deprecation of IExecutorFactory is a significant improvement. It addresses the need for more flexible execution management within TestNG, allowing for better resource utilization and potentially enhancing execution efficiency.
  • 2-2: Introducing the ability for users to define the ordering of TestNG listeners is a valuable addition. It provides users with more control over the execution flow of their tests, especially in complex scenarios where the order of listener execution can impact test outcomes or debugging processes.

Copy link
Member

@juherr juherr left a comment

Choose a reason for hiding this comment

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

@vlsi Please, could you review?

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Review Status

Actionable comments generated: 0

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between 02132bd and c88be92.
Files selected for processing (3)
  • testng-core/src/main/java/org/testng/internal/invokers/TestInvoker.java (1 hunks)
  • testng-core/src/test/java/test/listeners/factory/ExampleListener.java (1 hunks)
  • testng-core/src/test/java/test/thread/MultiThreadedDependentTest.java (2 hunks)
Files skipped from review as they are similar to previous changes (3)
  • testng-core/src/main/java/org/testng/internal/invokers/TestInvoker.java
  • testng-core/src/test/java/test/listeners/factory/ExampleListener.java
  • testng-core/src/test/java/test/thread/MultiThreadedDependentTest.java

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Review Status

Actionable comments generated: 0

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between 02132bd and 9d0ba52.
Files selected for processing (4)
  • testng-core/src/main/java/org/testng/internal/invokers/TestInvoker.java (1 hunks)
  • testng-core/src/test/java/test/listeners/factory/ExampleListener.java (1 hunks)
  • testng-core/src/test/java/test/listeners/factory/TestNGFactoryTest.java (1 hunks)
  • testng-core/src/test/java/test/thread/MultiThreadedDependentTest.java (2 hunks)
Files skipped from review as they are similar to previous changes (3)
  • testng-core/src/main/java/org/testng/internal/invokers/TestInvoker.java
  • testng-core/src/test/java/test/listeners/factory/ExampleListener.java
  • testng-core/src/test/java/test/thread/MultiThreadedDependentTest.java
Additional comments: 1
testng-core/src/test/java/test/listeners/factory/TestNGFactoryTest.java (1)
  • 26-26: The change from ExampleListener.instance to ExampleListener.getInstance() aligns with Java best practices for encapsulating access to class members, especially in the context of improving concurrency management. This is a positive change that supports the PR's objectives.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Review Status

Actionable comments generated: 1

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between 9d0ba52 and cc23c7b.
Files ignored due to path filters (1)
  • testng-core/src/test/resources/testng.xml is excluded by: !**/*.xml
Files selected for processing (4)
  • testng-core-api/src/main/java/org/testng/internal/AutoCloseableLock.java (1 hunks)
  • testng-core-api/src/main/java/org/testng/internal/KeyAwareAutoCloseableLock.java (1 hunks)
  • testng-core/src/main/java/org/testng/internal/invokers/TestMethodWorker.java (3 hunks)
  • testng-core/src/test/java/org/testng/internal/KeyAwareAutoCloseableLockTest.java (1 hunks)
Files skipped from review as they are similar to previous changes (3)
  • testng-core-api/src/main/java/org/testng/internal/AutoCloseableLock.java
  • testng-core-api/src/main/java/org/testng/internal/KeyAwareAutoCloseableLock.java
  • testng-core/src/main/java/org/testng/internal/invokers/TestMethodWorker.java

Comment on lines +9 to +25
@Test
public void ensureLockIsReEntrant() {
String key = "TestNG";
KeyAwareAutoCloseableLock lock = new KeyAwareAutoCloseableLock();
KeyAwareAutoCloseableLock.AutoReleasable outer, inner1, inner2;
try (KeyAwareAutoCloseableLock.AutoReleasable ignore = lock.lockForObject(key)) {
outer = ignore;
try (KeyAwareAutoCloseableLock.AutoReleasable ignore1 = lock.lockForObject(key)) {
inner1 = ignore1;
}
try (KeyAwareAutoCloseableLock.AutoReleasable ignore2 = lock.lockForObject(key)) {
inner2 = ignore2;
}
}
assertThat(outer).isEqualTo(inner1);
assertThat(inner1).isEqualTo(inner2);
}
Copy link

Choose a reason for hiding this comment

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

The test ensureLockIsReEntrant correctly verifies the re-entrant nature of KeyAwareAutoCloseableLock by locking on the same key multiple times and asserting that the lock instances (outer, inner1, inner2) are equal. This approach effectively demonstrates that the lock is re-entrant for the same key. However, the test could be enhanced by adding assertions to check that the lock is actually held during each try block, ensuring that the lock's behavior aligns with expectations beyond just re-entrancy. Additionally, consider adding negative test cases to verify that the lock is not re-entrant for different keys and that appropriate exceptions are thrown or behaviors are exhibited in such scenarios.

// Suggestion to enhance the test by verifying the lock is held
// and adding negative test cases for different keys.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Review Status

Actionable comments generated: 0

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between cc23c7b and 32d75e7.
Files selected for processing (1)
  • testng-core/src/main/java/org/testng/internal/invokers/TestMethodWorker.java (1 hunks)
Files skipped from review as they are similar to previous changes (1)
  • testng-core/src/main/java/org/testng/internal/invokers/TestMethodWorker.java

@krmahadevan
Copy link
Member Author

ping @juherr

@krmahadevan krmahadevan merged commit eb80671 into testng-team:master Mar 5, 2024
6 of 9 checks passed
@krmahadevan krmahadevan deleted the remove_synchronized_keyword branch March 5, 2024 07:02
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.

replace the usages of synchronized with ReentrantLock
3 participants