samedi 28 janvier 2017

Communication between two components - how to resolve a deadlock?

I have two components which use interprocess communication:

ErrorController

Services:

  • void ReportError(const std::string& str) throws (ServiceTempUnavailable);
  • void ClearError(const std::string& str) throws (ServiceTempUnavailable);

References:

  • can call TestComponent::SolveError

TestComponent

Services:

  • void SolveError(const std::string& str) throws (ServiceTempUnavailable);

References:

  • can call ErrorController::ReportError and ErrorController::ClearError

I develop TestComponent and I know that ErrorController locks the same mutex in each service (ReportError, ClearError) and when it calls SolveError (TestComponent service). Here are the most important parts of the code from TestComponent:

void TestComponent::SendError(const std::string& str)
{
     std::lock_guard<std::mutex> guard(mtx_);
     componentError_ = str;
     clearError_ = false:
     needUpdate_ = true;
     cv_.notify_one();
}

//Next SendError will not be called if TestComponent has some unresolved error 

bool TestComponent::HasError()
{
     std::lock_guard<std::mutex> guard(mtx_);
     return componentError_.empty() ? false : true;
}

//service impl - is called by ErrorController
void TestComponent::SolveError(const std::string& str)
{
     std::lock_guard<std::mutex> guard(mtx_);
     if(str == componentError_)
     {
         clearError_ = true;
         needUpdate_ = true;
         cv_.notify_one();
     }
}

//errors processing is async (separate thread)
void TestComponent::ErrorProcessing()
{
     while(componentStarted_.load())
     {
          std::unique_lock<std::mutex> guard(mtx_);
          cv_.wait(guard, [this]{ return needUpdate_; });

          while(needUpdate_)
          {
               if(!componentStarted_.load())
               {
                    break;
               }

               if(clearError_)
               {
                    try
                    {   
                         ptr->ClearError(componentError);
                    }
                    catch(ServiceTempUnavailable ex)
                    {
                         guard.unlock();
                         std::this_thread::sleep_for(std::chrono::seconds(1));
                         guard.lock();
                         continue;  
                    }
                    needUpdate_ = false;
                    componentError_ = "";
                    //clearCallback can call SendError again
                    guard.unlock();
                    clearCallback();
               }
               else
               {
                    try
                    {
                         ptr->ReportError(componentError);
                    }
                    catch(ServiceTempUnavailable ex)
                    {
                         guard.unlock();
                         std::this_thread::sleep_for(std::chrono::seconds(1));
                         guard.lock();
                         continue;  
                    }
                    needUpdate_ = false;
               }
          }
     }
}

I need some tips how to improve my code. Above implementation may still leads to a deadlock:

  • TestComponent SendError is called
  • TestComponent locks the mutex in ErrorProcessing
  • ErrorController calls SolveError (I assume in tests that SolveError can be called at any time - even if TestComponent hasn't reported an error)
  • TestComponent can't call ReportError because ErrorController locks the mutex when it calls SolveError, SolveError waits for the mutex which is locked in ErrorProcessing function

Currently I can't change ErrorController implementation but maybe there should be used other locking strategy. How to fix TestComponent implementation ?

Aucun commentaire:

Enregistrer un commentaire