ajalt / mordant

Multiplatform text styling for Kotlin command-line applications
https://ajalt.github.io/mordant/
Apache License 2.0
982 stars 34 forks source link

Task percentage goes beyond 100% sometimes #212

Closed sschuberth closed 1 month ago

sschuberth commented 1 month ago

I'm using this code to display at most 8 task (plus an overall task) at a time when downloading / verifying an arbitrary number of files (312 in my example):

with(tasks[index % parallelDownloads]) {
    reset { context = pkg to index }
    downloadPackage(pkg, dir, failureMessages)
    advance()
}

As you can see, I'm calling reset to reuse a given task for display of the progress of the current file, then download, and advance progress. As reset sets completed to 0 and advance increases it by 1, I wonder how it can ever be more than 1, resulting a percentage > 100 as can be seen here:

Verifying  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━  100%  eta -:--:--
> Package 'Maven:org.glassfish.jaxb:jaxb-core:3.0.2'...                          200%
> Package 'Maven:io.perfmark:perfmark-api:0.26.0'...                             200%
> Package 'NPM:@vue:compiler-dom:3.4.31'...                                      300%
> Package 'NPM:@jridgewell:sourcemap-codec:1.4.15'...                            100%
> Package 'Maven:jakarta.servlet:jakarta.servlet-api:6.1.0'...                   200%
> Package 'Maven:com.google.code.findbugs:jsr305:3.0.2'...                       200%
> Package 'Maven:org.eclipse.angus:angus-activation:2.0.2'...                    200%
> Package 'Maven:org.semver4j:semver4j:5.3.0'...                                 100%
ajalt commented 1 month ago

you're using limited parallelism, but launching all you async tasks al the same time, so sometimes the same progress task will be run concurrently, causing it to be reset multiple times then advanced multiple times.

You'll need to do something to ensure that you aren't running multiple asyncs on the same progress task at the same time. Maybe with a queue, or batches or mutexes or something.

AFAICT, this isn't an issue with mordant.

sschuberth commented 1 month ago

you're using limited parallelism, but launching all you async tasks al the same time

I thought that's exactly what limited parallelism takes care of: To work on an arbitrary number of tasks, but only N at a time, in a safe manner.

Anyway, thanks for the hints!

ajalt commented 1 month ago

It is running only N at a time, but there's no guarantees about which N. If you launch 10 asyncs for each progress task, the dispatcher might run all the ones that use the first task at the same time, rather than one from each task like you want. You'll have to enforce the ordering manually.