https://kotlinlang.org logo
Title
u

ursus

09/30/2019, 8:03 PM
What would be the easiest way to limit parallelism on IO dispatcher? I.e. without creating a separate Executor thread pool, reusing the IO threads, but imposing a logical limit in a given function?
s

Sergey Bondari

09/30/2019, 8:31 PM
Limiting the size of your thread pool will not help you much
You probably want to limit number of concurrent connections, number of coroutines
I’d use a semaphore
u

ursus

09/30/2019, 8:33 PM
Why limiting threadpool woudlnt help?
s

Sergey Bondari

09/30/2019, 8:34 PM
Coroutines are using your thread pool when they are not suspended (running).
You can have much more concurrently opened connections that are waiting for response, than you have threads, because coroutines are suspended while waiting for network
u

ursus

09/30/2019, 8:35 PM
Well yes, thats kind of what I want, concretely - limit number of parallel downloads
s

Sergey Bondari

09/30/2019, 8:35 PM
I figured
semaphore is the easiest to me, I’ve seen a suspending semaphore implementation in io.ktor.util.cio.Semaphore
dunno what can be more simple than a semphore
u

ursus

09/30/2019, 8:37 PM
I dont see what you mean. You're saying Dispatcher from a custom fixed thread pool executor would not work?
I thought it would always work but downside would be those threads cant be used for anything else but downloads
hence my attempt to impose this logically on top of IO
-- so youre saying, this should not be a dispatcher thing, bu a manual semaphore which would suspend a courinte while semaphore is turned on?
d

Dominaezzz

09/30/2019, 8:41 PM
There's a new
Semaphore
class.
You can also limit concurrency with channels.
s

Sergey Bondari

09/30/2019, 8:41 PM
you create a semaphore with initial value N (number of concurrent thread). SemDown suspends your Coroutine if more than N coroutines are in critical section.
Producer / Consumer with channels is also an option, just not as simple as a semaphore
Create N consumers (downloading coroutines) and feed them via a channel. your producer will suspend if more than N downloads are in progress
Stop thinking threads, this is not your concurrent unit of work anymore 🙂
☝️🏼 3
u

ursus

09/30/2019, 8:48 PM
true, but this all sounds like a dispatcher to me? i.e. number of concurrent ops / unbounded should be a a dispatcher responsibility, rather than semaphores mixed with business logic?
or maybe custom Dispatcher, wrapping IO and applying semaphores there?
g

gildor

09/30/2019, 10:09 PM
You also can limit global parallelism, set max amount of threads on IO dispatcher using system variables
number of concurrent operations
You cannot limit it with dispatcher, just because it's not how Coroutines work. I think to answer properly on your question you should give more details of what exactly you want to limit and what is your use case
u

ursus

09/30/2019, 10:24 PM
@gildor hmm what do you mean not how they work? afaik dispatcher dictates how many parallel coroutines can execute truly, i.e. always equal to the number of threads in the dispatcher
g

gildor

09/30/2019, 10:24 PM
No, dispatcher limits how many threads dispatch coroutine execution
u

ursus

09/30/2019, 10:25 PM
I think were talking about the definition of "coroutine is executing"
I mean unsuspended
g

gildor

09/30/2019, 10:25 PM
But you still can start so many coroutines as you want, launch 1kk coroutines that do not block thread (delay, or do non-blocking io) and they will run in parallel on 1 single thread
u

ursus

09/30/2019, 10:26 PM
yes yes, how do you say actually being executed coroutine by a thread?
g

gildor

09/30/2019, 10:26 PM
Block thread? :)
So, what is your use case and what you want to achive?
u

ursus

09/30/2019, 10:27 PM
okay, so dispatcher how many threads are being blocked at most, right?
g

gildor

09/30/2019, 10:27 PM
Yes
u

ursus

09/30/2019, 10:27 PM
suspend fun downloader.download()
now I run it on IO = unlimited parallel downloads, which is not good in my case and want to limit it to say 5
so overflowing requests need to be queued and processed when dispatcher is free
g

gildor

09/30/2019, 10:29 PM
You don't need IO for this, just use asyncronous http client (just assuming that it's some network operation) And actual parallelization will be limited by http client (for example okhttp had 5 parallel connections to the same host)
u

ursus

09/30/2019, 10:29 PM
so I figured if I create a dispatcher from a fixed thread pool, I'd get this for free
No I'd only want to limit file downloads / uploads explicitly, shouldn't leak to other requests, glide, etc
g

gildor

09/30/2019, 10:31 PM
Own http client for this downloader with own limit?
If you use Glide you probably use okhttp, right?
u

ursus

09/30/2019, 10:31 PM
yea okhttp backs everything
isnt it basically the same? separate dispatcher vs clone of okhttp threads
g

gildor

09/30/2019, 10:33 PM
Than no need to have own dispatcher, okhttp already have more powerful internal dispatcher to limit amount of parallel requests, and you can just can create new instance of okhttp client for your downloader with own limit
Own coroutines dispatcher wouldn't help you on this case if you use asyncronous API of okhttp
Solution with okhttp dispatcher instead of coroutines is more powerful and optimal, it knows about amount of connections and hosts
And also allows you limit parallelism without using blocking API of okhttp and in case of coroutines dispatcher you have to use it everywhere to limit parallel requests, but In case of limit on level of http client you can share this client between different parts of your code and even with third party libraries
u

ursus

09/30/2019, 10:46 PM
well its going to be uglier I think, because okhttp is not exposed, but hidden behind retrofit, now I'd need a new retrofit service to the same endpoint which is weird but ok
why wouldnt it even work you think? afaik there wouldn't be a thread free to call that retrofit/okhttp call
ooh you mean my thread would become free once hopped of to okhttp thread, hence allowing to call new okhttp calls?
g

gildor

09/30/2019, 10:49 PM
Sure
Only if you use blocking Retrofit API, but don't think you do
u

ursus

09/30/2019, 10:51 PM
I see
g

gildor

09/30/2019, 10:52 PM
If okhttp is not exposed it's even better, you create special Reteofit service instance with required parallelism of okhttp client and can use it with coroutines or any other call adapter
It also can be implementation details of your downloader if you wish, the same way as you want to create own coroutines dispatcher
And I disagree that it's uglier, it's just dependency injection, when downloader knows nothing about parallelism, but http client with required config is injected
u

ursus

09/30/2019, 11:21 PM
uglier in that another service is needed for the same api, which doesnt make sense unless you know about the business rule of limiting downloads
g

gildor

10/01/2019, 1:28 AM
another service is needed for the same api
I don’t understands what you mean
If you want to make this limit per instance of your downloader, not by Retrofit service, just use kotlinx.coroutines Semaphore, as was suggested above. We don’t have details of your architecture and what exactly you want to limit. how and why
u

ursus

10/01/2019, 1:33 AM
ok trhank you
was very helpful