Reviewing my policy provider code, I found a HashSet<Permission> being used to 
cache permissions before adding to an already published PermissionCollection.  
This was done to batch add permissions to that PermissionCollection so the jvm 
would optimise the write lock outside of the Iterator loop.

The set was used to avoid duplicate Permission objects.

Well it performed like chunky custard at a posh restraunt: not good.  

Due of course to the previously mentioned poor hashCode and equals 
implementations some permissions have. 

I could have used the PermissionComparator and a TreeSet for much better 
performance, however I discovered I could eliminate the Set entirely by not 
mutating published PermissionCollection's and replacing them on write instead.

This significantly improved the policy's single thread performance, I'll 
revisit the previously failling event tests that required increase delays, to 
see if I can't match or better sun's policy provider.  It certainly seems as 
fast now, based on test times.

DelegateCombinerSecurityManager has some neat performance optimisations that 
help both single threaded performance and scalability.

Permission checks are only performed once per AccessControlContext, results are 
cached, so the same permission isn't checked twice (unless cleared by a policy 
refresh). AccessControlContext has good hashcode and equals implementations. 
While the cache performes well under concurrent access, a PermissionComparator 
is used in a TreeSet that utilises soft references to avoid invoking Permission 
hashCode or equals.  JVM's TreeSet implementation uses a backing TreeMap which 
was replaced in 2009 with Harmony's red black tree implementation, modified 
further by Sun for optimum compatibility.  The TreeSet is wrapped with a multi 
read single write collection wrapper, then wrapped again by a soft reference 
collection wrapper.

A DomainCombiner is used to create a context (cached for reuse) to execute 
permission checks on ProtectionDomains in parallel. If the the number of 
domains in a context exceed 3, they're broken into separate tasks and submitted 
to an executor, the first protection domain to fail causes the other tasks to 
be cancelled, returning the result to the caller.  Contexts with 3 or less 
domains are typically priviledged or mostly priviledged and return quickly 
without needing to be divided.

The executor is tuned to the number of cpu cores and load, I'm thinking about 
providing a Property or two so administrators can fine tune it to their 
environment.

If the executor reaches its thread limit, tasks are executed by the caller 
instead, using SynchronousBlockingQueue, which has a queue size of zero and 
uses a lock free algorithm to hand tasks off to executor threads.  Since there 
may be many domains in a context, if load reduces on the executor, and more 
threads become available, the tasks will be divided among threads and execute 
in parallel.

Over time the cache will grow and reduce the executors load and in doing so 
reduce the number of writes to the cache, which will eventually become mostly 
read, for very good concurrency.  I can imagine that hotspot will also 
contribute.

The new SecurityManager and policy providers should remove any concerns about 
security impacting performance.

I haven't done anything about reimplementing SocketPermission at this stage, 
any help would of course be appreciated.

Cheers,

Peter.

Reply via email to