pool.c

Go to the documentation of this file.
00001 /*
00002   mysqlfs - MySQL Filesystem
00003   Copyright (C) 2006 Michal Ludvig <michal@logix.cz>
00004   $Id: pool.c 55 2009-07-12 22:23:33Z chickenandporn $
00005 
00006   This program can be distributed under the terms of the GNU GPL.
00007   See the file COPYING.
00008 */
00009 
00010 #ifdef HAVE_CONFIG_H
00011 #include "config.h"
00012 #endif
00013 
00014 #include <stdio.h>
00015 #include <stdlib.h>
00016 #include <string.h>
00017 #include <errno.h>
00018 #include <pthread.h>
00019 
00020 #include <fuse/fuse.h>
00021 #ifdef HAVE_MYSQL_MYSQL_H
00022 #include <mysql/mysql.h>
00023 #endif
00024 #ifdef HAVE_MYSQL_H
00025 #include <mysql.h>
00026 #endif
00027 
00028 #include "query.h"
00029 #include "pool.h"
00030 #include "log.h"
00031 
00032 struct mysqlfs_opt *opt;
00033 
00035 struct pool_lifo {
00036     struct pool_lifo    *next;          
00037     void                *conn;          
00038 };
00039 
00040 /* We have only one pool -> use global variables. */
00041 struct pool_lifo *lifo_pool = NULL;
00042 struct pool_lifo *lifo_unused = NULL;
00043 static pthread_mutex_t lifo_mutex = PTHREAD_MUTEX_INITIALIZER;
00044 unsigned int lifo_unused_cnt = 0;
00045 unsigned int lifo_pool_cnt = 0;
00046 
00047 /*********************************
00048  * Pool MySQL-specific functions *
00049  *********************************/
00050 
00051 static MYSQL *pool_open_mysql_connection()
00052 {
00053     MYSQL *mysql;
00054     my_bool reconnect = 1;
00055 
00056     mysql = mysql_init(NULL);
00057     if (!mysql) {
00058         log_printf(LOG_ERROR, "%s(): %s\n", __func__, strerror(ENOMEM));
00059         return NULL;
00060     }
00061 
00062     if (opt->mycnf_group)
00063         mysql_options(mysql, MYSQL_READ_DEFAULT_GROUP, opt->mycnf_group);
00064 
00065     if (! mysql_real_connect(mysql, opt->host, opt->user,
00066                              opt->passwd, opt->db,
00067                              opt->port, opt->socket, 0)) {
00068         log_printf(LOG_ERROR, "ERROR: mysql_real_connect(): %s\n",
00069                    mysql_error(mysql));
00070         mysql_close(mysql);
00071         return NULL;
00072     }
00073 
00074     /* Reconnect must be set *after* real_connect()! */
00075     mysql_options(mysql, MYSQL_OPT_RECONNECT, (char*)&reconnect);
00076 
00077     return mysql;
00078 }
00079 
00080 static void pool_close_mysql_connection(MYSQL *mysql)
00081 {
00082     if (mysql)
00083         mysql_close(mysql);
00084 }
00085 
00086 static int pool_check_mysql_setup(MYSQL *mysql)
00087 {
00088     int ret = 0;
00089 
00090     /* Check the server version.  */
00091     unsigned long mysql_version;
00092     mysql_version = mysql_get_server_version(mysql);
00093     if (mysql_version < MYSQL_MIN_VERSION) {
00094         log_printf(LOG_ERROR, "Your server version is %s. "
00095                    "Version %lu.%lu.%lu or higher is required.\n",
00096                    mysql_get_server_info(mysql), 
00097                    MYSQL_MIN_VERSION/10000L,
00098                    (MYSQL_MIN_VERSION%10000L)/100,
00099                    MYSQL_MIN_VERSION%100L);
00100         ret = -ENOENT;
00101         goto out;
00102     }
00103 
00104     /* Create root directory if it doesn't exist. */
00105     ret = query_inode_full(mysql, "/", NULL, 0, NULL, NULL, NULL);
00106     if (ret == -ENOENT)
00107         ret = query_mkdir(mysql, "/", 0755, 0);
00108     if (ret < 0)
00109         goto out;
00110 
00111     /* Cleanup. */
00112     if (opt->fsck == 1) {
00113         ret = query_fsck(mysql);
00114     }
00115 
00116 out:
00117     return ret;
00118 }
00119 
00120 /******************************************
00121  * Pool DB-independent (almost) functions *
00122  ******************************************/
00123 
00124 static inline int lifo_put(void *conn)
00125 {
00126     struct pool_lifo *ent;
00127 
00128     log_printf(LOG_D_POOL, "%s() <= %p\n", __func__, conn);
00129     pthread_mutex_lock(&lifo_mutex);
00130     if (lifo_unused) {
00131         ent = lifo_unused;
00132         lifo_unused = ent->next;
00133         lifo_unused_cnt--;
00134     } else {
00135         ent = calloc(1, sizeof(struct pool_lifo));
00136         if (!ent) {
00137             pthread_mutex_unlock(&lifo_mutex);
00138             return -ENOMEM;
00139         }
00140     }
00141     ent->conn = conn;
00142     ent->next = lifo_pool;
00143     lifo_pool = ent;
00144     lifo_pool_cnt++;
00145     pthread_mutex_unlock(&lifo_mutex);
00146 
00147     return 0;
00148 }
00149 
00150 static inline void *lifo_get()
00151 {
00152     struct pool_lifo *ent;
00153     void *conn;
00154 
00155     pthread_mutex_lock(&lifo_mutex);
00156     if (lifo_pool) {
00157         ent = lifo_pool;
00158         conn = ent->conn;
00159         lifo_pool = ent->next;
00160         lifo_pool_cnt--;
00161         ent->next = lifo_unused;
00162         ent->conn = NULL;
00163         lifo_unused = ent;
00164         lifo_unused_cnt++;
00165     } else
00166         conn = NULL;
00167     pthread_mutex_unlock(&lifo_mutex);
00168 
00169     return conn;
00170 }
00171 
00172 int pool_init(struct mysqlfs_opt *opt_arg)
00173 {
00174     int i, ret;
00175 
00176     log_printf(LOG_D_POOL, "%s()\n", __func__);
00177     opt = opt_arg;
00178 
00179     for (i = 0; i < opt->init_conns; i++) {
00180         void *conn = pool_open_mysql_connection();
00181         lifo_put(conn);
00182     }
00183 
00184     /* The following check should go to MySQL-specific section
00185      * so we can later add a whole new DB support without too
00186      * much trouble. But for now ... leave it here ... */
00187     MYSQL *mysql = pool_get();
00188     if (!mysql) {
00189         log_printf(LOG_ERROR, "Failed to connect MySQL server.\n");
00190         return -1;
00191     }
00192 
00193     ret = pool_check_mysql_setup(mysql);
00194 
00195     pool_put(mysql);
00196 
00197     return ret;
00198 }
00199 
00200 void pool_cleanup()
00201 {
00202     void *conn;
00203     log_printf(LOG_D_POOL, "%s()...\n", __func__);
00204     while ((conn = lifo_get())) {
00205         log_printf(LOG_D_POOL, "%s(): closing conn=%p\n", __func__, conn);
00206         pool_close_mysql_connection(conn);
00207     }
00208 }
00209 
00210 void *pool_get()
00211 {
00212     void *conn = lifo_get();
00213     if (!conn) {
00214         conn = pool_open_mysql_connection();
00215         log_printf(LOG_D_POOL, "%s(): Allocated new connection = %p\n", __func__, conn);
00216     } else
00217         log_printf(LOG_D_POOL, "%s(): Reused connection = %p\n", __func__, conn);
00218 
00219     return conn;
00220 }
00221 
00222 void pool_put(void *conn)
00223 {
00224     log_printf(LOG_D_POOL, "%s(%p)\n", __func__, conn);
00225 
00226     /* This doesn't have to be mutex-protected.
00227      * If we close more conns or don't close some nothing
00228      * too bad happens. */
00229     if (lifo_pool_cnt >= opt->max_idling_conns)
00230         pool_close_mysql_connection(conn);
00231     else
00232         if (lifo_put(conn) < 0)
00233             pool_close_mysql_connection(conn);
00234 }

Generated on Sun Jul 12 20:25:26 2009 for mysqlfs by  doxygen 1.4.7