From 9340af1a8a0f55a8fc96658ea0657f3e98e1e5ac Mon Sep 17 00:00:00 2001
From: Alexander Alekhin <alexander.a.alekhin@gmail.com>
Date: Thu, 25 Apr 2019 07:22:14 +0000
Subject: [PATCH] core: Async API / AsyncArray

---
 modules/core/include/opencv2/core.hpp         |   1 +
 modules/core/include/opencv2/core/async.hpp   | 105 +++++
 .../include/opencv2/core/bindings_utils.hpp   |  26 ++
 modules/core/include/opencv2/core/cvdef.h     |  13 +
 .../opencv2/core/detail/async_promise.hpp     |  71 ++++
 .../opencv2/core/detail/exception_ptr.hpp     |  27 ++
 modules/core/include/opencv2/core/mat.hpp     |   3 +
 modules/core/misc/python/pyopencv_async.hpp   |   8 +
 modules/core/src/async.cpp                    | 366 ++++++++++++++++++
 modules/core/src/matrix_wrap.cpp              |  70 ++++
 modules/core/src/parallel.cpp                 |  22 +-
 modules/core/test/test_async.cpp              | 154 ++++++++
 modules/python/test/test_async.py             |  33 ++
 13 files changed, 878 insertions(+), 21 deletions(-)
 create mode 100644 modules/core/include/opencv2/core/async.hpp
 create mode 100644 modules/core/include/opencv2/core/detail/async_promise.hpp
 create mode 100644 modules/core/include/opencv2/core/detail/exception_ptr.hpp
 create mode 100644 modules/core/misc/python/pyopencv_async.hpp
 create mode 100644 modules/core/src/async.cpp
 create mode 100644 modules/core/test/test_async.cpp
 create mode 100644 modules/python/test/test_async.py

diff --git a/modules/core/include/opencv2/core.hpp b/modules/core/include/opencv2/core.hpp
index 089c0db334..0a729ecd80 100644
--- a/modules/core/include/opencv2/core.hpp
+++ b/modules/core/include/opencv2/core.hpp
@@ -68,6 +68,7 @@
         @defgroup core_c_glue Connections with C++
     @}
     @defgroup core_array Operations on arrays
+    @defgroup core_async Asynchronous API
     @defgroup core_xml XML/YAML Persistence
     @defgroup core_cluster Clustering
     @defgroup core_utils Utility and system functions and macros
