1
Fork 0
mirror of git://git.sv.gnu.org/emacs.git synced 2026-04-27 08:43:40 -07:00

Merging branch/2014-10-01/finalize.

Copied from Perforce
 Change: 188932
 ServerID: perforce.ravenbrook.com
This commit is contained in:
Richard Brooksby 2016-01-20 16:32:38 +00:00
commit a9f83f1b87
15 changed files with 142 additions and 76 deletions

View file

@ -87,6 +87,14 @@ static mps_res_t stress(mps_arena_t arena, mps_pool_debug_option_s *options,
check_allocated_size(pool, ap, allocated);
}
/* Check introspection functions */
for (i = 0; i < NELEMS(ps); ++i) {
mps_pool_t addr_pool = NULL;
Insist(mps_arena_has_addr(arena, ps[i]));
Insist(mps_addr_pool(&addr_pool, arena, ps[i]));
Insist(addr_pool == pool);
}
mps_pool_check_fenceposts(pool);
for (k=0; k<testLOOPS; ++k) {

View file

@ -1450,10 +1450,10 @@ static void ArenaTrivCompact(Arena arena, Trace trace)
Bool ArenaHasAddr(Arena arena, Addr addr)
{
Seg seg;
Tract tract;
AVERT(Arena, arena);
return SegOfAddr(&seg, arena, addr);
return TractOfAddr(&tract, arena, addr);
}

View file

@ -24,6 +24,9 @@
#include "mps.h"
#include "mpsavm.h"
#include "mpscamc.h"
#include "mpscams.h"
#include "mpscawl.h"
#include "mpsclo.h"
#include "mpslib.h"
#include "mpstd.h"
#include "testlib.h"
@ -37,6 +40,7 @@
#define finalizationRATE 6
#define gcINTERVAL ((size_t)150 * 1024)
#define collectionCOUNT 3
#define messageCOUNT 3
/* 3 words: wrapper | vector-len | first-slot */
#define vectorSIZE (3*sizeof(mps_word_t))
@ -95,35 +99,37 @@ enum {
};
static void *test(void *arg, size_t s)
static void test(mps_arena_t arena, mps_pool_class_t pool_class)
{
unsigned i; /* index */
size_t i; /* index */
mps_ap_t ap;
mps_fmt_t fmt;
mps_chain_t chain;
mps_pool_t amc;
mps_pool_t pool;
mps_res_t e;
mps_root_t mps_root[2];
mps_addr_t nullref = NULL;
int state[rootCOUNT];
mps_arena_t arena;
void *p = NULL;
mps_message_t message;
size_t messages = 0;
void *p;
arena = (mps_arena_t)arg;
(void)s;
printf("---- finalcv: pool class %s ----\n", pool_class->name);
die(mps_fmt_create_A(&fmt, arena, dylan_fmt_A()), "fmt_create\n");
die(mps_chain_create(&chain, arena, genCOUNT, testChain), "chain_create");
die(mps_pool_create(&amc, arena, mps_class_amc(), fmt, chain),
"pool_create amc\n");
MPS_ARGS_BEGIN(args) {
MPS_ARGS_ADD(args, MPS_KEY_CHAIN, chain);
MPS_ARGS_ADD(args, MPS_KEY_FORMAT, fmt);
die(mps_pool_create_k(&pool, arena, pool_class, args), "pool_create\n");
} MPS_ARGS_END(args);
die(mps_root_create_table(&mps_root[0], arena, mps_rank_exact(), (mps_rm_t)0,
root, (size_t)rootCOUNT),
"root_create\n");
die(mps_root_create_table(&mps_root[1], arena, mps_rank_exact(), (mps_rm_t)0,
&p, (size_t)1),
"root_create\n");
die(mps_ap_create(&ap, amc, mps_rank_exact()), "ap_create\n");
die(mps_ap_create(&ap, pool, mps_rank_exact()), "ap_create\n");
/* Make registered-for-finalization objects. */
/* <design/poolmrg/#test.promise.ut.alloc> */
@ -142,12 +148,10 @@ static void *test(void *arg, size_t s)
}
p = NULL;
die(ArenaDescribe(arena, mps_lib_get_stdout(), 0), "ArenaDescribe");
mps_message_type_enable(arena, mps_message_type_finalization());
/* <design/poolmrg/#test.promise.ut.churn> */
while (mps_collections(arena) < collectionCOUNT) {
while (messages < messageCOUNT && mps_collections(arena) < collectionCOUNT) {
/* Perhaps cause (minor) collection */
churn(ap);
@ -197,36 +201,34 @@ static void *test(void *arg, size_t s)
if (rnd() % 2 == 0)
root[objind] = objaddr;
mps_message_discard(arena, message);
++ messages;
}
}
/* @@@@ <design/poolmrg/#test.promise.ut.nofinal.check> missing */
mps_arena_park(arena);
mps_ap_destroy(ap);
mps_root_destroy(mps_root[1]);
mps_root_destroy(mps_root[0]);
mps_pool_destroy(amc);
mps_pool_destroy(pool);
mps_chain_destroy(chain);
mps_fmt_destroy(fmt);
return NULL;
}
int main(int argc, char *argv[])
{
mps_arena_t arena;
mps_thr_t thread;
void *r;
testlib_init(argc, argv);
die(mps_arena_create(&arena, mps_arena_class_vm(), testArenaSIZE),
"arena_create\n");
die(mps_thread_reg(&thread, arena), "thread_reg\n");
mps_tramp(&r, test, arena, 0);
mps_thread_dereg(thread);
test(arena, mps_class_amc());
test(arena, mps_class_amcz());
test(arena, mps_class_awl());
test(arena, mps_class_ams());
test(arena, mps_class_lo());
mps_arena_destroy(arena);
printf("%s: Conclusion: Failed to find any defects.\n", argv[0]);

View file

@ -862,17 +862,19 @@ Bool ArenaStep(Globals globals, double interval, double multiplier)
Res ArenaFinalize(Arena arena, Ref obj)
{
Res res;
Pool refpool;
AVERT(Arena, arena);
AVER(ArenaHasAddr(arena, (Addr)obj));
AVER(PoolOfAddr(&refpool, arena, (Addr)obj));
AVER(PoolHasAttr(refpool, AttrGC));
if (!arena->isFinalPool) {
Pool pool;
Pool finalpool;
res = PoolCreate(&pool, arena, PoolClassMRG(), argsNone);
res = PoolCreate(&finalpool, arena, PoolClassMRG(), argsNone);
if (res != ResOK)
return res;
arena->finalPool = pool;
arena->finalPool = finalpool;
arena->isFinalPool = TRUE;
}

View file

@ -233,6 +233,7 @@ static void ap_create_v_test(mps_pool_t pool, ...)
/* addr_pool_test
*
* intended to test:
* mps_arena_has_addr
* mps_addr_pool
* mps_addr_fmt
*/
@ -270,6 +271,7 @@ static void addr_pool_test(mps_arena_t arena,
addr = obj1;
pool = poolDistinguished;
fmt = fmtDistinguished;
cdie(mps_arena_has_addr(arena, addr), "mps_arena_has_addr 0a");
b = mps_addr_pool(&pool, arena, addr);
/* printf("b %d; pool %p; sig %lx\n", b, (void *)pool,
b ? ((mps_word_t*)pool)[0] : (mps_word_t)0); */
@ -283,6 +285,7 @@ static void addr_pool_test(mps_arena_t arena,
addr = obj2;
pool = poolDistinguished;
fmt = fmtDistinguished;
cdie(mps_arena_has_addr(arena, addr), "mps_arena_has_addr 0b");
b = mps_addr_pool(&pool, arena, addr);
/* printf("b %d; pool %p; sig %lx\n", b, (void *)pool,
b ? ((mps_word_t*)pool)[0] : (mps_word_t)0); */
@ -296,6 +299,7 @@ static void addr_pool_test(mps_arena_t arena,
addr = &pool; /* point at stack, not in any chunk */
pool = poolDistinguished;
fmt = fmtDistinguished;
cdie(mps_arena_has_addr(arena, addr) == FALSE, "mps_arena_has_addr 5");
b = mps_addr_pool(&pool, arena, addr);
cdie(b == FALSE && pool == poolDistinguished, "mps_addr_pool 5");
b = mps_addr_fmt(&fmt, arena, addr);

View file

@ -745,7 +745,12 @@ Res MRGRegister(Pool pool, Ref ref)
}
/* MRGDeregister -- deregister (once) an object for finalization */
/* MRGDeregister -- deregister (once) an object for finalization
*
* TODO: Definalization loops over all finalizable objects in the heap,
* and so using it could accidentally be disastrous for performance.
* See job003953 and back out changelist 187123 if this is fixed.
*/
Res MRGDeregister(Pool pool, Ref obj)
{

View file

@ -630,10 +630,6 @@ All objects from the MRG pool will then be freed (thus dropping all
references to the AMC objects). This will test `.promise.faithful`_
and `.promise.live`_.
_`.test.promise.ut.not`: The following part of the test has not
implemented. This is because the messaging system has not yet been
implemented.
_`.test.promise.ut.alloc`: A number of objects will be allocated in
the AMC pool.

View file

@ -991,22 +991,12 @@ static char *symbol_name(obj_t symbol)
}
/* port_close -- close and definalize a port %%MPS
*
* Ports objects are registered for finalization when they are created
* (see make_port). When closed, we definalize them. This is purely an
* optimization: it would be harmless to finalize them because setting
* 'stream' to NULL prevents the stream from being closed multiple
* times. See topic/finalization.
*/
static void port_close(obj_t port)
{
assert(TYPE(port) == TYPE_PORT);
if(port->port.stream != NULL) {
mps_addr_t port_ref = port;
fclose(port->port.stream);
port->port.stream = NULL;
mps_definalize(arena, &port_ref);
}
}

View file

@ -1017,22 +1017,12 @@ static void table_delete(obj_t tbl, obj_t key)
}
/* port_close -- close and definalize a port %%MPS
*
* Ports objects are registered for finalization when they are created
* (see make_port). When closed, we definalize them. This is purely an
* optimization: it would be harmless to finalize them because setting
* 'stream' to NULL prevents the stream from being closed multiple
* times. See topic/finalization.
*/
static void port_close(obj_t port)
{
assert(TYPE(port) == TYPE_PORT);
if(port->port.stream != NULL) {
mps_addr_t port_ref = port;
fclose(port->port.stream);
port->port.stream = NULL;
mps_definalize(arena, &port_ref);
}
}

View file

@ -28,14 +28,6 @@ call ``close-input-file``, then the underlying file handle should still
be closed when the port object :term:`dies <dead>`. This procedure is
known as :term:`finalization`.
.. note::
It's generally a bad idea to depend on finalization to release your
resources (see the :ref:`topic-finalization-cautions` section in
:ref:`topic-finalization`). Treat it as a last resort when more
reliable mechanisms for releasing resources (like Scheme's
``with-open-input-file``) aren't available.
Any block in an :term:`automatically managed <automatic memory
management>` :term:`pool` can be registered for finalization by calling
:c:func:`mps_finalize`. In the toy Scheme interpreter, this can be done
@ -138,26 +130,37 @@ Here's an example session showing finalization taking place:
not_condemned 0
clock: 3807
The toy Scheme interpreter :dfn:`definalizes` ports by calling
:c:func:`mps_definalize` when they are closed. This is purely an
optimization: setting ``stream`` to ``NULL`` ensures that the file
handle wouldn't be closed more than once, even if the port object were
later finalized.
It's wise not to depend on finalization as the only method for
releasing resources (see the :ref:`topic-finalization-cautions`
section in :ref:`topic-finalization`), because the garbage collector
does not promise to collect particular objects at particular times,
and in any case it does so only when it can prove that the object is
:term:`dead`. So it is best to provide a reliable mechanism for
releasing the resource (here, the Scheme function
``close-input-port``), and use finalization as a backup strategy.
But this raises the possibility that a port will be closed twice: once
via ``close-input-port`` and a second time via finalization. So it's
necessary to make ports robust against be closed multiple times. The
toy Scheme interpreter does so by setting ``stream`` to ``NULL``: this
ensures that the file handle won't be closed more than once.
.. code-block:: c
:emphasize-lines: 8
:emphasize-lines: 6
static void port_close(obj_t port)
{
assert(TYPE(port) == TYPE_PORT);
if(port->port.stream != NULL) {
mps_addr_t port_ref = port;
fclose(port->port.stream);
port->port.stream = NULL;
mps_definalize(arena, &port_ref);
}
}
Note that because finalization messages are processed synchronously
via the message queue (and the Scheme interpreter is single-threaded)
there is no need for a lock here.
It's still possible that the toy Scheme interpreter might run out of
open file handles despite having some or all of its port objects being
finalizable. That's because the arena's message queue is only polled

View file

@ -65,6 +65,18 @@ Other changes
.. _job003899: https://www.ravenbrook.com/project/mps/issue/job003899/
#. Unfinalizable objects can no longer be registered for finalization.
Previously the objects would be registered but never finalized. See
job003865_.
.. _job003865: https://www.ravenbrook.com/project/mps/issue/job003865/
#. :c:func:`mps_arena_has_addr` now returns the correct result for
objects allocated from the :ref:`pool-mfs`, :ref:`pool-mv`, and
:ref:`pool-mvff` pools. See job003866_.
.. _job003866: https://www.ravenbrook.com/project/mps/issue/job003866/
.. _release-notes-1.114:

View file

@ -300,6 +300,13 @@ this documentation.
It is necessary to call :c:func:`mps_pool_destroy` first.
``global.c: PoolHasAttr(pool, AttrGC)``
The client program called :c:func:`mps_finalize` on a reference
that does not belong to an :term:`automatically managed <automatic
memory management>` :term:`pool`.
``lockix.c: res == 0``
``lockw3.c: lock->claims == 0``

View file

@ -221,10 +221,8 @@ Finalization interface
:term:`result code` if not.
This function registers the block pointed to by ``*ref_p`` for
finalization. This block must have been allocated from a
:term:`pool` in ``arena``. Violations of this constraint may not
be checked by the MPS, and may be unsafe, causing the MPS to crash
in undefined ways.
finalization. This block must have been allocated from an
automatically managed :term:`pool` in ``arena``.
.. note::
@ -252,6 +250,13 @@ Finalization interface
avoid placing the restriction on the :term:`client program`
that the C call stack be a :term:`root`.
.. warning::
Definalization is not yet efficient: the current
implementation just loops over all finalized objects. If you
need efficient definalization, please :ref:`contact us
<contact>`.
.. index::
pair: finalization; message

41
mps/test/function/228.c Normal file
View file

@ -0,0 +1,41 @@
/*
TEST_HEADER
id = $Id$
summary = can't register unfinalizable objects for finalization
language = c
link = testlib.o
OUTPUT_SPEC
assert = true
assertfile P= global.c
assertcond = PoolHasAttr(refpool, AttrGC)
END_HEADER
*/
#include "testlib.h"
#include "mpscmvff.h"
#include "mpsavm.h"
static void test(void)
{
mps_arena_t arena;
mps_pool_t pool;
mps_addr_t p;
die(mps_arena_create_k(&arena, mps_arena_class_vm(), mps_args_none),
"arena_create");
die(mps_pool_create_k(&pool, arena, mps_class_mvff(), mps_args_none),
"pool_create");
die(mps_alloc(&p, pool, 4096), "alloc");
die(mps_finalize(arena, &p), "finalize");
mps_pool_destroy(pool);
mps_arena_destroy(arena);
}
int main(void)
{
easy_tramp(test);
pass();
return 0;
}

View file

@ -168,4 +168,5 @@ function/224.c
% 225 -- no such test
function/226.c
function/227.c
function/229.c
function/228.c
function/229.c