Can continuation support finally solve the "How do I stop this thread" problem?

Archie Cobbs archie.cobbs at gmail.com
Thu Sep 1 17:15:53 UTC 2022


Thanks to Alan and Ron for your replies.

There are sort of two separate issues here, one narrow (how to reliably
stop a thread) and one larger (what is sandboxing and how best to do it).

The discussion about sandboxing is very interesting but for now I'm really
just using it as a motivating example for the more narrow question, which
is about whether and how it should be possible to stop a thread (no matter
what it's doing). There are other scenarios where one might want to ensure
a thread stops regardless of what it's doing (e.g., JShell).

So my question is simply what's the implementation distance between X and
Y? Where X = [We are now after implementing virtual threads], and Y = [The
ability to always unblock a blocked thread].

For the sake of clarity let's assume whoever wants to do this is able to
rewrite bytecode (e.g., to add checks for a "stop" flag on backward
branches) to avoid infinite loops, etc., so the only real barrier is the
thread being stuck in a blocking method call.

On Thu, Sep 1, 2022 at 4:04 AM Ron Pressler <ron.pressler at oracle.com> wrote:

> See Alan’s response re the details of interruption.
>

Thread interrupts definitely make sense as the right way to unblock a
thread.

But will that ALWAYS work? No, currently, it does not (see previous
example).

Question - what are the blocking operations that can't be interrupted, and
would it be possible to include them as well somehow? This would be one
solution.

>
> When it comes to the question of forcibly killing threads, for it to be
> generally useful, there must be limitations imposed on what the threads can
> do to data that is accessed by other threads, as an errant thread could
> otherwise harm other threads.
>

If you're talking about releasing object monitors, then I agree with you
(were you thinking of others?). I don't think a "kill -9" style solution
works. Instead, you would need to do something like this:

  1. Ensure the thread wakes up if blocked (this is what I'm asking about
currently)
  2. Trigger the throwing of ThreadDeath or similar exception to unwind the
stack

Step #2 could be via Thread.stop() or via bytecode rewriting (checks at
backward branches). Basically #1 is the only part that can't reliably be
solved today.


> The Java platform (and language) currently does not impose such
> “isolation” limitation, but some language targeting the Java platform
> could, and so it could also emit interruption checks when compiling to
> bytecode. A language that behaves in this way is Erlang (although I don’t
> know if implementations targeting Java are currently maintained), but even
> there there are pitfalls.
>

You've got me curious... What are the additional pitfalls?


> A similar path is available to jshell as well, as it can emit interruption
> checks in the bytecode.
>

Yep. Currently it just resorts to ThreadGroup.stop()... obviously, it's in
the same boat here.

---snip---

Transitioning now from the "how to stop a thread" discussion to the larger
"sandbox" discussion...


> Even if all “dangerous” APIs are blocked, any user code could allocate as
> much memory as it likes, exhausting the memory available for the entire
> process.
>

Not necessarily. Bytecode rewriting could be used to track memory
allocations with weak references used to track deallocations. This would
impose a performance penalty of course, and the accounting wouldn't be
perfectly accurate, but you could make that inaccuracy conservative so it
would work for the purposes of containment.


> If it is also allowed to spawn platform threads at will, it can also
> exhaust the CPU allocation for the entire process.
>

Not sure I understand... with bytecode rewriting (e.g., adding checks at
backward branches), you should be able to limit the total CPU utilization
of a thread or group of threads. Kind of clunky for sure but it should
work, right?


> The Java runtime could be changed to support isolated heaps in imitations
> of the isolation and memory restrictions offered by the OS, but if the goal
> is to share some internal runtime data structures for efficiency, there are
> ways to do that for multiple processes. Given that the kernel has more
> arrows in its quiver to support such isolation, including at the native
> code level, that is probably the most appropriate level to provide it, I
> think, so much so that trying to implement proper and secure isolation in
> multi-user server programs in user mode is a fool’s errand. A multi-user
> Java runtime running in *kernel* mode is a different matter altogether, but
> it is currently beyond the scope of the OpenJDK JDK.
>

OK so let me throw out a not-so hypothetical example. Oracle supports Java
stored procedures
<https://docs.oracle.com/database/121/JJDEV/chfive.htm#JJDEV13247>. I'm not
familiar with it, but is this not implemented as server-side sandboxed
code? Isn't that a valid motivating example?

The Java code may be prevented (via Java permissions) from doing any file
or network I/O, in which case the stuck thread problem gets a lot easier
(probably all blocking system calls require interaction with some file,
process, or network socket).

But I'm still curious how they stop runaway Java code or runaway memory
allocation?

-Archie

-- 
Archie L. Cobbs
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/loom-dev/attachments/20220901/bccae5fa/attachment.htm>


More information about the loom-dev mailing list