//
// indirect_handler_queue.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2008 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//

#ifndef ASIO_DETAIL_INDIRECT_HANDLER_QUEUE_HPP
#define ASIO_DETAIL_INDIRECT_HANDLER_QUEUE_HPP

#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)

#include "asio/detail/push_options.hpp"

#include "asio/detail/handler_alloc_helpers.hpp"
#include "asio/detail/handler_invoke_helpers.hpp"
#include "asio/detail/noncopyable.hpp"

#if defined(_MSC_VER) && (_MSC_VER >= 1310)
extern "C" void _ReadWriteBarrier();
# pragma intrinsic(_ReadWriteBarrier)
#endif // defined(_MSC_VER) && (_MSC_VER >= 1310)

namespace asio {
namespace detail {

class indirect_handler_queue
  : private noncopyable
{
public:
  class handler;

  // Element for a node in the queue.
  class node
  {
  public:
    node()
      : version_(0),
        handler_(0),
        next_(0)
    {
    }

  private:
    friend class indirect_handler_queue;
    unsigned long version_;
    handler* handler_;
    node* next_;
  };

  // Base class for handlers in the queue.
  class handler
    : private noncopyable
  {
  public:
    void invoke()
    {
      invoke_func_(this);
    }

    void destroy()
    {
      destroy_func_(this);
    }

  protected:
    typedef void (*invoke_func_type)(handler*);
    typedef void (*destroy_func_type)(handler*);

    handler(invoke_func_type invoke_func,
        destroy_func_type destroy_func)
      : node_(new node),
        invoke_func_(invoke_func),
        destroy_func_(destroy_func)
    {
    }

    ~handler()
    {
      if (node_)
        delete node_;
    }

  private:
    friend class indirect_handler_queue;
    node* node_;
    invoke_func_type invoke_func_;
    destroy_func_type destroy_func_;
  };

  // Smart point to manager handler lifetimes.
  class scoped_ptr
    : private noncopyable
  {
  public:
    explicit scoped_ptr(handler* h)
      : handler_(h)
    {
    }

    ~scoped_ptr()
    {
      if (handler_)
        handler_->destroy();
    }

    handler* get() const
    {
      return handler_;
    }

    handler* release()
    {
      handler* tmp = handler_;
      handler_ = 0;
      return tmp;
    }

  private:
    handler* handler_;
  };

  // Constructor.
  indirect_handler_queue()
    : front_(new node),
      back_(front_),
      next_version_(1)
  {
  }

  // Destructor.
  ~indirect_handler_queue()
  {
    while (front_)
    {
      node* tmp = front_;
      front_ = front_->next_;
      delete tmp;
    }
  }

  // Wrap a handler to be pushed into the queue.
  template <typename Handler>
  static handler* wrap(Handler h)
  {
    // Allocate and construct an object to wrap the handler.
    typedef handler_wrapper<Handler> value_type;
    typedef handler_alloc_traits<Handler, value_type> alloc_traits;
    raw_handler_ptr<alloc_traits> raw_ptr(h);
    handler_ptr<alloc_traits> ptr(raw_ptr, h);
    return ptr.release();
  }

  // Determine whether the queue has something ready to pop.
  bool poppable()
  {
    return front_->next_ != 0;
  }

  // The version number at the front of the queue.
  unsigned long front_version()
  {
    return front_->version_;
  }

  // The version number at the back of the queue.
  unsigned long back_version()
  {
    return back_->version_;
  }

  // Pop a handler from the front of the queue.
  handler* pop()
  {
    node* n = front_;
    node* new_front = n->next_;
    if (new_front)
    {
      handler* h = new_front->handler_;
      h->node_ = n;
      new_front->handler_ = 0;
      front_ = new_front;
      return h;
    }
    return 0;
  }

  // Push a handler on to the back of the queue.
  void push(handler* h)
  {
    node* n = h->node_;
    h->node_ = 0;
    n->version_ = next_version_;
    next_version_ += 2;
    n->handler_ = h;
    n->next_ = 0;
    memory_barrier();
    back_->next_ = n;
    back_ = n;
  }

private:
  // Template wrapper for handlers.
  template <typename Handler>
  class handler_wrapper
    : public handler
  {
  public:
    handler_wrapper(Handler h)
      : handler(
          &handler_wrapper<Handler>::do_call,
          &handler_wrapper<Handler>::do_destroy),
        handler_(h)
    {
    }

    static void do_call(handler* base)
    {
      // Take ownership of the handler object.
      typedef handler_wrapper<Handler> this_type;
      this_type* h(static_cast<this_type*>(base));
      typedef handler_alloc_traits<Handler, this_type> alloc_traits;
      handler_ptr<alloc_traits> ptr(h->handler_, h);

      // Make a copy of the handler so that the memory can be deallocated before
      // the upcall is made.
      Handler handler(h->handler_);

      // Free the memory associated with the handler.
      ptr.reset();

      // Make the upcall.
      asio_handler_invoke_helpers::invoke(handler, &handler);
    }

    static void do_destroy(handler* base)
    {
      // Take ownership of the handler object.
      typedef handler_wrapper<Handler> this_type;
      this_type* h(static_cast<this_type*>(base));
      typedef handler_alloc_traits<Handler, this_type> alloc_traits;
      handler_ptr<alloc_traits> ptr(h->handler_, h);

      // A sub-object of the handler may be the true owner of the memory
      // associated with the handler. Consequently, a local copy of the handler
      // is required to ensure that any owning sub-object remains valid until
      // after we have deallocated the memory here.
      Handler handler(h->handler_);
      (void)handler;

      // Free the memory associated with the handler.
      ptr.reset();
    }

  private:
    Handler handler_;
  };

  // Helper function to create a memory barrier.
  static void memory_barrier()
  {
#if defined(_GLIBCXX_WRITE_MEM_BARRIER)
    _GLIBCXX_WRITE_MEM_BARRIER;
#elif defined(_MSC_VER) && (_MSC_VER >= 1310)
    _ReadWriteBarrier();
#else
# error memory barrier required
#endif
  }

  // The front of the queue.
  node* front_;

  // The back of the queue.
  node* back_;

  // The next version counter to be assigned to a node.
  unsigned long next_version_;
};

} // namespace detail
} // namespace asio

#include "asio/detail/pop_options.hpp"

#endif // ASIO_DETAIL_INDIRECT_HANDLER_QUEUE_HPP
