Having had the opportunity to use ScopedValue to replace ThreadLocal, it was suggested I might provide some feedback here.

Background: I'm maintaining an OpenJDK fork that retains SM, I merge weekly from upstream, this isn't for untrusted code, it's just ensuring permissions are only granted to authenticated users using trusted code (checked using a SHA256 message digest, or CodeSigner).  I have good fortune; I'm not constrained by strict backward compatibility, nor am I attempting to create a Sandbox (Graal would be a better place to quarantine untrusted code). Permission is no longer Serializable, allowing obsolete serial form to be dropped and fields made final.

AccessControlContext is immutable now too, this was necessary, as I'm replacing AccessController functionality using ScopedValue to decorate doPrivileged calls, with AccessControlContext and the caller Class, and StackWalker is used to determine the stack context.

AccessController is initialized very early during the vm initialization phase, so initially it just runs privileged calls and returns privileged context, until init phase 3 is reached. ScopedValue, Stackwalker and other classes depend on by them are initialized prior to init phase 3 from a static method in threads.cpp.

The second use of ScopedValue is to prevent StackOverflowError occuring in a SecurityManager implementation, called CombinerSecurityManager, so called because it uses a DomainCombiner to execute permission checks on all ProtectionDomain's in a stack concurrently, but more importantly it contains a non-blocking timed and weakly reference cache, that uses Doug Lee's ConcurrentSkipListSet to cache the results of permission checks for AccessControlContext, in a ConcurrentMap. In a system running many tasks with the same context, this makes a big performance improvement, by avoiding repeat permission checks.   Previously StackOverflowError was prevented using a ThreadLocal variable, with a counter that was incremented each time recursion occurs, in try - finally blocks, to ensure that it was decremented each time any recursion completed, once the counter reached a limit, an exception was thrown.   However ScopedValue is a much better fit since it uses recursion, it doesn't require any try - finally blocks.

CombinerSecurityManager is also using virtual threads to perform permission checks on each ProtectionDomain, to avoid blocking on platform threads when SocketPermission, FilePermission, are inevitably checked.   There are also a non-caching Policy provider and policy parser implementations, that parse and stores policy grants in an immutable form following safe publication, it uses thread isolation to prevent contention on PermissionCollection instances.  There is a policy writing tool, that generates polp policy files.

ProtectionDomain now implements equals and hashcode methods, the hashcode is final and calculated during construction.  The reason for this is many ProtectionDomain instances are created for a Subject's Principal[]'s and caching their result improves performance.  Similarly AccessControlContext also now has a final hashcode calculated during construction.

I've also added support for privileged context to virtual threads, to allow virtual threads to be used with privileges, just like platform threads.

There are two test failures which occur due to the new AccessController functionality, with both platform threads and virtual threads.

I looked into the values of the Thread's inherited and Snapshot's Carrier, neither contain the ScopedValue used by AccessController.   It may have something to do with earlier class initialization, I'm still trying to determine the cause.

|ThreadFlockTest::testThreadExitWithOpenFlock '[1] java.lang.ThreadBuilders$VirtualThreadFactory@21edf8f8' java.lang.Exception: Stack trace at java.base/java.lang.Thread.dumpStack(Thread.java:2155) at ThreadFlockTest.lambda$testThreadExitWithOpenFlock$0(ThreadFlockTest.java:1018) at java.base/java.lang.VirtualThread.run(VirtualThread.java:470) Exception in thread "" java.util.concurrent.StructureViolationException: Scoped value bindings have changed at java.base/java.lang.Thread.inheritScopedValueBindings(Thread.java:324)|

--
Regards,
Peter

On 22/01/2025 8:39 pm, Volkan Yazıcı wrote:
Hello Peter,

Thanks so much for the feedback. AFAIK, that work is delivered by the Loom crew and they use the `loom-dev` mailing list <https://mail.openjdk.org/mailman/listinfo/loom-dev> for discussions. I think they would really appreciate hearing your feedback: What is the real-world use case you used SVs for? How was that particular logic implemented before? What are the performance/code/semantic changes you observed during migration? Did you encounter any problems? Did you find the API intuitive? etc.

Kind regards.

On Wed, Jan 22, 2025 at 4:11 AM Peter Firmstone <peter.firmst...@zeus.net.au> wrote:

    Just wanted to say, I've been experimenting with replacing
    ThreadLocal
    with ScopedValue, this is a great new API, I love the way ScopedValue
    uses recursion, we can have multiple immutable instances
    representing a
    scoped value for a short period and we're not worried about managing
    state as it goes out of scope as soon as it's no longer in use.

    Cheers,

    Peter.

Reply via email to