Using native coroutines in Java

Many modern languages ​​support working with coroutines at the language level. Java does not currently support coroutines, but there is hope that things can change in the future.



In C ++ 20, it is planned to introduce support for working with coroutines.

Using JNI we can write coroutines in C ++ and use in Java code.



Let's consider what native coroutines can be written and how to use them in Java code.



The generator allows you to create a sequence of values ​​of a certain type, while the values ​​are generated lazily and synchronously.



/* C++ code */ generator<int> generate(int count) { for (int i = 0; i < count; i++) { co_yield i; } }
      
      





  /* Java code */ Generator<Integer> gen1 = Coroutine.yield(5); Generator<Float> gen2 = Coroutine.yield(1f, 5); Generator<Double> gen3 = Coroutine.yield(v -> v * 2, 1d, 5); for (int item : gen1) { System.out.println("yield value: " + item); }
      
      





Asynchronous Generator allows you to create a sequence of values ​​of a certain type, while the values ​​are generated lazily and asynchronously.



  /* C++ code */ async_generator<int> generate(int count) { for (int i = 0; i < count; i++) { co_await 1s; co_yield i; } }
      
      





  /* Java code */ Generator<Integer> gen1 = Coroutine.yieldAsync(5); Generator<Float> gen2 = Coroutine.yieldAsync(1f, 5); Generator<Double> gen3 = Coroutine.yieldAsync(v -> v * 2, 1d, 5); for (int item : gen1) { System.out.println("yield value: " + item); }
      
      





Task performs asynchronous calculation, which is performed lazily, while coroutine is not executed until the task starts explicitly.



Coroutines can be used as light flows. In this case, the number of running threads in the system can be limited, for example, no more than 1000. And coroutine can be run as many as you like.



When starting a coroutine, it is checked whether it is ready. If not, then coroutine is paused and the OS passes the handler to it. At this point, a useful piece of code is executed. When coroutine is ready, then renewal of coroutine is performed.



  /* C++ code */ struct awaiter { bool await_ready() const { return false; } void await_resume() {} void await_suspend(std::coroutine_handle<> handler) { /* invoke java/jni code */ if (!handler.done()) { handler.resume(); } } }; co_await awaiter{};
      
      





As when starting a thread, coroutine can be passed Runnable or Callable.



  /* Java code */ Coroutine.await(() -> { int sum = 5 + 10; }); Task<Integer> task = Coroutine.await(() -> { int sum = 5 + 10; return sum; });
      
      





The timer pauses the current task for the required duration.



  auto operator co_await(const std::chrono::system_clock::duration& duration) { return timer{duration}; } co_await 10ms;
      
      





Can be used as a replacement for Thread.sleep ().



  Coroutine.await(10, TimeUnit.MILLISECONDS);
      
      





Coroutines can also be used to write non-blocking code for working with the file system, network, etc.



As you can see, coroutines make writing asynchronous code easier, allowing parts of the code to be executed without blocking the stream.



Coroutines that they plan to make in C ++ 20 will appear as pure language features.

Generators, tasks, and other coroutines are planned to be added to the C ++ 23 standard or later.

You can write your own coroutines yourself or use a ready-made library, for example cppcoro .



Compilers MVSC, Clang already support coroutines as an extension, and GCC is only at the development stage.



The full source code can be viewed on github: code



All Articles