ShMem Template Class

Declared:

#include <DML_shmem.h>    

Derived From:

none

Description

The ShMem template class is a class which is used to de-mystify some of the inner workings of Shared Memory on QNX machines. It takes care of all the details, provided a couple rules are followed, and voila: Any class or structure can be created in shared memory with another process very easily. Let's discuss the limitations of the class first.

The ShMem class is a template class which inherits from its instantiation type. It is therefore impossible to declare a ShMem<float> or a ShMem<int> instantiation: the compiler will flag it as an error, because classes cannot inherit from built-in types. All ShMem-type instances must be created from structures or classes, such as ShMem<struct1> or ShMem<class2>.

The ShMem template class will not work correctly with any class which performs dynamic memory allocation using the new/delete or malloc/free methods. In fact, it won't work correctly with any class which has a pointer in its data structure, so don't do it. Examine the definition of the class carefully: if the class has a pointer defined within it (even within the private or protected regions), then it's not a candidate for shared memory. It is for this reason that the ShMem template class is most useful for structures, or for carefully-designed user classes. The reason for this is that the pointer in question will point inside one process's memory space... which is out-of-bounds for any other processes which might use the shared memory.

The ShMem template class will not work correctly with any class which defines virtual functions, or inherits from a class with virtual functions, for similar reasons as for pointers and memory allocation. The ShMem class works just fine with regular derived classes: just make sure that there are no virtual functions, including virtual destructors!!!

The ShMem template class will not work correctly with any class which does not define a default (or no-argument) constructor. The ShMem template will always try to initialize the base class with no arguments. If the base class requires constructor arguments, it will be flagged as a compile-time error.

The ShMem template class only works properly if it is created using the 'new' method. Simply creating an instance of a ShMem<struct1> template class won't make it shared memory. For instance: this will NOT create a shared memory object, it will just create a myStruct object:

ShMem<struct1> myStruct;

Here is the correct syntax:

ShMem<struct1> * shm_struct_ptr = new("shmem_file_name") ShMem<struct1>; (to create the shared mem)
delete shm_struct_ptr; (to destroy the shared mem)

Pretty convoluted, eh? Let's break it down. The creation statement is really two statements, all rolled up into one. They are of the form:

class-type * variable_ptr_name;
variable_ptr_name = new class-type;

This just creates a pointer to a class-type, named variable_ptr_name, and then dynamically allocates storage and sets the pointer to point to the storage. A similar example would be:

String * foo;
foo = new String;

or, rolling it into one statement (as it is in the ShMem syntax example above):

String * foo = new String;

The class-type in the ShMem example is ShMem<struct1>. It's a template class, based on the struct1 structure/class. The variable name is 'shm_struct_ptr'. The only twist is that the operator new has an argument (bet you've never seen that before, eh?). The 'new' operator for ShMem variables requires one argument: the file-name where the shared memory is located. QNX stores shared memory in its file system... note that the shared memory isn't actually on the hard drive: it's just located in the filesystem so that it can be looked up and found easily.

The above examples are the "full" instantiation of a ShMem template object. It is possible to abbreviate that definition statement, if you don't mind losing a little bit of flexibility. Specifically, it becomes harder to easily delete the shared memory correctly. You can substitute these two statements for the creation and deletion:

struct1 * shm_struct_ptr = new("shmem_file_name") ShMem<struct1>; (creation)
delete ((ShMem<struct1> *)shm_struct_ptr); (destruction
)

The pointer must be re-cast as a ShMem<struct1> * pointer, otherwise the program will attempt to do a regular old delete on the data, which doesn't work on shared memory objects! The program will fail catastrophically at run-time if you forget to re-cast the pointer and call the delete function. Why does it work this way? Because the ShMem<struct1> class is derived from the struct1 class, an therefore any ShMem<struct1> * pointer can be automatically resolved into a struct1 * pointer. However, you lose the functions that come along with the ShMem<> part of the derived template class, so the delete function no longer works properly. You could solve the problem with a virtual delete function in the base class, but that requires too much programmer intervention, and, more importantly, shared memory classes can't have virtual functions, because it crashes the program every time with a SIGSEGV (see above)!

In addition to the ShMem template class, there is a skeleton version (waiting to be fleshed out) of a base ShMemBase class, which could be inherited by any class, and get the same capabilities, without all the cumbersome template class syntax. It's not implemented yet: go look at DML_shmem.h and see if you can make it work.

There are also some more basic shared memory functions, for use with built-in types (int, float, long, etc) since they don't work with the ShMem template class. Of particular use is the destroy_shm() function, which should be called within a process's signal handler to clean up after itself. It absolutely destroys any shared memory name which is passed to it, eliminating the need to meticulously make sure that all shared memory objects created with the 'new' method are appropriately destroyed with the 'delete' method.

