[comp.lang.c++] Objects and Shared Memory

trm@cbnews.ATT.COM (Tom R. Mueller) (04/07/89)

Does anyone know of solutions for the problems associated with
C++ objects and shared memory in multiple Unix(r) processes.  The
following problems come to mind:

What is a shared object?  An obvious answer is an instantiation using
'new' of a class that overloads new so that the storage is allocated
from shared memory.  Given this answer and assuming that it isn't
possible to cause the shared memory to be mapped to the same virtual
address in every process, there are some more problems.

Shared objects cannot have ordinary pointers inside them.

Shared objects cannot have virtual member functions because they are
implemented with pointers to functions.

These two problems take away many of the benefits afforded by
object-orientation when dealing with shared memory.

What other definitions of a shared object are there?

When two processes both want to access a single shared object, what is
the best way to make this come about?  Should 'new' be required and
then the second process doesn't really get a pointer to a new object
but rather a pointer to an already existing object?  Should
constructors be used and then the constructor doesn't really create a
new object.  Maybe one process should be required to somehow "pass"
the identity of the shared object to the other process.  How should
the semantics be defined?

- Tom Mueller			AT&T Bell Laboratories (CB 0D109)
 (614) 860-5287			6200 East Broad Street
  ...att!cblpn!trm		Columbus, OH  43213
-- 
- Tom Mueller			AT&T Bell Laboratories (CB 0D109)
 (614) 860-5287			6200 East Broad Street
  ...att!cblpn!trm		Columbus, OH  43213

shopiro@alice.UUCP (Jonathan Shopiro) (04/10/89)

I think the best way to deal with data in shared memory is with
what I like to call ``surrogates.''  For example, you might have

	class Actual_shared_data {	// this is an object in shared memory
	friend class Shared_data;
		// only data members here
		// no public data or functions at all
	};
	class Shared_data {	// this is the surrogate
		Actual_shared_data*	p;
	public:
		Shared_data();	// constructor
		// the public interface goes here
	};

Then the constructor Shared_data::Shared_data() is in charge of
allocating shared memory, or of finding the address of the appropriate
shared memory object.  Note that whenever you say ``new Shared_data''
you get a new Shared_data object, even if it points to an old block of
shared memory.  Storage management schemes that violate this rule are
unlikely to win.

If you can't have real pointers in shared memory because of address
mapping considerations, it is simple to put whatever transformations
are necessary between the data in Actual_shared_data and regular
pointers into access functions in Shared_data.  Shared_data can also
have whatever virtual functions are appropriate.  In exchange for
an extra level of indirection (which you would be likely to need anyway)
you can encapsulate all the complexity of dealing with shared memory
in Shared_data, which the user treats like any other object.
-- 
		Jonathan Shopiro
		AT&T Bell Laboratories, Warren, NJ  07060-0908
		research!shopiro   (201) 580-4229

pcg@aber-cs.UUCP (Piercarlo Grandi) (04/10/89)

In article <5462@cbnews.ATT.COM> trm@cbnews.ATT.COM (Tom R. Mueller) writes:

    .... Given this answer and assuming that it isn't possible to cause the
    shared memory to be mapped to the same virtual address in every process,
    there are some more problems.
    
    Shared objects cannot have ordinary pointers inside them.
    
    Shared objects cannot have virtual member functions because they are
    implemented with pointers to functions.

Frankly I do not see any problem with C++, or object orientedness.  There
are two solutions to your problem, and both do not involve C++ or object
oriented design:

RELATIVE POINTERS

    This is an extremely powerful and useful concept, and is missing
    from C. It is very useful to handle structures that may be present
    at different addresses at different times. What one wants is to
    define all pointers in a segment as relative to that segment.

    Relative pointers solve neatly also two other problems; segmented
    architectures (where they are definitely more general and subsume
    near pointers) and saving/restoring data to/from persistent storage,
    which is usually done instead by a clumsy process of linearization.
    Note that this usual alternative is essentially transforming on save
    all pointers to relative pointers, and then on reload transforming
    them back to absolute ones.

    Relative pointers may be defined in at least three ways (e.g. segmented,
    based ala PL/1, relative ala MUPL). I am uncertain as to which I prefer.
    Probably the segmented variety is what would serve you best. In it a
    pointer declared as relative when dereferenced has the segment number
    of ITS address prepended to it automatically.

