1 /*
   2  *  Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
   3  *  DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  *  This code is free software; you can redistribute it and/or modify it
   6  *  under the terms of the GNU General Public License version 2 only, as
   7  *  published by the Free Software Foundation.  Oracle designates this
   8  *  particular file as subject to the "Classpath" exception as provided
   9  *  by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  *  This code is distributed in the hope that it will be useful, but WITHOUT
  12  *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  *  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  *  version 2 for more details (a copy is included in the LICENSE file that
  15  *  accompanied this code).
  16  *
  17  *  You should have received a copy of the GNU General Public License version
  18  *  2 along with this work; if not, write to the Free Software Foundation,
  19  *  Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  *   Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  *  or visit www.oracle.com if you need additional information or have any
  23  *  questions.
  24  *
  25  */
  26 
  27 package jdk.internal.foreign;
  28 
  29 import java.util.concurrent.atomic.AtomicInteger;
  30 
  31 /**
  32  * This class manages the temporal bounds associated with a memory segment. A scope has a liveness bit, which is updated
  33  * when the scope is closed (this operation is triggered by {@link MemorySegmentImpl#close()}). Furthermore, a scope is
  34  * associated with an <em>atomic</em> counter which can be incremented (upon calling the {@link #acquire()} method),
  35  * and is decremented (when a previously acquired segment is later closed).
  36  */
  37 public final class MemoryScope {
  38 
  39     //reference to keep hold onto
  40     final Object ref;
  41     private boolean isAlive = true;
  42 
  43     final AtomicInteger activeCount = new AtomicInteger();
  44 
  45     final static int UNACQUIRED = 0;
  46     final static int CLOSED = -1;
  47 
  48     final Runnable cleanupAction;
  49 
  50     public MemoryScope(Object ref, Runnable cleanupAction) {
  51         this.ref = ref;
  52         this.cleanupAction = cleanupAction;
  53     }
  54 
  55     final boolean isAlive() {
  56         return isAlive;
  57     }
  58 
  59     final void checkAlive() {
  60         if (!isAlive) {
  61             throw new IllegalStateException("Segment is not alive");
  62         }
  63     }
  64 
  65     MemoryScope acquire() {
  66         int newValue = activeCount.updateAndGet(i -> (i < 0) ? i : i + 1);
  67         if (newValue == CLOSED) {
  68             //cannot get here - segment is not alive!
  69             throw new IllegalStateException();
  70         } else if (newValue < 0) {
  71             //overflow
  72             throw new IllegalStateException("Segment acquire limit exceeded");
  73         }
  74         return new MemoryScope(ref, this::release);
  75     }
  76 
  77     void release() {
  78         if (activeCount.getAndUpdate(i -> i <= UNACQUIRED ? i : i - 1) <= UNACQUIRED) {
  79             //cannot get here - we can't close segment twice
  80             throw new IllegalStateException();
  81         }
  82     }
  83 
  84     void close() {
  85         if (!activeCount.compareAndSet(UNACQUIRED, CLOSED)) {
  86             throw new IllegalStateException("Cannot close a segment that has active acquired views");
  87         }
  88         isAlive = false;
  89         if (cleanupAction != null) {
  90             cleanupAction.run();
  91         }
  92     }
  93 }