// Copyright (c) 2012 The Chromium 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 REMOTING_BASE_AUTO_THREAD_H_
#define REMOTING_BASE_AUTO_THREAD_H_

#include <string>

#include "base/callback_forward.h"
#include "base/macros.h"
#include "base/message_loop/message_pump_type.h"
#include "base/threading/platform_thread.h"
#include "base/threading/thread_checker.h"
#include "build/build_config.h"
#include "remoting/base/auto_thread_task_runner.h"

namespace remoting {

// Thread implementation that runs a MessageLoop on a new thread, and manages
// the lifetime of the MessageLoop and thread by tracking references to the
// thread's TaskRunner.  The caller passes the thread's TaskRunner to each
// object that needs to run code on the thread, and when no references to the
// TaskRunner remain, the thread will exit.  When the caller destroys this
// object they will be blocked until the thread exits.
// All pending tasks queued on the thread's message loop will run to completion
// before the thread is terminated.
//
// After the thread is stopped, the destruction sequence is:
//
//  (1) Thread::CleanUp()
//  (2) MessageLoop::~MessageLoop
//  (3.b) CurrentThread::DestructionObserver::WillDestroyCurrentMessageLoop
class AutoThread : base::PlatformThread::Delegate {
 public:
  // Create an AutoThread with the specified message-loop |type| and |name|.
  // The supplied AutoThreadTaskRunner will be used to join and delete the
  // new thread when no references to it remain.
  static scoped_refptr<AutoThreadTaskRunner> CreateWithType(
      const char* name,
      scoped_refptr<AutoThreadTaskRunner> joiner,
      base::MessagePumpType type);
  static scoped_refptr<AutoThreadTaskRunner> Create(
      const char* name,
      scoped_refptr<AutoThreadTaskRunner> joiner);

#if defined(OS_WIN)
  // Create an AutoThread initialized for COM.  |com_init_type| specifies the
  // type of COM apartment to initialize.
  enum ComInitType { COM_INIT_NONE, COM_INIT_STA, COM_INIT_MTA };
  static scoped_refptr<AutoThreadTaskRunner> CreateWithLoopAndComInitTypes(
      const char* name,
      scoped_refptr<AutoThreadTaskRunner> joiner,
      base::MessagePumpType pump_type,
      ComInitType com_init_type);
#endif

  // Construct the AutoThread.  |name| identifies the thread for debugging.
  explicit AutoThread(const char* name);

  // Waits for the thread to exit, and then destroys it.
  ~AutoThread() override;

  // Starts the thread, running the specified type of MessageLoop.  Returns
  // an AutoThreadTaskRunner through which tasks may be posted to the thread
  // if successful, or NULL on failure.
  //
  // Note: This function can't be called on Windows with the loader lock held;
  // i.e. during a DllMain, global object construction or destruction, atexit()
  // callback.
  //
  // NOTE: You must not call this MessageLoop's Quit method directly.  The
  // thread will exit when no references to the TaskRunner remain.
  scoped_refptr<AutoThreadTaskRunner> StartWithType(base::MessagePumpType type);

#if defined(OS_WIN)
  // Configures the thread to initialize the specified COM apartment type.
  // SetComInitType() must be called before Start().
  void SetComInitType(ComInitType com_init_type);
#endif

 private:
  AutoThread(const char* name, AutoThreadTaskRunner* joiner);

  void QuitThread(base::OnceClosure quit_when_idle_closure);
  void JoinAndDeleteThread();

  // base::PlatformThread::Delegate methods:
  void ThreadMain() override;

  // Used to pass data to ThreadMain.
  struct StartupData;
  StartupData* startup_data_;

#if defined(OS_WIN)
  // Specifies which kind of COM apartment to initialize, if any.
  ComInitType com_init_type_;
#endif

  // The thread's handle.
  base::PlatformThreadHandle thread_;

  // The name of the thread.  Used for debugging purposes.
  std::string name_;

  // Flag used to indicate whether MessageLoop was quit properly.
  // This allows us to detect premature exit via MessageLoop::QuitWhenIdle().
  bool was_quit_properly_;

  // AutoThreadTaskRunner to post a task to to join & delete this thread.
  scoped_refptr<AutoThreadTaskRunner> joiner_;

  // Verifies that QuitThread() is called on the same thread as ThreadMain().
  base::ThreadChecker thread_checker_;

  DISALLOW_COPY_AND_ASSIGN(AutoThread);
};

}  // namespace remoting

#endif  // REMOTING_BASE_AUTO_THREAD_H_
