RFR: JDK-6823527: java.util.logging.Handler has thread safety issues
Daniel Fuchs
daniel.fuchs at oracle.com
Fri Aug 30 08:38:11 UTC 2013
On 8/30/13 9:21 AM, David Holmes wrote:
> Hi Daniel,
>
> I'm fine with the approach in (c), just wanted to highlight the
> performance considerations (volatile and synchronized need not be
> equivalent in the uncontended case - biased-locking can mean there are
> no barriers emitted.)
>
> A few specific comments on [2]:
>
> Handler.java:
>
> reportError doesn't need changing now that the field is volatile and you
> aren't doing sync.
right. I will revert to original code.
> MemoryHandler.java:
>
> pushLevel should/could be volatile and the sync removed from getPushLevel.
right. thanks - I missed that.
> In setPushLevel this seems to be unused code:
>
> 262 LogManager manager = LogManager.getLogManager();
>
> unless there is some obscure side-effect in calling getLogManager ??
Well - yes it triggers the initialization of the logging system.
However the super class will already have called getLogManager() -
so this call should have no effect. I will remove it.
> setPushLevel might as well also be synchronized at the method level. All
> the Handler methods already include checkPermission inside the
> sync-block. Though I'm actually more inclined to change all the existing
> code to use sync-blocks so that checkPermission is done outside the lock
> region. But a consistent approach either way would be preferable.
Agreed. Once I have removed the call to LogManager then the method can
be synchronized at method level.
> StreamHandler.java
>
> writer doesn't need to be volatile - AFAICS it is only accessed within
> synchronized methods.
Ah - no - it's called in isLoggable() actually. The whole point of using
volatiles instead of regular synchronization was to allow isLoggable()
to be called concurrently with e.g. publish() - so I would think
it's better to make writer volatile too.
Thanks for the valuable comments! I will update the webrev and send out
a new revision...
best regards,
-- daniel
>
> Thanks,
> David
>
> On 30/08/2013 4:45 PM, Daniel Fuchs wrote:
>> On 8/30/13 3:27 AM, David Holmes wrote:
>>> Hi Daniel,
>>>
>>> As usual with these kinds of changes we start by trying to complete an
>>> incomplete/incorrect synchronization pattern and get diverted into
>>> changing the synchronization pattern. :) The latter of course has more
>>> risk than the former.
>>>
>>> Making everything synchronized has the benefit that we don't need to
>>> analyse the "real" methods to check what would happen if one of these
>>> data values changed mid-method. Might be harmless, might not.
>>> Atomicity makes things easy to reason about.
>> Agreed. We can analyze the 'real methods' of Handler classes in the JDK.
>> But I'm sure there are plenty of
>> subclasses of handlers in applications out there that we don't know
>> about. So we can't really be sure
>> what the effect of changing synchronization might have on those
>> subclasses.
>>
>>> Making the fields volatile and un-sync'ing the getters is a potential
>>> optimization for readers. But are these fields that are read
>>> frequently by multiple threads?
>>
>> At least three of them are accessed by isLoggable() which I believe is
>> called very frequently by
>> multiple threads (level, filter, and writer).
>>
>>> The downside of doing so is a negative performance impact inside the
>>> "real" methods where every volatile field access requires
>>> (platform-dependent) memory barriers.
>> That - I think - should not be such an issue since the fields are
>> private.
>> I mean - we can see whether there are methods that do multiple access to
>> the fields in the
>> classes that define these fields - and fix that if necessary.
>> For subclasses, they will have to call the getter, which will require
>> synchronization anyway.
>> I mean - for subclasses - isn't the fact that the getter accesses a
>> volatile field or the fact that the
>> getter is synchronized equivalent in terms of performance?
>>
>> JDK 8 is a major version of the JDK - so I think if we want to fix this
>> bug it's
>> probably better to do it now rather than in a minor update.
>>
>> It seems we have 5 choices:
>>
>> a. Not fixing
>> b. Using regular 'synchronized' pattern [1]
>> c. Using volatiles, synchronize setters only [2]
>> d. Using volatiles, only synchronize setters when obviously necessary
>> e. other? (like introducing new private locks - but I don't think
>> anybody would want that)
>>
>> [1] <http://cr.openjdk.java.net/~dfuchs/webrev_6823527/webrev.00/>
>> [2] <http://cr.openjdk.java.net/~dfuchs/webrev_6823527/webrev.01/>
>>
>> My own preference would lean to either b) or c).
>>
>> -- daniel
>>
>>
>>>
>>> David
>>>
>>> On 30/08/2013 5:54 AM, Daniel Fuchs wrote:
>>>> On 8/29/13 9:45 PM, Mandy Chung wrote:
>>>>> On 8/29/13 11:04 AM, Daniel Fuchs wrote:
>>>>>> On 8/29/13 7:58 PM, Mandy Chung wrote:
>>>>>>> On 8/29/13 9:46 AM, Daniel Fuchs wrote:
>>>>>>>> Here is the new webrev implementing Jason's suggestion.
>>>>>>>> Compared to previous webrev only Handler.java & StreamHandler.java
>>>>>>>> have changed.
>>>>>>>>
>>>>>>>> <http://cr.openjdk.java.net/~dfuchs/webrev_6823527/webrev.01/>
>>>>>>>>
>>>>>>>
>>>>>>> Looks good. I also agree that making those fields to be volatile
>>>>>>> is a
>>>>>>> good suggestion and only requires writer to acquire the lock.
>>>>>>>
>>>>>>> A minor comment: The setter methods in Handler class are
>>>>>>> synchronized
>>>>>>> at the method entry whereas StreamHandler.setPushLevel synchronizes
>>>>>>> after checkPermission() is called. checkPermission doesn't need
>>>>>>> to be
>>>>>>> synchronized. Either way is fine with me and it'd be good to do it
>>>>>>> consistently.
>>>>>>
>>>>>> Hi Mandy,
>>>>>>
>>>>>> Are you suggesting I should put checkPermission() within
>>>>>> the synchronized block? It might be more consistent with
>>>>>> what the other setters do. Although as you state it does
>>>>>> not matter much.
>>>>>
>>>>> Having a second thought - do the setters really need to be
>>>>> synchronized? My guess is that StreamHandler.publish is synchronized
>>>>> so as to ensure that the log messages are sequential and not
>>>>> interpersed
>>>>> when multiple threads are publishing. The spec is unclear. Perhaps it
>>>>> worths looking into this further?
>>>>
>>>> I believe at least setEncodings() needs synchronization.
>>>> setLevel() had it - so are we going to cause subtle race conditions
>>>> if we remove it?
>>>>
>>>> My own feeling is that keeping setters synchronized might be better.
>>>>
>>>> We could of course say that a subclass which needs to prevent
>>>> e.g. setFormatter() from being called concurrently with publish()
>>>> should override setFormatter just for the purpose of synchronizing
>>>> it...
>>>>
>>>>> One more minor note: MemoryHandler.pushLevel can also be made
>>>>> volatile.
>>>>> L262: look like the log manager is not needed in the existing code.
>>>>
>>>> hmmm. Right. It's already called once in the super class so removing
>>>> this call should have no effect. I will remove it.
>>>>
>>>> -- daniel
>>>>
>>>>>
>>>>> Mandy
>>>>>
>>>>
>>
More information about the core-libs-dev
mailing list