Skip to content
This repository has been archived by the owner on Dec 12, 2023. It is now read-only.

Merge vpicaver's fork #38

Merged
merged 30 commits into from
Mar 1, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
1fd9c4b
Added unit test building documentation - issue #18
Nov 4, 2019
912abff
Fix for issue #13 - check canceled before finished
vpicaver Aug 5, 2019
60f9cff
Fixed issue #15
vpicaver Aug 8, 2019
21b30a9
Added QBS product for import into other builds
Nov 1, 2019
99b4745
Added finished() - issue #21
Nov 1, 2019
4f2d4ae
Renamed finished() to completed()
Feb 17, 2020
6ec3c36
Fixed threaded cancel / finished crashing issue when context is in a …
vpicaver Feb 27, 2020
ba147cc
Merge branch 'local-02'
vpicaver Feb 28, 2020
c6a74ab
Merge branch 'masterDept2'
vpicaver Feb 28, 2020
76ecb29
Fixed Combiner should honor child progress #30
vpicaver Mar 18, 2020
cfe3f46
Fixed issue #31
vpicaver Mar 18, 2020
3dc6491
Working on cancel forward
vpicaver Mar 18, 2020
79d27b5
Fixed issue #4
vpicaver Mar 18, 2020
b0d1032
Fixed progress reporting for complete()
vpicaver Mar 19, 2020
6489ad7
Update to README.md
vpicaver Mar 19, 2020
a7a36be
Progress emit only when changed
vpicaver Mar 22, 2020
fbb2a0b
combiner can now take QList<QFuture<T>>
vpicaver Mar 22, 2020
2f68f49
Improved the Combined progress code
vpicaver Mar 22, 2020
3d7012e
Fixes issue #33
vpicaver Mar 27, 2020
0d4203f
Fixed completed() with empty QList()
vpicaver Mar 29, 2020
0da1348
Only cancel future if it's running and not finished.
Nov 30, 2020
570f82f
Fixed issue #37 - Cancelation now propagates to child futures
Feb 13, 2021
afcf034
Switch to QSharedPointer for cancelOnce
Feb 15, 2021
07066cc
Merge remote-tracking branch 'beu/master'
Feb 15, 2021
b99ffc2
Moved std::shared_ptr to QSharedPointer in bugtests.cpp
Feb 15, 2021
4e47d20
Fixed old Qt version build issue
Feb 15, 2021
8c4ccd1
Fixed memory leak in testcases
Feb 17, 2021
8ddec40
Merge remote-tracking branch 'beu/master'
Feb 17, 2021
2711954
Added valgrind suppressions for QVector
vpicaver Feb 17, 2021
c6268d3
Add more missing suppresions
Feb 17, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
164 changes: 159 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,6 @@ Observable<int> observable1 = AsyncFuture::observe(future);
// or
auto observable2 = AsyncFuture::observe(future);
```

**QFuture&lt;T&gt; Observable&lt;T&gt;::future()**

Obtain the QFuture object to represent the result.
Expand Down Expand Up @@ -361,13 +360,46 @@ observe(future).subscribe([](bool toggled) {

```