NO SHARED MEMORY

    I reckon that shared memory is absolutely unnecessary, just like
    message passing. I have designed and am building an OS nucleus (in C++)
    that will have neither; address spaces are going to be persistent, and
    threads will be able to jump from one to another. In this way when
    two threads communicate they are always in the same address space, and
    no portion of address or data space is ever shared (which means that
    the address space can be on different machines). By the way, I am also
    considering adding relative pointers (or based ones) to C++ as well
    (to make save/restore easier, not because of sharing).


-- 
Piercarlo "Peter" Grandi            |  ARPA: pcg%cs.aber.ac.uk@nss.cs.ucl.ac.uk
Dept of CS, UCW Aberystwyth         |  UUCP: ...!mcvax!ukc!aber-cs!pcg
Penglais, Aberystwyth SY23 3BZ, UK  |  INET: pcg@cs.aber.ac.uk

diamond@diamond.csl.sony.junet (Norman Diamond) (04/11/89)

In article <5462@cbnews.ATT.COM> trm@cbnews.ATT.COM (Tom R. Mueller) writes:

>Does anyone know of solutions for the problems associated with
>C++ objects and shared memory in multiple Unix(r) processes.

>... assuming that it isn't
>possible to cause the shared memory to be mapped to the same virtual
>address in every process, there are some more problems.

>Shared objects cannot have ordinary pointers inside them.
>Shared objects cannot have virtual member functions because they are
>implemented with pointers to functions.

These problems existed in older programming languages and in assembler
too, and even before virtual memory.  The necessary solution is the
same.  If some block of memory (or object) has different addresses for
access by different processors (or processes), then pointers within
the block cannot be absolute (whether real or virtual addresses).
They must be offsets of some sort, and the absolute addresses must be
computed by the code doing the access.  Each offset might be relative
to the beginning of the block (e.g. "23 bytes past where you think
the object begins") or self-relative (e.g. "152 bytes before me").

Norman Diamond, Sony Computer Science Lab (diamond%csl.sony.jp@relay.cs.net)
  The above opinions are my own.   |  Why are programmers criticized for
  If they're also your opinions,   |  re-inventing the wheel, when car
  you're infringing my copyright.  |  manufacturers are praised for it?

turner@sdti.SDTI.COM (Prescott K. Turner) (04/12/89)

In article <9171@alice.UUCP> shopiro@alice.UUCP (Jonathan Shopiro) writes:
>I think the best way to deal with data in shared memory is with
>what I like to call ``surrogates.''  ...
>
>If you can't have real pointers in shared memory because of address
>mapping considerations, it is simple to put whatever transformations
>are necessary between the data in Actual_shared_data and regular
>pointers into access functions in Shared_data.  Shared_data can also
>have whatever virtual functions are appropriate. 

At SDTI we have been dealing with a similar problem using a "surrogate"
solution.  Our most significant objects go in the memory which can't have
real pointers.  Even though Shared_data can have virtual functions,
and can refer to the Actual_shared_data object, it will behave according
to how the Shared_data object was constructed, not according to how the
Actual_shared_data object was constructed.  This has been so restrictive
that I wonder if I'm getting any experience with real object-oriented design
in my area of application.

I've been tempted to hack at Shared_data's constructors.  When a Shared_data
object is constructed which refers to a pre-existing Actual_shared_data
object, the underlying vtbl pointer of the constructed Shared_data object
would be modified.  It would then refer to the class derived from Shared_data
which corresponds correctly to the Actual_share_data object.

Anyone care to comment on the level of danger of such a hack?
--
Prescott K. Turner, Jr.
Software Development Technologies, Inc.
P.O. Box 366, Sudbury, MA 01776 USA         (508) 443-5779
UUCP: ...{harvard,mit-eddie}!sdti!turner    Internet: turner@sdti.sdti.com

ldg@druin.ATT.COM (Doug Gibbons) (04/13/89)

Here's another trick for passing objects with virtual functions
through shared memory:

	1) Use +e0/+e1 to create a vtbl.c

	2) Load vtbl.o at the same place in all a.outs that are
	   to share objects. This can be done simply by passing
	   vtbl.o as the first file to ld.

	3) When objects are placed in shared memory, their vtbl
	   ptrs are valid for any a.out address space, even though 
	   the contents of the vtbls may be different in each space.

-- 
--------------------------------------------------------------------------------
-- Doug Gibbons			| ldg@druhi.ATT.COM or att!druhi!ldg
-- AT&T Bell Laboratories 
-- Denver CO