diff --git a/modules/core/include/opencv2/core/async.hpp b/modules/core/include/opencv2/core/async.hpp
new file mode 100644
index 0000000000..54560c7d00
--- /dev/null
+++ b/modules/core/include/opencv2/core/async.hpp
@@ -0,0 +1,105 @@
+// This file is part of OpenCV project.
+// It is subject to the license terms in the LICENSE file found in the top-level directory
+// of this distribution and at http://opencv.org/license.html.
+
+#ifndef OPENCV_CORE_ASYNC_HPP
+#define OPENCV_CORE_ASYNC_HPP
+
+#include <opencv2/core/mat.hpp>
+
+#ifdef CV_CXX11
+//#include <future>
+#include <chrono>
+#endif
+
+namespace cv {
+
+/** @addtogroup core_async
+
+@{
+*/
+
+
+/** @brief Returns result of asynchronous operations
+
+Object has attached asynchronous state.
+Assignment operator doesn't clone asynchronous state (it is shared between all instances).
+
+Result can be fetched via get() method only once.
+
+*/
+class CV_EXPORTS_W AsyncArray
+{
+public:
+    ~AsyncArray() CV_NOEXCEPT;
+    CV_WRAP AsyncArray() CV_NOEXCEPT;
+    AsyncArray(const AsyncArray& o) CV_NOEXCEPT;
+    AsyncArray& operator=(const AsyncArray& o) CV_NOEXCEPT;
+    CV_WRAP void release() CV_NOEXCEPT;
+
+    /** Fetch the result.
+    @param[out] dst destination array
+
+    Waits for result until container has valid result.
+    Throws exception if exception was stored as a result.
+
+    Throws exception on invalid container state.
+
+    @note Result or stored exception can be fetched only once.
+    */
+    CV_WRAP void get(OutputArray dst) const;
+
+    /** Retrieving the result with timeout
+    @param[out] dst destination array
+    @param[in] timeoutNs timeout in nanoseconds, -1 for infinite wait
+
+    @returns true if result is ready, false if the timeout has expired
+
+    @note Result or stored exception can be fetched only once.
+    */
+    bool get(OutputArray dst, int64 timeoutNs) const;
+
+    CV_WRAP inline
+    bool get(OutputArray dst, double timeoutNs) const { return get(dst, (int64)timeoutNs); }
+
+    bool wait_for(int64 timeoutNs) const;
+
+    CV_WRAP inline
+    bool wait_for(double timeoutNs) const { return wait_for((int64)timeoutNs); }
+
+    CV_WRAP bool valid() const CV_NOEXCEPT;
+
+#ifdef CV_CXX11
+    inline AsyncArray(AsyncArray&& o) { p = o.p; o.p = NULL; }
+    inline AsyncArray& operator=(AsyncArray&& o) CV_NOEXCEPT { std::swap(p, o.p); return *this; }
+
+    template<typename _Rep, typename _Period>
+    inline bool get(OutputArray dst, const std::chrono::duration<_Rep, _Period>& timeout)
+    {
+        return get(dst, (int64)(std::chrono::nanoseconds(timeout).count()));
+    }
+
+    template<typename _Rep, typename _Period>
+    inline bool wait_for(const std::chrono::duration<_Rep, _Period>& timeout)
+    {
+        return wait_for((int64)(std::chrono::nanoseconds(timeout).count()));
+    }
+
+#if 0
+    std::future<Mat> getFutureMat() const;
+    std::future<UMat> getFutureUMat() const;
+#endif
+#endif
+
+
+    // PImpl
+    struct Impl; friend struct Impl;
+    inline void* _getImpl() const CV_NOEXCEPT { return p; }
+protected:
+    Impl* p;
+};
+
+
+//! @}
+} // namespace
+#endif // OPENCV_CORE_ASYNC_HPP
diff --git a/modules/core/include/opencv2/core/bindings_utils.hpp b/modules/core/include/opencv2/core/bindings_utils.hpp
index c1123f2b0e..3e73784f97 100644
--- a/modules/core/include/opencv2/core/bindings_utils.hpp
+++ b/modules/core/include/opencv2/core/bindings_utils.hpp
@@ -5,6 +5,9 @@
 #ifndef OPENCV_CORE_BINDINGS_UTILS_HPP
 #define OPENCV_CORE_BINDINGS_UTILS_HPP
 
+#include <opencv2/core/async.hpp>
+#include <opencv2/core/detail/async_promise.hpp>
+
 namespace cv { namespace utils {
 //! @addtogroup core_utils
 //! @{
@@ -17,6 +20,29 @@ CV_EXPORTS_W String dumpInputOutputArray(InputOutputArray argument);
 
 CV_EXPORTS_W String dumpInputOutputArrayOfArrays(InputOutputArrayOfArrays argument);
 
+CV_WRAP static inline
+AsyncArray testAsyncArray(InputArray argument)
+{
+    AsyncPromise p;
+    p.setValue(argument);
+    return p.getArrayResult();
+}
+
+CV_WRAP static inline
+AsyncArray testAsyncException()
+{
+    AsyncPromise p;
+    try
+    {
+        CV_Error(Error::StsOk, "Test: Generated async error");
+    }
+    catch (const cv::Exception& e)
+    {
+        p.setException(e);
+    }
+    return p.getArrayResult();
+}
+
 //! @}
 }} // namespace
 
diff --git a/modules/core/include/opencv2/core/cvdef.h b/modules/core/include/opencv2/core/cvdef.h
index 9805d96100..8000cec736 100644
--- a/modules/core/include/opencv2/core/cvdef.h
+++ b/modules/core/include/opencv2/core/cvdef.h
@@ -622,6 +622,19 @@ Cv64suf;
 #  define CV_FINAL
 #endif
 
+/****************************************************************************************\
+*                                     C++11 noexcept                                     *
+\****************************************************************************************/
+
+#ifndef CV_NOEXCEPT
+#  ifdef CV_CXX11
+#    define CV_NOEXCEPT noexcept
+#  endif
+#endif
+#ifndef CV_NOEXCEPT
+#  define CV_NOEXCEPT
+#endif
+
 
 
 // Integer types portatibility
