1. Method Parameters ==================== 1.1 Overview ============ This document defines the lifetime and writeability of memory passed by reference as a parameter under various circumstances (in or out, sync or async). The implementation of the semantics will be different for local and remote methods, and not all restrcitions are enforced as strictly for local methods, but the semantics when the rules are followed must be the same for both. 1.2 Definitions =============== implementation attribute: An attribute that is defined only for a specific server object, and affects only the server stubs. It can be changed without breaking client compatibility. interface attribute: An attribute that is defined as a part of an IDL interface, and cannot be changed without breaking compatibility. local: The method being called exists in the caller's address space, and thus no marshalling need occur. remote: The method being called exists in an address space other than the caller's. All data must be marshalled and passed through some form of IPC. 1.3 In Parameters ================= Built-in datatypes, bitfields, enums, and inline structs and arrays are passed directly by value (or, depending on the ABI, a reference valid until the end of the method for out parameters), and thus do not require memory management for local calls. For remote calls, data passed by value is treated as a single non-inline struct for synchronous methods, and as an inline struct (copied at invocation time, and freed when the message is removed from the queue by the ORB) for async methods. Object references are reference counted both at the ORB and process level, and are unique in that the client data structures are read-only and uncopyable (because a unique object needs a unique address) within an address space; most of this section does not apply to them. That leaves non-inline structs and arrays. These are passed as a simple pointer (plus a length field in the case of arrays), with the method copying anything it wants to change and/or keep beyond the end of the method. For async methods, the caller needs to avoid modifying referenced memory until it knows the method has been executed (in the remote case, it only matters until the ORB has COWed or copied the memory, but client code should not depend on this, or it will not work with an local server). There are no allocator or alignment restrictions on the memory being passed (though page alignment and overmap can improve performance when sending large buffers). If the server wants to keep the data past the end of the method, or if it wants to modify the data, it must call a duplicate() function to copy the data, map it copy-on-write. Struct duplications will need to do a deep copy without being confused by reference loops (possibly by using a hash table or other associative array to identify reference loops if IDLC determines that they are possible with that particular type of struct). To avoid a double-copy, remote methods may want to merely ask the ORB to give it full, permanent access (via COW if not already copied) to the memory. This may be hard to implement, though, as it requires the server to know that the data came from an remote implementation, and be able to modify the set of pages that the ORB reclaims at the end of the method call. This may not provide a significant benefit over simply creating a new copy-on-write mapping. In remote calls, the caller is charged with the temporary allocations made by the ORB in the callee's address space. 1.3.1 Copy (interface) ====================== The "copy" interface attribute may be used to achieve the effect of calling duplicate() in the server. For local calls, this simply results in a call to duplicate() in the client stubs. For remote calls, the copied data segments are marked with the Copy flag, which are read/write (using copy-on-write) and not freed at the end of the method. These pages are charged to the callee, and must be freed using the callee's ORB memory manager. The callee should be able to specify how much data it is willing to have copied to a given object. The "copy" interface attribute may not be used on "out" parameters, and for "inout" parameters only applies to the "in" phase. Do not confuse this with the implementation "copy" attribute used on "out" parameters. 1.4 Out parameters ================== When a non-inline struct or array is being returned via an out or inout parameter, there is no end of method on which to base reference lifetime. As such, the ownership of such data is transferred to the ORB memory manager in the caller's address space. For remote calls, the caller should be able to specify a limit on how much memory it is willing to receive from the callee. Inline "out" parameters are returned into a buffer provided by the caller. All value types are implicitly inline. 1.4.1 Copy (implementation) =========================== If the "copy" implementation attribute is specified on the out parameter, then the buffer the caller receives will be newly allocated, and the buffer provided by the callee remains the callee's. In this case, there are no allocator or alignment restrictions on the callee's buffer. If the "copy" attribute is not specified, then the buffer provided by the callee must be under the control of the ORB memory manager, and it must begin and end on page boundaries. When returning from a remote call, the pages will be unmapped from the callee and mapped into the caller. The "copy" implementation attribute may not be used on "in" parameters, and for "inout" parameters only applies to the "out" phase. Do not confuse this with the interface "copy" attribute used on "in" parameters. 1.5 Inline ========== When used as a parameter attribute (either explicitly or as part of the type definition), "inline" specifies that the struct or array will be passed directly as a value parameter. A NULL reference cannot be passed, and for out parameters, the method cannot return a reference to an existing struct or array, but instead must fill in the reference given. An inline array cannot be of variable length, and may be treated differently from other arrays in the language binding (e.g. in C++, inline arrays are bare language arrays, and do not use the Array or MutableArray wrappers). The "inline" attribute can also be used on members of a struct, to indicate that the member is embedded directly in the outer struct, rather than linked with a pointer. "Inline" is an interface attribute. 1.3 Asynchronous methods ======================== Most of the semantics are the same for asynchronous methods; however, the points at which the method begins and ends are less clear. As far as the ORB is concerned, an async method begins when it processes the message. When invoking an async method, there should be a mechanism to get a handle to track the progress of the invocation. This can be used by the caller to try to cancel the method on a timeout, which can only be done if the message has not yet been accepted by the recipient. Once the message has been accepted, in the local, non-"copy" case, the caller must not touch the data after this point until it receives a message from the callee that it is finished. In the remote case, if a caller loses patience with the callee, it can free the memory (thus making it exist only in the callee's address space, non-shared). 1.5 Exceptions ============== Exceptions are thrown as copied out parameters. This will often mean unnecessary copies at local IDL method boundaries, but exceptions should be small and infrequently thrown, and usually not managed by the ORB memory manager except across method boundaries. 2. The ORB Memory Manager (ORBMM) ================================= The ORB memory manager keeps track of memory that is allocated in the process of calling or returning from a method. This happens with "copy" in parameters, and all non-inline out parameters. 2.1 Methods =========== Each language binding shall provide a mechanism by which code may call the following functions on a given type of array or struct. The prototypes are for C++; other language bindings express these methods in whatever form is most appropriate. In C++, the ORB memory manager is at System::RunTime::orbmm, which is a pointer to an instance of System::RunTime::ORBMM. 2.1.1 alloc =========== void *ORBMM::alloc(size_t size, int refs = 1); Allocate the memory required for the given type, array length, and inital refcount. Alternate forms: Type *obj = new(orbmm) Type; Type *obj = new(orbmm) Type[]; Type *obj = new(orbmm, refs) Type; Type *obj = new(orbmm, refs) Type[]; 2.1.2 retain ============ void ORBMM::retain(void *ptr, int refs = 1); Add the specified number of references to the ORBMM object. "ptr" can be anywhere inside the object; it does not have to point to the beginning of the object. 2.1.3 release ============= void ORBMM::free(void *ptr, int refs = 1); Alternate forms: delete(orbmm) Type; delete(orbmm) Type[]; delete(orbmm, refs) Type; delete(orbmm, refs) Type[]; Release the specified number of references to the ORBMM object. If the refcount falls to zero, the memory will be unmapped. If the refcount goes below zero, an exception may be thrown. "ptr" can be anywhere inside the object; it does not have to point to the beginning of the object. 2.1.3 add_region ================ void ORBMM::add_region(System::Mem::Region region, bool unmap_orig, int refs = 1); The add_region method places a non-ORBMM controlled region under ORBMM control. This can be used to return data that was not allocated using ORBMM (such as static data or non-anonymous mappings), without having to always do so (as the "copy" attribute would require). It is also used internally by IDL stubs. The entire region will be one ORBMM object, and will be freed all at once when the refcount goes to zero. When it is freed, the region will cease to be under ORBMM control. The original region will only be unmapped if unmap_orig is true.