Linya: Simple Concurrent Progress Bars

I'm happy to announce linya (Github), a new terminal progress bar library focused on the concurrent multibar use case.


(click gif if not running)

In porting Aura to Rust, I found myself in need of progress bars, but existing crates didn't quite do what I needed, or had caveats when used in a concurrent context. Just give me something that works with rayon, I thought, and that's Linya.

Linya lets you spawn as many bars as you want on the fly, and performs efficient, allocation-free redraws. In terms of customizability however, Linya is fairly opinionated. If you need full control, consider the wonderful indicatif crate.

Future versions will be more customizable, but for now Linya is in a usable state. I hope you enjoy it, and please let me know if you have any issues!

:wave:

27 Likes

Nice!

The documentation claims that "Progress does not implement [...] Send or Sync", but in fact it does: Send, Sync. Note that these are "auto traits" that will be implicitly implemented for any of your types that do not include a !Send/!Sync field (possibly as PhantomData if you want to force the question). Should I open an issue?

1 Like

Now that you mention it, Send and Sync are probably not the problem, and perhaps I can remove that mention. The issue is Clone, namely that a user should only ever expect to have one true instance of a given Progress in their program (and should only be using a single one at a time).

Thoughts? Should I more less/more restrictive there? My intent was to force usage of Arc, etc.

Not implementing Clone won't suffice to enforce uniqueness of a Progress object -- the user can always just call Progress::new multiple times. You might look at what the log crate does for inspiration.

I think I'm satisfied in saying "usage of multiple Progress simultaneously results in undefined behaviour". Which shouldn't cause issues if they're following the examples :wink:

Ah, I think I understand now. Because Progress is not Clone, you need to wrap it in Arc to do things like (0..10).into_par_iter().for_each_with(progress, ...). And then you need an inner Mutex to regain mutability. The fact that Progress: Send + Sync doesn't enter into it at all.

Right, and although I haven't tested it, the constraints as they are should play way well with other async tooling.

So at the very least, I suppose I should remove the mention of Send and Sync there?

2 Likes

AFAICS, you don't have a single usage of unsafe, i.e. the term you're looking for is "unspecified behavior" and that is generally OK. Undefined behavior happens when executing unsafe code, that violates the promise it made to the compiler / language specification.

4 Likes

Right yes, unspecified is the better term. I will make a note about that too, just in case.

1 Like

Updated to 0.1.1 thanks to contributions from @cole-miller!