is ```GlobalScope.launch(Dispatchers.Unconfined) {...
# coroutines
c
is
Copy code
GlobalScope.launch(Dispatchers.Unconfined) {
    coroutineScope {
...
   }
}
a good way to use structured concurrency in an async handler that does not know about coroutines?
z
GlobalScope is never good, for anything. Create a scope object, use that, then cancel the scope when the owner gets disposed/shutdown.
Also you don’t need coroutineScope inside a launch, the receiver is already the launched coroutine’s scope.
👍 1
c
ok, that makes sense. I put the `coroutineScope`there because i wanted to go from the unstructured concurrency of GlobalScope to a local scope that ends when the block ends. Is there no way to have create a block that has a local scope that ends at the end of the block?
Copy code
....launch(Dispatchers.Unconfined) {
// ....do stuff


// scope should end here
}
so in shortened code:
Copy code
val requestScope = CoroutineScope(Dispatchers.Unconfined)
    exchange.addExchangeCompleteListener {             requestScope.cancel()
    }
    requestScope.launch {
....
        }
    })
}
z
Is there no way to have create a block that has a local scope that ends at the end of the block?
All the standard coroutine builders (
launch
,
async
,
runBlocking
, etc) already do that. Remember,
CoroutineScope
is just syntactic sugar around a
CoroutineContext
, which is just a map, and structured concurrency is implemented via the
Job
stored in the context. Whenever you create a new coroutine, it gets its own
Job
, and that
Job
is stored in the context wrapped by the scope that is the receiver to the block passed to them.
The problem with that solution is you’re still potentially leaking coroutines - you’re creating your own scope, but you’re never cancelling it. In the network request case, it looks like your HttpHandlers, or something above them, should define the scope and pass it down, and then be responsible for cancelling it whenever they are disposed.
1
c
I'm canceling it when the exchange finishes => at the end of the http response
Copy code
exchange.addExchangeCompleteListener {             requestScope.cancel()
    }
"All the standard coroutine builders (
launch
async
runBlocking
, etc) already do that" <= even when invoked on global scope?
z
Copy code
parentScope.launch { childScope = this }
childScope.coroutineContext.job
will always be a child of
parentScope.coroutineContext.job
, even if
parentScope
happens to be
GlobalScope
. This is what makes structured concurrency work. (Technically,
GlobalScope
doesn’t actually have a Job I believe, in which case
childScope.coroutineContext.job
is actually a root job, but that is effectively the same as if
GlobalScope
did have a job that was just never cancelled)
c
Ah you are Right that makes sense.
But in the end it seems to be the right thing to manually create a scope and cancel it when the http response is finished.
z
You don’t need to cancel it if it completes normally. The reason for cancelling it is if the thing that did the request is going away and will never use the response.
c
Is it bad to always cancel it or is it just a no op when it’s already finished?
l
no-op
☝️ 1
z
Not only is it not bad, it’s a best practice to always cancel.
c
cool. thanks guys!