river-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Peter Firmstone <j...@zeus.net.au>
Subject Re: QA Test debugging
Date Tue, 26 Feb 2013 12:02:07 GMT
On 26/02/2013 9:05 PM, Dan Creswell wrote:
> On 26 February 2013 10:30, Peter Firmstone<jini@zeus.net.au>  wrote:
>> Thanks Dan&  Gregg,
>>
>> I've treated Permission object's as immutable, but that's clearly a poor
>> assumption, despite the Permission javadoc suggesting implementations should
>> be immutable, just shows you can't be too careful with other people's code.
>>
>> To make an object effectively immutable (without final fields), it must be
>> safely published after construction, which means writing to a concurrent
>> collection, queue, final field, volatile field or a field protected by
>> synchronization for the hand over from one thread to the next, it cannot be
>> modified after publication either.
>>
>> A truely immutable object has final fields, safe construction and can't be
>> modified, it's safely published after the constructor returns, so is more
>> robust as Gregg mentioned.
> Mmm observation, there are two immutables:
>
> (1) Immutable as perceived by the user of the object.
> (2) Immutable internal implementation.
>
>> In this case, I've broken this rule, I've added SocketPermission to an
>> unsynchronized collection (protected from external modification) and that
>> collection is published to a final field in a constructor.
> Final of course meaning nothing other than "value won't change" - a
> compile/language level guarantee.
>
> It doesn't interact in any meaningful way with the JVM concurrency
> mechanics. i.e. it doesn't have any relationship to a synchronization
> action (or barrier as I prefer). Thus final provides no multi-thread
> guarantee explicitly. What one can say is that once the value becomes
> visible to a set of threads it won't change.
>
>> Now this is where things get a little uncertain, this final field only
>> applies to the safe publication of the Collection, the Permission objects it
>> contains are not referenced by final fields nor synchronized so they're not
>> safely published, even though Permission objects are written to this
>> collection prior to the final field being initialised.  (At least that's
>> what I gather from reading Concurrency in practise).
>>
> I agree with that assessment.
>
>> So I'm going to have to take a minor performance hit.
>>
>> There are two solutions:
>>
>>    1. Use a concurrent collection instead (ConcurrentSkipListSet, which
>>       is a ConcurrentSkipListMap discuised as a Set)
>>    2. Wrap each Permission in an Object and safely publish the
>>       Permission to a final field, then add the wrapper to the
>>       collection, the other thread then accesses the final field
>>       ensuring safe publication.  The collection is still an
>>       unsynchronized collection, but now contains immutable objects and
>>       hasn't been changed since it was published.
>>
>> Option 2 requires the creation of more temporary objects, but should still
>> avoid cache misses, while option 1 uses volatile variables to safely publish
>> the Permission's.  Since this collection isn't modified after construction
>> volatile reads will be almost as cheap as nonvolatile reads and I can use
>> the existing PermissionComparator.  I think I'll go with option 1.
>>
> +1 for option 1.
>
>> If you look at any security debugging output, remembering that many
>> duplicate checks are eliminated by CombinerSecurityManager, the security
>> architecture in a network environment gets absolutely hammered.
>>
> As an aside, do you know what the typical overall cost on CPU this
> represents? Or another dimension: What additional latency might this
> add to your "average" (loose, I know) method invocation?
>
Firstly pretend you've got a standard SecurityManager and Sun's PolicyFile.

It really depends on the permission being checked, if it's 
AllPermission, it's going to be very fast, but if it's a blocking 
Permission, like SocketPermission and there are 10 ProtectionDomain's on 
the call stack, (one for each jar file) and some of those 
ProtectionDomain's are granted more than one SocketPermission, (most 
admins have probably learned to keep this simple, but the proxy server 
origin has already been granted at ClassLoader & ProtectionDomain 
creation time, and the order's never how you want it, you might be 
waiting minutes for a DNS lookup for two dns hostnames, just to see if 
they've got identical IP addresses and that's just for one PD.  So 
you're now blocking other security checks for that ProtectionDomain too, 
no AccessControlContext containing that PD is able to proceed until 
SocketPermission completes on that ProtectionDomain, even if they're 
trying to check other unrelated permissions.

To make matters worse, if the Policy fails, the ProtectionDomain is 
checked as well, so the same SocketPermission can be checked twice, 
although at least i won't lookup dns again.

To build your cache Sun's PolicyFile, uses CodeSource.implies, which 
also blocks on dns, because it uses URL.

Once it enters the dns cache you've still got contention for that (which 
is why we've included dnsjava).

With CombinerSecurityManager and ConcurrentPolicyFile, your 
SocketPermission's will be ordered dynamically for every permission 
check, so if you have a wildcard as well as dns names, the wild card 
will be checked first, unless you have an exact match, in which case 
SocketPermission.implies won't even be called.

If you've got 10 ProtectionDomain's on the stack, and the 
AccessControlContext doesn't already exist in the cache, a number of 
Runnable tasks are created and submitted to an Executor to perform 
checks in Parallel on each ProtectionDomain in that 
AccessControlContext.  Some of these will return immediately, while 
those that block will probably be suspended by the OS allowing other non 
blocking threads to continue.  DNS lookups will proceed in parallel 
instead of series when required, allowing other permission checks to 
avoid blocking.

A big advantage of a non blocking policy and security manager is 
deadlock elimination.

If you have multiple threads with identical AccessControlContext's 
calling SecurityMangager.checkPermission, then after a permission has 
been checked by the policy it's added to the SecurityManager's non 
blocking cache until the policy is refreshed.   So other threads making 
the same call will perform a very fast hash lookup for their 
AccessControlContext, followed by a PermissionComparator based lookup on 
all the Permissions checked for that AccessControlContext.  When 
AccessControlContext's are due to be garbage collected, they're removed 
from the cache by a background thread, Permission's that aren't used for 
5 to 10 minutes are removed also by the same background thread.

When there are only a few ProtectionDomain's in an AccessControlContext, 
checks are executed in series.  If there are no available Executor 
threads, the permission check is performed by the calling thread (to 
avoid deadlock).

CodeSource.implies is not utilised by ConcurrentPolicyFile, instead, 
CodeSource URL's are converted to URI, normalised and compared, this 
avoids dns lookups and disk access so is much faster.

So the bigger the system, the better it will perform, because the 
likelyhood of blocking increases.

This is an old although interesting paper on SecurityManager 
performance: http://rewerse.eu/publications/download/REWERSE-RP-2005-141.pdf

Creating the AccessControlContext from stack inspection is about half 
the cost.

Startup cost for ConcurrentPolicyFile and CombinerSecurityManager is 
greater.

I'd be interested to see what the results are like in a real djinn.

Cheers,

Peter.

Cheers,

Peter.

Mime
View raw message