summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorStanislaw Halik <sthalik@misaki.pl>2016-12-30 04:39:26 +0100
committerStanislaw Halik <sthalik@misaki.pl>2016-12-30 04:39:26 +0100
commite712a6eea96d381d2f1e3c0f8328d870321b31de (patch)
tree2df5a79fb5d3b219f0fbeae68c41332608e98661
parent07ef302c354369ffdecf7016207a01097c3cdc59 (diff)
compat: prevent deadlock with race
We can't depend on cvar getting notified only after "src" runs out of scope. Now, in case signal destroyed() runs first: - mtx locked - flag set to true - empty cvar notified Thus, doesn't hang. In case we wait first: - mtx locked - flag is false - cvar notification arrives Of course semaphore code always runs serially since they're covered by a mutex. We have all our bases covered. Previously the code never hung simply because the "curthread" condition was always true. I removed the "curthread" code paths since they don't add anything. Also rvalue references got used incorrectly.
-rw-r--r--compat/run-in-thread.hpp70
1 files changed, 42 insertions, 28 deletions
diff --git a/compat/run-in-thread.hpp b/compat/run-in-thread.hpp
index 90aa143b..ba99d0b9 100644
--- a/compat/run-in-thread.hpp
+++ b/compat/run-in-thread.hpp
@@ -16,10 +16,21 @@ template<typename t>
struct run_in_thread_traits
{
using type = t;
- using ret_type = t&&;
+ using ret_type = t;
+ static inline void assign(t& lvalue, const t& rvalue) { lvalue = rvalue; }
+ static inline t pass(const t& val) { return val; }
+ template<typename F> static inline t call(F&& fun) { return std::move(fun()); }
+};
+
+template<typename u>
+struct run_in_thread_traits<u&&>
+{
+ using t = typename std::remove_reference<u>::type;
+ using type = t;
+ using ret_type = u;
static inline void assign(t& lvalue, t&& rvalue) { lvalue = rvalue; }
- static inline ret_type&& pass(ret_type&& val) { return std::move(val); }
- template<typename F> static ret_type call(F& fun) { return std::move(fun()); }
+ static inline t&& pass(t&& val) { return val; }
+ template<typename F> static inline t&& call(F&& fun) { return std::move(fun()); }
};
template<>
@@ -29,7 +40,7 @@ struct run_in_thread_traits<void>
using ret_type = void;
static inline void assign(unsigned char&, unsigned char&&) {}
static inline void pass(type&&) {}
- template<typename F> static type call(F& fun) { fun(); return type(0); }
+ template<typename F> static type call(F&& fun) { fun(); return type(0); }
};
}
@@ -40,45 +51,48 @@ auto run_in_thread_sync(QObject* obj, F&& fun)
{
using lock_guard = std::unique_lock<std::mutex>;
- std::mutex mtx;
- lock_guard guard(mtx);
- std::condition_variable cvar;
-
- std::thread::id waiting_thread = std::this_thread::get_id();
-
using traits = qt_impl_detail::run_in_thread_traits<decltype(std::forward<F>(fun)())>;
typename traits::type ret;
- bool skip_wait = false;
+ struct semaphore final
+ {
+ std::mutex mtx;
+ std::condition_variable cvar;
+ bool flag;
+
+ semaphore() : flag(false) {}
+
+ void wait()
+ {
+ lock_guard guard(mtx);
+ while (!flag)
+ cvar.wait(guard);
+ }
+
+ void notify()
+ {
+ lock_guard guard(mtx);
+ flag = true;
+ cvar.notify_one();
+ }
+ };
+
+ semaphore sem;
{
QObject src;
- QThread* t(obj->thread());
- assert(t);
- src.moveToThread(t);
QObject::connect(&src,
&QObject::destroyed,
obj,
[&]() {
- std::thread::id calling_thread = std::this_thread::get_id();
- if (waiting_thread == calling_thread)
- {
- skip_wait = true;
- traits::assign(ret, traits::call(fun));
- }
- else
- {
- lock_guard guard(mtx);
- traits::assign(ret, traits::call(fun));
- cvar.notify_one();
- }
+ traits::assign(ret, traits::call(fun));
+ sem.notify();
},
Qt::AutoConnection);
}
- if (!skip_wait)
- cvar.wait(guard);
+ sem.wait();
return traits::pass(std::move(ret));
}