In a multi-threaded environment, this template class is NOT threadsafe ONLY if the programmer creates two different shared memory objects of the same structure (i.e. from the same class), but with different shared-memory file names. No dire effects will occur, except that the delete operator and the fd() function might not operate correctly.

Finally, this template class might not work for REALLY REALLY BIG classes, i.e. larger than one page on the machine. On LordVader (Pentium II - 233 MHz), I think that the page size is like 16 megabytes, so don't worry too much about this one.

Public Class Members

Public member data:

none  

Public member functions:

operator new( size_t, const char *, int = O_RDWR )
operator delete( void *, size_t )
ShMem()
~ShMem()
int fd()
  

See Also:

create_shm, close_shm, destroy_shm, open_shm_fd, map_shm, in the ShMem Related Module, shm_open, shm_create, shm_destroy functions in the QNX library.

 

Class Member Functions

ShMem::operator new

Synopsis:

#include <DML_shmem.h>


template void * ShMem<class Type>::operator new( size_t, const char *, int = O_RDWR )

Semantics:

The new operator for the ShMem template class is essentially a memory-allocation function, just like any redefined 'new' function should be. Instead of allocating the space for the new object out of the free store, this version of 'new' allocates the space in shared memory, then maps the memory location into the process's memory space. To make a real shared memory object, you must use the new operator, rather than simply creating an instance:

i.e. do this:

ShMem<myclass> * myVariableName = new("shm_file_name", O_RDONLY) ShMem<myclass>;

not this:

ShMem<myclass> myVariableName.

The operator new may be invoked only with a shared memory file name, or with both a file name and an access level. Usually, you just want to open it on the O_RDWR (read-write) mode, but occasionally you might want to open in read-only mode (O_RDONLY). The QNX operating system stores references to shared memory objects as files in the filesystem, accessed via file descriptors, and mapped into the memory space.

Code Dissection:

A few things to note. Since the operator 'new' is by definition a static function, it does not have access to non-static member data, nor does it have a 'this' pointer passed to it. Therefore, it is necessary to copy the name of the shared file location and also the shared memory file descriptor into static member variables. This data will then be copied into the non-static member data when the constructor is called.

The code is fairly straightforward: it calls the open_shm_fd and map_shm functions (which are in the same module as the ShMem template class but are NOT member functions), then copies the name and the file descriptor.

Results:

Returns a pointer to the newly created shared memory object, or NULL if there was an error.

See Also:

create_shm, open_shm_fd, map_shm

 

ShMem::operator Delete

Synopsis:

#include <DML_shmem.h>


template void ShMem<class Type>::operator delete( void *, size_t )

Semantics:

The delete oeprator is like the new function for this class, in that it deletes shared memory instead of regularly-allocated memory. Like the new operator, it is by definition a static member function, so it has no this pointer and it has no access to non-static member data. The destructor takes care of copying the necessary member data into the static member data areas. The delete operator unmaps the shared memory and, if it's the last reference to the shared memory object, removes the shared memory entry from the file system. Just to be safe, though, it's always good to call destroy_shm() on any shared memory names that were used at the end of a process termination.

Code Dissection:

This function just calls munmap() and then close_shm.

Results:

The function does not return a value, but the shared memory object is unmapped from the process's memory space and, if appropriate, the shared memory name is deleted from the file system.

See Also:

munmap, close_shm

 

ShMem::ShMem

Synopsis:

#include <DML_shmem.h>


template ShMem<class Type>::ShMem()

Semantics:

This function is the constructor for the ShMem template class. Since this class inherits from the template formal parameter, the constructor will attempt to initialize the base class without any arguments. If the base class won't accept this, then you're out of luck: you must re-write the base class to accept a no-argument constructor, or else use the less friendly create_shm and destroy_shm functions.

Code Dissection:

This function takes care of copying static member data (which is all that the operator new can get at) into the non-static member data areas, as well as the obvious initialization of the parent class.

Results:

The data format of the object is created.

See Also:

ShMem::operator new, ShMem::~ShMem

 

ShMem::~ShMem

Synopsis:

#include <DML_shmem.h>


template ShMem<class type>::~ShMem()

Semantics:

The destructor simply takes care of shuttling some data from the non-static member area into the static member area, so that the delete function (which does all the work) can get at it.

Code Dissection:

Nothing more to say here.

Results:

The data structure of the object is destroyed.

See Also:

ShMem::operator delete, ShMem::ShMem

 

ShMem::fd

Synopsis:

#include <DML_shmem.h>


template int ShMem<class Type>::fd()

Semantics:

This function provides access to the file-descriptor associated with the shared memory object, if you'd ever want it (though I can't think of a good reason WHY anyone would ever want it). Shared memory is mapped and kept track of by the file system, and so it has a file descriptor associated with it... although I'm not sure it ever gets used, except by the mmap() function.

Code Dissection:

Just a simple access function

Results:

The shared memory file descriptor is returned.

See Also:

none