CompletableFuture and CoroutineScope.future

Can someone tell me if this code will block and why? Is there an alternative to using runBlocking? I tried using coroutineScope but it needs to be within either another coroutine or a suspend function. I cannot add suspend to the get function because its an override

override fun get(environment: DataFetchingEnvironment?): CompletableFuture<Task?> {
return runBlocking {
  return@runBlocking future {
      val title = environment?.getArgument("title") ?: ""
      val desc = environment?.getArgument<String?>("desc")
      EntityData.addTask(title, desc)
  }
}

}

Yes, runBlocking will wait for the new coroutine to finish before it returns. So if one of those things you’re calling blocks, then it will too.

If you really don’t have a better enclosing scope to use (like something tied to the lifetime of the DataFetchingEnvironment, then using GlobalScope.future will do what you want. You may have to be careful to ensure that the coroutine completes at some point, though, and appreciate that it will run on a different thread.

EDIT: Now that I look back at this, CompletableFuture<Task?> is a strange return type. Are you sure that is what you want? Normally you would make a future that returns the result of the task.

1 Like

Thank you. I am using graphql-java to create a DataFetcher<CompleteableFuture<Task?>> so I don’t block the vertx event loop. Is there a better way?

val addTaskFetcher = object: DataFetcher<CompletableFuture<Task?>> {
  override fun get(environment: DataFetchingEnvironment?): CompletableFuture<Task?> {
    return GlobalScope.future {
      val title = environment?.getArgument("title") ?: ""
      val desc = environment?.getArgument<String?>("desc")
      EntityData.addTask(title, desc)
    }
  }

}

OIC. Task is a kind of entity and not something like FutureTask that needs to be executed to produce the result of the request, so this is OK.

1 Like

Thanks still wrapping my head around coroutines. One more thing in the ide I see this comment for runBlocking. First what does interruptibley mean? That another caller can take time on the same thread? The explanation references an event loop. Is this internally how all coroutines work or just the runBlocking? Why use an eventloop when it also seems to use one or more threads?

“interruptibly” means the the thread can be interrupted by Thread.interrupt while it’s waiting. In this case all the coroutines in the scope will be cancelled and runBlocking will throw an exception.

The event loop is just for runBlocking. Unless a different dispatcher is specified, all coroutines created inside the runBlocking will run in the event loop and no other threads will be used.

1 Like

Appreciate your help thank you

One more question is there a way to execute an asynchronous call at a top level without using GlobalScope? If not how to handle exceptions, errors, hangs and infinite loops when using the GlobalScope?

Using GlobalScope is probably best in your case, since you’re extending graphql-java, and it’s equivalent to the java way. If an exception is thrown by the asynchronous block, the CompletableFuture will be completed unsuccessfully, and the exception will be wrapped in an ExecutionException and rethrown in Future.get.

The way I would usually do it in a web server application, though, is to execute each entire request inside one runBlocking, and remember the scope for launching coroutines in the context of that request. That is to avoid multithreading within each request, which makes the rest of the code a lot easier to write.

Beware, however, that if you’re not using GlobalScope, then you can be surprised when failures in those async coroutines end up cancelling their parent: https://github.com/Kotlin/kotlinx.coroutines/issues/763#issue-374059542

Brilliant thanks