Skip to content

First pass - trivial synchronized blocks #8652

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

Merged
merged 10 commits into from
Jun 21, 2023

Conversation

tzolov
Copy link
Contributor

@tzolov tzolov commented Jun 16, 2023

  • Convert the "trivial" synchronized block into reentrant locks.

Resolves #8643

Copy link
Member

@artembilan artembilan left a comment

Choose a reason for hiding this comment

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

Mostly this is OK, but really too much and it is easy to miss something.
Please, see if you really were right about changing tryLock() to lock() everywhere you found it.

@garyrussell ,

as we already know this is easier to review with ?w=1.

@tzolov ,

let's see next time if can propose the change in baby steps, per use-case. Really hard to review.

if (name == null) {
name = this.applicationContext.getEnvironment().getProperty("spring.application.name");
private Graph buildGraph() {
this.lock.lock();
Copy link
Member

Choose a reason for hiding this comment

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

May we have this optimized like call this.lock.lock() only once?
What I mean we probably need to extract a private Graph doBuildGraph() without lock and have it called from this buildGraph() surrounded with lock and also call it from that getGraph().
Right now we from that getGraph() we have two this.lock.lock().
Although it might be just my paranoia and that is not a big deal since we would have just increased count on the lock.

And that it, you know, why huge PRs are not OK, when they touch a lot of not related stuff 🤷

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Technically it should work as it is given the preemtive nature of the locks.
But looking at the getGraph() it seems like the mutex is redundat there. I belive something like this should be correct:

	public Graph getGraph() {
		if (this.graph == null) {
			buildGraph(); // already under mutex
		}
		return this.graph;
	}

what do you think?

Copy link
Member

Choose a reason for hiding this comment

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

If several threads call this getGraph() and encounter with this.graph == null, they step into that buildGraph() and wait for a lock.
When first one is done with building, it releases lock and the next one takes it and start building a fresh graph.
We do that unconditionally in the rebuild(), but getGraph() should build only once.

Well, let's leave it as you have in the PR right no and revise it later in the separate change!

Copy link
Member

@artembilan artembilan left a comment

Choose a reason for hiding this comment

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

Just one concern so far I've noticed.

Copy link
Member

@artembilan artembilan left a comment

Choose a reason for hiding this comment

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

Looks like the change in STOMP module has affected somehow tests:

StompInboundChannelAdapterWebSocketIntegrationTests > testWebSocketStompClient() FAILED
    java.lang.AssertionError at StompInboundChannelAdapterWebSocketIntegrationTests.java:162

@tzolov
Copy link
Contributor Author

tzolov commented Jun 19, 2023

StompInboundChannelAdapterWebSocketIntegrationTests > testWebSocketStompClient() FAILED
java.lang.AssertionError at StompInboundChannelAdapterWebSocketIntegrationTests.java:162

It is a bit strange as we have not changed much on the Stomp Inbound channel code.
Anyway the core of the issue is even stranger as it complains that

Error creating bean with name 'webSocketEventListener' defined in org.springframework.integration.stomp.inbound.StompInboundChannelAdapterWebSocketIntegrationTests$ServerConfig: 
Unsatisfied dependency expressed through method 'webSocketEventListener' parameter 0: 
No qualifying bean of type 'org.springframework.messaging.support.AbstractSubscribableChannel' available: 
expected single matching bean but found 3: clientInboundChannel,clientOutboundChannel,brokerChannel

After qualifying explicitly the taskScheduler :

@Bean
public WebSocketStompClient stompClient(@Qualifier("taskScheduler") TaskScheduler taskScheduler) {
...

and the clientOutboundChannel :

@Bean
public ApplicationListener<SessionSubscribeEvent> webSocketEventListener(@Qualifier("clientOutboundChannel") final AbstractSubscribableChannel clientOutboundChannel) {
...

The test passes. But i'm no sure if this fixes or masks the core reason that lead to this.

@artembilan artembilan changed the title [IN PROGRESS] First pass - trivial synchronized blocks First pass - trivial synchronized blocks Jun 20, 2023
Copy link
Member

@artembilan artembilan left a comment

Choose a reason for hiding this comment

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

This is OK with me.

@garyrussell ,
let us know if you are going to review it, or just let it go and come back to specific concern when it raises!

@garyrussell
Copy link
Contributor

I don't see a need for me to review.

Thanks.

@artembilan
Copy link
Member

Then I'll merge this as is and we can revise other concerns in other PRs or so.

@artembilan artembilan merged commit c38ed96 into spring-projects:main Jun 21, 2023
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.

Revise synchronized blocks/methods in critical code path to avoid virtual threads pinning
3 participants