1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
.. index::
   pair: allocation frames; design

.. _design-alloc-frame:


Allocation frame protocol
=========================

.. mps:prefix:: design.mps.alloc-frame


Introduction
------------

:mps:tag:`intro` This document explains the design of the support for
allocation frames in MPS.

:mps:tag:`readership` This document is intended for any MM developer.

:mps:tag:`overview` Allocation frames are used for implementing stack pools;
each stack frame corresponds to an allocation frame. Allocation frames
may also be suitable for implementing other sub-pool groupings, such
as generations and ramp allocation patterns.

:mps:tag:`overview.ambition` We now believe this to be a design that loses
too many advantages of stack allocation for questionable gains. The
requirements are almost entirely based on unanalysed anecdote, instead
of actual clients.

.. note::

    We plan to supersede this with a stack pool design at some point
    in the future. Pekka P. Pirinen, 2000-03-09.


Definitions
-----------

:mps:tag:`def.alloc-frame` An allocation frame is a generic name for a
device which groups objects together with other objects at allocation
time, and which may have a parent/child relationship with other
allocation frames.


Purpose
-------

:mps:tag:`purpose.stack-allocation` The allocation frame protocol is
intended to support efficient memory management for stack allocation,
that is, the allocation of objects which have dynamic extent.

:mps:tag:`purpose.general` The allocation frame protocol is intended to be
sufficiently general that it will be useful in supporting other types
of nested allocation patterns too. For example, it could be used to
for EPVM-style save and restore, ramp allocation patterns or
generations.


Requirements
------------

Known requirements
..................

:mps:tag:`req.stack-alloc` Provide a interface for clients to describe a
stack allocation pattern, as an alternative to using the control
stack.

:mps:tag:`req.efficient` Permit an implementation which is comparable in
efficiency to allocating on the control stack.

:mps:tag:`req.ap` Support allocation via allocation points (APs).

:mps:tag:`req.format` Support the allocation of formatted objects.

:mps:tag:`req.scan` Ensure that objects in allocation frames can participate
in garbage collection by being scanned.

:mps:tag:`req.fix` Ensure that objects in allocation frames can participate
in garbage collection by accepting Fix requests.

:mps:tag:`req.condemn` Ensure that objects in allocation frames can
participate in garbage collection by being condemned.

:mps:tag:`attr.locking` Minimize the synchronization cost for the creation
and destruction of frames.


Proto-requirements
..................

:mps:tag:`proto-req` The following are possible requirements that might be
important in the future. The design does not necessarily meet all
these requirements, but it does consider them all. Each requirement
either has direct support in the framework, or could be supported with
future additions to the framework.

:mps:tag:`req.parallels` The allocation frame protocol should provide a
framework for exploiting the parallels between stack extents,
generations and "ramps".

:mps:tag:`req.pool-destroy` It should be possible to use allocation frames
to free all objects in a pool without destroying the pool.

:mps:tag:`req.epvm` It should be possible to implement EPVM-style save and
restore operations by creating and destroying allocation frames.

:mps:tag:`req.subst` It should be possible to substitute a stack pool with a
GC-ed pool so that erroneous use of a stack pool can be detected.

:mps:tag:`req.format-extensions` It should be possible for stack pools to
utilize the same format as any other pool, including debugging formats
that include fenceposting, etc.

:mps:tag:`req.mis-nest` Should ensure "mis-nested" stacks are safe.

:mps:tag:`req.non-top-level` Should support allocation in the non-top stack
extent.

:mps:tag:`req.copy-if-necessary` Should ensure that stack pools can support
"copy-if-necessary" (so that low-level system code can heapify stack
objects.)

:mps:tag:`req.preserve` When an object is in an allocation frame which is
being destroyed, it should be possible to preserve that object in the
parent frame.

:mps:tag:`req.contained` Should allow clients to ask if an object is
"contained" in a frame. The object is contained in a frame if it is
affected when the frame is ended.

:mps:tag:`req.alloc-with-other` Should allow clients to allocate an object
in the same frame as another object.


Overview
--------

:mps:tag:`frame-classes` The protocol supports different types of allocation
frames, which are represented as "frame classes". It's up to pools to
determine which classes of allocation frames they support. Pools which
support more than one frame class rely on the client to indicate which
class is currently of interest. The client indicates this by means of
an operation which stores the class in the buffer to which the
allocation point is attached.

:mps:tag:`frame-handles` Allocation frames are described via abstract "frame
handles". Pools may choose what the representation of a frame handle
should be. Frame handles are static, and the client need not store
them in a GC root.

:mps:tag:`lightweight-frames` The design includes an extension to the
allocation point protocol, which permits the creation and destruction
of allocation frames without the necessity for claiming the arena
lock. Such frames are called "lightweight frames".


Operations
----------

:mps:tag:`op.intro` Each operation has both an external (client) interface
and an internal (MPS) interface. The external function takes an
allocation point as a parameter, determines which buffer and pool it
belongs to, and calls the internal function with the buffer and pool
as parameters.

:mps:tag:`op.obligatory` The following operations are supported on any
allocation point which supports allocation frames:-

:mps:tag:`operation.push` The :c:func:`PushFrame()` operation creates a new
allocation frame of the currently chosen frame class, makes this new
frame the current frame, and returns a handle for the frame.

:mps:tag:`operation.pop` The :c:func:`PopFrame()` operation takes a frame handle
as a parameter. Some pool classes might insist or assume that this is
the handle for the current frame. It finds the parent of that frame
and makes it the current frame. The operation indicates that all
children of the new current frame contain objects which are likely to
be dead. The reclaim policy is up to the pool; some classes might
insist or assume that the objects must be dead, and eagerly free them.
Note that this might introduce the possibility of leaving dangling
pointers elsewhere in the arena. If so, it's up to the pool to decide
what to do about this.

:mps:tag:`op.optional` The following operations are supported for some
allocation frames, but not all. Pools may choose to support some or
all of these operations for certain frame classes. An unsupported
operation will return a failure value:-

:mps:tag:`operation.select` The :c:func:`SelectFrame()` operation takes a frame
handle as a parameter and makes that frame the current frame. It does
not indicate that any children of the current frame contain objects
which are likely to be dead.

:mps:tag:`operation.select-addr` The :c:func:`SelectFrameOfAddr()` operation takes
an address as a parameter and makes the frame of that address the
current frame. It does not indicate that any children of the current
frame contain objects which are likely to be dead.

:mps:tag:`operation.in-frame` The :c:func:`AddrInFrame()` operation determines
whether the supplied address is the address of an object allocated in
the supplied frame, or any child of that frame.

:mps:tag:`operation.set` The :c:func:`SetFrameClass()` operation takes a frame
class and an allocation point as parameters, and makes that the
current frame class for the allocation point. The next :c:func:`PushFrame()`
operation will create a new frame of that class.


Interface
---------

External types
..............

:mps:tag:`type.client.frame-handle` Frame handles are defined as the abstract
type :c:type:`mps_frame_t`.

.. c:type:: struct mps_frame_class_s *mps_frame_class_t

:mps:tag:`type.client.frame-class` Frame classes are defined as an abstract
type.

:mps:tag:`type.client.frame-class.access` Clients access frame classes by
means of dedicated functions for each frame class.

External functions
..................

:mps:tag:`fn.client.push` :c:func:`mps_ap_frame_push()` is used by clients to
invoke the :c:func:`PushFrame()` operation. For lightweight frames, this
might not invoke the corresponding internal function.

:mps:tag:`fn.client.pop` :c:func:`mps_ap_frame_pop()` is used by clients to invoke
the :c:func:`PopFrame()` operation. For lightweight frames, this might not
invoke the corresponding internal function.
       
.. c:function:: mps_res_t mps_ap_frame_select(mps_ap_t buf, mps_frame_t frame)

:mps:tag:`fn.client.select` This following function is used by clients to
invoke the :c:func:`SelectFrame()` operation.

.. c:function:: mps_res_t mps_ap_frame_select_from_addr(mps_ap_t buf, mps_addr_t addr)

:mps:tag:`fn.client.select-addr` This function is used by clients to invoke
the :c:func:`SelectFrameOfAddr()` operation.

.. c:function:: mps_res_t mps_ap_addr_in_frame(mps_bool_t *inframe_o, mps_ap_t buf, mps_addr_t *addrref, mps_frame_t frame)

:mps:tag:`fn.client.in-frame` This function is used by clients to invoke the
:c:func:`AddrInFrame()` operation.

.. c:function:: mps_res_t mps_ap_set_frame_class(mps_ap_t buf, mps_frame_class_t class)

:mps:tag:`fn.client.set` This function is used by clients to invoke the 
:c:func:`SetFrameClass()` operation.

.. c:function:: mps_frame_class_t mps_alloc_frame_class_stack(void)

:mps:tag:`fn.client.stack-frame-class` This function is used by clients to
access the frame class used for simple stack allocation.


Internal types
..............

.. c:type:: struct AllocFrameStruct *AllocFrame

:mps:tag:`type.frame-handle` Frame handles are defined as an abstract type.

.. c:type:: struct AllocFrameClassStruct *AllocFrameClass

:mps:tag:`type.frame-class` Frame classes are defined as an abstract type.

.. c:type:: Res (*PoolFramePushMethod)(AllocFrame *frameReturn, Pool pool, Buffer buf)

:mps:tag:`fn.push` A pool method of this type is called (if needed) to
invoke the :c:func:`PushFrame()` operation.

.. c:type:: Res (*PoolFramePopMethod)(Pool pool, Buffer buf, AllocFrame frame)

:mps:tag:`fn.pop` A pool method of this type is called (if needed)
to invoke the PopFrame operation:

.. c:type:: Res (*PoolFrameSelectMethod)(Pool pool, Buffer buf, AllocFrame frame)

:mps:tag:`fn.select` A pool method of this type is called to invoke the
:c:func:`SelectFrame()` operation.

.. c:type:: Res (*PoolFrameSelectFromAddrMethod)(Pool pool, Buffer buf, Addr addr)

:mps:tag:`fn.select-addr` A pool method of this type is called to invoke the
:c:func:`SelectFrameOfAddr()` operation.

.. c:type:: Res (*PoolAddrInFrameMethod)(Bool *inframeReturn, Pool pool, Seg seg, Addr *addrref, AllocFrame frame)

:mps:tag:`fn.in-frame` A pool method of this type is called to invoke the 
:c:func:`AddrInFrame()` operation.

.. c:type:: Res (*PoolSetFrameClassMethod)(Pool pool, Buffer buf, AllocFrameClass class)

:mps:tag:`fn.set` A pool method of this type is called to invoke the 
:c:func:`SetFrameClass()` operation.


Lightweight frames 
-------------------

Overview
........

:mps:tag:`lw-frame.overview` Allocation points provide direct support for
lightweight frames, and are designed to permit PushFrame and PopFrame
operations without the need for locking and delegation to the pool
method. Pools can disable this mechanism for any allocation point, so
that the pool method is always called. The pool method will be called
whenever synchronization is required for other reasons (e.g. the
buffer is tripped).

:mps:tag:`lw-frame.model` Lightweight frames offer direct support for a
particular model of allocation frame use, whereby the PushFrame
operation returns the current allocation pointer as a frame handle,
and the PopFrame operation causes the allocation pointer to be reset
to the address of the frame handle. This model should be suitable for
simple stack frames, where more advanced operations like SelectFrame
are not supported. It may also be suitable for more advanced
allocation frame models when they are being used simply. The use of a
complex operation always involves synchronization via locking, and the
pool may disable lightweight synchronization temporarily at this time.

State
.....

:mps:tag:`lw-frame.states` Allocation points supporting lightweight frames
will be in one of the following states:

============ ================================================================
Valid        Indicates that :c:func:`PushFrame()` can be a lightweight
             operation and need not be synchronized.
PopPending   Indicates that there has been a :c:func:`PopFrame()` operation
             that the pool must respond to.
Disabled     Indicates that the pool has disabled support for lightweight 
             operations for this AP.
============ ================================================================

These states are in addition to the state normally held by an AP for
allocation purposes. An AP will be in the Disabled state at creation.

:mps:tag:`lw-frame.transitions` State transitions happen under the following
circumstances:

======================= ====================================================
Valid → PopPending      As a result of a client :c:func:`PopFrame()`
                        operation.
Valid → Disabled        At the choice of the pool (for example, when
                        responding to a :c:func:`SelectFrame()` operation).
PopPending → Valid      At the choice of the pool, when processing a
                        :c:func:`PopFrame()`.
PopPending → Disabled   At the choice of the pool, when processing a 
                        :c:func:`PopFrame()`.
Disabled → Valid        At the choice of the pool.
Disabled → Popframe     Illegal.
======================= ====================================================

:mps:tag:`lw-frame.state-impl` Each AP contains 3 additional fields to hold this state::

    mps_addr_t frameptr; 
    mps_bool_t enabled; 
    mps_bool_t lwPopPending;

:mps:tag:`lw-frame.enabled` The ``enabled`` slot holds the following values for
each state:

========== ==========
Valid      :c:macro:`TRUE`
PopPending :c:macro:`TRUE`
Disabled   :c:macro:`FALSE`
========== ==========

:mps:tag:`lw-frame.frameptr` The ``frameptr`` slot holds the following values
for each state:

========== ============================================
Valid      :c:macro:`NULL`
PopPending Frame handle for most recently popped frame.
Disabled   :c:macro:`NULL`
========== ============================================

:mps:tag:`lw-frame.lwPopPending` The ``lwPopPending`` slot holds the
following values for each state:

========== =========
Valid      :c:macro:`FALSE`
PopPending :c:macro:`TRUE`
Disabled   :c:macro:`FALSE`
========== =========

:mps:tag:`lw-frame.state-for-gc` It is not necessary for the tracer, format
code, pool, or any other part of the GC support in MPS to read either
of the two additional AP fields in order to scan a segment which
supports a lightweight allocation frame.


Synchronization
...............

:mps:tag:`lw-frame.sync` The purpose of the design is that mutator may
access the state of an AP without locking with MPS (via the external
functions). The design assumes the normal MPS restriction that an
operation on an AP may only be performed by a single mutator thread at
a time. Each of the operations on allocation frames counts as an
operation on an AP.

:mps:tag:`lw-frame.sync.pool` Pools are permitted to read or modify the
lightweight frame state of an AP only in response to an operation on
that AP.

:mps:tag:`lw-frame.sync.external` The external functions
:c:func:`mps_ap_frame_push()` and :c:func:`mps_ap_frame_pop()` are permitted to
read the values of the ``enabled`` and ``frameptr`` fields for the
supplied AP without claiming the arena lock. They are permitted to
modify the ``frameptr`` field if and only if ``enabled == FALSE``.

:mps:tag:`lw-frame.sync.trip` When a buffer trip happens, and the trap
wasn't set by MPS itself (that is, it wasn't because of a flip or for
logging), then the buffer code must check whether the AP has state
PopPending. If it does, the buffer code must call the Pool.


Implementation
..............

:mps:tag:`lw-frame.push` The external :c:func:`PushFrame()` operation
(:c:func:`mps_ap_frame_push()`) performs the following operations::

    IF (!APIsTrapped(ap) && StateOfFrame(ap) == Valid && ap->init == ap->alloc)
       *frame_o = ap->init;
    ELSE
      WITH_ARENA_LOCK
        PerformInternalPushFrameOperation(...)
      END
    END

:mps:tag:`lw-frame.pop` The external :c:func:`PopFrame()` operation
(:c:func:`mps_ap_frame_pop()`) performs the following operations::

    IF (StateOfFrame(ap) != Disabled)
      TrapAP(ap);  /* ensure next allocation or push involves the pool */
      ap->frameptr = frame;
      ap->lwpopPending = TRUE;
    ELSE
      WITH_ARENA_LOCK
        PerformInternalPopFrameOperation(...)
      END
    END