Boardy
Boardy

Reputation: 36217

Threading issue where 1 thread works but causes the calling method to not return

I am currently learning C++ and I am having an odd issue with threads. I've done lots of threaded stuff in Java and C# and not had an issue. I am currently trying to replicate a C# library that I have in C++ with a library and a test app.

In main.cpp I create an instance of a InitialiseLibrary class and call the method initialise. This does config loading and is then supposed to start a thread which stays running through the duration the application is running. I am expecting this thread to start, and then my initialise function returns true where I then continue and I create an instance of a test class which writes to a log file every 1 second.

Below is main.cpp

InitialiseLibrary initLibrary("config.ini");
        if (!initLibrary.initialise(1))
        {
            cout << "Failed to initialise library" << endl;
            return EXIT_FAILURE;
        }
TestClass testClass;
        testClass.writeSomeLogsInThread();


        cout << "The library config log file is: " << GlobalConfig::GeneralConfig::logFile << endl;

In my Initialise method (which is in the library) I have:

bool InitialiseLibrary::initialise(int applicationAlarmID, int applicationTerminationTimeout)
{
    //statusManager.setApplicationStatus(StatusManager::ApplicationStatus::Starting);
    if (!this->loadInconfiguration(applicationAlarmID))
    {
        cout << "*****Failed to configuration. Cannot continue******" << endl;
        return false;
    }

    GlobalConfig::libraryInitialised = true;
    LogRotation logRotation;
    logRotation.startLogRotation();
    BitsLibrary bitsLibrary;
    //Set up the signal handler if its needed, 0 means it terminates instantly, doesn't wait -1 is don't use signal handler
    if (applicationTerminationTimeout >= 0)
    {
        bitsLibrary.setupSignalHandler(applicationTerminationTimeout);
    }
    return true;
}

As you can see, I read in the configuration and I then call `logRotation.startLogRotation().

Where I have the following code:

void LogRotation::startLogRotation()
{
    //Is the configuration successfully loaded
    if (!LogRotateConfiguration::configurationLoaded)
    {
        throw exception("Log Rotation not initialised");
    }
    BitsLibrary bitsLibrary;
    stringstream logStream;
    logStream << "Log rotation thread starting, monitor cycle time is " << LogRotateConfiguration::archiveSleepTimeInSeconds << " second(s)";
    bitsLibrary.writeToLog(logStream.str(), "LogRotation", "startLogRotation");
    thread logRotationThread(&LogRotation::logRotationThread, this);
    logRotationThread.join();
}

void LogRotation::logRotationThread()
{
    BitsLibrary bitsLibrary;
    while (bitsLibrary.getApplicationStatus() == StatusManager::ApplicationStatus::Starting || bitsLibrary.getApplicationStatus() == StatusManager::ApplicationStatus::Running)
    {
        bitsLibrary.writeToLog("Running log monitoring");
        this_thread::sleep_for(chrono::seconds(LogRotateConfiguration::archiveSleepTimeInSeconds));
    }
    stringstream logStream;
    logStream << "Log rotation archive monitoring stopped. Current application status: " << bitsLibrary.getApplicationStatus();
    bitsLibrary.writeToLog(logStream.str(), "LogRotation", "logRotationThread");
}

Here I am expecting, startLogRotation() to start run the method logRotationThread within a thread, the thread starts, and the startLogrotation() method finishes and backs through the stack to the initialise() method where that returns true, back up to main where I can then call my TestClass method within a thread.

For some reason though, the thread starts and keeps logging every few seconds Running log monitoring so I know the thread has started, yet it doesn't seem to return back to the initialise function to return true, so the app gets stuck on that function call and doesn't go any further.

I've read that you need to run join on the thread to keep it in sync with the main thread, otherwise the main thread exits, while the new threads are running and cause a SIGABRT which in deed it does, but having the join there seems to stop the method returning.

Upvotes: 0

Views: 79

Answers (4)

robor
robor

Reputation: 3089

From what I can tell,

logRotationThread is a long running thread (it runs for the entire duration of the application). There is no need for the main thread to ever wait (join) on the logRotationThread.

For starters remove logRotationThread.join().

Upvotes: 0

molbdnilo
molbdnilo

Reputation: 66451

join waits for a thread to finish executing, so when you join, startLogRotation won't return until that happens.

Also, the normal rules of scope and lifetime apply to thread objects - logRotationThread will be destroyed when startLogRotation returns.
If the thread is "joinable" at the time of destruction, it's an error.

The simplest solution is probably to have a LogRotation member in InitialiseLibrary and a thread member in LogRotation.

You can then join the thread in LogRotation's destructor.

Upvotes: 1

Rob K
Rob K

Reputation: 8926

Your main thread blocks at this line, waiting for the logRotationThread to end.

logRotationThread.join();

Your main thread should go and do whatever work it needs to do after spawning the other thread, then only when it has nothing left to do should it join() the log rotate thread.

Upvotes: 1

UKMonkey
UKMonkey

Reputation: 6993

"having an odd issue with threads" - welcome to the world of threading!

I think you mis-understand the stack of the thread you create, and how it will interact with the main thread. The created thread's stack starts at the function you tell it to start with - when that function completes, the thread dies (which will then allow your join() to stop blocking)

With regards to your communication between threads, I think you should have a read about mutex's, conditions and semaphores.

Upvotes: 0

Related Questions