Max Gierlachowski
Max Gierlachowski

Reputation: 446

Valid way to reuse coroutine scope?

So I have a class that has a lifecycle, containing a start() and an stop() function to start and stop the lifecycle. Now in that start() function I have to start a coroutine and in stop() I have to cancel it (The coroutine in start() runs infinitely until it is canceled, kinda like a socket listening for messages).

Because I would like a nice syntax to handle Coroutines I would like this class to implement CoroutineScope. The thing is that start() and stop() can be called multiple times.

My question is if this implementation is valid and does not contain any errors. The purpose is to make a reusable CoroutineScope.

class ReusableCoroutines() : CoroutineScope {

    private var job = Job()

    override val coroutineContext: CoroutineContext
        get() = job

    fun start() {
        job = Job()
        launch {
            println("Started and running!")
            while(true) {

            }
        }
    }

    fun stop() {
        job.cancel()
        println("Stoped")
    }

}

Upvotes: 0

Views: 1891

Answers (1)

Adam Arold
Adam Arold

Reputation: 30578

Disclaimer: I wouldn't do this. You don't get much out of reusable objects unless you are creating them by the thousands every seconds. I'd just create throwaway classes that are initialized on creation (without a start function) and then cancelled when stop is called. That being said this might work:

class ReusableCoroutines(
        private val scope: CoroutineScope = CoroutineScope(Dispatchers.Default)
) : CoroutineScope by scope {

    private lateinit var job: Job
    private var stopped: Boolean = false
    private var started: Boolean = false

    @Synchronized
    fun start() {
        require(stopped.not()) {
            "Already stopped!"
        }
        require(started.not()) {
            "Already started!"
        }
        job = launch {
            println("Started and running!")
            while (true) {

            }
        }
    }

    @Synchronized
    fun stop() {
        require(stopped.not()) {
            "Already stopped!"
        }
        stopped = true
        cancel()
        println("Stopped")
    }

}

So you need some synchronization @Synchronized to prevent start being called from multiple threads and ending up in an inconsistent state.

You also take the scope as a parameter so you can get Structured Concurrency with this. You can also add SupervisorJob to scope to prevent the parent from being canceled when you cancel this scope.

Upvotes: 1

Related Questions