// Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

#include "vm/thread_registry.h"

#include "vm/json_stream.h"
#include "vm/lockers.h"

namespace dart {

ThreadRegistry::~ThreadRegistry() {
  // Go over the free thread list and delete the thread objects.
  {
    MonitorLocker ml(threads_lock());
    // At this point the active list should be empty.
    ASSERT(active_list_ == nullptr);

    // Now delete all the threads in the free list.
    while (free_list_ != nullptr) {
      Thread* thread = free_list_;
      free_list_ = thread->next_;
      delete thread;
    }
  }
}

Thread* ThreadRegistry::GetFreeThreadLocked(bool is_bootstrapping) {
  ASSERT(threads_lock()->IsOwnedByCurrentThread());
  Thread* thread = GetFromFreelistLocked(is_bootstrapping);
  ASSERT(thread->api_top_scope() == nullptr);
  // Now add this Thread to the active list for the isolate.
  AddToActiveListLocked(thread);
  return thread;
}

void ThreadRegistry::ReturnThreadLocked(Thread* thread) {
  ASSERT(threads_lock()->IsOwnedByCurrentThread());
  // Remove thread from the active list for the isolate.
  RemoveFromActiveListLocked(thread);
  ReturnToFreelistLocked(thread);
}

void ThreadRegistry::VisitObjectPointers(
    IsolateGroup* isolate_group_of_interest,
    ObjectPointerVisitor* visitor,
    ValidationPolicy validate_frames) {
  MonitorLocker ml(threads_lock());
  Thread* thread = active_list_;
  while (thread != nullptr) {
    if (thread->isolate_group() == isolate_group_of_interest) {
      // The mutator thread with isolate is visited by the isolate itself (see
      // [IsolateGroup::VisitStackPointers]).
      // All other threads are visited here.
      if (thread->scheduled_dart_mutator_isolate() == nullptr) {
        thread->VisitObjectPointers(visitor, validate_frames);
      }
    }
    thread = thread->next_;
  }
}

void ThreadRegistry::ForEachThread(
    std::function<void(Thread* thread)> callback) {
  MonitorLocker ml(threads_lock());
  Thread* thread = active_list_;
  while (thread != nullptr) {
    callback(thread);
    thread = thread->next_;
  }
}

void ThreadRegistry::ReleaseStoreBuffers() {
  MonitorLocker ml(threads_lock());
  Thread* thread = active_list_;
  while (thread != nullptr) {
    if (!thread->BypassSafepoints()) {
      thread->ReleaseStoreBuffer();
    }
    thread = thread->next_;
  }
}

void ThreadRegistry::AcquireMarkingStacks() {
  MonitorLocker ml(threads_lock());
  Thread* thread = active_list_;
  while (thread != nullptr) {
    if (!thread->BypassSafepoints()) {
      thread->AcquireMarkingStacks();
    }
    thread = thread->next_;
  }
}

void ThreadRegistry::ReleaseMarkingStacks() {
  MonitorLocker ml(threads_lock());
  Thread* thread = active_list_;
  while (thread != nullptr) {
    if (!thread->BypassSafepoints()) {
      thread->ReleaseMarkingStacks();
      ASSERT(!thread->is_marking());
    }
    thread = thread->next_;
  }
}

void ThreadRegistry::FlushMarkingStacks() {
  MonitorLocker ml(threads_lock());
  Thread* thread = active_list_;
  while (thread != nullptr) {
    if (!thread->BypassSafepoints() && thread->is_marking()) {
      thread->FlushMarkingStacks();
      ASSERT(thread->is_marking());
    }
    thread = thread->next_;
  }
}

intptr_t ThreadRegistry::StealActiveMutators(ThreadPool* pool) {
  MonitorLocker ml(threads_lock());
  intptr_t count = 0;
  Thread* thread = active_list_;
  while (thread != nullptr) {
    if (thread->TryStealActiveMutator()) {
      pool->MarkWorkerAsBlocked(thread->os_thread());
      count++;
    }
    thread = thread->next_;
  }
  return count;
}

void ThreadRegistry::AddToActiveListLocked(Thread* thread) {
  ASSERT(thread != nullptr);
  ASSERT(threads_lock()->IsOwnedByCurrentThread());
  thread->next_ = active_list_;
  active_list_ = thread;
  active_isolates_count_.fetch_add(1);
}

void ThreadRegistry::RemoveFromActiveListLocked(Thread* thread) {
  ASSERT(thread != nullptr);
  ASSERT(threads_lock()->IsOwnedByCurrentThread());
  Thread* prev = nullptr;
  Thread* current = active_list_;
  while (current != nullptr) {
    if (current == thread) {
      if (prev == nullptr) {
        active_list_ = current->next_;
      } else {
        prev->next_ = current->next_;
      }
      active_isolates_count_.fetch_sub(1);
      break;
    }
    prev = current;
    current = current->next_;
  }
}

Thread* ThreadRegistry::GetFromFreelistLocked(bool is_bootstrapping) {
  ASSERT(threads_lock()->IsOwnedByCurrentThread());
  Thread* thread = nullptr;
  // Get thread structure from free list or create a new one.
  if (free_list_ == nullptr) {
    thread = new Thread(is_bootstrapping);
  } else {
    thread = free_list_;
    free_list_ = thread->next_;
  }
  return thread;
}

void ThreadRegistry::ReturnToFreelistLocked(Thread* thread) {
  ASSERT(thread != nullptr);
  ASSERT(thread->os_thread() == nullptr);
  ASSERT(thread->isolate_ == nullptr);
  ASSERT(thread->isolate_group_ == nullptr);
  ASSERT(thread->field_table_values_ == nullptr);
  ASSERT(thread->shared_field_table_values_ == nullptr);
  ASSERT(thread->thread_locals_ == Array::null());
  ASSERT(threads_lock()->IsOwnedByCurrentThread());
  // Add thread to the free list.
  thread->next_ = free_list_;
  free_list_ = thread;
}

}  // namespace dart
