// Copyright (c) 2011 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 CHROME_BROWSER_METRICS_THREAD_WATCHER_H_
#define CHROME_BROWSER_METRICS_THREAD_WATCHER_H_
#pragma once

#if 0

#include <map>
#include <string>
#include <vector>

#include "base/basictypes.h"
#include "base/gtest_prod_util.h"
#include "base/metrics/histogram.h"
#include "base/ref_counted.h"
#include "base/scoped_ptr.h"
#include "base/synchronization/lock.h"
#include "base/task.h"
#include "base/threading/thread.h"
#include "base/time.h"
#include "chrome/browser/browser_process_sub_thread.h"
#include "chrome/browser/browser_thread.h"
#include "chrome/common/notification_observer.h"
#include "chrome/common/notification_registrar.h"

class CustomThreadWatcher;
class ThreadWatcherList;

//------------------------------------------------------------------------------
// This class performs health check on threads that would like to be watched. It
// sends ping message to the watched thread and the watched thread responds back
// with a pong message. It uploads response time (difference between ping and
// pong times) as a histogram.
// TODO(raman:) ThreadWatcher can detect hung threads. If a hung thread is
// detected, we should probably just crash, and allow the crash system to gather
// then stack trace.
//
// Example Usage:
//
//   The following is an example for watching responsiveness of IO thread.
//   sleep_time specifies how often ping messages have to be sent to IO thread.
//   unresponsive_time is the wait time after ping message is sent, to check if
//   we have received pong message or not.
//
//   base::TimeDelta sleep_time = base::TimeDelta::FromSeconds(5);
//   base::TimeDelta unresponsive_time = base::TimeDelta::FromSeconds(10);
//   ThreadWatcher::StartWatching(BrowserThread::IO, "IO", sleep_time,
//                                unresponsive_time);

class ThreadWatcher {
 public:
  // This method starts performing health check on the given thread_id. It will
  // create ThreadWatcher object for the given thread_id, thread_name,
  // sleep_time and unresponsive_time. sleep_time_ is the wait time between ping
  // messages. unresponsive_time_ is the wait time after ping message is sent,
  // to check if we have received pong message or not. It will register that
  // ThreadWatcher object and activate the thread watching of the given
  // thread_id.
  static void StartWatching(const BrowserThread::ID thread_id,
                            const std::string& thread_name,
                            const base::TimeDelta& sleep_time,
                            const base::TimeDelta& unresponsive_time);

  // Return the thread_id of the thread being watched.
  BrowserThread::ID thread_id() const { return thread_id_; }

  // Return the name of the thread being watched.
  std::string thread_name() const { return thread_name_; }

  // Return the sleep time between ping messages to be sent to the thread.
  base::TimeDelta sleep_time() const { return sleep_time_; }

  // Return the the wait time to check the responsiveness of the thread.
  base::TimeDelta unresponsive_time() const { return unresponsive_time_; }

  // Returns true if we are montioring the thread.
  bool active() const { return active_; }

 protected:
  // Construct a ThreadWatcher for the given thread_id. sleep_time_ is the
  // wait time between ping messages. unresponsive_time_ is the wait time after
  // ping message is sent, to check if we have received pong message or not.
  ThreadWatcher(const BrowserThread::ID thread_id,
                const std::string& thread_name,
                const base::TimeDelta& sleep_time,
                const base::TimeDelta& unresponsive_time);
  virtual ~ThreadWatcher();

  // This method activates the thread watching which starts ping/pong messaging.
  virtual void ActivateThreadWatching();

  // This method de-activates the thread watching and revokes all tasks.
  virtual void DeActivateThreadWatching();

  // This will ensure that the watching is actively taking place, and awaken
  // (i.e., post a PostPingMessage) if the watcher has stopped pinging due to
  // lack of user activity. It will also reset ping_count_ to kPingCount.
  virtual void WakeUp();

  // This method records when ping message was sent and it will Post a task
  // (OnPingMessage) to the watched thread that does nothing but respond with
  // OnPongMessage. It also posts a task (OnCheckResponsiveness) to check
  // responsiveness of monitored thread that would be called after waiting
  // unresponsive_time_.
  // This method is accessible on WATCHDOG thread.
  virtual void PostPingMessage();

  // This method handles a Pong Message from watched thread. It will track the
  // response time (pong time minus ping time) via histograms. It posts a
  // PostPingMessage task that would be called after waiting sleep_time_.  It
  // increments ping_sequence_number_ by 1.
  // This method is accessible on WATCHDOG thread.
  virtual void OnPongMessage(uint64 ping_sequence_number);

  // This method will determine if the watched thread is responsive or not. If
  // the latest ping_sequence_number_ is not same as the ping_sequence_number
  // that is passed in, then we can assume that watched thread has responded
  // with a pong message.
  // This method is accessible on WATCHDOG thread.
  virtual bool OnCheckResponsiveness(uint64 ping_sequence_number);

 private:
  friend class ThreadWatcherList;

