c++boostmessage-queueboost-interprocess

boost::interprocess message queue not removing


Using boost::interprocess message queue, I ran into a problem with removing the queue, was hoping someone could provide an explanation for the behavior, or correct my misunderstanding. I'm on a Linux RHEL7 box. I have two processes, one that creates and feeds the queue, and the other one that opens the message queue and reads from it. For clarity, the initial process is started first, before the second process that does the reading/removing msgs from queue. The one that creates, is create_only_t. I want it to fail if it already exists. The creation of the first queue always fails however. The specific exception it throws is File exists.

When switched to an open_or_create_t queue, it works fine. I took this information as I wasn't cleaning it up properly, so I made sure I was trying to remove the queue before I tried to create it, as well as after the process finished sending all messages. I would log if the remove was successful. One of my assumptions is that if the remove returns true it means it successfully removed the queue. The boost docs for remove reads: "Removes the message queue from the system. Returns false on error.", I wasn't sure if maybe a true just means that it had a successful 'attempt' at removing it. Upon further looking at another Boost Inprocess pdf it explains:

The remove operation might fail returning false if the shared memory does not exist, the file is open or the file is still memory mapped by other processes

Either case, I feel like one would expect the queue to be removed if it always returns true, which is currently my case. Still when trying to do a 'create_t' message queue it will continue to fail, but 'open_or_create_t' still works.

I had a hard time understanding the behavior, so I also tried to remove the message queue twice in a row before trying to initialize a create_t queue to see if the second one would fail/return false, however both returned true (which was not what I expected based on what the documentation said). The first remove should it be successful means the second one should fail as there should no longer be a message queue that exists anymore.

I've attached a snippet of my create process code. And I'll note, that this error happens without the "open process" being run.

Maybe I'm missing something obvious, thank you in advance.

try {
    bool first_removal = remove(msg_queue_name);
    if (first_removal) {
      log_info("first removal - true"); // this log always prints
      bool second_removal = remove(msg_queue_name);
      if (second_removal ) {
        log_info("removal was again true"); // this also always prints
      } else {
        log_info("second removal - false");
      }
    } else {
      log_info("did not remove queue before creation");
    }

    log_info("attempting to initialize msg queue");
    message_queue mq(ooc, msg_queue_name, max_num_msgs, max_msg_size); // this is where it will fail (File exists)
  
    while(1) {
      // insertion logic, but does not get here
    }
  } catch ( interprocess_exception& err ) {
    log_error(err.what()); // File exists
    bool removal_after_failure = remove(msg_queue_name);
    if (removal_after_failure) {
      log_info("Message queue was successfully removed"); // always logs here after every failure
    } else {
      log_warn("Message queue was NOT removed");
    }
  }

Solution

  • It worked for me.

    Then it dawned on me. You're probably using namespace. Don't. For this reason:

    bool first_removal = remove(msg_queue_name);
    

    This doesn't call the function you expect. It calls ::remove from the C standard library.

    Simply qualify your call:

    bool first_removal = message_queue::remove(msg_queue_name);
    

    Measures

    What you can do:

    Linters?

    E.g. clang-tidy reported:

    test.cpp|27 col 30| warning: implicit conversion 'int' -> bool [readability-implicit-bool-conversion]
    ||         bool first_removal = remove(msg_queue_name);
    

    Suggesting to write:

    bool first_removal = remove(msg_queue_name) != 0;
    

    This tipped me off something might be fishy.

    Fixes

    Several of these fixes later and the code runs

    Live On Coliru

    #include <boost/interprocess/ipc/message_queue.hpp>
    #include <chrono>
    #include <iostream>
    
    namespace bip = boost::interprocess;
    using bip::message_queue;
    using bip::interprocess_exception;
    using namespace std::chrono_literals;
    using C = std::chrono::high_resolution_clock;
    
    static constexpr char const* msg_queue_name = "my_mq";
    static constexpr bip::open_or_create_t ooc;
    static constexpr auto max_num_msgs = 10;
    static constexpr auto max_msg_size = 10;
    
    static auto log_impl = [start=C::now()](auto severity, auto const& ... args) {
        std::clog << severity << " at " << std::fixed << (C::now()-start)/1.ms << "ms ";
        (std::clog << ... << args) << std::endl;
    };
    
    static auto log_error = [](auto const& ... args) { log_impl("error", args...); };
    static auto log_warn = [](auto const& ... args) { log_impl("warn", args...); };
    static auto log_info = [](auto const& ... args) { log_impl("info", args...); };
    
    int main() {
        try {
            bool first_removal = message_queue::remove(msg_queue_name);
            if (first_removal) {
                log_info("first removal - true"); // this log always prints
                bool second_removal = message_queue::remove(msg_queue_name);
                if (second_removal) {
                    log_info("removal was again true"); // this also always prints
                } else {
                    log_info("second removal - false");
                }
            } else {
                log_info("did not remove queue before creation");
            }
    
            log_info("attempting to initialize msg queue");
            message_queue mq(
                ooc, msg_queue_name, max_num_msgs,
                max_msg_size); // this is where it will fail (File exists)
    
            log_info("Start insertion");
        } catch (interprocess_exception& err) {
            log_error(err.what()); // File exists
            bool removal_after_failure = message_queue::remove(msg_queue_name);
            if (removal_after_failure) {
                log_info("Message queue was successfully removed"); // always logs
                                                                    // here after
                                                                    // every failure
            } else {
                log_warn("Message queue was NOT removed");
            }
        }
    }
    

    Printing, on Coliru:

    info at 22.723521ms did not remove queue before creation
    info at 22.879425ms attempting to initialize msg queue
    error at 23.098989ms Function not implemented
    warn at 23.153540ms Message queue was NOT removed
    

    On my system:

    info at 0.148484ms first removal - true
    info at 0.210316ms second removal - false
    info at 0.232181ms attempting to initialize msg queue
    info at 0.299645ms Start insertion
    ./sotest 
    info at 0.099407ms first removal - true
    info at 0.173156ms second removal - false
    info at 0.188026ms attempting to initialize msg queue
    info at 0.257117ms Start insertion
    

    Of course now your logic can be greatly simplified:

    Live On Coliru

    int main() {
        try {
            bool removal = message_queue::remove(msg_queue_name);
            log_info("attempting to initialize msg queue (removal:", removal, ")");
            message_queue mq(
                ooc, msg_queue_name, max_num_msgs,
                max_msg_size); // this is where it will fail (File exists)
    
            log_info("insertion");
        } catch (interprocess_exception const& err) {
            bool removal = message_queue::remove(msg_queue_name);
            log_info(err.what(), " (removal:", removal, ")");
        }
    }
    

    Printing

    info at 0.462333ms attempting to initialize msg queue (removal:false)
    info at 0.653085ms Function not implemented (removal:false)
    

    Or

    info at 0.097283ms attempting to initialize msg queue (removal:true)
    info at 0.239138ms insertion