7 The writeability, sharedness, and lifetime of memory passed by reference
8 as a parameter (in or out) depends on the parameter attributes, as well as
9 whether the method is asynchronous. The implementation of the semantics
10 will be different for in-process and remote methods, and not all
11 restrcitions are enforced as strictly for in-process methods, but the
12 semantics when the rules are followed must be the same for both.
14 The data type of parameter also affects how it is handled. Built-in
15 datatypes, bitfields, enums, and inline structs and arrays are passed
16 directly by value (or, depending on the ABI, simple reference-valid-
17 until-end-of-method for out parameters), and thus do not require memory
18 management. Object references are reference counted both at the ORB and
19 process level, and are unique in that the client data structures are
20 read-only and uncopyable (because a unique object needs a unique address)
21 within an address space; most of this section does not apply to them.
23 That leaves non-inline structs and arrays. The ideal way of treating
24 these depends on how they're being used. Simple, small structs and arrays
25 would benefit from being passed as a simple pointer (plus a length field
26 in the case of arrays), with the method copying anything it wants to keep
27 and/or change. For async methods, the caller would need to avoid
28 modifying referenced memory until it knows the method has been executed
29 (in the case of remote methods, it only matters until the ORB has COWed or
30 copied the memory, but client code should not depend on this, or it will
31 not work with an in-process server). This is the default behavior if no
32 parameter attributes are specified. With this type of parameter, there
33 are no restrictions on what allocator the memory being passed came from
34 for in parameters. "Out" and inout parameters will be discussed later.
36 The only major complexity here is in how the duplicate is made. Struct
37 duplications will need to do a deep copy without being confused by
38 reference loops; the stubs will need to provide a "duplicate" function
39 that does this (possibly using a hash table or other associative array to
40 identify reference loops if IDLC determines that they are possible with
41 that particular type of struct).
43 To avoid a double-copy, out-of-process methods may want to merely ask the
44 ORB to give it full, permanent access (via COW if not already copied) to
45 the memory. This may be hard to implement, though, as it requires the
46 server to know that the data came from an out-of-process implementation
47 (even if it's been passed to other functions, some of which may be
48 in-process object-model methods). This optimization may only be useful
49 for large amounts of data that is not likely to have most of its pages
50 modified; if it is likely to be heavily modified, then COW doesn't help
51 much, and may hurt (and if it's large, it probably hasn't already been
52 copied). It may be better to not implement this optimization, and instead
53 recommend that the user use a parameter attribute to deal with the
56 1.2 Parameter Attributes
57 ========================
59 With larger arrays and complex data structures, one would often benefit
60 from being able to avoid the copy altogether. This can be accomplished by
61 altering the interface semantics. All of the parameter attributes but
62 "copy" do this, and thus cannot be changed without breaking interface
63 compatibility. None of the current parameter attributes can be combined
64 with any other current parameter attribute.
66 1.2.1 Default Semantics
67 =======================
69 If no attribute is specified, "in" parameters are visible only until the
70 method returns, and are read-only. There will be a "duplicate" function
71 provided by the language binding for the server to use if it wants to
72 retain and/or write to the data. For "small" data (the threshold needs to
73 be empirically determined), it just makes a copy. For "large" data, the
74 pages will be copy-on-write mapped (unless the caller asks for an
75 immediate copy). The only real reason not to use the immediate flag for
76 small data (as determined by the programmer) as well (rather than have a
77 threshold) is so that the threshold can be tunable based on the relative
78 performance of copies versus traps on a given system. It'd also be nice
79 if the programmer could ask a profiler to determine whether large data
80 should be COWed or copied immediately on a per-call basis.
82 When the "duplicate" function is called, a copy-on-write mapping of the
83 data will be created. Edge data will be overmapped regardless of page
84 type, but the overmap status will be retained (so that edge pages will not
85 be overmapped into some other address space), though read overmap will be
86 promoted to read/write overmap, as the extra data in the copy will not be
87 used anymore. There will be an option to the "duplicate" function to
88 create fully overmappable pages by copying the edge data and zeroing the
89 rest of the edge pages (in case the caller wants to share the data).
94 The "copy" attribute affects only the implementation; changing it does not
95 break interface compatibility (and thus require a new GUID). As such, the
96 use of this attribute is specified in the CDL rather than the IDL.
97 Language bindings that do not require a CDL file will provide some way of
98 specifying copy semantics directly from the implementation language.
100 This attribute directs the ORB to automatically make a copy (possibly via
101 COW, but no read-only or shared mappings) of the parameter. For
102 in-process invocation, methods with any "copy" parameters will need to go
103 through a stub to do the copy before calling the real method.
108 The "shared" attribute declares that the method implementation and caller
109 will treat the data as a read/write shared memory region, lasting beyond
110 the end of the method. The caller must ensure that all segments of data
111 provided (including every nested struct, or struct/array in an array of
112 structs/arrays) either begins and ends exactly on a page boundary, or has
113 all partial pages marked for read/write overmap. For out-of-process
114 methods, an exception will be thrown if this is not the case. For
115 in-process methods, you'll merely go to hell for writing bugs that won't
116 show up until someone hands your code a reference to an out-of-process
117 implementation. All data must be under the management of the ORB memory
120 The shared region is terminated when the caller or callee frees the memory
121 using the ORB memory manager. This requires calling some function that
122 knows whether to actually free it (in the out-of-process case), or release
123 a reference or something (in the in-process case). For arrays, where
124 we're already using a struct with pointer and length, adding a reference
125 count pointer wouldn't be a big deal. Struct pointers, OTOH, are
126 currently plain pointers, and adding an indirection struct with pointer
127 and reference pointer would be ugly, but doable. The alternative is to
128 have the memory manager look up the memory fragment and find the refcount
129 in its internal data structures, which would require a lookup for every
130 reference count operation.
135 The "push" attribute transfers the memory region to the destination,
136 unlinking it from the source address space. The source memory region will
137 be unmapped for out-of-process invocations; for in-process invocations,
138 the memory region will simply belong to the callee, and the caller must
139 not reference it any more. Like "shared", all data fragments must either
140 begin and end on a page boundary, or be in a page with read/write overmap
141 enabled. It is also required that every page being pushed be under the
142 management of the ORB allocator.
147 When used as a parameter attribute (either explicitly or as part of the
148 type definition), "inline" specifies that the struct or array will be
149 passed directly as a value parameter. A NULL reference cannot be passed,
150 and for out parameters, the method cannot return a reference to an
151 existing struct or array, but instead must fill in the reference given.
153 The "inline" attribute is similar to "copy", except that no in-process
154 stub is required because it is part of the interface (though a stub may be
155 required in certain languages if they do not provide automatic copying of
156 value-passed structs/arrays).
158 An inline array cannot be of variable length, and may be treated
159 differently from other arrays in the language binding (e.g. in C++, inline
160 arrays are bare language arrays, and do not use the Array or MutableArray
163 The "inline" attribute can also be used on members of a struct, to
164 indicate that the member is embedded directly in the outer struct, rather
165 than linked with a pointer.
170 The "immutable" attribute is similar to "const" in C/C++ ("const" in
171 IDL is used only for compile-time constants), and specifies that the
172 array or struct cannot be modified through this particular reference.
173 It is the default for parameters when neither "shared" nor "push" is
174 used ("copy" and "inline" parameters will accept immutable references
175 on the caller side, but will produce a mutable copy for the server
176 side). It may be specified without effect when it is the default.
178 Immutable "shared"/"push" parameters will result in read-only
179 mappings in the destination address space, though for "push"
180 parameters, the process will have permission to enable writes (this
181 is intended for use by the ORB memory manager when the memory is
184 The "immutable" attribute may also be used on members of a struct.
186 1.3 Asynchronous methods
187 ========================
189 Most of the semantics are the same for asynchronous methods; however, the
190 points at which the method begins and ends are less clear. As far as the
191 ORB is concerned, an async method begins when it processes the message.
192 When invoking an async method, there should be a mechanism to get a handle
193 to track the progress of the invocation. This can be used by the caller
194 to try to cancel the method on a timeout, which can only be done if the
195 message has not yet been accepted by the recipient. Once the message has
196 been accepted, in the in-process, non-"copy" case, the caller must not
197 touch the data after this point until it receives a message from the
198 callee that it is finished. In the out-of-process case, if a caller loses
199 patience with the callee, it can free the memory (thus making it exist
200 only in the callee's address space, non-shared).
205 When a struct or array is being returned via an out or inout parameter,
206 there is no end of method on which to base reference lifetime. As such,
207 if neither "shared" nor "inline" is specified, an out parameter is treated
208 as "push". The default of "push" only applies to the out half of an inout
209 parameter; in general, use of inout should be probably limited to value
210 types and parameters that use "push" in both directions so as to avoid
211 potentially confusing semantics.
213 To return static data that does not need to be freed, out parameters can
214 use the "copy" implementation attribute. The interface semantics will
215 still be "push", but the ORB (or a wrapper function for in-process calls)
216 will allocate a pushable buffer and copy the data into it. If the static
217 data is managed by the ORB memory manager, it will reference count the
218 page rather than make a copy if the buffer is of sufficient size.
223 Exceptions are thrown as copy/push out parameters. This will often mean
224 unnecessary copies at in-process IDL method boundaries, but exceptions
225 should be small and infrequently thrown, and usually not managed by the
226 ORB memory manager except across method boundaries.
231 It is currently impossible to specify attributes (other than "immutable"
232 and "inline") on individual struct members. This would be useful to pass
233 a struct normally that contains a status code or other metadata along with
234 a pushed or shared buffer, without making the outer struct also pushed or
235 shared. It would also be useful to pass a pushed buffer through some
236 fixed superstruct such as a NotifierInfo.
238 2. The ORB Memory Manager (ORBMM)
239 =================================
241 The ORB memory manager keeps track of memory allocated by the ORB during
242 an out-of-process method invocation. It is also used for allocations made
243 by user code for memory that may need to be freed by another component in
244 the same address space (such as when using the shared or push attributes).
246 A reference count is kept on each page, so that shared-within-a-process
247 mappings must be released by both caller and callee before the memory is
248 freed. Passing a chunk of data through a "shared" parameter to in-process
249 method increments the page's reference count; this requires a memory
250 manager lookup for every such parameter. Because of this, consider using
251 "push" instead if sharing is not required.
253 In the out-of-process case, each mapping is freed separately, and the
254 kernel handles reference counting the physical page.
259 Each language binding shall provide a mechanism by which code may call the
260 following functions on a given type of array or struct. The prototypes
261 are for C++; other language bindings express these methods in whatever
262 form is most appropriate. In C++, the ORB memory manager is at
263 System::RunTime::orbmm, which is a pointer to an instance of
264 System::RunTime::ORBMM.
269 void *ORBMM::alloc(size_t size, ORBMM::AllocGroup *group = NULL);
271 Allocate the memory required for the given type (and the given array size,
272 if the type is an array). A group handle may be passed; if not, no page
273 will contain more than one allocation. The reference count on the page is
274 incremented if a page has been reused and per-object refcounts are not
275 supported; otherwise, the object's reference count is one. If the
276 allocation spans multiple pages, it will be tracked as an "object", so
277 that each page will have its reference count incremented and/or
278 decremented when appropriate.
280 The implementation may, but is not required to, track reference counts on
281 a per-page basis rather than per-object. The former will generally be
282 more efficient, but will preclude the reuse of an object's memory upon
283 release until the entire page is released.
286 Type *obj = new(orbmm) Type;
287 Type *obj = new(orbmm) Type[];
288 Type *obj = new(orbmm, group) Type;
289 Type *obj = new(orbmm, group) Type[];
294 void ORBMM::retain(Region region);
296 Increment the reference count on the specified object.
298 The region must refer to only one ORBMM object; the implementation may,
299 but is not required to, throw an exception if this rule is violated. If a
300 region smaller than the object is retained, it will not prevent other
301 pages in the region from being freed.
306 void ORBMM::release(Region region);
308 Decrement the reference count on the specified object, freeing it if it
311 It is allowed, but not required, that space in multi-object groups be
312 reused when freed, if the same group is used to allocate new objects.
313 This is only possible if reference counts are kept on a per-object basis
314 rather than per-page.
316 The region must refer to only one ORBMM object; the implementation may,
317 but is not required to, throw an exception if this rule is violated. If a
318 region smaller than the object is released resulting in a reference count
319 of zero, portions may be freed prior to the rest of the region's reference
325 void ORBMM::super_retain(Region region);
327 Increment the reference and super-reference counts on the specified
328 object. If the reference count ever goes below the super-reference count,
329 an exception is thrown. This mechanism is intended to ease debugging
330 reference count problems, by turning memory corruption into an exception.
332 It would typically be used when a given object is not intended to be
333 released until the program exits (or some well-defined cleanup procedure
334 is done), such as program and module code and static data. It should also
335 be used when a mapping is created using mmap() or other higher-level
336 function, so as to be able to detect if such a reference is released
337 through release() rather than through the high-level mechanism.
339 The region must refer to only one ORBMM object; the implementation may,
340 but is not required to, throw an exception if this rule is violated.
345 void ORBMM::super_release(Region region);
347 Decrement the reference and super-reference counts on the given object.
349 The region must refer to only one ORBMM object; the implementation may,
350 but is not required to, throw an exception if this rule is violated.
355 ORBMM::AllocGroup *ORBMM::create_group();
357 Create a group handle that can be passed to the alloc function to pack
358 multiple allocations into the same page(s).
363 void ORBMM::destroy_group(ORBMM::AllocGroup *group);
365 Free the memory associated with the group handle returned by create_group.
366 The allocations made under the group are unaffected, and must be released
372 void *ORBMM::add_region(System::Mem::Region region);
374 The ORB memory manager can manage reference counts of pages that were not
375 allocated using ORBMM. This can be used to refcount non-anonymous
376 mappings (and thus make them usable with parameters that require ORBMM
377 memory). It can also be used on static pages that will never be freed
378 until the program exits.
380 The add_region method places a non-ORBMM controlled region under ORBMM
381 control. The ORBMM may use the existing mapping, or it may remap the
382 pages into its own region of the virtual address space. The address that
383 it uses will be returned. The entire region will be one ORBMM object.
385 Upon a reference count of zero, the pages will be unmapped using
386 System.AddrSpace.unmap().