Boost shared memory and synchronized queue issue/crash in consumer process

喜夏-厌秋 提交于 2020-12-06 12:35:59

问题


I'm trying to consume from a child process a synchronized queue in c++. I'm using this synchronized queue in C++ () (http://www.internetmosquito.com/2011/04/making-thread-safe-queue-in-c-i.html)

I modified the queue to be serializable in boost and also replaced the used boost::mutex io_mutex_ to use instead an inteprocess mutex (thanks @Sehe) boost::interprocess::interprocess_mutex io_mutex_ And when locking I changed every line that has boost::mutex::scoped_lock lock(io_mutex_); to scoped_lock<interprocess_mutex> lock(io_mutex_);

template<class T>
class SynchronizedQueue
{
    friend class boost::serialization::access;
    template<class Archive>
    void serialize(Archive & ar, const unsigned int version)
    {
        ar & sQueue;
        ar & io_mutex_;
        ar & waitCondition;
    }
    ... // queue implementation (see [http://www.internetmosquito.com/2011/04/making-thread-safe-queue-in-c-i.html][2])

}

In my Test app, I'm creating the synchronized queue and storing in it 100 instances of this class:

class gps_position
{
    friend class boost::serialization::access;
    template<class Archive>
    void serialize(Archive & ar, const unsigned int version)
    {
        ar & degrees;
        ar & minutes;
        ar & seconds;
    }
public:
 int degrees;
 int minutes;
 float seconds;

 gps_position() {};
 gps_position(int d, int m, float s) :
 degrees(d), minutes(m), seconds(s)
 {}
};

Common definitions between Consumer and producer:

 char *SHARED_MEMORY_NAME = "MySharedMemory";
 char *SHARED_QUEUE_NAME  =  "MyQueue";
 typedef SynchronizedQueue<gps_position> MySynchronisedQueue;

Producer process code:

    // Remove shared memory if it was created before
    shared_memory_object::remove(SHARED_MEMORY_NAME);
    // Create a new segment with given name and size
    managed_shared_memory mysegment(create_only,SHARED_MEMORY_NAME, 65536);
    MySynchronisedQueue *myQueue = mysegment.construct<MySynchronisedQueue>(SHARED_QUEUE_NAME)();
    //Insert data in the queue
    for(int i = 0; i < 100; ++i)  {
        gps_position position(i, 2, 3);
        myQueue->push(position);
    }
    // Start 1 process (for testing for now)
    STARTUPINFO info1={sizeof(info1)};
    PROCESS_INFORMATION processInfo1;
    ZeroMemory(&info1, sizeof(info1));
    info1.cb = sizeof info1 ; //Only compulsory field
    ZeroMemory(&processInfo1, sizeof(processInfo1));
    // Launch child process
    LPTSTR szCmdline = _tcsdup(TEXT("ClientTest.exe"));
    CreateProcess(NULL, szCmdline, NULL, NULL, TRUE, 0, NULL, NULL, &info1, &processInfo1);
    // Wait a little bit ( 5 seconds) for the started client process to load
    WaitForSingleObject(processInfo1.hProcess, 5000);

    /* THIS TESTING CODE WORK HERE AT PARENT PROCESS BUT NOT IN CLIENT PROCESS
    // Open the managed segment memory
    managed_shared_memory openedSegment(open_only, SHARED_MEMORY_NAME);
    //Find the synchronized queue using it's name
    MySynchronisedQueue *openedQueue = openedSegment.find<MySynchronisedQueue>(SHARED_QUEUE_NAME).first;
    gps_position position;
    while (true) {
        if (myQueue->pop(position)) {
            std::cout << "Degrees= " << position.degrees << " Minutes= " << position.minutes << " Seconds= " << position.seconds;
            std::cout << "\n";
        }
        else
            break;
    }*/


    // Wait until the queue is empty: has been processed by client(s)
    while(myQueue->sizeOfQueue() > 0) continue;

    // Close process and thread handles. 
    CloseHandle( processInfo1.hThread );

My consumer code is as follow:

    //Open the managed segment memory
    managed_shared_memory segment(open_only, SHARED_MEMORY_NAME);
    //Find the vector using it's name
    MySynchronisedQueue *myQueue = segment.find<MySynchronisedQueue>(SHARED_QUEUE_NAME).first;
    gps_position position;
    // Pop each position until the queue become empty and output its values
    while (true)
    {
        if (myQueue->pop(position)) { // CRASH HERE
            std::cout << "Degrees= " << position.degrees << " Minutes= " << position.minutes << " Seconds= " << position.seconds;
            std::cout << "\n";
        }
        else
            break;
    }

When I run the parent process (producer) that create the queue and create the child (consumer) process, the child crash when trying to 'pop' from the queue.

What I'm doing wrong here ? Any idea ? Thanks for any insight. This is my first app creating using boost and shared memory.

My goal is to be able to consume this queue from multiple process. In the example above I'm creating only one child process to make sure first it works before creating other child process. The idea is the queue will be filled in advance by items and multiple created process will 'pop' items from it without clashing on each other.


回答1:


To the updated code:

  • you should be using interprocess_mutex if you're gonna share the queue; This implies a host of dependent changes.
  • your queue should be using a shared-memory allocator if you're gonna share the queue
  • the conditions should be raised under the mutex for reliable behaviour on all platforms
  • you failed to lock inside toString(). Even though you copy the collection, that's not nearly enough because the container may get modified during that copy.
  • The queue design makes much sense (what is the use of a "thread safe" function that returns empty()? It could be no longer empty/just empty before you process the return value... These are called race conditions and lead to really hard to track bugs
  • What has Boost Serialization got to do with anything? It seems just there to muddle the picture, because it's not required and not being used.
  • Likewise for Boost Any. Why is any used in toString()? Due to the design of the queue, the typeid is always gpsposition anyways.
  • Likewise for boost::lexical_cast<> (why are you doing string concatenation if you already have the stringstream anyways?)
  • Why are empty(), toString(), sizeOfQueue() not const?

I highly recommend to use boost::interprocess::message_queue. This seems to be what you actually wanted to use (since you were somehow

Here's a modified version that puts the container in shared memory and it works:

#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/interprocess/containers/deque.hpp>
#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/sync/interprocess_condition.hpp>
#include <boost/interprocess/sync/interprocess_mutex.hpp>
#include <boost/thread/lock_guard.hpp>
#include <sstream>

namespace bip = boost::interprocess;

template <class T> class SynchronizedQueue {

  public:
    typedef bip::allocator<T, bip::managed_shared_memory::segment_manager> allocator_type;
  private:
    bip::deque<T, allocator_type> sQueue;
    mutable bip::interprocess_mutex io_mutex_;
    mutable bip::interprocess_condition waitCondition;
  public:
    SynchronizedQueue(allocator_type alloc) : sQueue(alloc) {} 

    void push(T element) {
        boost::lock_guard<bip::interprocess_mutex> lock(io_mutex_);
        sQueue.push_back(element);
        waitCondition.notify_one();
    }
    bool empty() const {
        boost::lock_guard<bip::interprocess_mutex> lock(io_mutex_);
        return sQueue.empty();
    }
    bool pop(T &element) {
        boost::lock_guard<bip::interprocess_mutex> lock(io_mutex_);

        if (sQueue.empty()) {
            return false;
        }

        element = sQueue.front();
        sQueue.pop_front();

        return true;
    }
    unsigned int sizeOfQueue() const {
        // try to lock the mutex
        boost::lock_guard<bip::interprocess_mutex> lock(io_mutex_);
        return sQueue.size();
    }
    void waitAndPop(T &element) {
        boost::lock_guard<bip::interprocess_mutex> lock(io_mutex_);

        while (sQueue.empty()) {
            waitCondition.wait(lock);
        }

        element = sQueue.front();
        sQueue.pop();
    }

    std::string toString() const {
        bip::deque<T> copy;
        // make a copy of the class queue, to reduce time locked
        {
            boost::lock_guard<bip::interprocess_mutex> lock(io_mutex_);
            copy.insert(copy.end(), sQueue.begin(), sQueue.end());
        }

        if (copy.empty()) {
            return "Queue is empty";
        } else {
            std::stringstream os;
            int counter = 0;

            os << "Elements in the Synchronized queue are as follows:" << std::endl;
            os << "**************************************************" << std::endl;

            while (!copy.empty()) {
                T object = copy.front();
                copy.pop_front();
                os << "Element at position " << counter << " is: [" << typeid(object).name()  << "]\n";
            }
            return os.str();
        }
    }
};

struct gps_position {
    int degrees;
    int minutes;
    float seconds;

    gps_position(int d=0, int m=0, float s=0) : degrees(d), minutes(m), seconds(s) {}
};

static char const *SHARED_MEMORY_NAME = "MySharedMemory";
static char const *SHARED_QUEUE_NAME  =  "MyQueue";
typedef SynchronizedQueue<gps_position> MySynchronisedQueue;

#include <boost/interprocess/shared_memory_object.hpp>
#include <iostream>

void consumer()
{
    bip::managed_shared_memory openedSegment(bip::open_only, SHARED_MEMORY_NAME);

    MySynchronisedQueue *openedQueue = openedSegment.find<MySynchronisedQueue>(SHARED_QUEUE_NAME).first;
    gps_position position;

    while (openedQueue->pop(position)) {
        std::cout << "Degrees= " << position.degrees << " Minutes= " << position.minutes << " Seconds= " << position.seconds;
        std::cout << "\n";
    }
}

void producer() {
    bip::shared_memory_object::remove(SHARED_MEMORY_NAME);

    bip::managed_shared_memory mysegment(bip::create_only,SHARED_MEMORY_NAME, 65536);

    MySynchronisedQueue::allocator_type alloc(mysegment.get_segment_manager());
    MySynchronisedQueue *myQueue = mysegment.construct<MySynchronisedQueue>(SHARED_QUEUE_NAME)(alloc);

    for(int i = 0; i < 100; ++i)          
        myQueue->push(gps_position(i, 2, 3));

    // Wait until the queue is empty: has been processed by client(s)
    while(myQueue->sizeOfQueue() > 0) 
        continue;
}

int main() {
    producer();
    // or enable the consumer code for client:
    // consumer();
}


来源:https://stackoverflow.com/questions/26509815/boost-shared-memory-and-synchronized-queue-issue-crash-in-consumer-process

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!