I am creating a message queue which is used by two processes. One of them is putting something in it and the other is reading it. The message queue is following struct I created.
struct MSGQueue {
Action actions_[256];
int count;
MSGQueue() { count = 0; }
interprocess_mutex mutex;
Action Pop() {
--count;
return actions_[count];
}
void Put(Action act) {
actions_[count] = act;
++count;
}
};
Action is a custom class I created.
class Action {
public:
// Getter functions for the member
private:
std::string name_;
ActionFn action_fn_; // this is an enum
void* additional_data_;
}
I am creating a shared memory like this in the main program
shm_messages = shared_memory_object(create_only,"MySharedMemory", read_write);
shm_messages.truncate(sizeof(MSGQueue));
region = mapped_region(shm_messages_, read_write);
In my other program I am opening it and put an action in the queues array of actions.
boost::interprocess::shared_memory_object shm_messages_;
boost::interprocess::mapped_region region_;
shm_messages_ = shared_memory_object(open_only, "MySharedMemory", read_write);
shm_messages_.truncate(sizeof(MSGQueue));
region_ = mapped_region(shm_messages_, read_write);
//Get the address of the mapped region
void * addr = region_.get_address();
//Construct the shared structure in memory
MSGQueue * data = static_cast<MSGQueue*>(addr);
Action open_roof("OpenRoof", ActionFn::AFN_ON, NULL);
{ // Code block for scoped_lock. Mutex will automatically unlock after block.
// even if an exception occurs
scoped_lock<interprocess_mutex> lock(data->mutex);
// Put the action in the shared memory object
data->Put(open_roof);
}
The main program is checking if we got some new messages and if there is one it shall read it and put it in a list.
std::vector<ghpi::Action> actions;
//Get the address of the mapped region
void * addr = region_.get_address();
//Construct the shared structure in memory
MSGQueue * data = static_cast<ghpi::Operator::MSGQueue*>(addr);
if (!data) {
std::cout << " Error while reading shared memory" << std::endl;
return actions;
}
{
scoped_lock<interprocess_mutex> lock(data->mutex);
while (data->count > 0) {
actions.push_back(data->Pop()); // memory access violation here
std::cout << " Read action from shm" << std::endl;
}
}
The second program which is putting the action works fine. But after it run the main program is seeing the count has increased and is trying to read and throws an memory access violation at me.
I don't know why i am getting this violation error. Is there something special about sharing class objects or structs?
Let's take a look at the objects you're trying to pass between processes:
class Action {
// ...
std::string name_;
}
Well, looky here. What do we have here? We have here a std::string
.
Did you know that sizeof(x)
, where x
is a std::string
will always give you the same answer, whether the string is empty, or has their entire contents of "War And Peace"? That's because your std::string
does a lot of work that you don't really have to think about. It takes care of allocating the requirement memory for the string, and deallocating when it is no longer used. When a std::string
gets copied or moved, the class takes care of handling these details correctly. It does its own memory allocation and deallocation. You can think of your std::string
to consist of something like this:
namespace std {
class string {
char *data;
size_t length;
// More stuff
};
}
Usually there's a little bit more to this, in your typical garden-variety std::string
, but this gives you the basic idea of what's going on.
Now try to think of what happens when you put your std::string
into shared memory. Where do you think that char
pointer still points to? Of course, it still points to somewhere, someplace, in your process's memory where your std::string
allocated the memory for whatever string it represents. You have no idea where, because all that information is hidden in the string.
So, you placed this std::string
in your shared memory region. You did place the std::string
itself but, of course, not the actual string that it contains. There's no way you can possibly do this, because you have no means of accessing std::string
's internal pointers and data. So, you've done that, and you're now trying to access this std::string
from some other process.
This is not going to end well.
Your only realistic option is to replace the std::string
with a plain char
array, and then go through the extra work of making sure that it's initialized properly, doesn't overflow, etc...
Generally, in the context of IPC, shared memory, etc..., using any kind of a non-trivial class is a non-starter.