  // Allow tests to access our innards for testing purposes.
  FRIEND_TEST(ThreadWatcherTest, Registration);
  FRIEND_TEST(ThreadWatcherTest, ThreadResponding);
  FRIEND_TEST(ThreadWatcherTest, ThreadNotResponding);
  FRIEND_TEST(ThreadWatcherTest, MultipleThreadsResponding);
  FRIEND_TEST(ThreadWatcherTest, MultipleThreadsNotResponding);

  // Post constructor initialization.
  void Initialize();

  // Watched thread does nothing except post callback_task to the WATCHDOG
  // Thread. This method is called on watched thread.
  static void OnPingMessage(const BrowserThread::ID thread_id,
                            Task* callback_task);

  // This is the number of ping messages to be sent when the user is idle.
  // ping_count_ will be initialized to kPingCount whenever user becomes active.
  static const int kPingCount;

  // The thread_id of the thread being watched. Only one instance can exist for
  // the given thread_id of the thread being watched.
  const BrowserThread::ID thread_id_;

  // The name of the thread being watched.
  const std::string thread_name_;

  // It is the sleep time between between the receipt of a pong message back,
  // and the sending of another ping message.
  const base::TimeDelta sleep_time_;

  // It is the duration from sending a ping message, until we check status to be
  // sure a pong message has been returned.
  const base::TimeDelta unresponsive_time_;

  // This is the last time when ping message was sent.
  base::TimeTicks ping_time_;

  // This is the sequence number of the next ping for which there is no pong. If
  // the instance is sleeping, then it will be the sequence number for the next
  // ping.
  uint64 ping_sequence_number_;

  // This is set to true if thread watcher is watching.
  bool active_;

  // The counter tracks least number of ping messages that will be sent to
  // watched thread before the ping-pong mechanism will go into an extended
  // sleep. If this value is zero, then the mechanism is in an extended sleep,
  // and awaiting some observed user action before continuing.
  int ping_count_;

  // Histogram that keeps track of response times for the watched thread.
  scoped_refptr<base::Histogram> histogram_;

  ScopedRunnableMethodFactory<ThreadWatcher> method_factory_;

  DISALLOW_COPY_AND_ASSIGN(ThreadWatcher);
};

//------------------------------------------------------------------------------
// Class with a list of all active thread watchers.  A thread watcher is active
// if it has been registered, which includes determing the histogram name.
// Only one instance of this class exists.
class ThreadWatcherList : public NotificationObserver {
 public:
  // A map from BrowserThread to the actual instances.
  typedef std::map<BrowserThread::ID, ThreadWatcher*> RegistrationList;

  // This singleton holds the global list of registered ThreadWatchers.
  ThreadWatcherList();
  // Destructor deletes all registered ThreadWatcher instances.
  ~ThreadWatcherList();

  // Register() stores a pointer to the given ThreadWatcher in a global map.
  static void Register(ThreadWatcher* watcher);

  // This method posts a task on WATCHDOG thread to RevokeAll tasks and to
  // deactive thread watching of other threads and tell NotificationService to
  // stop calling Observe.
  static void StopWatchingAll();

  // RemoveAll NotificationTypes that are being observed.
  static void RemoveNotifications();

 private:
  // Allow tests to access our innards for testing purposes.
  FRIEND_TEST(ThreadWatcherTest, Registration);

  // This will ensure that the watching is actively taking place. It will wakeup
  // all thread watchers every 2 seconds. This is the implementation of
  // NotificationObserver. When a matching notification is posted to the
  // notification service, this method is called.
  virtual void Observe(NotificationType type,
                       const NotificationSource& source,
                       const NotificationDetails& details);

  // This will ensure that the watching is actively taking place, and awaken
  // all thread watchers that are registered.
  virtual void WakeUpAll();

  // The Find() method can be used to test to see if a given ThreadWatcher was
  // already registered, or to retrieve a pointer to it from the global map.
  static ThreadWatcher* Find(const BrowserThread::ID thread_id);

  // Helper function should be called only while holding lock_.
  ThreadWatcher* PreLockedFind(const BrowserThread::ID thread_id);

  static ThreadWatcherList* global_;  // The singleton of this class.

  // Lock for access to registered_.
  base::Lock lock_;
  RegistrationList registered_;

  // The registrar that holds NotificationTypes to be observed.
  NotificationRegistrar registrar_;

  // This is the last time when woke all thread watchers up.
  base::TimeTicks last_wakeup_time_;

  DISALLOW_COPY_AND_ASSIGN(ThreadWatcherList);
};

//------------------------------------------------------------------------------
// Class for WATCHDOG thread and in its Init method, we start watching UI, IO,
// DB, FILE, CACHED threads.
class WatchDogThread : public BrowserProcessSubThread {
 public:
  WatchDogThread();
  virtual ~WatchDogThread();

 protected:
  virtual void Init();

  DISALLOW_COPY_AND_ASSIGN(WatchDogThread);
};


DISABLE_RUNNABLE_METHOD_REFCOUNT(ThreadWatcher);
DISABLE_RUNNABLE_METHOD_REFCOUNT(ThreadWatcherList);

#endif  // 0

#endif  // CHROME_BROWSER_METRICS_THREAD_WATCHER_H_