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

Replace synchronized blocks with Locks #5565

Merged
merged 2 commits into from
Mar 18, 2024
Merged

Conversation

jansupol
Copy link
Contributor

No description provided.

Signed-off-by: jansupol <jan.supol@oracle.com>
}
if (state == SUSPENDED) {
stateLock.readLock().unlock(); // unlock before handleTimeout to prevent write lock in handleTimeout
handler.handleTimeout(this);
Copy link
Member

Choose a reason for hiding this comment

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

I am not sure that I understand this. Previously handler.handleTimeout(this); was invoked within a lock, and now it does not. This timeouthandler suggests that can be re-set because it is not final and contains volatile:
private volatile TimeoutHandler timeoutHandler = DEFAULT_TIMEOUT_HANDLER;

Are we sure about that missing lock?.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@jbescos There is UNLOCK before the handleTimeout. handleTimeout calls writeLock.lock() and it is not possible to do with readLock locked.

Copy link
Contributor Author

@jansupol jansupol Mar 15, 2024

Choose a reason for hiding this comment

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

I understand now.

synchronized (stateLock) {
                    if (state == SUSPENDED) {
                        handler.handleTimeout(this);
                    }
}

guarded state, not handleTimeout. User defined handleTimeout can do for instance (AsyncTimoutResource in servlet-3-async integration test) :

        asyncResponse.setTimeoutHandler(new TimeoutHandler() {

            @Override
            public void handleTimeout(final AsyncResponse asyncResponse) {
                final boolean timeout2 = asyncResponse.setTimeout(millis, TimeUnit.MILLISECONDS);
                asyncResponse.setTimeoutHandler(new TimeoutHandler() {

                    @Override
                    public void handleTimeout(final AsyncResponse asyncResponse) {
                        asyncResponse.resume("timeout1=" + timeout1 + "_timeout2=" + timeout2 + "_handled");
                        asyncResponse.cancel();
                    }
                });
            }
        });

And asyncResponse.resume() has its own locks.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Previously there was just one lock, whereas with ReentrantReadWriteLock it can be differenciate with readLock and writeLock, having less waiting for an unlock with multiple threads being in readLocks when there is no writeLock

Copy link
Contributor Author

@jansupol jansupol Mar 15, 2024

Choose a reason for hiding this comment

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

while previously there was one write lock, now the thread would do readLock.lock(); writeLock.lock() but that's not possible even on a single thread to upgrade the lock from read to write, first an unlock needs to happen.

Copy link
Contributor Author

@jansupol jansupol Mar 15, 2024

Choose a reason for hiding this comment

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

The user-defined TimeoutHandler cannot possibly directly access guarded fields state and cancelled to need it in a guarded block, can it? Every access to these fields is possible only through methods that are all guarded separately.

Copy link
Member

@jbescos jbescos Mar 15, 2024

Choose a reason for hiding this comment

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

Just I wanted to stress that, if for example the TimeoutHandler implementation could come from third party code, and for example contains something stupid like increasing a variable, or put something in a HashMap, or things like that, it could not work as expected, because it can be invoked by more than 1 thread at the same time.

If this case cannot happen, then it is fine as you have it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It definitely CAN happen. But should Jersey guard the customer code? I'd say not, but you are right that it would be a behavior change. Perhaps there should be another extra lock for TimoutHandler execution?

Copy link
Member

Choose a reason for hiding this comment

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

To me it looks more correct to not invoke the TimeoutHandler in a lock. If the user needs it, he can synchronize it. But if we change this I think it is better to have a mention in this issue, or in a new issue, to know about this change.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The TimoutHandler can be invoked simultaneously only when

  • the timeout is scheduled multiple times for the same AsyncResponse, but it is already guarded by a lock in JerseyRequestTimeoutHandler
  • the timeout handler timeouts at the same time on different AsyncResponses, so the lock would need to be static which is different from synchronize, which synchronizes over the instance.
  • the customer would invoke the TimoutHandler method on their own, but in this case, the Lock would not be on the customer side.

In that case, I guess it is safe not to have the lock

Signed-off-by: jansupol <jan.supol@oracle.com>
@senivam senivam merged commit 984ba24 into eclipse-ee4j:3.1 Mar 18, 2024
3 checks passed
@senivam senivam added this to the 3.1.6 milestone Mar 20, 2024
This was referenced Apr 5, 2024
@jansupol jansupol deleted the lock1 branch April 26, 2024 10:08
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.

3 participants