boost::interprocess::message_queue no message received in second process

天大地大妈咪最大 提交于 2021-01-28 09:10:37

问题


I am using boost's message queue to write a basic class with just two char arrays, but the data is not being received in the second process is empty, even though get_num_msg() returns 1 before the read and returns 0 after reading. For debugging purposes I also tried writing and reading from the same process, and that worked fine. I am using the shared pointer because earlier while just reading and writing integers, it would not read the integer at the receiver unless it was declared as shared ptr.

AccessQueue

class AccessQueue {
    public:
    char name[64];
    char action[64];
    AccessQueue(char name[64], char action[64]) {
        strcpy(this->name, name);
        strcpy(this->action, action);
    }
    AccessQueue() {}
};

sending function

// std::shared_ptr<AccessQueue> p1;
this->p1.reset(new AccessQueue("asd", "vsq"));
try {
    this->mq->send(&p1, sizeof(p1), 0);
} catch(boost::interprocess::interprocess_exception & ex) {
    std::cout << ex.what() << std::endl;
}

receiving function

std::cout << this->mq->get_num_msg() << "\t" << this->mq->get_max_msg_size() << "\t" << this->mq->get_max_msg() << std::endl;
AccessQueue * a;
unsigned int priority;
boost::interprocess::message_queue::size_type recvd_size;
try {
    this->mq->try_receive(&a, sizeof(AccessQueue), recvd_size, priority);
} catch(boost::interprocess::interprocess_exception & ex) {
    std::cout << ex.what() << std::endl;
}
std::cout << this->mq->get_num_msg() << "\t" << this->mq->get_max_msg_size() << "\t" << this->mq->get_max_msg() << std::endl;
std::cout << "It clearly maybe works " << a->action << "\t" << a->name << std::endl;

output at receiver's end:

1       128     20
0       128     20

回答1:


Looks like p1 (in the sending function) is a smart pointer (like std::unique_ptr or std::shared_ptr). In that case

this->mq->send(&p1, sizeof(p1), 0);

is obviously wrong, because it puts the pointer object on the queue, instead of the data structure. Use

this->mq->send(*p1, sizeof(*p1), 0);

Or, indeed, don't use dynamic allocation in the first place:

AccessQueue packet("asd", "vsq");
mq.send(&packet, sizeof(packet), 0);

Uhoh there's more

On the receiving side, there's a similar problem:

    AccessQueue * a;
    // ..
    mq.try_receive(&a, sizeof(AccessQueue), ...);

That receives INTO the pointer, not the object. You don't even have an object, because a (the pointer) is never initialized. Here the fix is syntactically simple:

    AccessQueue a;

No more pointers. Now, a is an object and &a is the address of that object.

Note how the original was UB because you read sizeof(AccessQueue) bytes into a pointer. However the pointer is only 8 bytes and the struct is 128 bytes. Ooops!

Simplified Working Demo

This works:

Live On Wandbox¹

#include <boost/interprocess/ipc/message_queue.hpp>
#include <iostream>
#include <iomanip>

namespace bip = boost::interprocess;
using MQ = bip::message_queue;

template<size_t N>
static inline void safe_copy(std::array<char, N>& dst, std::string_view src) {
    std::copy_n(src.data(), std::min(src.size(), N), dst.data());
    dst.back() = 0; // make sure of NUL termination
}

struct AccessQueue {
    std::array<char, 64> name{0};
    std::array<char, 64> action{0};

    AccessQueue(std::string_view n = "", std::string_view a = "") {
        safe_copy(name, n);
        safe_copy(action, a);
    }
};

static_assert(std::is_standard_layout_v<AccessQueue>);

struct X {
    void send() {
        AccessQueue packet("asd", "vsq");
        try {
            mq.send(&packet, sizeof(packet), 0);
        } catch(std::exception const & ex) {
            std::cout << ex.what() << std::endl;
        }
    }

    AccessQueue receive() {
        AccessQueue retval;
        
        report();
        try {
            unsigned int priority;
            MQ::size_type recvd_size;
            mq.try_receive(&retval, sizeof(AccessQueue), recvd_size, priority);
        } catch(std::exception const & ex) {
            std::cout << ex.what() << std::endl;
        }
        report();
        return retval;
    }

    void report() {
        std::cout << mq.get_num_msg() << "\t" << mq.get_max_msg_size() << "\t" << mq.get_max_msg() << std::endl;
    }

    MQ mq { bip::open_or_create, "somequeue", 10, sizeof(AccessQueue) };
};

int main() {
    X tryit;
    tryit.send();
    auto const& [name, action] = tryit.receive();

    std::cout << std::quoted(name.data()) << " " << std::quoted(action.data()) << std::endl;
}

Prints

1       128     10
0       128     10
"asd" "vsq"

Note

  • using std::array over C arrays gives you copy semantics by default
  • guard the POD-ness of AccessQueue
  • make sure the members are initialized
  • make sure the copies are safe
  • make sure the copies are NUL-terminated always
  • Don't use new or delete. Why should C++ programmers minimize use of 'new'?
  • make sure your receive buffer size matches the max_msg_size (boost interprocess message_queue and fork)

¹ shared emmory is prohibited on Wandbox :(



来源:https://stackoverflow.com/questions/65233816/boostinterprocessmessage-queue-no-message-received-in-second-process

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