diff --git a/modules/core/include/opencv2/core/detail/async_promise.hpp b/modules/core/include/opencv2/core/detail/async_promise.hpp
new file mode 100644
index 0000000000..6eb3fb52c1
--- /dev/null
+++ b/modules/core/include/opencv2/core/detail/async_promise.hpp
@@ -0,0 +1,71 @@
+// This file is part of OpenCV project.
+// It is subject to the license terms in the LICENSE file found in the top-level directory
+// of this distribution and at http://opencv.org/license.html.
+
+#ifndef OPENCV_CORE_ASYNC_PROMISE_HPP
+#define OPENCV_CORE_ASYNC_PROMISE_HPP
+
+#include "../async.hpp"
+
+#include "exception_ptr.hpp"
+
+namespace cv {
+
+/** @addtogroup core_async
+@{
+*/
+
+
+/** @brief Provides result of asynchronous operations
+
+*/
+class CV_EXPORTS AsyncPromise
+{
+public:
+    ~AsyncPromise() CV_NOEXCEPT;
+    AsyncPromise() CV_NOEXCEPT;
+    explicit AsyncPromise(const AsyncPromise& o) CV_NOEXCEPT;
+    AsyncPromise& operator=(const AsyncPromise& o) CV_NOEXCEPT;
+    void release() CV_NOEXCEPT;
+
+    /** Returns associated AsyncArray
+    @note Can be called once
+    */
+    AsyncArray getArrayResult();
+
+    /** Stores asynchronous result.
+    @param[in] value result
+    */
+    void setValue(InputArray value);
+
+    // TODO "move" setters
+
+#if CV__EXCEPTION_PTR
+    /** Stores exception.
+    @param[in] exception exception to be raised in AsyncArray
+    */
+    void setException(std::exception_ptr exception);
+#endif
+
+    /** Stores exception.
+    @param[in] exception exception to be raised in AsyncArray
+    */
+    void setException(const cv::Exception& exception);
+
+#ifdef CV_CXX11
+    explicit AsyncPromise(AsyncPromise&& o) { p = o.p; o.p = NULL; }
+    AsyncPromise& operator=(AsyncPromise&& o) CV_NOEXCEPT { std::swap(p, o.p); return *this; }
+#endif
+
+
+    // PImpl
+    typedef struct AsyncArray::Impl Impl; friend struct AsyncArray::Impl;
+    inline void* _getImpl() const CV_NOEXCEPT { return p; }
+protected:
+    Impl* p;
+};
+
+
+//! @}
+} // namespace
+#endif // OPENCV_CORE_ASYNC_PROMISE_HPP
diff --git a/modules/core/include/opencv2/core/detail/exception_ptr.hpp b/modules/core/include/opencv2/core/detail/exception_ptr.hpp
new file mode 100644
index 0000000000..d98ffc40c6
--- /dev/null
+++ b/modules/core/include/opencv2/core/detail/exception_ptr.hpp
@@ -0,0 +1,27 @@
+// This file is part of OpenCV project.
+// It is subject to the license terms in the LICENSE file found in the top-level directory
+// of this distribution and at http://opencv.org/license.html.
+
+#ifndef OPENCV_CORE_DETAILS_EXCEPTION_PTR_H
+#define OPENCV_CORE_DETAILS_EXCEPTION_PTR_H
+
+#ifndef CV__EXCEPTION_PTR
+#  if defined(__ANDROID__) && defined(ATOMIC_INT_LOCK_FREE) && ATOMIC_INT_LOCK_FREE < 2
+#    define CV__EXCEPTION_PTR 0  // Not supported, details: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=58938
+#  elif defined(CV_CXX11)
+#    define CV__EXCEPTION_PTR 1
+#  elif defined(_MSC_VER)
+#    define CV__EXCEPTION_PTR (_MSC_VER >= 1600)
+#  elif defined(__clang__)
+#    define CV__EXCEPTION_PTR 0  // C++11 only (see above)
+#  elif defined(__GNUC__) && defined(__GXX_EXPERIMENTAL_CXX0X__)
+#    define CV__EXCEPTION_PTR (__GXX_EXPERIMENTAL_CXX0X__ > 0)
+#  endif
+#endif
+#ifndef CV__EXCEPTION_PTR
+#  define CV__EXCEPTION_PTR 0
+#elif CV__EXCEPTION_PTR
+#  include <exception>  // std::exception_ptr
+#endif
+
+#endif // OPENCV_CORE_DETAILS_EXCEPTION_PTR_H
diff --git a/modules/core/include/opencv2/core/mat.hpp b/modules/core/include/opencv2/core/mat.hpp
index 2c23a2abb4..a3ee195976 100644
--- a/modules/core/include/opencv2/core/mat.hpp
+++ b/modules/core/include/opencv2/core/mat.hpp
@@ -377,6 +377,9 @@ public:
 
     void assign(const std::vector<UMat>& v) const;
     void assign(const std::vector<Mat>& v) const;
