summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPatrick Ohly <patrick.ohly@intel.com>2013-04-26 02:41:41 -0700
committerPatrick Ohly <patrick.ohly@intel.com>2013-05-13 17:49:50 +0200
commit9d2df20cccc3ef096fc42d23c8bd8444562b8db3 (patch)
treebbd2db56de495dd4794b0c7d8e8560ba2fdfc3e1
parent9a4c770d8e8b2ee5d8ac766faf7c7362fde3a82b (diff)
engine: override blocking threading code in libsynthesis
We need to keep the glib event loop running while the main thread waits for the background thread to finish. Otherwise code using glib events (like EDS) may get stuck (observed with Sleep() when using g_timeout_add() and synchronous calls in EDS 3.6). We also need to watch for abort requests. When aborted, we give up waiting for the thread and tell the engine to proceed. This will eventually return control to us, where we can shut down. When libsynthesis shuts down, it'll wait forever for the background thread's shutdown. In this case we must wait and hope that the background thread completes or aborts by itself.
-rw-r--r--src/syncevo/SyncContext.cpp77
1 files changed, 77 insertions, 0 deletions
diff --git a/src/syncevo/SyncContext.cpp b/src/syncevo/SyncContext.cpp
index a2b2b338..b044b4a4 100644
--- a/src/syncevo/SyncContext.cpp
+++ b/src/syncevo/SyncContext.cpp
@@ -2931,6 +2931,81 @@ extern "C" int (*SySync_ConsolePrintf)(FILE *stream, const char *format, ...);
static int nopPrintf(FILE *stream, const char *format, ...) { return 0; }
+extern "C"
+{
+ extern int (*SySync_CondTimedWait)(pthread_cond_t *cond, pthread_mutex_t *mutex, bool &aTerminated, long aMilliSecondsToWait);
+}
+
+#ifdef HAVE_GLIB
+static gboolean timeout(gpointer data)
+{
+ // Call me again...
+ return true;
+}
+
+static int CondTimedWaitGLib(pthread_cond_t * /* cond */, pthread_mutex_t *mutex,
+ bool &terminated, long milliSecondsToWait)
+{
+ int result = 0;
+
+ // return abstime ? pthread_cond_timedwait(cond, mutex, abstime) : pthread_cond_wait(cond, mutex);
+ try {
+ pthread_mutex_unlock(mutex);
+
+ SE_LOG_DEBUG(NULL, "wait for background thread: %lds", milliSecondsToWait);
+ SuspendFlags &flags = SuspendFlags::getSuspendFlags();
+
+ Timespec now = Timespec::system();
+ Timespec wait(milliSecondsToWait / 1000, milliSecondsToWait % 1000);
+ Timespec deadline = now + wait;
+
+ // We don't need to react to thread shutdown immediately (only
+ // called once per sync), so a relatively long check interval of
+ // one second is okay.
+ GLibEvent id(g_timeout_add_seconds(1, timeout, NULL), "timeout");
+
+ while (true) {
+ // Thread has terminated?
+ pthread_mutex_lock(mutex);
+ if (terminated) {
+ pthread_mutex_unlock(mutex);
+ SE_LOG_DEBUG(NULL, "background thread completed");
+ break;
+ }
+ pthread_mutex_unlock(mutex);
+
+ // Abort? Ignore when waiting for final thread shutdown, because
+ // in that case we just get called again.
+ if (milliSecondsToWait > 0 && flags.isAborted()) {
+ SE_LOG_DEBUG(NULL, "give up waiting for background thread, aborted");
+ // Signal error. libsynthesis then assumes that the thread still
+ // runs and enters its parallel message sending, which eventually
+ // returns control to us.
+ result = 1;
+ break;
+ }
+
+ // Timeout?
+ if (milliSecondsToWait > 0 && deadline <= Timespec::system()) {
+ SE_LOG_DEBUG(NULL, "give up waiting for background thread, timeout");
+ result = 1;
+ break;
+ }
+
+ // Check event loop with blocking. We'll return after one
+ // second.
+ g_main_context_iteration(NULL, true);
+ }
+ } catch (...) {
+ Exception::handle(HANDLE_EXCEPTION_FATAL);
+ }
+
+ pthread_mutex_lock(mutex);
+ return result;
+}
+
+#endif
+
void SyncContext::initMain(const char *appname)
{
#if defined(HAVE_GLIB)
@@ -2948,6 +3023,8 @@ void SyncContext::initMain(const char *appname)
// when called by the main thread, otherwise falls back to
// select().
g_main_context_acquire(NULL);
+
+ SySync_CondTimedWait = CondTimedWaitGLib;
#endif
if (atoi(getEnv("SYNCEVOLUTION_DEBUG", "0")) > 3) {
SySync_ConsolePrintf = Logger::sysyncPrintf;