Propagate exceptions from worker threads back to main thread
diff --git a/common/rfb/DecodeManager.cxx b/common/rfb/DecodeManager.cxx
index 724cf21..d6c3b0b 100644
--- a/common/rfb/DecodeManager.cxx
+++ b/common/rfb/DecodeManager.cxx
@@ -36,7 +36,7 @@
 static LogWriter vlog("DecodeManager");
 
 DecodeManager::DecodeManager(CConnection *conn) :
-  conn(conn)
+  conn(conn), threadException(NULL)
 {
   size_t cpuCount;
 
@@ -71,6 +71,8 @@
     threads.pop_back();
   }
 
+  delete threadException;
+
   while (!freeBuffers.empty()) {
     delete freeBuffers.back();
     freeBuffers.pop_back();
@@ -121,6 +123,9 @@
 
   queueMutex->unlock();
 
+  // First check if any thread has encountered a problem
+  throwThreadException();
+
   // Read the rect
   bufferStream->clear();
   decoder->readRect(r, conn->getInStream(), conn->cp, bufferStream);
@@ -163,6 +168,33 @@
     producerCond->wait();
 
   queueMutex->unlock();
+
+  throwThreadException();
+}
+
+void DecodeManager::setThreadException(const rdr::Exception& e)
+{
+  os::AutoMutex a(queueMutex);
+
+  if (threadException == NULL)
+    return;
+
+  threadException = new rdr::Exception("Exception on worker thread: %s", e.str());
+}
+
+void DecodeManager::throwThreadException()
+{
+  os::AutoMutex a(queueMutex);
+
+  if (threadException == NULL)
+    return;
+
+  rdr::Exception e(*threadException);
+
+  delete threadException;
+  threadException = NULL;
+
+  throw e;
 }
 
 DecodeManager::DecodeThread::DecodeThread(DecodeManager* manager)
@@ -218,8 +250,9 @@
       entry->decoder->decodeRect(entry->rect, entry->bufferStream->data(),
                                  entry->bufferStream->length(),
                                  *entry->cp, entry->pb);
+    } catch (rdr::Exception e) {
+      manager->setThreadException(e);
     } catch(...) {
-      // FIXME: Try to get the exception back to the main thread
       assert(false);
     }