/*
 * Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved.
 * Copyright (c) 2018, 2020 SAP SE. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 *
 */

#ifndef SHARE_MEMORY_METASPACE_VIRTUALSPACELIST_HPP
#define SHARE_MEMORY_METASPACE_VIRTUALSPACELIST_HPP

#include "memory/allocation.hpp"
#include "memory/metaspace/commitLimiter.hpp"
#include "memory/metaspace/counters.hpp"
#include "memory/metaspace/virtualSpaceNode.hpp"
#include "memory/virtualspace.hpp"
#include "utilities/globalDefinitions.hpp"

class outputStream;

namespace metaspace {

class Metachunk;
class FreeChunkListVector;

// VirtualSpaceList manages a series of virtual memory regions used
//  for metaspace.
//
// Internally it holds a list of nodes (VirtualSpaceNode) each
//  managing a single contiguous memory region. The first node of
//  this list is the current node and used for allocation of new
//  root chunks.
//
// The list will only ever grow, never shrink. It will be immortal,
//  never to be destroyed.
//
// The list will only be modified under lock protection, but may be
//  read concurrently without lock.
//
// The list may be prevented from expanding beyond a single node -
//  in that case it degenerates to a one-node-list (used for
//  class space).
//

class VirtualSpaceList : public CHeapObj<mtClass> {

  // Name
  const char* const _name;

  // Head of the list (last added).
  VirtualSpaceNode* volatile _first_node;

  // Number of nodes (kept for statistics only).
  IntCounter _nodes_counter;

  // Whether this list can expand by allocating new nodes.
  const bool _can_expand;

  // Used to check limits before committing memory.
  CommitLimiter* const _commit_limiter;

  // Statistics

  // Holds sum of reserved space, in words, over all list nodes.
  SizeCounter _reserved_words_counter;

  // Holds sum of committed space, in words, over all list nodes.
  SizeCounter _committed_words_counter;

  // Create a new node and append it to the list. After
  // this function, _current_node shall point to a new empty node.
  // List must be expandable for this to work.
  void create_new_node();

public:

  // Create a new, empty, expandable list.
  VirtualSpaceList(const char* name, CommitLimiter* commit_limiter);

  // Create a new list. The list will contain one node only, which uses the given ReservedSpace.
  // It will be not expandable beyond that first node.
  VirtualSpaceList(const char* name, ReservedSpace rs, CommitLimiter* commit_limiter);

  virtual ~VirtualSpaceList();

  // Allocate a root chunk from this list.
  // Note: this just returns a chunk whose memory is reserved; no memory is committed yet.
  // Hence, before using this chunk, it must be committed.
  // May return null if vslist would need to be expanded to hold the new root node but
  // the list cannot be expanded (in practice this means we reached CompressedClassSpaceSize).
  Metachunk* allocate_root_chunk();

  //// Statistics ////

  // Return sum of reserved words in all nodes.
  size_t reserved_words() const     { return _reserved_words_counter.get(); }

  // Return sum of committed words in all nodes.
  size_t committed_words() const    { return _committed_words_counter.get(); }

  // Return number of nodes in this list.
  int num_nodes() const             { return _nodes_counter.get(); }

  //// Debug stuff ////
  DEBUG_ONLY(void verify() const;)
  DEBUG_ONLY(void verify_locked() const;)

  // Print all nodes in this space list.
  void print_on(outputStream* st) const;

  // Returns true if this pointer is contained in one of our nodes.
  bool contains(const MetaWord* p) const;

  // Convenience methods to return the global class-space vslist
  //  and non-class vslist, respectively.
  static VirtualSpaceList* vslist_class();
  static VirtualSpaceList* vslist_nonclass();

  // These exist purely to print limits of the compressed class space;
  // if we ever change the ccs to not use a degenerated-list-of-one-node this
  // will go away.
  MetaWord* base_of_first_node() const { return _first_node != nullptr ? _first_node->base() : nullptr; }
  size_t word_size_of_first_node() const { return _first_node != nullptr ? _first_node->word_size() : 0; }

};

} // namespace metaspace

#endif // SHARE_MEMORY_METASPACE_VIRTUALSPACELIST_HPP
