Merge "[569144] Fix FORTE_LINKED_STRINGDICT definition" into develop
diff --git a/src/arch/posix/forte_sem.cpp b/src/arch/posix/forte_sem.cpp
index ef7cefb..288d5c4 100644
--- a/src/arch/posix/forte_sem.cpp
+++ b/src/arch/posix/forte_sem.cpp
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2016, 2018 fortiss GmbH, TU Vienna/ACIN
+ * Copyright (c) 2016, 2020 fortiss GmbH, TU Vienna/ACIN, OFFIS e.V.
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License 2.0 which is available at
  * http://www.eclipse.org/legal/epl-2.0.
@@ -10,12 +10,15 @@
  *  Alois Zoitl - initial API and implementation and/or initial documentation
  *  Peter Gsellmann, Martin Melik-Merkumians - adds timed wait and try and no wait
  *    and documentation
+ *  Jörg Walter - make timed wait work withoout busy-loop, switch to binary
+ *    semaphore
+ *
  *******************************************************************************/
 
-#include <errno.h>
-#include <string.h>
-#include <sys/time.h>
-#include <time.h>
+#include <cerrno>
+#include <cstring>
+#include <ctime>
+#include <cassert>
 
 #include "forte_sem.h"
 #include "../devlog.h"
@@ -24,44 +27,102 @@
 namespace forte {
   namespace arch {
 
-    CPThreadSemaphore::CPThreadSemaphore(unsigned int paInitialValue){
-      if(-1 == sem_init(&mSemaphore, 0, paInitialValue)){
-        DEVLOG_ERROR("Could not initialize suspend sempaphore: %s\n", strerror(errno));
+    CPThreadSemaphore::CPThreadSemaphore(bool paInitialValue)
+      : mPosted(paInitialValue)
+    {
+      pthread_condattr_t condAttr;
+
+      if (pthread_condattr_init(&condAttr) != 0) {
+        DEVLOG_ERROR("Could not initialize cv attributes\n");
+      }
+      if (pthread_condattr_setclock(&condAttr, CLOCK_MONOTONIC) != 0) {
+        DEVLOG_ERROR("Could not set cv clock\n");
+      }
+      if (pthread_cond_init(&mCond, &condAttr) != 0) {
+        DEVLOG_ERROR("Could not initialize condition variable\n");
+      }
+      pthread_condattr_destroy(&condAttr);
+
+      if (pthread_mutex_init(&mMutex, NULL) != 0) {
+        DEVLOG_ERROR("Could not initialize mutex\n");
       }
     }
 
+    
     CPThreadSemaphore::~CPThreadSemaphore(){
-      sem_destroy(&mSemaphore);
+      pthread_cond_destroy(&mCond);
+      pthread_mutex_destroy(&mMutex);
     }
 
+
     void CPThreadSemaphore::inc(){
-      sem_post(&mSemaphore);
+      pthread_mutex_lock(&mMutex);
+      mPosted = true;
+      pthread_cond_signal(&mCond);
+      pthread_mutex_unlock(&mMutex);
     }
 
+
     void CPThreadSemaphore::waitIndefinitely(){
-      while((-1 == sem_wait(&mSemaphore)) && (errno == EINTR)); //handle interrupts from signals
+      pthread_mutex_lock(&mMutex);
+      while (!mPosted) {
+        pthread_cond_wait(&mCond, &mMutex);
+      }
+      mPosted = false;
+      pthread_mutex_unlock(&mMutex);
     }
 
+
     bool CPThreadSemaphore::timedWait(const TForteUInt64 paRelativeTimeout){
-      timespec timeoutSpec = { static_cast<time_t>(paRelativeTimeout / scmSecondInNanoSeconds), static_cast<time_t>(paRelativeTimeout % scmSecondInNanoSeconds) };
+      pthread_mutex_lock(&mMutex);
+
+      if (mPosted) {
+        mPosted = false;
+        pthread_mutex_unlock(&mMutex);
+        return true;
+      }
+
+      timespec timeoutSpec = {
+        static_cast<time_t>(paRelativeTimeout / 1000000000ULL),
+        static_cast<time_t>(paRelativeTimeout % 1000000000ULL)
+      };
+
       timespec currentTime = { 0, 0 };
       clock_gettime(CLOCK_MONOTONIC, &currentTime);
 
-      timespec expectedAbsoluteTimeoutTime = {0, 0};
+      timespec expectedAbsoluteTimeoutTime = { 0, 0 };
       timespecAdd(&currentTime, &timeoutSpec, &expectedAbsoluteTimeoutTime);
 
-      do{
-        if(0 == sem_trywait(&mSemaphore)){
-          return true;
-        }
-        clock_gettime(CLOCK_MONOTONIC, &currentTime);
+      int rc = 0;
+      while (!mPosted && rc == 0) {
+        rc = pthread_cond_timedwait(&mCond, &mMutex, &expectedAbsoluteTimeoutTime);
+      }
 
-      } while(timespecLessThan(&currentTime, &expectedAbsoluteTimeoutTime));
-      return false;
+      if (rc != 0 && rc != ETIMEDOUT) {
+        DEVLOG_ERROR("Unexpected error during condition variable wait: %i\n", rc);
+      }
+
+      assert(!(rc == 0 && !mPosted)
+             && (bool)"should have been posted when waiting successfully");
+
+      bool success = (mPosted && rc == 0);
+      if (success) {
+        mPosted = false;
+      }
+
+      pthread_mutex_unlock(&mMutex);
+      return success;
     }
 
+
     bool CPThreadSemaphore::tryNoWait(){
-      return (0 == sem_trywait(&mSemaphore));
+      pthread_mutex_lock(&mMutex);
+
+      bool success = mPosted;
+      mPosted = false;
+
+      pthread_mutex_unlock(&mMutex);
+      return success;
     }
   } /* namespace arch */
 } /* namespace forte */
diff --git a/src/arch/posix/forte_sem.h b/src/arch/posix/forte_sem.h
index 2ce6be9..a85ec20 100644
--- a/src/arch/posix/forte_sem.h
+++ b/src/arch/posix/forte_sem.h
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2016, 2018 fortiss GmbH, TU Vienna/ACIN
+ * Copyright (c) 2016, 2020 fortiss GmbH, TU Vienna/ACIN, OFFIS e.V.
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License 2.0 which is available at
  * http://www.eclipse.org/legal/epl-2.0.
@@ -10,25 +10,28 @@
  *  Alois Zoitl - initial API and implementation and/or initial documentation
  *  Peter Gsellmann, Martin Melik-Merkumians - adds timed wait and try and no wait
  *    and documentation
+ *  Jörg Walter - make timed wait work withoout busy-loop, switch to binary
+ *    semaphore
+ *
  *******************************************************************************/
 
 #ifndef SRC_ARCH_POSIX_SEMAPHORE_H_
 #define SRC_ARCH_POSIX_SEMAPHORE_H_
 
-#include <semaphore.h>
+#include <pthread.h>
 #include "datatype.h"
 
 namespace forte {
   namespace arch {
 
-    /*!\brief counting semaphore for syncing operation in FORTE
+    /*!\brief binary semaphore for syncing operation in FORTE
      *
      * The semaphore is initialized with the value given.
      */
     class CPThreadSemaphore{
       public:
 
-        explicit CPThreadSemaphore(unsigned int paInitialValue = 0);
+        explicit CPThreadSemaphore(bool paInitialValue = false);
         ~CPThreadSemaphore();
 
         /** @brief Unlocks (increments) the semaphore
@@ -55,8 +58,24 @@
         bool tryNoWait();
 
       private:
-        sem_t mSemaphore;
-        static const TForteUInt64 scmSecondInNanoSeconds = (TForteUInt64)1E9;
+        /* TODO: in C++11, this class should be properly made non-copyable since
+         * mutexes cannot be copied after initialisation
+
+        CPThreadSemaphore(const CPThreadSemaphore &) = delete;
+        CPThreadSemaphore &operator=(const CPThreadSemaphore &) = delete;
+        */
+        CPThreadSemaphore(const CPThreadSemaphore &);
+        CPThreadSemaphore &operator=(const CPThreadSemaphore &);
+
+        /* Implementation is based on POSIX condition variables instead of POSIX
+         * semaphores, because POSIX semaphores cannot safely wait without busy
+         * looping. Derived from https://stackoverflow.com/a/57496953 */
+
+        pthread_mutex_t mMutex;
+        pthread_cond_t mCond;
+
+        bool mPosted;
+
     };
 
     typedef CPThreadSemaphore CSemaphore;
diff --git a/src/com/mqtt_paho/MQTTHandler.cpp b/src/com/mqtt_paho/MQTTHandler.cpp
index 6041eac..0a476e3 100644
--- a/src/com/mqtt_paho/MQTTHandler.cpp
+++ b/src/com/mqtt_paho/MQTTHandler.cpp
@@ -35,7 +35,7 @@
 
 
 MQTTStates MQTTHandler::smMQTTS_STATE = NOT_CONNECTED;
-forte::arch::CSemaphore MQTTHandler::mStateSemaphore = forte::arch::CSemaphore();
+forte::arch::CSemaphore MQTTHandler::mStateSemaphore;
 bool MQTTHandler::mIsSemaphoreEmpty = true;
 
 MQTTHandler::MQTTHandler(CDeviceExecution& paDeviceExecution) : CExternalEventHandler(paDeviceExecution)  {