+
+    void move(UMat& u) const;
+    void move(Mat& m) const;
 };
 
 
diff --git a/modules/core/misc/python/pyopencv_async.hpp b/modules/core/misc/python/pyopencv_async.hpp
new file mode 100644
index 0000000000..6a8e73526e
--- /dev/null
+++ b/modules/core/misc/python/pyopencv_async.hpp
@@ -0,0 +1,8 @@
+#ifdef HAVE_OPENCV_CORE
+
+#include "opencv2/core/async.hpp"
+
+CV_PY_TO_CLASS(AsyncArray);
+CV_PY_FROM_CLASS(AsyncArray);
+
+#endif
diff --git a/modules/core/src/async.cpp b/modules/core/src/async.cpp
new file mode 100644
index 0000000000..a2f4612365
--- /dev/null
+++ b/modules/core/src/async.cpp
@@ -0,0 +1,366 @@
+// This file is part of OpenCV project.
+// It is subject to the license terms in the LICENSE file found in the top-level directory
+// of this distribution and at http://opencv.org/license.html.
+
+#include "precomp.hpp"
+//#undef CV_CXX11  // debug non C++11 mode
+#include "opencv2/core/async.hpp"
+#include "opencv2/core/detail/async_promise.hpp"
+
+#include "opencv2/core/cvstd.hpp"
+
+#include <opencv2/core/utils/logger.defines.hpp>
+#undef CV_LOG_STRIP_LEVEL
+#define CV_LOG_STRIP_LEVEL CV_LOG_LEVEL_DEBUG + 1
+#include <opencv2/core/utils/logger.hpp>
+
+
+#ifdef CV_CXX11
+#include <mutex>
+#include <condition_variable>
+#include <chrono>
+#endif
+
+namespace cv {
+
+/**
+Manages shared state of asynchronous result
+*/
+struct AsyncArray::Impl
+{
+    int refcount;
+    void addrefFuture() CV_NOEXCEPT { CV_XADD(&refcount_future, 1); CV_XADD(&refcount, 1); } \
+    void releaseFuture() CV_NOEXCEPT { CV_XADD(&refcount_future, -1); if(1 == CV_XADD(&refcount, -1)) delete this; } \
+    int refcount_future;
+    void addrefPromise() CV_NOEXCEPT { CV_XADD(&refcount_promise, 1); CV_XADD(&refcount, 1); } \
+    void releasePromise() CV_NOEXCEPT { CV_XADD(&refcount_promise, -1); if(1 == CV_XADD(&refcount, -1)) delete this; } \
+    int refcount_promise;
+
+#ifdef CV_CXX11
+    mutable std::mutex mtx;
+    mutable std::condition_variable cond_var;
+#else
+    mutable cv::Mutex mtx;
+#endif
+
+    mutable bool has_result; // Mat, UMat or exception
+
+    mutable cv::Ptr<Mat> result_mat;
+    mutable cv::Ptr<UMat> result_umat;
+
+
+    bool has_exception;
+#if CV__EXCEPTION_PTR
+    std::exception_ptr exception;
+#endif
+    cv::Exception cv_exception;
+
+    mutable bool result_is_fetched;
+
+    bool future_is_returned;
+
+    Impl()
+        : refcount(1), refcount_future(0), refcount_promise(1)
+        , has_result(false)
+        , has_exception(false)
+        , result_is_fetched(false)
+        , future_is_returned(false)
+    {
+        // nothing
+    }
+
+    ~Impl()
+    {
+        if (has_result && !result_is_fetched)
+        {
+            CV_LOG_INFO(NULL, "Asynchronous result has not been fetched");
+        }
+    }
+
+    bool get(OutputArray dst, int64 timeoutNs) const
+    {
+        CV_Assert(!result_is_fetched);
+        if (!has_result)
+        {
+            if(refcount_promise == 0)
+                CV_Error(Error::StsInternal, "Asynchronous result producer has been destroyed");
+            if (!wait_for(timeoutNs))
+                return false;
+        }
+#ifdef CV_CXX11
+        std::unique_lock<std::mutex> lock(mtx);
+#else
+        cv::AutoLock lock(mtx);
+#endif
+        if (has_result)
+        {
+            if (!result_mat.empty())
+            {
+                dst.move(*result_mat.get());
+                result_mat.release();
+                result_is_fetched = true;
+                return true;
+            }
+            if (!result_umat.empty())
+            {
+                dst.move(*result_umat.get());
+                result_umat.release();
+                result_is_fetched = true;
+                return true;
+            }
+#if CV__EXCEPTION_PTR
+            if (has_exception && exception)
+            {
+                result_is_fetched = true;
+                std::rethrow_exception(exception);
+            }
+#endif
+            if (has_exception)
+            {
+                result_is_fetched = true;
+                throw cv_exception;
+            }
+            CV_Error(Error::StsInternal, "AsyncArray: invalid state of 'has_result = true'");
+        }
+        CV_Assert(!has_result);
+        CV_Assert(timeoutNs < 0);
+        return false;
+    }
+
+    bool valid() const CV_NOEXCEPT
+    {
+        if (result_is_fetched)
+            return false;
+        if (refcount_promise == 0 && !has_result)
+            return false;
+        return true;
+    }
+
+    bool wait_for(int64 timeoutNs) const
+    {
+        CV_Assert(valid());
+        if (has_result)
+            return has_result;
+        if (timeoutNs == 0)
+            return has_result;
+        CV_LOG_INFO(NULL, "Waiting for async result ...");
+#ifdef CV_CXX11
+        std::unique_lock<std::mutex> lock(mtx);
+        const auto cond_pred = [&]{ return has_result == true; };
+        if (timeoutNs > 0)
+            return cond_var.wait_for(lock, std::chrono::nanoseconds(timeoutNs), cond_pred);
+        else
+        {
+            cond_var.wait(lock, cond_pred);
+            CV_Assert(has_result);
+            return true;
+        }
+#else
+        CV_Error(Error::StsNotImplemented, "OpenCV has been built without async waiting support (C++11 is required)");
+#endif
+    }
+
+    AsyncArray getArrayResult()
+    {
+        CV_Assert(refcount_future == 0);
+        AsyncArray result;
+        addrefFuture();
+        result.p = this;
+        future_is_returned = true;
+        return result;
+    }
+
+    void setValue(InputArray value)
+    {
+        if (future_is_returned && refcount_future == 0)
+            CV_Error(Error::StsError, "Associated AsyncArray has been destroyed");
+#ifdef CV_CXX11
+        std::unique_lock<std::mutex> lock(mtx);
+#else
+        cv::AutoLock lock(mtx);
+#endif
+        CV_Assert(!has_result);
+        int k = value.kind();
+        if (k == _InputArray::UMAT)
+        {
+            result_umat = makePtr<UMat>();
+            value.copyTo(*result_umat.get());
+        }
+        else
+        {
+            result_mat = makePtr<Mat>();
+            value.copyTo(*result_mat.get());
+        }
+        has_result = true;
+#ifdef CV_CXX11
+        cond_var.notify_all();
+#endif
+    }
+
+#if CV__EXCEPTION_PTR
+    void setException(std::exception_ptr e)
+    {
+        if (future_is_returned && refcount_future == 0)
+            CV_Error(Error::StsError, "Associated AsyncArray has been destroyed");
+#ifdef CV_CXX11
+        std::unique_lock<std::mutex> lock(mtx);
+#else
+        cv::AutoLock lock(mtx);
+#endif
+        CV_Assert(!has_result);
+        has_exception = true;
+        exception = e;
+        has_result = true;
+#ifdef CV_CXX11
+        cond_var.notify_all();
+#endif
+    }
+#endif
+
+    void setException(const cv::Exception e)
+    {
+        if (future_is_returned && refcount_future == 0)
+            CV_Error(Error::StsError, "Associated AsyncArray has been destroyed");
+#ifdef CV_CXX11
+        std::unique_lock<std::mutex> lock(mtx);
+#else
+        cv::AutoLock lock(mtx);
+#endif
+        CV_Assert(!has_result);
+        has_exception = true;
+        cv_exception = e;
+        has_result = true;
+#ifdef CV_CXX11
+        cond_var.notify_all();
+#endif
+    }
+};
+
+
+AsyncArray::AsyncArray() CV_NOEXCEPT
+    : p(NULL)
+{
+}
+
+AsyncArray::~AsyncArray() CV_NOEXCEPT
+{
+    release();
+}
+
+AsyncArray::AsyncArray(const AsyncArray& o) CV_NOEXCEPT
+    : p(o.p)
+{
+    if (p)
+        p->addrefFuture();
+}
+
+AsyncArray& AsyncArray::operator=(const AsyncArray& o) CV_NOEXCEPT
+{
+    Impl* newp = o.p;
+    if (newp)
+        newp->addrefFuture();
+    release();
+    p = newp;
+    return *this;
+}
+
+void AsyncArray::release() CV_NOEXCEPT
+{
+    Impl* impl = p;
+    p = NULL;
+    if (impl)
+        impl->releaseFuture();
+}
+
+bool AsyncArray::get(OutputArray dst, int64 timeoutNs) const
+{
+    CV_Assert(p);
+    return p->get(dst, timeoutNs);
+}
+
+void AsyncArray::get(OutputArray dst) const
+{
+    CV_Assert(p);
+    bool res = p->get(dst, -1);
+    CV_Assert(res);
+}
+
+bool AsyncArray::wait_for(int64 timeoutNs) const
+{
+    CV_Assert(p);
+    return p->wait_for(timeoutNs);
+}
+
+bool AsyncArray::valid() const CV_NOEXCEPT
+{
+    if (!p) return false;
+    return p->valid();
+}
+
+
+//
+// AsyncPromise
+//
+
+AsyncPromise::AsyncPromise() CV_NOEXCEPT
+    : p(new AsyncArray::Impl())
+{
+}
+
+AsyncPromise::~AsyncPromise() CV_NOEXCEPT
+{
+    release();
+}
+
+AsyncPromise::AsyncPromise(const AsyncPromise& o) CV_NOEXCEPT
+    : p(o.p)
+{
+    if (p)
+        p->addrefPromise();
+}
+
+AsyncPromise& AsyncPromise::operator=(const AsyncPromise& o) CV_NOEXCEPT
+{
+    Impl* newp = o.p;
+    if (newp)
+        newp->addrefPromise();
+    release();
+    p = newp;
+    return *this;
+}
+
+void AsyncPromise::release() CV_NOEXCEPT
+{
+    Impl* impl = p;
+    p = NULL;
+    if (impl)
+        impl->releasePromise();
+}
+
+AsyncArray AsyncPromise::getArrayResult()
+{
+    CV_Assert(p);
+    return p->getArrayResult();
+}
+
+void AsyncPromise::setValue(InputArray value)
+{
+    CV_Assert(p);
+    return p->setValue(value);
+}
+
+void AsyncPromise::setException(const cv::Exception& exception)
+{
+    CV_Assert(p);
+    return p->setException(exception);
+}
+
+#if CV__EXCEPTION_PTR
+void AsyncPromise::setException(std::exception_ptr exception)
+{
+    CV_Assert(p);
+    return p->setException(exception);
+}
+#endif
+
+} // namespace
diff --git a/modules/core/src/matrix_wrap.cpp b/modules/core/src/matrix_wrap.cpp
index 1f5d861cdd..e16e2f3f83 100644
--- a/modules/core/src/matrix_wrap.cpp
+++ b/modules/core/src/matrix_wrap.cpp
@@ -1879,6 +1879,76 @@ void _OutputArray::assign(const Mat& m) const
 }
 
 
