// Copyright (c) 2008 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.

#include "base/message_pump_glib.h"

#include <fcntl.h>
#include <math.h>

#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/platform_thread.h"

namespace base {

static const char kWorkScheduled = '\0';
static const char kDelayedWorkScheduled = '\1';

// I wish these could be const, but g_source_new wants a non-const GSourceFunc
// pointer.

// static
GSourceFuncs MessagePumpForUI::WorkSourceFuncs = {
  WorkSourcePrepare,
  WorkSourceCheck,
  WorkSourceDispatch,
  NULL
};

// static
GSourceFuncs MessagePumpForUI::IdleSourceFuncs = {
  IdleSourcePrepare,
  IdleSourceCheck,
  IdleSourceDispatch,
  NULL
};

static int GetTimeIntervalMilliseconds(Time from) {
  if (from.is_null())
    return -1;

  // Be careful here.  TimeDelta has a precision of microseconds, but we want a
  // value in milliseconds.  If there are 5.5ms left, should the delay be 5 or
  // 6?  It should be 6 to avoid executing delayed work too early.
  double timeout = ceil((from - Time::Now()).InMillisecondsF());

  // If this value is negative, then we need to run delayed work soon.
  int delay = static_cast<int>(timeout);
  if (delay < 0)
    delay = 0;

  return delay;
}

MessagePumpForUI::MessagePumpForUI()
    : state_(NULL),
      context_(g_main_context_default()) {
  // Create a pipe with a non-blocking read end for use by ScheduleWork to
  // break us out of a poll.  Create the work source and attach the file
  // descriptor to it.
  int pipe_fd[2];
  CHECK(0 == pipe(pipe_fd)) << "Could not create pipe!";
  write_fd_work_scheduled_ = pipe_fd[1];
  read_fd_work_scheduled_ = pipe_fd[0];
  int flags = fcntl(read_fd_work_scheduled_, F_GETFL, 0);
  if (-1 == flags)
    flags = 0;
  CHECK(0 == fcntl(read_fd_work_scheduled_, F_SETFL, flags | O_NONBLOCK)) <<
      "Could not set file descriptor to non-blocking!";
  GPollFD poll_fd;
  poll_fd.fd = read_fd_work_scheduled_;
  poll_fd.events = G_IO_IN | G_IO_HUP | G_IO_ERR;
  work_source_ = AddSource(&WorkSourceFuncs, G_PRIORITY_DEFAULT, &poll_fd);
}

MessagePumpForUI::~MessagePumpForUI() {
  close(read_fd_work_scheduled_);
  close(write_fd_work_scheduled_);
  g_source_destroy(work_source_);
  g_source_unref(work_source_);
}

struct ThreadIdTraits {
  static void New(void* instance) {
    int* thread_id = static_cast<int*>(instance);
    *thread_id = PlatformThread::CurrentId();
  }
  static void Delete(void* instance) {
  }
};

void MessagePumpForUI::Run(Delegate* delegate) {
  // Make sure we only run this on one thread.  GTK only has one message pump
  // so we can only have one UI loop per process.
  static LazyInstance<int, ThreadIdTraits> thread_id(base::LINKER_INITIALIZED);
  DCHECK(thread_id.Get() == PlatformThread::CurrentId()) <<
      "Running MessagePumpForUI on two different threads; "
      "this is unsupported by GLib!";

  RunState state;
  state.delegate = delegate;
  state.keep_running = true;
  // We emulate the behavior of MessagePumpDefault and try to do work at once.
  state.should_do_work = true;
  state.should_do_idle_work = false;
  state.idle_source = NULL;

  RunState* previous_state = state_;
  state_ = &state;

  while (state.keep_running)
    g_main_context_iteration(context_, true);

  if (state.idle_source) {
    // This removes the source from the context and releases GLib's hold on it.
    g_source_destroy(state.idle_source);
    // This releases our hold and destroys the source.
    g_source_unref(state.idle_source);
  }

  state_ = previous_state;
}

void MessagePumpForUI::Quit() {
  DCHECK(state_) << "Quit called outside Run!";
  state_->keep_running = false;
}

void MessagePumpForUI::ScheduleWork() {
  // This can be called on any thread, so we don't want to touch any state
  // variables as we would then need locks all over.  This ensures that if
  // we are sleeping in a poll that we will wake up, and we check the pipe
  // so we know when work was scheduled.
  CHECK(1 == write(write_fd_work_scheduled_, &kWorkScheduled, 1)) <<
      "Could not write to pipe!";
}

void MessagePumpForUI::ScheduleDelayedWork(const Time& delayed_work_time) {
  delayed_work_time_ = delayed_work_time;
  // This is an optimization.  Delayed work may not be imminent, we may just
  // need to update our timeout to poll.  Hence we don't want to go overkill
  // with kWorkScheduled.
  CHECK(1 == write(write_fd_work_scheduled_, &kDelayedWorkScheduled, 1)) <<
      "Could not write to pipe!";
}

// A brief refresher on GLib:
//     GLib sources have four callbacks: Prepare, Check, Dispatch and Finalize.
// On each iteration of the GLib pump, it calls each source's Prepare function.
// This function should return TRUE if it wants GLib to call its Dispatch, and
// FALSE otherwise.  It can also set a timeout in this case for the next time
// Prepare should be called again (it may be called sooner).
//     After the Prepare calls, GLib does a poll to check for events from the
// system.  File descriptors can be attached to the sources.  The poll may block
// if none of the Prepare calls returned TRUE.  It will block indefinitely, or
// by the minimum time returned by a source in Prepare.
//     After the poll, GLib calls Check for each source that returned FALSE
// from Prepare.  The return value of Check has the same meaning as for Prepare,
// making Check a second chance to tell GLib we are ready for Dispatch.
//     Finally, GLib calls Dispatch for each source that is ready.  If Dispatch
// returns FALSE, GLib will destroy the source.  Dispatch calls may be recursive
// (i.e., you can call Run from them), but Prepare and Check cannot.
//     Finalize is called when the source is destroyed.

// static
gboolean MessagePumpForUI::WorkSourcePrepare(GSource* source,
                                             gint* timeout_ms) {
  MessagePumpForUI* self = static_cast<WorkSource*>(source)->self;
  RunState* state = self->state_;

  if (state->should_do_work) {
    state->should_do_idle_work = false;
    *timeout_ms = 0;
    return TRUE;
  }

  *timeout_ms = GetTimeIntervalMilliseconds(self->delayed_work_time_);

  state->should_do_idle_work = true;
  // We want to do idle work right before poll goes to sleep.  Obviously
  // we are not currently asleep, but we may be about to since we have
  // no work to do.  If we don't have an idle source ready to go it's
  // probably because it fired already (or we just started and it hasn't
  // been added yet) and we should add one for when we are ready.  Note
  // that this new source will get Prepare called on this current pump
  // iteration since it gets added at the end of the source list.
  if (!state->idle_source) {
    state->idle_source =
        self->AddSource(&IdleSourceFuncs, G_PRIORITY_DEFAULT_IDLE, NULL);
  }

  return FALSE;
}

// static
gboolean MessagePumpForUI::WorkSourceCheck(GSource* source) {
  MessagePumpForUI* self = static_cast<WorkSource*>(source)->self;
  RunState* state = self->state_;

  // Make sure we don't attempt idle work until we are really sure we don't
  // have other work to do.  We'll know this in the call to Prepare.
  state->should_do_idle_work = false;

  // Check if ScheduleWork or ScheduleDelayedWork was called.  This is a
  // non-blocking read.
  char byte;
  while (0 < read(self->read_fd_work_scheduled_, &byte, 1)) {
    // Don't assume we actually have work yet unless the stronger ScheduleWork
    // was called.
    if (byte == kWorkScheduled)
      state->should_do_work = true;
  }

  if (state->should_do_work)
    return TRUE;

  if (!self->delayed_work_time_.is_null())
    return self->delayed_work_time_ <= Time::Now();

  return FALSE;
}

// static
gboolean MessagePumpForUI::WorkSourceDispatch(GSource* source,
                                              GSourceFunc unused_func,
                                              gpointer unused_data) {
  MessagePumpForUI* self = static_cast<WorkSource*>(source)->self;
  RunState* state = self->state_;
  DCHECK(!state->should_do_idle_work) <<
      "Idle work should not be flagged while regular work exists.";

  // Note that in this function we never return FALSE.  This source is owned
  // by GLib and shared by multiple calls to Run.  It will only finally get
  // destroyed when the loop is destroyed.

  state->should_do_work = state->delegate->DoWork();
  if (!state->keep_running)
    return TRUE;

  state->should_do_work |=
      state->delegate->DoDelayedWork(&self->delayed_work_time_);

  return TRUE;
}

// static
gboolean MessagePumpForUI::IdleSourcePrepare(GSource* source,
                                             gint* timeout_ms) {
  RunState* state = static_cast<WorkSource*>(source)->self->state_;
  *timeout_ms = 0;
  return state->should_do_idle_work;
}

// static
gboolean MessagePumpForUI::IdleSourceCheck(GSource* source) {
  RunState* state = static_cast<WorkSource*>(source)->self->state_;
  return state->should_do_idle_work;
}

// static
gboolean MessagePumpForUI::IdleSourceDispatch(GSource* source,
                                              GSourceFunc unused_func,
                                              gpointer unused_data) {
  RunState* state = static_cast<WorkSource*>(source)->self->state_;
  // We should not do idle work unless we didn't have other work to do.
  DCHECK(!state->should_do_work) << "Doing idle work in non-idle time!";
  state->should_do_idle_work = false;
  state->should_do_work = state->delegate->DoIdleWork();

  // This is an optimization.  We could always remove ourselves right now,
  // but we will just get re-added when WorkSourceCheck eventually returns
  // FALSE.
  if (!state->should_do_work) {
    // This is so that when we return FALSE, GLib will not only remove us
    // from the context, but since it holds the last reference, it will
    // destroy us as well.
    g_source_unref(source);
    state->idle_source = NULL;
  }

  return state->should_do_work;
}

GSource* MessagePumpForUI::AddSource(GSourceFuncs* funcs, gint priority,
                                     GPollFD *optional_poll_fd) {
  GSource* source = g_source_new(funcs, sizeof(WorkSource));

  // Setting the priority is actually a bit expensive since it causes GLib
  // to resort an internal list.
  if (priority != G_PRIORITY_DEFAULT)
    g_source_set_priority(source, priority);

  // This is needed to allow Run calls inside Dispatch.
  g_source_set_can_recurse(source, TRUE);
  static_cast<WorkSource*>(source)->self = this;

  if (optional_poll_fd)
    g_source_add_poll(source, optional_poll_fd);

  g_source_attach(source, context_);
  return source;
}

}  // namespace base