1 /*
   2  * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
   3  * Copyright (c) 2020 SAP SE. All rights reserved.
   4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   5  *
   6  * This code is free software; you can redistribute it and/or modify it
   7  * under the terms of the GNU General Public License version 2 only, as
   8  * published by the Free Software Foundation.
   9  *
  10  * This code is distributed in the hope that it will be useful, but WITHOUT
  11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  13  * version 2 for more details (a copy is included in the LICENSE file that
  14  * accompanied this code).
  15  *
  16  * You should have received a copy of the GNU General Public License version
  17  * 2 along with this work; if not, write to the Free Software Foundation,
  18  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  19  *
  20  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  21  * or visit www.oracle.com if you need additional information or have any
  22  * questions.
  23  *
  24  */
  25 
  26 import java.util.ArrayList;
  27 import java.util.Random;
  28 
  29 /**
  30  * RandomAllocator sits atop an arena and allocates from it.
  31  *
  32  * It will, according to an allocation profile, allocate random blocks in a certain size range and, from time to time,
  33  * deallocate old blocks.
  34  *
  35  * At some point it will reach a limit: either the commit/reserve limit of the underlying MetaspaceTestContext,
  36  * or the allocation ceiling imposed by the test. From that point on allocations will start failing. We can (and do)
  37  * deallocate a bit more, but since that will only exercise the Arena's internal free block list and nothing much else,
  38  * this is unexciting in terms of stressing Metaspace. So, the caller may decide to kill the arena and create a new one.
  39  *
  40  */
  41 public class RandomAllocator {
  42 
  43     final MetaspaceTestArena arena;
  44     final AllocationProfile profile;
  45 
  46     ArrayList<Allocation> to_dealloc = new ArrayList<>();
  47 
  48     long ticks = 0;
  49     boolean allocationError = false;
  50 
  51     Random localRandom;
  52 
  53     // Roll dice and return true if probability was hit
  54     private boolean rollDice(double probability) {
  55         return ((double)localRandom.nextInt(100) > (100.0 * (1.0 - probability))) ? true : false;
  56     }
  57 
  58     // Allocate a random amount from the arena. If dice hits right, add this to the deallocation list.
  59     void allocateRandomly() {
  60         allocationError = false;
  61         long word_size = profile.randomAllocationSize();
  62         Allocation a = arena.allocate(word_size);
  63         if (a != null) {
  64             if (to_dealloc.size() < 10000) {
  65                 to_dealloc.add(a);
  66             }
  67         } else {
  68             allocationError = true;
  69         }
  70     }
  71 
  72     // Randomly choose one of the allocated in the deallocation list and deallocate it
  73     void deallocateRandomly() {
  74         if (to_dealloc.size() == 0) {
  75             return;
  76         }
  77         int n = localRandom.nextInt(to_dealloc.size());
  78         Allocation a = to_dealloc.remove(n);
  79         arena.deallocate(a);
  80     }
  81 
  82     public void tick() {
  83 
  84         if (!allocationError) {
  85             allocateRandomly();
  86             if(rollDice(profile.randomDeallocProbability)) {
  87                deallocateRandomly();
  88             }
  89         } else {
  90             deallocateRandomly();
  91             allocationError = false;
  92         }
  93 
  94         ticks ++;
  95 
  96     }
  97 
  98     public RandomAllocator(MetaspaceTestArena arena) {
  99         this.arena = arena;
 100         this.profile = AllocationProfile.randomProfile();
 101         // reproducable randoms (we assume each allocator is only used from within one thread, and gets created from the main thread).
 102         this.localRandom = new Random(RandomHelper.random().nextInt());
 103     }
 104 
 105 
 106     @Override
 107     public String toString() {
 108         return  arena.toString() + ", ticks=" + ticks;
 109     }
 110 
 111 }