chromium/remoting/host/win/worker_process_launcher.h

// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef REMOTING_HOST_WIN_WORKER_PROCESS_LAUNCHER_H_
#define REMOTING_HOST_WIN_WORKER_PROCESS_LAUNCHER_H_

#include <stdint.h>

#include <memory>

#include "base/compiler_specific.h"
#include "base/functional/callback.h"
#include "base/memory/raw_ptr.h"
#include "base/sequence_checker.h"
#include "base/timer/timer.h"
#include "base/win/object_watcher.h"
#include "base/win/scoped_handle.h"
#include "mojo/public/cpp/bindings/generic_pending_associated_receiver.h"
#include "net/base/backoff_entry.h"

namespace base {
class Location;
class TimeDelta;
}  // namespace base

namespace mojo {
class ScopedInterfaceEndpointHandle;
}

namespace remoting {

class WorkerProcessIpcDelegate;

// Launches a worker process that is controlled via an IPC channel. All
// interaction with the spawned process is through WorkerProcessIpcDelegate and
// Send() method. In case of error the channel is closed and the worker process
// is terminated.
class WorkerProcessLauncher : public base::win::ObjectWatcher::Delegate {
 public:
  class Delegate {
   public:
    virtual ~Delegate();

    // Asynchronously starts the worker process and creates an IPC channel it
    // can connect to. |event_handler| must remain valid until KillProcess() has
    // been called.
    virtual void LaunchProcess(WorkerProcessLauncher* event_handler) = 0;

    // Provides a way to request an associated interface from the worker process
    // IPC channel.
    virtual void GetRemoteAssociatedInterface(
        mojo::GenericPendingAssociatedReceiver receiver) = 0;

    // Closes the IPC channel.
    virtual void CloseChannel() = 0;

    // Ask the worker process to voluntarily crash and generate a dump.
    // |location| represents the caller who made the original request.
    virtual void CrashProcess(const base::Location& location) = 0;

    // Terminates the worker process and closes the IPC channel.
    virtual void KillProcess() = 0;
  };

  // Creates the launcher that will use |launcher_delegate| to manage the worker
  // process and |ipc_handler| to handle IPCs. The caller must ensure that
  // |ipc_handler| outlives this object.
  WorkerProcessLauncher(std::unique_ptr<Delegate> launcher_delegate,
                        WorkerProcessIpcDelegate* ipc_handler);

  WorkerProcessLauncher(const WorkerProcessLauncher&) = delete;
  WorkerProcessLauncher& operator=(const WorkerProcessLauncher&) = delete;

  ~WorkerProcessLauncher() override;

  // Asks the worker process to crash and generate a dump, and closes the IPC
  // channel. |location| is passed to the worker so that it is on the stack in
  // the dump. Restarts the worker process forcefully, if it does not exit on
  // its own.
  void Crash(const base::Location& location);

  // Provides a way to request an associated interface from the worker process
  // IPC channel.
  void GetRemoteAssociatedInterface(
      mojo::GenericPendingAssociatedReceiver receiver);

  // Notification methods invoked by |Delegate|.

  // Invoked to pass a handle of the launched process back to the caller of
  // Delegate::LaunchProcess(). The delegate has to make sure that this method
  // is called before OnChannelConnected().
  void OnProcessLaunched(base::win::ScopedHandle worker_process);

  // Called when a fatal error occurs (i.e. a failed process launch).
  // The delegate must guarantee that no other notifications are delivered once
  // OnFatalError() has been called.
  void OnFatalError();

  // Mirrors methods of IPC::Listener to be invoked by |Delegate|. |Delegate|
  // has to validate |peer_pid| if necessary.
  void OnChannelConnected(int32_t peer_pid);
  void OnChannelError();
  void OnAssociatedInterfaceRequest(const std::string& interface_name,
                                    mojo::ScopedInterfaceEndpointHandle handle);

 private:
  friend class WorkerProcessLauncherTest;

  // base::win::ObjectWatcher::Delegate implementation used to watch for
  // the worker process exiting.
  void OnObjectSignaled(HANDLE object) override;

  // Returns true when the object is being destroyed.
  bool stopping() const { return ipc_handler_ == nullptr; }

  // Attempts to launch the worker process. Schedules next launch attempt if
  // creation of the process fails.
  void LaunchWorker();

  // Called to record outcome of a launch attempt: success or failure.
  void RecordLaunchResult();

  // Called by the test to record a successful launch attempt.
  void RecordSuccessfulLaunchForTest();

  // Set the desired timeout for |kill_process_timer_|.
  void SetKillProcessTimeoutForTest(const base::TimeDelta& timeout);

  // Stops the worker process and schedules next launch attempt unless the
  // object is being destroyed already.
  void StopWorker();

  // Handles IPC messages sent by the worker process.
  raw_ptr<WorkerProcessIpcDelegate> ipc_handler_;

  // Implements specifics of launching a worker process.
  std::unique_ptr<WorkerProcessLauncher::Delegate> launcher_delegate_;

  // Keeps the exit code of the worker process after it was closed. The exit
  // code is used to determine whether the process has to be restarted.
  DWORD exit_code_;

  // The timer used to delay termination of the worker process when an IPC error
  // occured or when Crash() request is pending
  base::OneShotTimer kill_process_timer_;

  // The default timeout for |kill_process_timer_|.
  base::TimeDelta kill_process_timeout_;

  // State used to backoff worker launch attempts on failure.
  net::BackoffEntry launch_backoff_;

  // Timer used to schedule the next attempt to launch the process.
  base::OneShotTimer launch_timer_;

  // Monitors |worker_process_| to detect when the launched process
  // terminates.
  base::win::ObjectWatcher process_watcher_;

  // Timer used to detect whether a launch attempt was successful or not, and to
  // cancel the launch attempt if it is taking too long.
  base::OneShotTimer launch_result_timer_;

  // The handle of the worker process, if launched.
  base::win::ScopedHandle worker_process_;

  SEQUENCE_CHECKER(sequence_checker_);
};

}  // namespace remoting

#endif  // REMOTING_HOST_WIN_WORKER_PROCESS_LAUNCHER_H_