/***********************************************************************

Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved.

This program is free software; you can redistribute it and/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, Suite 500, Boston, MA 02110-1335 USA

***********************************************************************/

/**************************************************//**
@file memcached_mysql.cc
InnoDB Memcached Plugin 

Created 04/12/2011 Jimmy Yang
*******************************************************/

#include "memcached_mysql.h"
#include <stdlib.h>
#include <ctype.h>
#include <mysql_version.h>
#include "sql_plugin.h"

/** Configuration info passed to memcached, including
the name of our Memcached InnoDB engine and memcached configure
string to be loaded by memcached. */
struct mysql_memcached_context
{
	pthread_t		memcached_thread;
	memcached_context_t	memcached_conf;
};

/** Variables for configure options */
static char*	mci_engine_library = NULL;
static char*	mci_eng_lib_path = NULL; 
static char*	mci_memcached_option = NULL;
static unsigned int mci_r_batch_size = 1048576;
static unsigned int mci_w_batch_size = 32;
static my_bool	mci_enable_binlog = false;

static MYSQL_SYSVAR_STR(engine_lib_name, mci_engine_library,
			PLUGIN_VAR_READONLY | PLUGIN_VAR_MEMALLOC,
			"memcached engine library name", NULL, NULL,
			"innodb_engine.so");

static MYSQL_SYSVAR_STR(engine_lib_path, mci_eng_lib_path,
			PLUGIN_VAR_READONLY | PLUGIN_VAR_MEMALLOC,
			"memcached engine library path", NULL, NULL, NULL);

static MYSQL_SYSVAR_STR(option, mci_memcached_option,
			PLUGIN_VAR_READONLY | PLUGIN_VAR_MEMALLOC,
			"memcached option string", NULL, NULL, NULL);

static MYSQL_SYSVAR_UINT(r_batch_size, mci_r_batch_size,
			 PLUGIN_VAR_READONLY,
			 "read batch commit size", 0, 0, 1,
			 1, 1073741824, 0);

static MYSQL_SYSVAR_UINT(w_batch_size, mci_w_batch_size,
			 PLUGIN_VAR_READONLY,
			 "write batch commit size", 0, 0, 1,
			 1, 1048576, 0);

static MYSQL_SYSVAR_BOOL(enable_binlog, mci_enable_binlog,
			 PLUGIN_VAR_READONLY,
			 "whether to enable binlog",
			 NULL, NULL, FALSE);

static struct st_mysql_sys_var *daemon_memcached_sys_var[] = {
	MYSQL_SYSVAR(engine_lib_name),
	MYSQL_SYSVAR(engine_lib_path),
	MYSQL_SYSVAR(option),
	MYSQL_SYSVAR(r_batch_size),
	MYSQL_SYSVAR(w_batch_size),
	MYSQL_SYSVAR(enable_binlog),
	0
};

static int daemon_memcached_plugin_deinit(void *p)
{
	struct st_plugin_int*		plugin = (struct st_plugin_int *)p;
	struct mysql_memcached_context*	con = NULL;
	int				loop_count = 0;

        /* If memcached plugin is still initializing, wait for a
        while.*/
	while (!init_complete() && loop_count < 15 ) {
                sleep(1);
                loop_count++;
	}

        if (!init_complete()) {
		fprintf(stderr," InnoDB_Memcached: Memcached plugin is still"
			" initializing. Can't shut down it.\n");
                return(0);
        }

	if (!shutdown_complete()) {
		shutdown_server();
	}

        loop_count = 0;
	while (!shutdown_complete() && loop_count < 25) {
		sleep(2);
		loop_count++;
	}

	if(!shutdown_complete()) {
		fprintf(stderr," InnoDB_Memcached: Waited for 50 seconds"
			" for memcached thread to exit. Now force terminating"
			" the thread\n");
	}

	con = (struct mysql_memcached_context*) (plugin->data);

	pthread_cancel(con->memcached_thread);

	if (con->memcached_conf.m_engine_library) {
		my_free(con->memcached_conf.m_engine_library);
	}

	my_free(con);

	return(0);
}

static int daemon_memcached_plugin_init(void *p)
{
	struct mysql_memcached_context*	con;
	pthread_attr_t			attr;
	struct st_plugin_int*		plugin = (struct st_plugin_int *)p;

	con = (mysql_memcached_context*) my_malloc(sizeof(*con), MYF(0));

	if (mci_engine_library) {
		char*	lib_path = (mci_eng_lib_path)
					? mci_eng_lib_path : opt_plugin_dir;
		int	lib_len = strlen(lib_path)
				  + strlen(mci_engine_library)
				  + strlen(FN_DIRSEP) + 1;

		con->memcached_conf.m_engine_library = (char*) my_malloc(
			lib_len, MYF(0));

		strxmov(con->memcached_conf.m_engine_library, lib_path,
			FN_DIRSEP, mci_engine_library, NullS);
	} else {
		con->memcached_conf.m_engine_library = NULL;
	}

	con->memcached_conf.m_mem_option = mci_memcached_option;
	con->memcached_conf.m_innodb_api_cb = plugin->data;
	con->memcached_conf.m_r_batch_size = mci_r_batch_size;
	con->memcached_conf.m_w_batch_size = mci_w_batch_size;
	con->memcached_conf.m_enable_binlog = mci_enable_binlog;

	pthread_attr_init(&attr);
	pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);

	/* now create the thread */
	if (pthread_create(&con->memcached_thread, &attr,
			   daemon_memcached_main,
			   (void *)&con->memcached_conf) != 0)
	{
		fprintf(stderr,"Could not create memcached daemon thread!\n");
		exit(0);
	}

	plugin->data= (void *)con;

	return(0);
}

struct st_mysql_daemon daemon_memcached_plugin =
	{MYSQL_DAEMON_INTERFACE_VERSION};

mysql_declare_plugin(daemon_memcached)
{
	MYSQL_DAEMON_PLUGIN,
	&daemon_memcached_plugin,
	"daemon_memcached",
	"Oracle Corporation",
	"Memcached Daemon",
	PLUGIN_LICENSE_GPL,
	daemon_memcached_plugin_init,	/* Plugin Init */
	daemon_memcached_plugin_deinit,	/* Plugin Deinit */
	0x0100 /* 1.0 */,
	NULL,				/* status variables */
	daemon_memcached_sys_var,	/* system variables */
	NULL,				/* config options */
	0				/* flags */
}
mysql_declare_plugin_end;
