[concurrency-interest] ITL and revised ThreadContext

Dawid Kurzyniec dawidk@mathcs.emory.edu
Mon, 5 Jan 2004 22:27:06 -0500


Here is the problem that I have in my application. 

I have an asynchronous RMI library. When the remote async call is
initiated by the client, the invocation request is wrapped into a
FutureTask, and then passed on to an executor. Hence, the actual call
occurs in a thread different than the initiator thread. 

The library allows user to provide custom transport protocol layer, via
custom RMI socket factories known from standard RMI. I want to use this
mechanism to enable SSL connections to a secure service which requires
clients to authenticate themselves. Client application may have multiple
threads of execution, each of them has its own X.509 identity (stored in
a thread-local) and makes calls to the secure service.

To achieve that, I defined SSLSocketFactory which creates SSL sockets
using X.509 identity fetched from a thread-local during factory
construction. However, here comes the problem. The common case in RMI is
that remote method called on a remote stub returns another remote stub
as a result. When we make such a call, the resulting stub (and factory
which sits inside it) is constructed by the thread which is processing
the call in question. In case of an async call, it is a thread supplied
by the executor, and it does not have proper X.509 ID set. It leads to
"UnmarshallException: cannot determine client ID.".

The missing link is propagation of a thread local from call-initiating
thread to the executor thread. Usually, I was able to workaround such
issues using specific knowledge about the thread-local that needed to be
propagated. But here, the RMI library has no knowledge of specific
socket factories, hence it has no idea which thread-locals they depend
on.


Given the above example, and unless somebody can offer a reasonable
workaround, I would like to propose a revised (weakened) version of the
ThreadContext idea. The weakening here is that only the inheritable
thread locals are propagated:

/**
 * Represents the snapshot of a inheritable state of a thread. 
 * The inheritable state consists of inherited thread local values, 
 * context class loader, and priority.
 */
public class ThreadContext {

    /**
     * Returns the snapshot of the inheritable stat of the current
thread.
     * Note that this method does not have race condition problems as it

     * accesses only the *current* thread.
     */
    public static ThreadContext getContext() { ... }

    /**
     * Performs a given task within this context, as if by a new thread
created
     * by the original thread. (The "as if" means "with context class
loader,
     * priority, and inherited thread locals of".)
     */
    public void perform(final Runnable task) { ... }
}

Please note that the properties delegated by the ThreadContext are
subset of those which newly created threads inherit from their parent
threads anyway.

This solution does not suffer from race conditions, and it does not
propagate more data than normal thread creation, so, for Heaven's sake,
please tell me what's possibly wrong with it before you reject it :)

And in response to earlier comments:

Tim Peierls:
> The implementation of perform(Runnable) copies ThreadLocalMaps, whose 
> size is unpredictable, so this is not necessarily a lightweight call. 

I seems to me that application which has very large number of
thread-local variables (rather than few variables pointing to large data
structures) is ill-conceived and will probably suffer from more serious
bottlenecks (e.g. lookup costs). Also, the perform(Runnable) is more
lightweight than a thread construction which, too, copies inheritable
thread locals.

Tim Peierls:
> apparently the temporary adoption of another thread's ThreadLocals
raises 
> thorny issues. Josh Bloch's brief summary was that "it causes more
problems 
> than it solves."

If Josh Bloch says so, he is probably right (what does he think about
the weakened version, anyway?) but since it would definitely solve MY
problems, I insist on being explained where the thorns are :)

Seriously, I could see problems with my original proposal: it would
break the assertion that an object stored in a thread local is not
accessible to other threads unless the reference is explicitly leaked
out; this assertion blessed "synchronized-less" access. But there never
was such assertion for inheritable thread locals, so the revised
proposal is not prone to this.

Doug Lea:
> For example, a one-time ITL "capture" doesn't address requests to 
> save/restore across tasks, which can otherwise only be done one-by-one

> inside ThreadPoolExecutor before/after methods.

I am not fully familiar with the issue, but if I understand correctly,
it is about tasks or executors which maintain "thread local state"
between calls. If so, why can't the task/executor create the thread
snapshot on task completion, then perform the next task within that
snapshot, then remember the new snapshot on completion, and so on, thus
passing the state from call to call as a ThreadContext?...

Regards,
Dawid