Question about blocking queues and continuations

oliver at objectscape.org oliver at objectscape.org
Wed Jul 29 08:04:07 UTC 2020


Hello, 

I have a question concerning blocking takes from BlockingQueue and
continuations. Let's say there is a method executed by a virtual thread
that does blocking takes in a row from two queues: 

int queuesSum(LinkedBlockingQueue<Integer> queueOne,
LinkedBlockingQueue<Integer> queueTwo) {       
  var numOne = queueOne.take(); // 1
  var numTwo = queueTwo.take(); // 2
  return numOne + numTwo;
} 

Now, let's assume in the code snippet above queueOne contains an element
while queueTwo doesn't. So the virtual thread is blocked at queueTwo
(line 2) until some element is added to it. 

I had a short look at Go and from what I understand in the same
situation as in the code above the Go scheduler has the option to
withdraw the goroutine from queueSum (you know, in Go a goroutine is
conceptually what a virtual thread is on Loom). 

Let's say all threads the Go scheduler has available to serve goroutines
are stuck on a blocking takes on channels (a channel in Go is what a
BlockingQueue is in Java). In that situation the Go scheduler could
withdraw the goroutine from queueTwo.take() and assign it to some take
on a channel that is not empty. At least, that is my understand of how
things work in Go. Once some item is added to queueTwo the Go scheduler
would assign a goroutine again to queuesSum where it would jump to line
2. Continuations in Go would make sure that the goroutine would not jump
to line 1 and it would make sure that the value stored in numOne from a
previous method call to queuesSum is remembered. 

Problem is now that the Java scheduler that schedules virtual threads in
Loom has no access to queueTwo as LinkedBlockingQueue is a class in the
class library. LinkedBlockingQueue is not a language built-in type as
channels in Go. So the Loom Java scheduler has no way to ask queueTwo
whether it is empty and therefore there is also no possibility to
withdraw the virtual thread from line 2 and assign it to some other
blocking channel as in Go. 

Given this situation the Java Loom scheduler could run out of threads
that serve all virtual threads if queuesSum is called often enough with
queueTwo every time being empty. As a result the application would run
out of virtual threads. 

So far what I have written is how things work from my understanding I
have obtained so far. I'm not 100% whether everything is correct. That
is also why I'm asking my question here. 

If everything I have made up so far is correct I could work around this
problem using ContinuationScope and Continuation:

ContinuationScope scope = new ContinuationScope("queueTwo");
var numTwo = queueTwo.peek(); // peek() is not blocking
if(numTwo == null) {
    Continuation a = new Continuation(scope, () -> {
        Continuation.yield(scope);
        numTwo = queueTwo.take(); // blocking take now as we knew that
queueTwo were no longer empty at this place
    });
} 

The code above is dummy code to show the intention. I believe it would
not work like that. 

My question is whether I have to do things this way myself with
ContinuationScope and Continuation or whether Loom would insert that
continuation itself transparently. 

In some presentation of Ron Pressler on Youtube he says that the people
in the project Loom might decide to lock ContinuationScope and
Continuation away from the user. It is in question whether those things
with ContinuationScope and Continuation should happen transparently
behind the scenes and the user should not have to know about it. 

My other question whether some decision has been made on this whether
ContinuationScope and Continuation will be inaccessible for the user or
not. 

Thanks a lot, Oliver


More information about the loom-dev mailing list