1

I encountered this snippet of code in a book and it looks very strange to me since the convention is to release always the lock but when notify_all() gets called it is not.

My question is whether the lock called from unique_lock gets released automatically after the block gets exited? I know this is released by using RAII lock_guard but that is not my concerned.

class Logger
{
    public:
    // Starts a background thread writing log entries to a file.
    Logger();
    // Prevent copy construction and assignment.
    Logger(const Logger& src) = delete;
    Logger& operator=(const Logger& rhs) = delete;
    // Add log entry to the queue.
    void log(const std::string& entry);

    // Gracefully shut down background thread.
    virtual ~Logger();

    private:
    // The function running in the background thread.

    void processEntries();
    // Mutex and condition variable to protect access to the queue.
    std::mutex mMutex;
    std::condition_variable mCondVar;
    std::queue<std::string> mQueue;
    // The background thread.
    std::thread mThread;

    std::atomic<bool> mExit;
    // Other members omitted for brevity.
};

Logger::Logger()
{
    // Start background thread.
    mThread  = thread{ &Logger::processEntries, this};


}

void Logger::log(const std::string& entry)
{
    // Lock mutex and add entry to the queue.
    unique_lock<mutex> lock(mMutex);
    mQueue.push(entry);
    // Notify condition variable to wake up thread.
    mCondVar.Notify_all();

    // the lock should be released?
}

void Logger::processEntries() 
{
    // Open log file.
    ofstream ofs("log.txt");
    if (ofs.fail()){
        cerr << "Failed to open logfile." << endl;
        return;
    }

    // Start processing loop.
    unique_lock<mutex> lock(mMutex);

    while (true){
        // Wait for a notification.
        mCondVar.wait(lock)
        // Condition variable is notified, so something might be in the queue.
        lock.unlock();

// I am pretty sure the moment lock.unlock() is called the other threads will acquire the lock and the instruction will not jump directly to while(true) which kind of defeats the purpose of the application.
// Code continues...
        while (true){
            lock.lock();
            if (mQueue.empty()) {
                break;
            } else {
                ofs << mQueue.front() << endl;
                mQueue.pop();
            }
            lock.unllock();
        }
    }
}


void logSomeMessages(int id, Logger& logger)
{
    for (int =0; i < 10; ++i){
        stringstream ss;
        ss << "Log entry " << i << " from thread " << id;
        logger.log(ss.str());
    }
}

int main()
{
    Logger logger;
    vector<thread> threads;
    // Create a few threads all working with the same Logger instance.
    for (int i=0; i <10; ++i){
        threads.emplace_back(logSomeMessages, i, ref(logger))
    }

}
Barry
  • 286,269
  • 29
  • 621
  • 977
Erindy
  • 155
  • 11
  • 1
    Is that the full code example? The lock _is_ released there: [RAII](https://stackoverflow.com/q/2321511/2069064) – Barry Aug 31 '18 at 15:41
  • Yes this is the full one, of course the concurrent queue has been omitted, I am familiar with raii, but I don't see it being used here, at least not with lock_guard, as far as I know when using unique_lock you have to lock it and unlock it manually and doesn't get unlocked automatically. – Erindy Aug 31 '18 at 16:01
  • That's the nice thing about RAII - you don't see it being used. It just works. `unique_lock` manages the lock for you. – Barry Aug 31 '18 at 17:15
  • So am I correct to assume that the lock is automatically released at the end of the block regardless whether I don't unlock the mutex manually? – Erindy Aug 31 '18 at 17:29
  • Yep. [`~unique_lock()`](https://en.cppreference.com/w/cpp/thread/unique_lock/~unique_lock) – Barry Aug 31 '18 at 17:41

0 Answers0