**Observable&lt;R&gt; Observable&lt;T&gt;::context(QObject&#42; contextObject, Completed onCompleted)**
**Observable&lt;R&gt; Observable&lt;T&gt;::context(QObject&#42; contextObject, Completed onCompleted, Cancel onCanceled)**

*This API is for advanced users only*

Add a callback function that listens to the finished signal from the observing QFuture object. The callback won't be triggered if the future is cancelled.
Add a callback function that listens to the finished and canceled signals from the observing QFuture object.

The callback is invoked in the thread of the context object. In case the context object is destroyed before the finished signal, the callback functions (onCompleted and onCanceled) won't be triggered and the returned Observable object will cancel its future.

Note: An event loop, must be excuting on the the contextObject->thread() for nested observe().context() calls to work.
Threads on the QThreadPool, generally don't have a QEventLoop executing, so manually creating and calling QEventLoop is
necessary. For example:

```c++
auto worker = [&]() {
auto localTimeout = [](int sleepTime) {
return QtConcurrent::run([sleepTime]() {
QThread::currentThread()->msleep(sleepTime);
});
};

QEventLoop loop;

auto context = QSharedPointer<QObject>::create();

The callback is invoked in the thread of the context object, In case the context object is destroyed before the finished signal, the callback function won't be triggered and the returned Observable object will cancel its future.
QThread* workerThread = QThread::currentThread();

observe(localTimeout(50)).context(context.data(), [localTimeout, context]() {
qDebug() << "First time localTimeout() finished
return localTimeout(50);
}).context(context.data(), [context, &called, workerThread, &loop]() {
qDebug() << "Second time localTimeout() finished
loop.quit();
});

loop.exec();
};

QtConcurrent::run(worker);

```

The return value is an `Observable<R>` object where R is the return type of the onCompleted callback.

Expand Down Expand Up @@ -404,11 +436,41 @@ AsyncFuture::observe(future).onProgress([=]() -> bool {
AsyncFuture::observe(future).onProgress([=]() -> void {
qDebug() << future.progressValue();
});

```

Added since v0.3.6.4

**Chained Progress**

`observe().subscribe().future()` future will report progress accordingly to the underlying future chain. When watching the final future in the chain, `progressRangeChanged` may be be updated multiple times as futures in the chain update their individual `progressRangeChanged`. When visualizing final future's progress in a progress bar, progressValue may appear to go in reverse, as progressRange increases. `progressValueChanged` will never go down as execution continues.

Example:

```{c++}
QVector<int> ints(100);
std::iota(ints.begin(), ints.end(), ints.size()); // Make ints from 1 to 100, increament by 1

// Worker function
std::function<int (int)> func = [](int x)->int {
QThread::msleep(100);
return x * x;
};

//First execution of workers
//Will increase the progressRange to 100
QFuture<int> mappedFuture = QtConcurrent::mapped(ints, func);

auto nextFuture = AsyncFuture::observe(mappedFuture).subscribe([ints, func](){
//Execute another batch of workers
//Will increase the progressRange to 200
QFuture<int> mappedFuture2 = QtConcurrent::mapped(ints, func);
return mappedFuture2;
}).future();

AsyncFuture::observe(nextFuture).onProgress([nextFuture](){
//Report the progress for the sum of mappedFuture and nextFuture from 0 to 200.
});
```

Deferred&lt;T&gt;
-----------
Expand All @@ -417,6 +479,8 @@ The `deferred<T>()` function return a Deferred<T> object that allows you to mani

The usage of complete/cancel in a Deferred object is pretty similar to the resolve/reject in a Promise object. You could complete a future by calling complete with a result value. If you give it another future, then it will observe the input future and change status once that is finished.

`deffered<T>()` that are created and immediately completed it's recommend to use `completed<T>()` instead.

**Auto Cancellation**

The `Deferred<T>` object is an explicitly shared class. You may own multiple copies and they are pointed to the same piece of shared data. In case, all of the instances are destroyed, it will cancel its future automatically.
Expand Down Expand Up @@ -475,6 +539,28 @@ Remarks: It won't complete a future even the `progressValue` has been reached th

Added since v0.3.6

completed();
-----------

The `completed<T>(const T&)` and `completed()` function return finished `QFuture<T>`
and `QFuture<void>` . `completed<T>(const T&)` can be used instead of a `deferred<T>()`
when the result is already available. For example:

```c++
auto defer = deferred<int>();
defer.complete(5);
auto completedFuture = defer.future();
```

is equivalent to

```c++
auto completedFuture = completed<int>(5);
```

`completed<T>(const T&)` is more convenient and light weight (memory and performance efficient) method of creating
a completed `QFuture<T>`.

Example

```
Expand Down Expand Up @@ -556,6 +642,32 @@ observe(f2).subscribe([=]() {
});
```

Cancelling the future at the end of the chain will cancel the whole chain. This will cancel all `QtConcurrent` execution. Worker threads that have already been started by `QtConcurrent` will continue running until finished, but no new ones will be started (this is how `QtConcurrent` works).

Example:

```c++
QVector<int> ints(100);
std::iota(ints.begin(), ints.end(), ints.size());
std::function<int (int)> func = [](int x)->int {
QThread::msleep(100);
return x * x;
};

QFuture<int> mappedFuture = QtConcurrent::mapped(ints, func);

auto future = AsyncFuture::observe(mappedFuture).subscribe(
[]{
// it won't be executed
},
[]{
// it will be executed.
}
).future();

future.cancel(); //Will cancel mappedFuture and future
```

Future Object Tracking
---------------

Expand Down Expand Up @@ -608,5 +720,47 @@ There has few examples of different use-cases in this source file:

[asyncfuture/example.cpp at master · benlau/asyncfuture](https://github.com/benlau/asyncfuture/blob/master/tests/asyncfutureunittests/example.cpp)

Building Testcases
==================

qpm needs to install, see download instructions at http:https://www.qpm.io/

After cloning the asyncfuture repository run the following commands:

```shell

cd asyncfuture/tests/asyncfutureunittests
cat qpm.json

```

qpm.json should look something like this:

```json

{
"dependencies": [
"[email protected]",
"[email protected]",
"[email protected]"
]
}

```

Install all the dependencies like this:


```shell

qpm install [email protected]
qpm install [email protected]
qpm install [email protected]


```

Now open asyncfuture.pro in QtCreator and build and run testcases.



Loading