Blocking Operations Within Script
Script runs on a single thread, hence special care needs to be taken while performing blocking (CPU/IO) operations withing the script.
This section explains following two types of blocking operations and patterns/recommendations to be followed while performing those.
- CPU Bound
- IO Bound
Calling CPU intensive or IO operations from the main script is dangerous and should be avoided at all cost.
Breaking this rule will cause all the background tasks started in script to halt and unexpected deadlocks.
Main sequencer script, and the techniques mentioned here for performing blocking tasks executes on different threads.
Hence, accessing/updating mutable state defined in script from these blocking functions is not thread safe.
CPU bound
For any CPU bound operations follow these steps:
- Create a new function and mark that with
suspend
keyword - Wrap function body inside
withContext(Dispatchers.Default)
Following example demonstrate writing CPU bound operation, In this example BigInteger.probablePrime(4096, Random())
is CPU bound and takes more than few seconds to finish.
- Kotlin
-
// Calculating probablePrime is cpu bound operation and should be wrapped inside Default dispatcher // Following function takes around 10 seconds to find a 4096 bit length prime number suspend fun findBigPrime(): BigInteger = withContext(Dispatchers.Default) { BigInteger.probablePrime(4096, Random()) }
Following shows, usage of the above compute heavy function in main sequencer script
- Kotlin
-
script { loopAsync(100.milliseconds) { // loop represents the computation running on the main script thread. } onSetup("prime number") { // by default calling findBigPrime cpu intensive task suspends and waits for result // but this runs on different thread than the main script thread // which allows other background tasks started previously to run concurrenlty val bigPrime1: BigInteger = findBigPrime() // if you want to run findBigPrime in the background, then wrap it within async val bigPrimeDeferred: Deferred<BigInteger> = async { findBigPrime() } // ... // wait for compute intensive operation to finish which was previously started val bigPrime2: BigInteger = bigPrimeDeferred.await() // script continues... } }
IO bound
For any IO bound operations follow these steps:
- Create a new function and mark that with
suspend
keyword - Wrap function body inside
withContext(Dispatchers.IO)
Following example demonstrate writing IO bound operation, In this example BufferredReader.readLine()
is IO bound and takes more than few seconds to finish.
- Kotlin
-
// Reading a line from a file is blocking IO operation and should be wrapped inside IO dispatcher suspend fun BufferedReader.readMessage(): CharSequence? = withContext(Dispatchers.IO) { readLine() }
Following shows, usage of the above io heavy function in main sequencer script
- Kotlin
-
script { loopAsync(100.milliseconds) { // loop represents the computation running on the main script thread. } onSetup("read file") { val reader = File("someFile.txt").bufferedReader() // by default calling readMessage (blocking io) task suspends and waits for result // but this runs on different thread than the main script thread // which allows other background tasks started previously to run concurrenlty val message1 = reader.readMessage() // if you want to run readMessage in the background, then wrap it within async val message2Deferred = async { reader.readMessage() } // ... // wait for blocking operation to finish which was previously started val message2: CharSequence? = message2Deferred.await() // script continues... } }
How does it work behind the scenes?
withContext(Dispatchers.Default)
or withContext(Dispatchers.IO)
construct calls specified function on a provided dispatcher which is different from the main script.
Default
or IO
dispatchers maintains separate thread pool than the main sequencer script which usage single thread. This means, accessing/updating mutable variables defined in sequencer script is not thread safe from these functions and should be avoided.
You can read more about these patterns of blocking here