/* 
TEST_HEADER
 id = $Id: //info.ravenbrook.com/project/mps/master/test/function/235.c#3 $
 summary = regression test for job004102
 language = c
 link = testlib.o
 parameters = GRAINSIZE=4096
END_HEADER
*/

#include "mpsavm.h"
#include "mpscmvff.h"
#include "testlib.h"

static void test(void *stack_pointer)
{
  mps_arena_t arena;
  mps_pool_t pool;
  void *addr[GRAINSIZE];
  size_t i;

  MPS_ARGS_BEGIN(args) {
    MPS_ARGS_ADD(args, MPS_KEY_ARENA_GRAIN_SIZE, GRAINSIZE);
    MPS_ARGS_ADD(args, MPS_KEY_SPARE, 0.0);
    cdie(mps_arena_create_k(&arena, mps_arena_class_vm(), args),
         "create arena");
  } MPS_ARGS_END(args);

  MPS_ARGS_BEGIN(args) {
    MPS_ARGS_ADD(args, MPS_KEY_EXTEND_BY, GRAINSIZE);
    MPS_ARGS_ADD(args, MPS_KEY_SPARE, 0.0);
    cdie(mps_pool_create_k(&pool, arena, mps_class_mvff(), args),
         "create pool");
  } MPS_ARGS_END(args);

  /* Allocate objects, each consisting of one arena grain. */
  for (i = 0; i < GRAINSIZE; ++i) {
    die(mps_alloc(&addr[i], pool, GRAINSIZE), "alloc");
  }

  /* 1. The MVFF pool was configured to keep no spare memory, so the
     freed memory will be returned to the arena immediately and added
     to the free land.

     2. We are freeing every other object, and so each object is an
     isolated continguous free range and so requires an additional CBS
     block.

     3. Eventually the free land's block pool runs out of memory and
     requires extending.

     4. The arena was configured to have no spare committed memory,
     and no additional memory can be mapped, because the commit limit
     is set to the committed memory. This means that the arena will
     have to "steal" a grain from the freed memory to extend the block
     pool.

     5. The freed memory consists of a single grain, so after stealing
     a grain there is nothing left to add to the free land. This is
     the case we are testing. */

  for (i = 0; i < GRAINSIZE; i += 2) {
    mps_arena_commit_limit_set(arena, mps_arena_committed(arena));
    mps_free(pool, addr[i], GRAINSIZE);
  }

  mps_pool_destroy(pool);
  mps_arena_destroy(arena);
}

int main(void)
{
 run_test(test);
 pass();
 return 0;
}