+void _OutputArray::move(UMat& u) const
+{
+    if (fixedSize())
+    {
+        // TODO Performance warning
+        assign(u);
+        return;
+    }
+    int k = kind();
+    if (k == UMAT)
+    {
+#ifdef CV_CXX11
+        *(UMat*)obj = std::move(u);
+#else
+        *(UMat*)obj = u;
+        u.release();
+#endif
+    }
+    else if (k == MAT)
+    {
+        u.copyTo(*(Mat*)obj); // TODO check u.getMat()
+        u.release();
+    }
+    else if (k == MATX)
+    {
+        u.copyTo(getMat()); // TODO check u.getMat()
+        u.release();
+    }
+    else
+    {
+        CV_Error(Error::StsNotImplemented, "");
+    }
+}
+
+
+void _OutputArray::move(Mat& m) const
+{
+    if (fixedSize())
+    {
+        // TODO Performance warning
+        assign(m);
+        return;
+    }
+    int k = kind();
+    if (k == UMAT)
+    {
+        m.copyTo(*(UMat*)obj); // TODO check m.getUMat()
+        m.release();
+    }
+    else if (k == MAT)
+    {
+#ifdef CV_CXX11
+        *(Mat*)obj = std::move(m);
+#else
+        *(Mat*)obj = m;
+        m.release();
+#endif
+    }
+    else if (k == MATX)
+    {
+        m.copyTo(getMat());
+        m.release();
+    }
+    else
+    {
+        CV_Error(Error::StsNotImplemented, "");
+    }
+}
+
+
 void _OutputArray::assign(const std::vector<UMat>& v) const
 {
     int k = kind();
diff --git a/modules/core/src/parallel.cpp b/modules/core/src/parallel.cpp
index 0745b5a6e9..ce996ef6a8 100644
--- a/modules/core/src/parallel.cpp
+++ b/modules/core/src/parallel.cpp
@@ -129,27 +129,7 @@
 
 #include "parallel_impl.hpp"
 
-
-#ifndef CV__EXCEPTION_PTR
-#  if defined(__ANDROID__) && defined(ATOMIC_INT_LOCK_FREE) && ATOMIC_INT_LOCK_FREE < 2
-#    define CV__EXCEPTION_PTR 0  // Not supported, details: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=58938
-#  elif defined(CV_CXX11)
-#    define CV__EXCEPTION_PTR 1
-#  elif defined(_MSC_VER)
-#    define CV__EXCEPTION_PTR (_MSC_VER >= 1600)
-#  elif defined(__clang__)
-#    define CV__EXCEPTION_PTR 0  // C++11 only (see above)
-#  elif defined(__GNUC__) && defined(__GXX_EXPERIMENTAL_CXX0X__)
-#    define CV__EXCEPTION_PTR (__GXX_EXPERIMENTAL_CXX0X__ > 0)
-#  endif
-#endif
-#ifndef CV__EXCEPTION_PTR
-#  define CV__EXCEPTION_PTR 0
-#elif CV__EXCEPTION_PTR
-#  include <exception>  // std::exception_ptr
-#endif
-
-
+#include "opencv2/core/detail/exception_ptr.hpp"  // CV__EXCEPTION_PTR = 1 if std::exception_ptr is available
 
 using namespace cv;
 
diff --git a/modules/core/test/test_async.cpp b/modules/core/test/test_async.cpp
new file mode 100644
index 0000000000..f898a22878
--- /dev/null
+++ b/modules/core/test/test_async.cpp
@@ -0,0 +1,154 @@
+// This file is part of OpenCV project.
+// It is subject to the license terms in the LICENSE file found in the top-level directory
+// of this distribution and at http://opencv.org/license.html.
+#include "test_precomp.hpp"
+#include <opencv2/core/async.hpp>
+#include <opencv2/core/detail/async_promise.hpp>
+
+#include <opencv2/core/bindings_utils.hpp>
+
+#ifdef CV_CXX11
+#include <thread>
+#include <chrono>
+#endif
+
+namespace opencv_test { namespace {
+
+TEST(Core_Async, BasicCheck)
+{
+    Mat m(3, 3, CV_32FC1, Scalar::all(5.0f));
+    AsyncPromise p;
+    AsyncArray r = p.getArrayResult();
+    EXPECT_TRUE(r.valid());
+
+    // Follow the limitations of std::promise::get_future
+    // https://en.cppreference.com/w/cpp/thread/promise/get_future
+    EXPECT_THROW(AsyncArray r2 = p.getArrayResult(), cv::Exception);
+
+    p.setValue(m);
+
+    Mat m2;
+    r.get(m2);
+    EXPECT_EQ(0, cvtest::norm(m, m2, NORM_INF));
+
+    // Follow the limitations of std::future::get
+    // https://en.cppreference.com/w/cpp/thread/future/get
+    EXPECT_FALSE(r.valid());
+    Mat m3;
+    EXPECT_THROW(r.get(m3), cv::Exception);
+}
+
+TEST(Core_Async, ExceptionCheck)
+{
+    Mat m(3, 3, CV_32FC1, Scalar::all(5.0f));
+    AsyncPromise p;
+    AsyncArray r = p.getArrayResult();
+    EXPECT_TRUE(r.valid());
+
+    try
+    {
+        CV_Error(Error::StsOk, "Test: Generated async error");
+    }
+    catch (const cv::Exception& e)
+    {
+        p.setException(e);
+    }
+
+    try {
+        Mat m2;
+        r.get(m2);
+        FAIL() << "Exception is expected";
+    }
+    catch (const cv::Exception& e)
+    {
+        EXPECT_EQ(Error::StsOk, e.code) << e.what();
+    }
+
+    // Follow the limitations of std::future::get
+    // https://en.cppreference.com/w/cpp/thread/future/get
+    EXPECT_FALSE(r.valid());
+}
+
+
+TEST(Core_Async, LikePythonTest)
+{
+    Mat m(3, 3, CV_32FC1, Scalar::all(5.0f));
+    AsyncArray r = cv::utils::testAsyncArray(m);
+    EXPECT_TRUE(r.valid());
+    Mat m2;
+    r.get(m2);
+    EXPECT_EQ(0, cvtest::norm(m, m2, NORM_INF));
+
+    // Follow the limitations of std::future::get
+    // https://en.cppreference.com/w/cpp/thread/future/get
+    EXPECT_FALSE(r.valid());
+}
+
+
+#ifdef CV_CXX11
+TEST(Core_Async, AsyncThread_Simple)
+{
+    Mat m(3, 3, CV_32FC1, Scalar::all(5.0f));
+    AsyncPromise p;
+    AsyncArray r = p.getArrayResult();
+
+    std::thread t([&]{
+        std::this_thread::sleep_for(std::chrono::milliseconds(100));
+        try {
+            p.setValue(m);
+        } catch (const std::exception& e) {
+            std::cout << e.what() << std::endl;
+        } catch (...) {
+            std::cout << "Unknown C++ exception" << std::endl;
+        }
+    });
+
+    try
+    {
+        Mat m2;
+        r.get(m2);
+        EXPECT_EQ(0, cvtest::norm(m, m2, NORM_INF));
+
+        t.join();
+    }
+    catch (...)
+    {
+        t.join();
+        throw;
+    }
+}
+
+TEST(Core_Async, AsyncThread_DetachedResult)
+{
+    Mat m(3, 3, CV_32FC1, Scalar::all(5.0f));
+    AsyncPromise p;
+    {
+        AsyncArray r = p.getArrayResult();
+        r.release();
+    }
+
+    bool exception_ok = false;
+
+    std::thread t([&]{
+        std::this_thread::sleep_for(std::chrono::milliseconds(100));
+        try {
+            p.setValue(m);
+        } catch (const cv::Exception& e) {
+            if (e.code == Error::StsError)
+                exception_ok = true;
+            else
+                std::cout << e.what() << std::endl;
+        } catch (const std::exception& e) {
+            std::cout << e.what() << std::endl;
+        } catch (...) {
+            std::cout << "Unknown C++ exception" << std::endl;
+        }
+    });
+    t.join();
+
+    EXPECT_TRUE(exception_ok);
+}
+
+#endif
+
+}} // namespace
diff --git a/modules/python/test/test_async.py b/modules/python/test/test_async.py
new file mode 100644
index 0000000000..1f816208e9
--- /dev/null
+++ b/modules/python/test/test_async.py
@@ -0,0 +1,33 @@
+#!/usr/bin/env python
+from __future__ import print_function
+
+import numpy as np
+import cv2 as cv
+
+from tests_common import NewOpenCVTests
+
+class AsyncTest(NewOpenCVTests):
+
+    def test_async_simple(self):
+        m = np.array([[1,2],[3,4],[5,6]])
+        async_result = cv.utils.testAsyncArray(m)
+        self.assertTrue(async_result.valid())
+        ret, result = async_result.get(timeoutNs=10**6)  # 1ms
+        self.assertTrue(ret)
+        self.assertFalse(async_result.valid())
+        self.assertEqual(cv.norm(m, result, cv.NORM_INF), 0)
+
+
+    def test_async_exception(self):
+        async_result = cv.utils.testAsyncException()
+        self.assertTrue(async_result.valid())
+        try:
+            _ret, _result = async_result.get(timeoutNs=10**6)  # 1ms
+            self.fail("Exception expected")
+        except cv.error as e:
+            self.assertEqual(cv.Error.StsOk, e.code)
+
+
+
+if __name__ == '__main__':
+    NewOpenCVTests.bootstrap()