RFR [6968459] JNDI timeout fails before timeout is reached
David Holmes
david.holmes at oracle.com
Fri Dec 20 02:19:29 UTC 2013
If you track the elapsed waiting time using System.nanoTime you do not
need to be concerned whether anyone messes with the TOD clock.
David
On 4/12/2013 2:05 AM, Peter Levart wrote:
> On 12/03/2013 03:35 PM, Ivan Gerasimov wrote:
>> Hi Peter!
>>
>> Thank you for your review!
>>
>> You are right, the patch changed the behavior of the code.
>> I've reverted back all the unnecessary changes. This should minimize
>> the risk.
>>
>> I've also made another correction: After decrementing the remaining
>> timeOut, the startTime should be set to currTime.
>>
>> Would you please help review the updated webrev:
>> http://cr.openjdk.java.net/~igerasim/6968459/2/webrev/
>>
>> Sincerely yours,
>> Ivan
>
> Hi Ivan,
>
> That's better. You could move the initial request for ldr.getReplyBer()
> (line 447) out of while loop, since it is not needed in the loop (all
> further ldr.getReplyBer() calls in loop are performed in synchronized
> block already - line 465). "else { break; }" (line 469) is not needed in
> that case. I would also make timeOut variable long to avoid overflows.
> Simplified logic could look like this:
>
>
> BerDecoder readReply(LdapRequest ldr)
> throws IOException, NamingException {
> long timeOut = (readTimeout > 0) ?
> readTimeout : 15 * 1000; // Default read timeout of 15 sec
>
> long currTime = System.currentTimeMillis();
>
> BerDecoder rber = ldr.getReplyBer();
>
> while (rber == null && timeOut > 0L) {
> long startTime = currTime;
> // If socket closed, don't even try
> synchronized (this) {
> if (sock == null) {
> throw new ServiceUnavailableException(host + ":" +
> port +
> "; socket closed");
> }
> }
> synchronized (ldr) {
> // check if condition has changed since our last check
> rber = ldr.getReplyBer();
> if (rber == null) {
> try {
> ldr.wait(timeOut);
> } catch (InterruptedException ex) {
> throw new InterruptedNamingException(
> "Interrupted during LDAP operation");
> }
> }
> }
> currTime = System.currentTimeMillis();
> if (startTime < currTime) {
> timeOut -= (currTime - startTime);
> }
> // else system time must have changed backwards (or not at
> all)
> }
>
> if (rber == null && readTimeout > 0) {
> removeRequest(ldr);
> throw new NamingException("LDAP response read timed out,
> timeout used:"
> + readTimeout + "ms." );
> }
>
> return rber;
> }
>
>
> What do you think?
>
> Regards, Peter
>
>>
>>>
>>> From quick look I notice a danger that Connection.readReply() pauses
>>> (for the timeOut time) in spite of the fact that a reply is ready and
>>> enqueued before waiting.
>>>
>>> Imagine a situation where the 1st try of obtaining result returns null:
>>>
>>> 450 // Get the reply if it is already available.
>>> 451 BerDecoder rber = ldr.getReplyBer();
>>>
>>>
>>> ...after that, but before reaching the following lines:
>>>
>>> 471 synchronized (ldr) {
>>> 472 ldr.wait(timeOut);
>>> 473 }
>>>
>>>
>>> The "other" thread enqueues a reply (LdapRequest.addReplyBer())
>>> because the code in readReply() is executing without holding a lock
>>> on "ldr". When "this" thread (executing readReply()) finally enters
>>> synchronized block and waits, it will wait until wait() times out or
>>> another reply is enqueued which will issue another notify(). This can
>>> lead to unnecessary pauses. Old code does not exhibit this problem,
>>> because it re-checks the readiness of a reply immediately after
>>> entering the synchronized block.
>>>
>>>
>>> Regards, Peter
>>>
>>>
>>>
>>
>
More information about the core-libs-dev
mailing list