grantila / q

A platform-independent promise library for C++, implementing asynchronous continuations.
http://libq.io
Apache License 2.0
193 stars 24 forks source link

progress notifications example #16

Closed reklis closed 7 years ago

reklis commented 7 years ago

This isn't really a bug, but I don't know how else to contact you so I'm putting it here.

Is there some example of how to report progress back to the main thread for ongoing tasks? I'm thinking of a scenario like a download client where you have a handful of files downloading at the same time that you have dispatched into a queue and you want to report back the progress of each file in real time to the main thread as the data is saving to disk on the background i/o thread? I'm sure there are many ways to accomplish this, I was wondering if there is a recommended way specifically when using libq for the downloading.

grantila commented 7 years ago

Consider using a q::channel.

You probably have a transfer descriptor (class/struct) with the properties such as the total file size and the current amount of received/sent bytes. When you receive/send more, you update this descriptor. Could this be the case? If so, you can send this descriptor, or just parts of it, over a channel. You can do this from any thread, the channel is thread safe. When you create it, specify your main_queue as the queue, which will cause the reading to be performed on the main thread, where you can thread-safely update the UI.

Create a q::channel< std::size_t, std::size_t > and add its writer to the transfer_info and the reader to the UI (or wherever you want read the transfer):

q::channel< std::size_t, std::size_t > ch( main_queue );
auto readable = ch.get_readable( ); // This you give the UI
auto writable = ch.get_writable( ); // This you attach to the transfer_info struct

On the writer-side you do something like writable.write( received_bytes, total_bytes );. When the transfer is complete, you writable.close( );.

In the main/UI thread you consume from the reader-end of the channel:

readable.consume(
    [ ]( std::size_t current, std::size_t total )
    {
        set_progress_bar( current, total );
    }
)
.then( [ ]( ) { /* Transfer complete */ } );

Surely you'll need more if you have multiple transfers and whatnot, but this gives you a direction anyway.

Have a look at the channel unit tests for (proper) source code examples.