// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef V8_HEAP_LOCAL_HEAP_H_
#define V8_HEAP_LOCAL_HEAP_H_

#include <atomic>
#include <memory>

#include "src/base/platform/condition-variable.h"
#include "src/base/platform/mutex.h"
#include "src/execution/isolate.h"
#include "src/heap/concurrent-allocator.h"

namespace v8 {
namespace internal {

class Heap;
class Safepoint;
class LocalHandles;
class PersistentHandles;

class LocalHeap {
 public:
  V8_EXPORT_PRIVATE explicit LocalHeap(
      Heap* heap,
      std::unique_ptr<PersistentHandles> persistent_handles = nullptr);
  V8_EXPORT_PRIVATE ~LocalHeap();

  // Invoked by main thread to signal this thread that it needs to halt in a
  // safepoint.
  void RequestSafepoint();

  // Frequently invoked by local thread to check whether safepoint was requested
  // from the main thread.
  V8_EXPORT_PRIVATE void Safepoint();

  LocalHandles* handles() { return handles_.get(); }

  V8_EXPORT_PRIVATE Handle<Object> NewPersistentHandle(Address value);
  V8_EXPORT_PRIVATE std::unique_ptr<PersistentHandles>
  DetachPersistentHandles();

  bool IsParked();

  Heap* heap() { return heap_; }

  ConcurrentAllocator* old_space_allocator() { return &old_space_allocator_; }

  // Mark/Unmark linear allocation areas black. Used for black allocation.
  void MarkLinearAllocationAreaBlack();
  void UnmarkLinearAllocationArea();

  // Give up linear allocation areas. Used for mark-compact GC.
  void FreeLinearAllocationArea();

  // Create filler object in linear allocation areas. Verifying requires
  // iterable heap.
  void MakeLinearAllocationAreaIterable();

 private:
  enum class ThreadState {
    // Threads in this state need to be stopped in a safepoint.
    Running,
    // Thread was parked, which means that the thread is not allowed to access
    // or manipulate the heap in any way.
    Parked,
    // Thread was stopped in a safepoint.
    Safepoint
  };

  V8_EXPORT_PRIVATE void Park();
  V8_EXPORT_PRIVATE void Unpark();
  void EnsureParkedBeforeDestruction();

  bool IsSafepointRequested();
  void ClearSafepointRequested();

  void EnterSafepoint();

  Heap* heap_;

  base::Mutex state_mutex_;
  base::ConditionVariable state_change_;
  ThreadState state_;

  std::atomic<bool> safepoint_requested_;

  bool allocation_failed_;

  LocalHeap* prev_;
  LocalHeap* next_;

  std::unique_ptr<LocalHandles> handles_;
  std::unique_ptr<PersistentHandles> persistent_handles_;

  ConcurrentAllocator old_space_allocator_;

  friend class Heap;
  friend class GlobalSafepoint;
  friend class ParkedScope;
  friend class ConcurrentAllocator;
};

class ParkedScope {
 public:
  explicit ParkedScope(LocalHeap* local_heap) : local_heap_(local_heap) {
    local_heap_->Park();
  }

  ~ParkedScope() { local_heap_->Unpark(); }

 private:
  LocalHeap* local_heap_;
};

}  // namespace internal
}  // namespace v8

#endif  // V8_HEAP_LOCAL_HEAP_H_