/* Copyright(C) 2019, 2020, MariaDB
 *
 * This program is free software; you can redistribute itand /or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 2 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111 - 1301 USA*/

#if defined (HAVE_POOL_OF_THREADS)
#include <my_global.h>
#include <sql_plist.h>
#include <my_pthread.h>
#include <mysqld.h>
#include <threadpool.h>
#include <violite.h>

#ifdef _WIN32
#include <windows.h>
/* AIX may define this, too ?*/
#define HAVE_IOCP
#endif


#ifdef _WIN32
typedef HANDLE TP_file_handle;
#else
typedef int TP_file_handle;
#define  INVALID_HANDLE_VALUE -1
#endif

#ifdef __linux__
#include <sys/epoll.h>
typedef struct epoll_event native_event;
#elif defined(HAVE_KQUEUE)
#include <sys/event.h>
typedef struct kevent native_event;
#elif defined (__sun)
#include <port.h>
typedef port_event_t native_event;
#elif defined (HAVE_IOCP)
typedef OVERLAPPED_ENTRY native_event;
#else
#error threadpool is not available on this platform
#endif

struct thread_group_t;

/* Per-thread structure for workers */
struct worker_thread_t
{
  ulonglong  event_count; /* number of request handled by this thread */
  thread_group_t* thread_group;
  worker_thread_t* next_in_list;
  worker_thread_t** prev_in_list;
  mysql_cond_t  cond;
  bool          woken;
};

typedef I_P_List<worker_thread_t, I_P_List_adapter<worker_thread_t,
  & worker_thread_t::next_in_list,
  & worker_thread_t::prev_in_list>,
  I_P_List_counter
>
worker_list_t;

struct TP_connection_generic :public TP_connection
{
  TP_connection_generic(CONNECT* c);
  ~TP_connection_generic();

  virtual int init() { return 0; };
  virtual void set_io_timeout(int sec);
  virtual int  start_io();
  virtual void wait_begin(int type);
  virtual void wait_end();

  thread_group_t* thread_group;
  TP_connection_generic* next_in_queue;
  TP_connection_generic** prev_in_queue;
  ulonglong abs_wait_timeout;
  ulonglong enqueue_time;
  TP_file_handle fd;
  bool bound_to_poll_descriptor;
  int waiting;
  bool fix_group;
#ifdef HAVE_IOCP
  OVERLAPPED overlapped;
#endif
#ifdef _WIN32
  enum_vio_type vio_type;
#endif
};


typedef I_P_List<TP_connection_generic,
  I_P_List_adapter<TP_connection_generic,
  & TP_connection_generic::next_in_queue,
  & TP_connection_generic::prev_in_queue>,
  I_P_List_counter,
  I_P_List_fast_push_back<TP_connection_generic> >
  connection_queue_t;

const int NQUEUES = 2; /* We have high and low priority queues*/

enum class operation_origin
{
  WORKER,
  LISTENER
};

struct thread_group_counters_t
{
  ulonglong thread_creations;
  ulonglong thread_creations_due_to_stall;
  ulonglong wakes;
  ulonglong wakes_due_to_stall;
  ulonglong throttles;
  ulonglong stalls;
  ulonglong dequeues[2];
  ulonglong polls[2];
};

struct thread_group_t
{
  mysql_mutex_t mutex;
  connection_queue_t queues[NQUEUES];
  worker_list_t waiting_threads;
  worker_thread_t* listener;
  pthread_attr_t* pthread_attr;
  TP_file_handle  pollfd;
  int  thread_count;
  int  active_thread_count;
  int  connection_count;
  /* Stats for the deadlock detection timer routine.*/
  int io_event_count;
  int queue_event_count;
  ulonglong last_thread_creation_time;
  int  shutdown_pipe[2];
  bool shutdown;
  bool stalled;
  thread_group_counters_t counters;
  char pad[CPU_LEVEL1_DCACHE_LINESIZE];
};

#define TP_INCREMENT_GROUP_COUNTER(group,var) do {group->counters.var++;}while(0)

extern thread_group_t* all_groups;
#endif

