Skip to content

PPLX move-only task parameter support #1231

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 22 commits into from

Conversation

miherius
Copy link
Contributor

It is possible now to pass move-only objects as task parameter to the pplx::task constructor and to the pplx::create_task() function. So, code below is compilable now:

auto moveOnlyPtr = std::unique_ptr<Data>();
auto task = pplx::create_task( [data = std::move(moveOnlyPtr)]() {/*process data*/} );

Also, optimization comes as a free bonus, since we don't copy task parameter. For example, code below:

pplx::create_task( [v = std::move(hugeVector)]() {/*process v*/} ).get();
  • in current master realization it performs 7 copies and 2 moves of v object;
  • in this pull request realization it makes no copies, and performs 2 moves of v object.

Another minor interface refactor - redundant pplx::task constructor has been removed for better match with pplx::create_task() overloads.


This pull request is similar to #47 change. And like in #47 change, all pplx-specific tests were placed under corresponding #ifdef.

Checked in GCC 7.4.0 and MSVC2017 (both native windows ppl implementation, and forced pplx).

miherius added 17 commits July 11, 2019 21:16
(functor copy count reduced from 7 to 5)
…ut compiler errors.

Also static_assert for task result type check added.
…pass void-result tce only into void-result task.

Also adding tests for these cases.
…) function by introducing forwarding-reference parameter.
…or from details::_FilterValidTaskType() function, so initial tasks can be constructed from move-only objects.
`TestTasks_move_only_then` test logic moved to `create_task_with_move_only_continuation` test.
@msftclas
Copy link

msftclas commented Aug 25, 2019

CLA assistant check
All CLA requirements met.

Copy link
Member

@BillyONeal BillyONeal left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm I'm not sure how reasonable this is; we generally want pplx to have the exact same API as PPL, and PPL can never take such a change due to ABI restrictions (it often puts the callables into a std::function).

@@ -3066,25 +3081,58 @@ auto _IsValidTaskCtor(_Ty _Param, int, int, ...)
-> decltype(_Param.set(stdx::declval<_ReturnType>()), std::true_type());

template<typename _ReturnType, typename _Ty>
auto _IsValidTaskCtor(_Ty _Param, int, ...) -> decltype(_Param.set(), std::true_type());
auto _IsValidTaskCtor(_Ty _Param, int, ...)
-> decltype(_Param.set(), typename std::enable_if<std::is_same<_ReturnType, void>::value>::type(), std::true_type());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change appears to rely on expression SFINAE which is not safe to use on one of our supported compilers, VS2015.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe that existing logic like decltype(_Param.set(), /*...*/), which check a method existence, already use expression SFINAE. So I decided that this change should be safe. Anyway, I just tried to compile it in VS2015 (update 3) with forced PPLX, and it seems to work there.
Furthermore, VS2015 is not using PPLX by default, and this code enabled only for PPLX.

template<typename _Ty, typename _ReturnType, typename =
typename std::enable_if<!std::is_base_of<task<_ReturnType>, _Ty>::value>::type
>
struct _EnableIfNotTaskRet
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You probably want to derive from enable_if rather than making formation of _EnableIfNotTaskRet itself impossible.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, this is not a common practice. Changed to inheritance for _EnableIfNotTaskRet and _EnableIfNotTask structs in 19f0b83 and 0df92d9

@@ -4391,11 +4408,11 @@ class task<void>
/// Runtime)"/>.</para>
/// </remarks>
/**/
template<typename _Ty>
template<typename _Ty, typename = typename details::_EnableIfNotTaskRet<typename std::decay<_Ty>::type, void>>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Conventionally, preventing this from being a special member function should use the injected class name; e.g. `typename = enable_if_t<is_same_v<task, decay_t<_Ty>>>"

(Occurs elsewhere)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is exactly what _EnableIfNotTaskRet doing: checks that _Ty is not derived from task<_ReturnType> (is_base_of check includes is_same logic in this case). So we can catch that tricky case when _Ty is not a task class itself, but its child class, when compiler may decide to use this forwarding-reference overload instead of copy- or move-constructor.

}
#endif // !(defined(_MSC_VER) && (_MSC_VER >= 1800) && !CPPREST_FORCE_PPLX)

//TODO: tests for _AsyncTaskCtorArgsValidator (__cplusplus_winrt only)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We shouldn't check in TODO comments; feel free to file an 'XXX should be tested' issue.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed that __cplusplus_winrt stuff is not presented in tests at all, so I removed this TODO (6d8c603)

VERIFY_ARE_EQUAL(t2.get(), 22);
}

#if !(defined(_MSC_VER) && (_MSC_VER >= 1800) && !CPPREST_FORCE_PPLX)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If _MSC_VER is not defined, then it can't be defined to greater than 1800. Perhaps demorgan?

#if !defined(_MSC_VER) || CPPREST_FORCE_PPLX

?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But when _MSC_VER is defined, it can be less than 1800, and this should make this #if work.

P.S. This all is just opposite condition from the beginning of the pplxtasks.h file, where choose between PPL and pplx is made.

@miherius
Copy link
Contributor Author

miherius commented Sep 1, 2019

we generally want pplx to have the exact same API as PPL

I tried to save existing compatibility conditions. And I used interface deviations brought by #47 as a reference. That is, when you can use #47's stuff, you can use this pull-request's stuff, and vice-versa. The only difference in place where movable-only functor can appear: continuation in case of #47 and initial task in case of current pull-request.

@miherius
Copy link
Contributor Author

miherius commented Sep 2, 2019

All failed tests throws exception Unhandled exception: bind: Address already in use. And each of them tried to listen localhost:45678 address. So I think it should be a test environment issue.

@BillyONeal
Copy link
Member

I'm sorry but as I indicated, for API compatibility reasons between PPL and PPLX I don't think I'm able to merge this :(

@BillyONeal BillyONeal closed this Feb 19, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants