diff options
author | Patrick Ohly <patrick.ohly@intel.com> | 2013-04-26 02:41:41 -0700 |
---|---|---|
committer | Patrick Ohly <patrick.ohly@intel.com> | 2013-05-13 17:49:50 +0200 |
commit | 9d2df20cccc3ef096fc42d23c8bd8444562b8db3 (patch) | |
tree | bbd2db56de495dd4794b0c7d8e8560ba2fdfc3e1 | |
parent | 9a4c770d8e8b2ee5d8ac766faf7c7362fde3a82b (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.cpp | 77 |
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; |