To restate the title, I'm wondering if there is a way to convert the MyThread
class below to a Kotlin Coroutine.
If you look closely, you will notice that the MyThread
class has a property variable called someObject
that can be modified from inside the both the run
and the cancel
methods. In this case SomeObject
is completely encapsulated inside MyThread
and I want to keep it that way. Is there a way to convert MyThread
to a coroutine or do I already have the most elegant version of the code?
class MyCancellable: Thread(){
val someObject= SomeObject()
override fun run() {
super.run()
while(someObject.keepGoing){
someObject.value
}
}
fun cancel(){
someObject.keepGoing=false
}
}
CodePudding user response:
A reusable coroutine is a suspend
function where the only parameter is CoroutineScope
, so something roughly equivalent to what you have is:
fun CoroutineScope.cancellableCounter() = withContext(Dispatchers.Default) {
val someObject = SomeObject()
while (someObject.keepRunning) {
yield()
someObject.value
}
}
The function can be called from inside another coroutine, or it can be passed to async
or launch
, such as myScope.launch(::cancellableCounter)
. The returned Job can be cancelled by calling cancel()
on it.
But as mentioned in the comments, there may be a better way to design it depending on how SomeObject is supposed to be used.
Edit: Maybe for the ServerSocket you'd need to do something like this. I haven't tested it, so not totally sure. But I don't think you want to directly call accept()
in a coroutine because it blocks for potentially a long time and does not cooperate with cancellation. So I'm suggesting you still need a dedicated thread. suspendCancellableCoroutine
can bridge this to a suspend function.
suspend fun awaitSomeSocket(): Socket = suspendCancellableCoroutine { continuation ->
val socket: ServerSocket = generateSocket()
continuation.invokeOnCancellation { socket.close() }
thread {
runCatching {
val result = socket.use(ServerSocket::accept)
continuation.resume(result)
}
}
}
CodePudding user response:
I think you want a class that can start its own coroutine? That seems like the equivalent, something like:
class MyCancellable(private val scope: CoroutineScope) {
private var job: Job? = null
val someObject = SomeObject()
fun run() {
if (job != null) return
job = scope.launch {
while(someObject.keepGoing) {
someObject.value
}
}
}
fun cancel() {
someObject.keepGoing = false
}
}
Typically you'd do job.cancel()
instead, and check isActive
in the while loop - I don't think it matters here, but it might be worth doing it "properly" (and it is technically different to someObject.keepGoing
going false for some other reason). And if you're doing that, maybe TenFour04's suggestion is better, since the only reason you need a class/object is so you can put externally visible run
and cancel
functions in it. If the coroutine just run
s anyway, and you call cancel
on the Job
it returns, it's all good!