Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

dugenkui03
Copy link
Contributor

ApplicationAvailabilityBean#onApplicationEvent could be invoked concurrently, we should replace HashMap(event map) with ConcurrentHashMap.

Details in jdk - HashMap.java - L89

Note that this implementation is not synchronized. If multiple threads access a hash map concurrently, and at least one of the threads modifies the map structurally, it must be synchronized externally.

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Mar 31, 2022
@mhalbritter
Copy link
Contributor

In which case would this method be invoked concurrently? The JavaDoc from EventListener states that concurrency is used if the method is annotated with @Async, which isn't the case here.

@mhalbritter mhalbritter added the status: waiting-for-feedback We need additional information before we can continue label Apr 4, 2022
@dugenkui03
Copy link
Contributor Author

In which case would this method be invoked concurrently?

spring boot allow developer inject ApplicationAvailabilityBean in multiple custom beans and publish their custom AvailabilityState event. there is a chance that ApplicationAvailabilityBean#onApplicationEvent could be invoked concurrently.

@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels Apr 5, 2022
@mhalbritter
Copy link
Contributor

It's not expected that users inject the bean and call onApplicationEvent directly. If you want to change the application availability status, you should emit an AvailabilityChangeEvent. The spring event system will then ensure that these listeners are called. And they are not called concurrently.

@mhalbritter mhalbritter closed this Apr 6, 2022
@mhalbritter mhalbritter added status: invalid An issue that we don't feel is valid and removed status: waiting-for-triage An issue we've not yet triaged status: feedback-provided Feedback has been provided labels Apr 6, 2022
@wilkinsona
Copy link
Member

I think we should accept this. While onApplicationEvent is being called on one thread by the event multicaster and it mutates the map, another thread could call one of the get… methods that reads from the map.

@wilkinsona wilkinsona reopened this Apr 6, 2022
@wilkinsona wilkinsona added type: bug A general bug and removed status: invalid An issue that we don't feel is valid labels Apr 6, 2022
@wilkinsona wilkinsona added this to the 2.5.x milestone Apr 6, 2022
@mhalbritter
Copy link
Contributor

Ah, yeah, you're right. I missed that case. Will merge the PR.

@mhalbritter mhalbritter self-assigned this Apr 6, 2022
@mhalbritter mhalbritter changed the title Thread safe about ApplicationAvailabilityBean Make ApplicationAvailabilityBean threadsafe Apr 6, 2022
mhalbritter pushed a commit that referenced this pull request Apr 6, 2022
@mhalbritter mhalbritter modified the milestones: 2.5.x, 2.5.13 Apr 6, 2022
@wilkinsona wilkinsona changed the title Make ApplicationAvailabilityBean threadsafe ApplicationAvailabilityBean is not thread-safe Apr 6, 2022
@dugenkui03
Copy link
Contributor Author

... they are not called concurrently.

@mhalbritter I trace the code from AvailabilityChangeEvent, and find there is nothing to ensure that listeners are not called concurrently —— I can't find any lock or things like BlockingQueue.

// 1. multiple thread invoke this static method concurrently and publish different state.
 AvailabilityChangeEvent.publish(this.eventPublisher, ex, LivenessState.BROKEN);

// 2.  call multicastEvent in ‘AbstractApplicationContext#publishEvent(Object, ResolvableType)‘
//      still don't have any lock, 
getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType)

// 3. multiple thread could invoke 'SimpleApplicationEventMulticaster#multicastEvent' at same time and publish different event concurrently.
protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
                 ...
		doInvokeListener(listener, event);
}

private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
	try {
		listener.onApplicationEvent(event);
	}
        ...
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: bug A general bug
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants