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
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
.. Sources:

    `<https://info.ravenbrook.com/project/mps/doc/2002-06-18/obsolete-mminfo/mmdoc/protocol/mps/alloc-point/>`_
    `<https://info.ravenbrook.com/project/mps/master/manual/wiki/apguide.html>`_
    `<https://info.ravenbrook.com/project/mps/master/design/buffer/>`_

.. index::
   single: allocation

.. _topic-allocation:

Allocation
==========


.. index::
   single: allocation; manual
   single: manual allocation

Manual allocation
-----------------

.. note::

    Not all :term:`pool classes` support this interface:
    :term:`automatically managed <automatic memory management>` pools
    typically support none of it, and even :term:`manually managed
    <manual memory management>` pools may not support the whole
    interface. Consult the pool class documentation for details. For
    example, the :ref:`pool-mvt` pool class supports deallocation via
    :c:func:`mps_free` but allocation must use allocation points, as
    described below.


.. c:function:: mps_res_t mps_alloc(mps_addr_t *p_o, mps_pool_t pool, size_t size)

    Allocate a :term:`block` of memory in a :term:`pool`.

    ``p_o`` points to a location that will hold the address of the
    allocated block.

    ``pool`` the pool to allocate in.

    ``size`` is the :term:`size` of the block to allocate. If it is
    unaligned, it will be rounded up to the pool's :term:`alignment`
    (unless the pool documentation says otherwise).


.. c:function:: void mps_free(mps_pool_t pool, mps_addr_t addr, size_t size)

    Free a :term:`block` of memory to a :term:`pool`.

    ``pool`` is the pool the block belongs to.

    ``addr`` is the address of the block to be freed.

    ``size`` is the :term:`size` of the block to be freed. If it is
    unaligned, it will be rounded up to the pool's :term:`alignment`
    (unless the pool documentation says otherwise).

    The freed block of memory becomes available for allocation by the
    pool, or the pool might decide to make it available to other
    pools, or it may be returned to the operating system.

    .. note::

        :c:func:`mps_free` takes a ``size`` parameter because it is
        most efficient to do so. In most programs, the type of an
        object is known at the point in the code that frees it, hence
        the size is trivially available. In such programs, storing the
        size on the MPS side would cost time and memory, and make it
        hard to get good virtual memory behaviour because of the need
        to touch the object in order to free it. As it is, the
        deallocation code doesn't have to touch the dead object at
        all.



.. index::
   single: allocation point

Allocation points
-----------------

:term:`Allocation points` provide fast, :term:`inline <inline
allocation (1)>`, nearly :term:`lock-free <lock free>` allocation.
They allow code to allocate without calling an allocation function:
this is vital for performance in languages or programs that allocate
many small objects. They must be used according to the
:ref:`topic-allocation-point-protocol`.

.. c:type:: mps_ap_t

    The type of :term:`allocation points`. It is a
    :term:`transparent alias <transparent type>` for a pointer to
    :c:type:`mps_ap_s`.


.. c:function:: mps_res_t mps_ap_create_k(mps_ap_t *ap_o, mps_pool_t pool, mps_arg_s args[])

    Create an :term:`allocation point` in a :term:`pool`.

    ``ap_o`` points to a location that will hold the address of the
    allocation point, if successful.

    ``pool`` is the pool.

    ``args`` are :term:`keyword arguments` specific to the pool class
    to which ``pool`` belong. See the documentation for that pool
    class. (Most pool classes don't take any keyword arguments; in
    those cases you can pass :c:macro:`mps_args_none`.)

    Returns :c:macro:`MPS_RES_OK` if successful, or another
    :term:`result code` if not.

    .. warning::

        An allocation point must not be used by more than one
        :term:`thread`: each thread must create its own allocation
        point or points.

    .. note::

        There's an alternative function :c:func:`mps_ap_create_v` that
        takes its extra arguments using the standard :term:`C`
        ``va_list`` mechanism.


.. c:function:: mps_res_t mps_ap_create(mps_ap_t *ap_o, mps_pool_t pool, ...)

    .. deprecated:: starting with version 1.112.

        Use :c:func:`mps_ap_create_k` instead: the :term:`keyword
        arguments` interface is more reliable and produces better
        error messages.

    An alternative to :c:func:`mps_ap_create_k` that takes its extra
    arguments using the standard :term:`C` variable argument list
    mechanism.


.. c:function:: mps_res_t mps_ap_create_v(mps_ap_t *ap_o, mps_pool_t pool, va_list args)

    .. deprecated:: starting with version 1.112.

        Use :c:func:`mps_ap_create_k` instead: the :term:`keyword
        arguments` interface is more reliable and produces better
        error messages.

    An alternative to :c:func:`mps_ap_create_k` that takes its extra
    arguments using the standard :term:`C` ``va_list`` mechanism.


.. c:function:: void mps_ap_destroy(mps_ap_t ap)

    Destroy an :term:`allocation point`.

    ``ap`` is the allocation point to destroy.

    Destroying an allocation point has no effect on blocks that were
    allocated from it, so long as they were successfully
    :term:`committed (2)` by :c:func:`mps_commit`.


.. index::
   single: allocation point protocol

.. _topic-allocation-point-protocol:

Allocation point protocol
-------------------------

This protocol is designed to work with :term:`incremental garbage
collection` and multiple :term:`threads`, where between any
two instructions in the :term:`client program`, the MPS may run part
of a :term:`garbage collection`, :term:`move <moving memory manager>`
blocks in memory, rewrite pointers, and reclaim space. In order to
reliably handle this, the allocation point protocol consists of (at
least) two steps, a *reserve* followed by a *commit*.

.. note::

    The description of the protocol assumes that you have declared
    your threads' :term:`control stacks` and :term:`registers` to be
    :term:`ambiguous roots`, by passing :c:func:`mps_stack_scan_ambig`
    to :c:func:`mps_root_create_reg`. This is the simplest way to
    write a client, but other scenarios are possible. Please
    :ref:`contact us <contact>` if your use case is not covered here
    (for example, if you need an exact collector).

When the client program is initializing a newly allocated object, you
can think of it as being "in a race" with the MPS. Until the object is
initialized, the MPS cannot manage it in the usual way: in particular,
it cannot ensure that the new object remains correct if other objects
move during its initialization. So if other objects *do* move, the MPS
tells the client program that it has "lost the race": the
partially-initialized object may be invalid, and the client must
initialize it again from scratch.

The allocation point protocol is as follows:

1. Call :c:func:`mps_reserve` to reserve a block of memory on an
   allocation point. The size of the block must be a multiple of the
   :term:`alignment` of the pool in which the allocation point was
   created.

   If :c:func:`mps_reserve` returns :c:macro:`MPS_RES_OK`, go to step 2.

   Otherwise, the block cannot be reserved (this might happen if the
   MPS is out of memory).

2. Initialize the block. During this step the block must not be
   referenced by an :term:`exact reference`, and references stored in
   it must not be followed.

   The block need not be initialized completely, but if the pool has
   an :term:`object format`, then by the end of this step, the block
   must be capable of being passed to the format's :term:`scan method`
   and :term:`skip method`.

3. Call :c:func:`mps_commit` to attempt to commit the object to the
   care of the MPS.

   If :c:func:`mps_commit` returns true, this means that the object is
   valid, and is now under the management of the MPS. The client program
   may rely on references stored in the object, and may store references
   to the new object in its other objects.

   If :c:func:`mps_commit` returns false, this means that the block is
   invalid. It is usual in this case to go back to step 1 and re-reserve
   and re-initialize it, but other courses of action are permitted.

   .. note::

       In this case, the reason the block is invalid because a
       :term:`flip` took place after the call to
       :c:func:`mps_reserve` and before the call to
       :c:func:`mps_commit`. This means that references in the block
       may point to the old location of blocks that moved.

The usual implementation of the allocation point protocol in :term:`C`
is thus::

    mps_addr_t p;
    obj_t obj;
    do {
        mps_res_t res = mps_reserve(&p, ap, size);
        if (res != MPS_RES_OK) /* handle the error */;
        /* p is now an ambiguous reference to the reserved block */
        obj = p;
        /* initialize obj */
    } while (!mps_commit(ap, p, size));
    /* obj is now valid and managed by the MPS */

It is not necessary to worry about going around this loop many times:
:c:func:`mps_commit` can fail at most once per thread per
:term:`flip`.


.. c:function:: mps_res_t mps_reserve(mps_addr_t *p_o, mps_ap_t ap, size_t size)

    Reserve a :term:`block` of memory on an :term:`allocation point`.

    ``p_o`` points to a location that will hold the address of the
    reserved block.

    ``ap`` is the allocation point.

    ``size`` is the :term:`size` of the block to allocate. It must be
    a multiple of the :term:`alignment` of the pool (or of the pool's
    :term:`object format` if it has one).

    Returns :c:macro:`MPS_RES_OK` if the block was reserved
    successfully, or another :term:`result code` if not.

    The reserved block may be initialized but must not otherwise be
    used 

    Until it has been :term:`committed (2)` via a successful call to
    :c:func:`mps_commit`, the reserved block may be:

    * initialized;
    * referenced by an :term:`ambiguous reference`;

    but:

    * it must not be referenced by an :term:`exact reference`;
    * references stored in it must not be followed;
    * it is not scanned, moved, or protected (even if it belongs to a
      pool with these features).

    .. note::

        :c:func:`mps_reserve` must only be called according to the
        :ref:`topic-allocation-point-protocol`.

        :c:func:`mps_reserve` is implemented as a macro for speed. It
        may evaluate its arguments multiple times.

        There is an alternative, :c:func:`MPS_RESERVE_BLOCK`, which
        may generate faster code on some compilers.


.. c:function:: MPS_RESERVE_BLOCK(mps_res_t res_v, mps_addr_t *p_v, mps_ap_t ap, size_t size)

    An alternative to :c:func:`mps_reserve`. On compilers that do not
    perform common-subexpression elimination, it may generate faster
    code than :c:func:`mps_reserve` (but may not). It may only be used
    in statement context (not as an expression).

    The second argument is an lvalue ``p_v``, which is assigned the
    address of the reserved block. It takes an additional first
    argument, the lvalue ``res_v``, which is assigned the
    :term:`result code`.


.. c:function:: mps_bool_t mps_commit(mps_ap_t ap, mps_addr_t p, size_t size)

    :term:`Commit <committed (2)>` a reserved :term:`block` on an
    :term:`allocation point`.

    ``ap`` is an allocation point.

    ``p`` points to a block that was reserved by :c:func:`mps_reserve`
    but has not yet been committed.

    ``size`` is the :term:`size` of the block to allocate. It must be
    the same size that was passed to :c:func:`mps_reserve`.

    If :c:func:`mps_commit` returns true, the block was successfully
    committed, which means that the :term:`client program` may use it,
    create references to it, and rely on references from it. It also
    means that the MPS may scan it, move it, protect it, or reclaim it
    (if ``ap`` was attached to a pool with those features).

    If :c:func:`mps_commit` returns false, the block was not
    committed. This means that the client program must not create
    references to the block, rely on references from it, or otherwise
    use it. It is normal to attempt the reserve operation again when
    this happens.

    It is very rare for :c:func:`mps_commit` to return false: this
    only happens if there was a :term:`flip` between the call to
    :c:func:`mps_reserve` and the call to
    :c:func:`mps_commit`. Nonetheless, it can happen, so it is
    important not to perform operations with side effects (that you
    aren't prepared to repeat) between calling :c:func:`mps_reserve`
    and :c:func:`mps_commit`. Also, the shorter the interval, the less
    likely :c:func:`mps_commit` is to return false.

    .. note::

        :c:func:`mps_commit` must only be called according to the
        :ref:`topic-allocation-point-protocol`.

        :c:func:`mps_commit` is implemented as a macro for speed. It
        may evaluate its arguments multiple times.


.. index::
   single: allocation point protocol; example

Example: allocating a symbol
----------------------------

::

    typedef struct symbol_s {
        type_t type;                  /* TYPE_SYMBOL */
        size_t length;                /* length of symbol string (excl. NUL) */
        char string[1];               /* symbol string, NUL terminated */
    } symbol_s, *symbol_t;

    symbol_t make_symbol(size_t length, char string[])
    {
        symbol_t symbol;
        mps_addr_t addr;
        size_t size = ALIGN(offsetof(symbol_s, string) + length+1);
        do {
            mps_res_t res = mps_reserve(&addr, ap, size);
            if (res != MPS_RES_OK) error("out of memory in make_symbol");
            symbol = addr;
            symbol->type = TYPE_SYMBOL;
            symbol->length = length;
            memcpy(symbol->string, string, length+1);
        } while (!mps_commit(ap, addr, size));
        return symbol;
    }


.. index::
   pair: allocation point protocol; cautions

.. _topic-allocation-cautions:

Cautions
--------

While a block is reserved but not yet committed:

1.  The client program must not create an :term:`exact reference` to
    the reserved block (for example, by referring to the reserved block
    from a :term:`formatted object`). All references to it must be
    ambiguous (for example, local variables).

2.  Similar restrictions apply to a reference that has been stored in
    the reserved block. Such a reference might be invalid, and must
    not be copied to an :term:`exact reference` or dereferenced. It is
    safe to copy such a reference if it remains ambiguous (for
    example, copying to a local variable or to another part of the new
    block).

Before calling :c:func:`mps_commit`:

1.  The new block must be validly formatted. If it belongs to an
    :term:`object format`, then it must be correctly recognized by the
    format methods (the :term:`skip method` must return the object's
    correct size; the :term:`scan method` must scan it; the
    :term:`is-forwarded method` must report that it is not a
    forwarding object, and so on).

2.  All exact references in the new block (references that are
    :term:`fixed` by scanning functions) must contain valid
    references or null pointers.

3.  The new object must be ambiguously :term:`reachable`.

You do not have to initialize the whole block so long as you satisfy
these conditions. For example, it is permissible to defer
initialization completely (for example, by writing
``TYPE_UNINITIALIZED`` into a tag field), so long as you handle this
correctly in the format methods.

However, if you do not initialize the whole block then you should
beware: the uninitialized contents of the block is likely to consist
of dead objects. If, due to a bug, you created an exact reference into
the middle of the uninitialized block, this might by bad luck point to
a dead object, which would be resurrected (and it might well contain
further exact references to other dead objects). To ensure detection
of such a bug promptly you should consider filling the uninitialized
object with dummy values that cannot be mistaken for part of a valid
formatted object (at least in the debugging version of your program).

.. note::

    Some :term:`pool classes` have debugging counterparts that
    automatically overwrite free space with a pattern of bytes of your
    choosing. See :ref:`topic-debugging`.


.. index::
   single: allocation point protocol; bugs
   single: bug; allocation point protocol

Example: inserting into a doubly linked list
--------------------------------------------

This example contains several mistakes. See the highlighted lines:

.. code-block:: c
    :emphasize-lines: 21, 22, 23, 25

    typedef struct link_s {
        type_t type;                       /* TYPE_LINK */
        /* all three of these pointers are fixed: */
        struct link_s *prev;
        struct link_s *next;
        obj_t obj;
    } link_s, *link_t;

    /* insert 'obj' into the doubly-linked list after 'head' */
    link_t insert_link(link_t head, obj_t obj)
    {
        mps_addr_t p;
        link_t link;
        size_t size = ALIGN(sizeof(link_s));
        do {
            mps_res_t res = mps_reserve(&p, ap, size);
            if (res != MPS_RES_OK) error("out of memory");
            link = p;
            link->type = TYPE_LINK;
            link->prev = head;
            link->next = link->prev->next; /* (1) */
            head->next = link;             /* (2) */
            link->next->prev = link;       /* (3) */
        } while (!mps_commit(ap, p, size));
        link->obj = obj;                   /* (4) */
        return link;
    }

The mistakes are:

1. Dereferencing a reference (here, ``link->prev``) that was stored in
   the reserved block.

2. Making an exact reference to the reserved block (here,
   ``head->next`` becomes an exact reference to ``link``). This must
   be deferred until after a successful commit.

3. This line makes both mistakes made by lines (1) and (2).

4. The ``obj`` slot contains an exact reference that gets fixed by the
   scan method, so it must be initialized before the call to commit.

A correct version of ``insert_link`` looks like this::

    link_t insert_link(link_t head, obj_t obj)
    {
        mps_addr_t p;
        link_t link;
        size_t size = ALIGN(sizeof(link_s));
        do {
            mps_res_t res = mps_reserve(&p, ap, size);
            if (res != MPS_RES_OK) error("out of memory");
            link = p;
            link->type = TYPE_LINK;
            link->prev = head;
            link->next = head->next;
            link->obj = obj;
        } while (!mps_commit(ap, p, size));
        head->next->prev = link;
        head->next = link;
        return link;
    }


.. index::
   single: allocation points; implementation

.. _topic-allocation-point-implementation:

Allocation point implementation
-------------------------------

An allocation point consists of a structure of type :c:type:`mps_ap_s`
and an associated :term:`buffer`.

.. figure:: ../diagrams/ap-buffer.svg
    :align: center
    :alt: Diagram: Allocation point and its associated buffer.

    Allocation point and its associated buffer.

The buffer is structured as shown in the figure, with free space at
the end of the buffer, *committed* blocks at the beginning, and
(possibly) one *reserved* block in the middle. The :c:type:`mps_ap_s`
structure contains three addresses into the associated buffer:
``limit`` points to the end of the buffer, ``alloc`` points to the
beginning of the free space, and ``init`` points to the end of the
initialized blocks.

Allocation points are fast and nearly lock-free because in order to
reserve space for a new block, the client program first checks that
``ap->alloc + size <= ap->limit`` and in the common case that it is,
it takes a copy of ``ap->init`` (which now points to the reserved
block) and sets ``ap->alloc += size``.

What happens when ``ap->alloc + size > ap->limit``, that is, when the
new block won't fit in the buffer? Then the buffer needs to be
*refilled* by calling :c:func:`mps_ap_fill`, with typical results
shown in the diagram below.

.. figure:: ../diagrams/ap-fill.svg
    :align: center
    :alt: Diagram: Allocation point after refilling.

    Allocation point after refilling.

Refilling is why allocation points are only *nearly* lock-free:
:c:func:`mps_ap_fill` has to take locks on internal MPS data
structures.

Note that :c:func:`mps_ap_fill` reserves the requested block as well
as refilling the buffer.

The *reserve* operation thus looks like this::

    if (ap->alloc + size <= ap->limit) {
        ap->alloc += ap->size;
        p = ap->init;
    } else {
        res = mps_ap_fill(&p, ap, size);
        if (res != MPS_RES_OK) {
            /* handle error */;
        }
    }

The critical path consists of an add, a store, and a branch (and
branch prediction should work well since the test usually succeeds).

.. note::

    Normally the client program would use the macro
    :c:func:`mps_reserve` to perform this operation, as described
    above, rather than directly accessing the fields of the allocation
    point structure. But there are use cases where direct access is
    needed to generate the fastest code (for example, in the case of a
    compiler generating machine code that needs to interface with the
    MPS), and it is for these use cases that the details of
    :c:type:`mps_ap_s` are made public and supported.

When the new block has been initialized it must be :term:`committed
(2)`. To do this, set ``ap->init = ap->alloc`` and then check to see
if the allocation point has been *trapped*: that is, if the garbage
collector might have moved some objects since the new block was
reserved. The garbage collector traps an allocation point by setting
``ap->limit = 0``, so if this case is found, then the reserved block
may have been invalidated, and must be discarded and re-reserved, and
the buffer must be refilled. The function :c:func:`mps_ap_trip`
determines whether or not this case applies, returning true if the
block is valid, false if not.

The *commit* operation thus looks like this::

    ap->init = ap->alloc;
    if (ap->limit == 0 && !mps_ap_trip(ap, p, size)) {
        /* p is invalid */
    } else {
        /* p is valid */
    }

The critical path here consists of a store and a branch (and again,
branch prediction should work well since the test almost never fails).

.. note::

    Normally the client program would use :c:func:`mps_commit` to
    perform this operation, as described above, rather than directly
    accessing the fields of the allocation point structure. But direct
    access is supported by the MPS.

.. note::

    The commit operation relies on atomic ordered access to words in
    memory to detect a :term:`flip` that occurs between the assignment
    ``ap->init = ap->alloc`` and the test ``ap->limit == 0``. A
    compiler or processor that reordered these two instructions would
    break the protocol. On some processor architectures and some
    compilers, it may be necessary to insert a memory barrier
    instruction at this point.


.. c:type:: mps_ap_s

    The type of the structure used to represent :term:`allocation
    points`::

         typedef struct mps_ap_s {
             mps_addr_t init;
             mps_addr_t alloc;
             mps_addr_t limit;
             /* ... private fields ... */
         } mps_ap_s;

    ``init`` is the limit of initialized memory.

    ``alloc`` is the limit of allocated memory.

    ``limit`` is the limit of available memory.

    An allocation point is an interface to a :term:`pool` which
    provides very fast allocation, and defers the need for
    synchronization in a multi-threaded environment.

    Create an allocation point for a pool by calling
    :c:func:`mps_ap_create_k`, and allocate memory via one by calling
    :c:func:`mps_reserve` and :c:func:`mps_commit`.


.. c:function:: mps_res_t mps_ap_fill(mps_addr_t *p_o, mps_ap_t ap, size_t size)

    Reserve a :term:`block` of memory on an :term:`allocation point`
    when the allocation point has insufficient space.

    :c:func:`mps_ap_fill` has same interface as :c:func:`mps_reserve`.

    .. note::

        :c:func:`mps_ap_fill` must only be called according to the
        :ref:`topic-allocation-point-protocol`.


.. c:function:: mps_bool_t mps_ap_trip(mps_ap_t ap, mps_addr_t p, size_t size)

    Test whether a reserved block was successfully :term:`committed
    (2)` when an :term:`allocation point` was trapped.

    :c:func:`mps_ap_trip` has the same interface as :c:func:`mps_commit`.

    .. note::

        :c:func:`mps_ap_trip` must only be called according to the
        :ref:`topic-allocation-point-protocol`.