Drizzled Public API Documentation

cache.cc
00001 /* -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*-
00002  *  vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
00003  *
00004  *  Copyright (C) 2010 Brian Aker
00005  *
00006  *  This program is free software; you can redistribute it and/or modify
00007  *  it under the terms of the GNU General Public License as published by
00008  *  the Free Software Foundation; either version 2 of the License, or
00009  *  (at your option) any later version.
00010  *
00011  *  This program is distributed in the hope that it will be useful,
00012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  *  GNU General Public License for more details.
00015  *
00016  *  You should have received a copy of the GNU General Public License
00017  *  along with this program; if not, write to the Free Software
00018  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00019  */
00020 
00021 #include <config.h>
00022 
00023 #include <sys/types.h>
00024 #include <sys/stat.h>
00025 #include <fcntl.h>
00026 
00027 
00028 #include <drizzled/identifier.h>
00029 #include <drizzled/table.h>
00030 #include <drizzled/session.h>
00031 #include <drizzled/sql_base.h>
00032 #include <drizzled/table/concurrent.h>
00033 
00034 #include <drizzled/table/cache.h>
00035 #include <drizzled/table/unused.h>
00036 
00037 #include <drizzled/pthread_globals.h>
00038 
00039 namespace drizzled
00040 {
00041 
00042 class Session;
00043 
00044 namespace table
00045 {
00046 
00047 CacheMap &getCache(void)
00048 {
00049   return Cache::singleton().getCache();
00050 }
00051 
00052 /*
00053   Remove table from the open table cache
00054 
00055   SYNOPSIS
00056   free_cache_entry()
00057   entry   Table to remove
00058 
00059   NOTE
00060   We need to have a lock on table::Cache::singleton().mutex() when calling this
00061 */
00062 
00063 static void free_cache_entry(table::Concurrent *table)
00064 {
00065   table->intern_close_table();
00066   if (not table->in_use)
00067   {
00068     getUnused().unlink(table);
00069   }
00070 
00071   boost::checked_delete(table);
00072 }
00073 
00074 void remove_table(table::Concurrent *arg)
00075 {
00076   CacheRange ppp;
00077   ppp= getCache().equal_range(arg->getShare()->getCacheKey());
00078 
00079   for (CacheMap::const_iterator iter= ppp.first;
00080          iter != ppp.second; ++iter)
00081   {
00082     table::Concurrent *found_table= iter->second;
00083 
00084     if (found_table == arg)
00085     {
00086       free_cache_entry(arg);
00087       getCache().erase(iter);
00088       return;
00089     }
00090   }
00091 }
00092 
00093 /*
00094   Wait until all threads has closed the tables in the list
00095   We have also to wait if there is thread that has a lock on this table even
00096   if the table is closed
00097 */
00098 
00099 bool Cache::areTablesUsed(Table *table, bool wait_for_name_lock)
00100 {
00101   do
00102   {
00103     const identifier::Table::Key &key(table->getShare()->getCacheKey());
00104 
00105     table::CacheRange ppp= table::getCache().equal_range(key);
00106 
00107     for (table::CacheMap::const_iterator iter= ppp.first; iter != ppp.second; ++iter)
00108     {
00109       Table *search= iter->second;
00110       if (search->in_use == table->in_use)
00111         continue;                               // Name locked by this thread
00112       /*
00113         We can't use the table under any of the following conditions:
00114         - There is an name lock on it (Table is to be deleted or altered)
00115         - If we are in flush table and we didn't execute the flush
00116         - If the table engine is open and it's an old version
00117         (We must wait until all engines are shut down to use the table)
00118       */
00119       if ( (search->locked_by_name && wait_for_name_lock) ||
00120            (search->is_name_opened() && search->needs_reopen_or_name_lock()))
00121         return 1;
00122     }
00123   } while ((table=table->getNext()));
00124   return 0;
00125 }
00126 
00127 /*
00128   Invalidate any cache entries that are for some DB
00129 
00130   SYNOPSIS
00131   removeSchema()
00132   db    Database name. This will be in lower case if
00133   lower_case_table_name is set
00134 
00135 NOTE:
00136 We can't use hash_delete when looping hash_elements. We mark them first
00137 and afterwards delete those marked unused.
00138 */
00139 
00140 void Cache::removeSchema(const identifier::Schema &schema_identifier)
00141 {
00142   boost::mutex::scoped_lock scopedLock(_mutex);
00143 
00144   for (table::CacheMap::const_iterator iter= table::getCache().begin();
00145        iter != table::getCache().end();
00146        iter++)
00147   {
00148     table::Concurrent *table= iter->second;
00149 
00150     if (not schema_identifier.getPath().compare(table->getShare()->getSchemaName()))
00151     {
00152       table->getMutableShare()->resetVersion();     /* Free when thread is ready */
00153       if (not table->in_use)
00154         table::getUnused().relink(table);
00155     }
00156   }
00157 
00158   table::getUnused().cullByVersion();
00159 }
00160 
00161 /*
00162   Mark all entries with the table as deleted to force an reopen of the table
00163 
00164   The table will be closed (not stored in cache) by the current thread when
00165   close_thread_tables() is called.
00166 
00167   PREREQUISITES
00168   Lock on table::Cache::singleton().mutex()()
00169 
00170   RETURN
00171   0  This thread now have exclusive access to this table and no other thread
00172   can access the table until close_thread_tables() is called.
00173   1  Table is in use by another thread
00174 */
00175 
00176 bool Cache::removeTable(Session *session, identifier::Table &identifier, uint32_t flags)
00177 {
00178   const identifier::Table::Key &key(identifier.getKey());
00179   bool result= false;
00180   bool signalled= false;
00181 
00182   for (;;)
00183   {
00184     result= signalled= false;
00185 
00186     table::CacheRange ppp;
00187     ppp= table::getCache().equal_range(key);
00188 
00189     for (table::CacheMap::const_iterator iter= ppp.first;
00190          iter != ppp.second; ++iter)
00191     {
00192       table::Concurrent *table= iter->second;
00193       Session *in_use;
00194 
00195       table->getMutableShare()->resetVersion();   /* Free when thread is ready */
00196       if (not (in_use= table->in_use))
00197       {
00198         table::getUnused().relink(table);
00199       }
00200       else if (in_use != session)
00201       {
00202         /*
00203           Mark that table is going to be deleted from cache. This will
00204           force threads that are in lockTables() (but not yet
00205           in thr_multi_lock()) to abort it's locks, close all tables and retry
00206         */
00207         in_use->some_tables_deleted= true;
00208         if (table->is_name_opened())
00209         {
00210           result= true;
00211         }
00212         /*
00213           Now we must abort all tables locks used by this thread
00214           as the thread may be waiting to get a lock for another table.
00215           Note that we need to hold table::Cache::singleton().mutex() while going through the
00216           list. So that the other thread cannot change it. The other
00217           thread must also hold table::Cache::singleton().mutex() whenever changing the
00218           open_tables list. Aborting the MERGE lock after a child was
00219           closed and before the parent is closed would be fatal.
00220         */
00221         for (Table *session_table= in_use->open_tables;
00222              session_table ;
00223              session_table= session_table->getNext())
00224         {
00225           /* Do not handle locks of MERGE children. */
00226           if (session_table->db_stat) // If table is open
00227             signalled|= session->abortLockForThread(session_table);
00228         }
00229       }
00230       else
00231       {
00232         result= result || (flags & RTFC_OWNED_BY_Session_FLAG);
00233       }
00234     }
00235 
00236     table::getUnused().cullByVersion();
00237 
00238     /* Remove table from table definition cache if it's not in use */
00239     table::instance::release(identifier);
00240 
00241     if (result && (flags & RTFC_WAIT_OTHER_THREAD_FLAG))
00242     {
00243       /*
00244         Signal any thread waiting for tables to be freed to
00245         reopen their tables
00246       */
00247       locking::broadcast_refresh();
00248       if (not (flags & RTFC_CHECK_KILLED_FLAG) || not session->getKilled())
00249       {
00250         dropping_tables++;
00251         if (likely(signalled))
00252         {
00253           boost_unique_lock_t scoped(table::Cache::singleton().mutex(), boost::adopt_lock_t());
00254           COND_refresh.wait(scoped);
00255           scoped.release();
00256         }
00257         else
00258         {
00259           /*
00260             It can happen that another thread has opened the
00261             table but has not yet locked any table at all. Since
00262             it can be locked waiting for a table that our thread
00263             has done LOCK Table x WRITE on previously, we need to
00264             ensure that the thread actually hears our signal
00265             before we go to sleep. Thus we wait for a short time
00266             and then we retry another loop in the
00267             table::Cache::singleton().removeTable routine.
00268           */
00269           boost::xtime xt;
00270           xtime_get(&xt, boost::TIME_UTC);
00271           xt.sec += 10;
00272           boost_unique_lock_t scoped(table::Cache::singleton().mutex(), boost::adopt_lock_t());
00273           COND_refresh.timed_wait(scoped, xt);
00274           scoped.release();
00275         }
00276         dropping_tables--;
00277         continue;
00278       }
00279     }
00280     break;
00281   }
00282 
00283   return result;
00284 }
00285 
00286 
00287 bool Cache::insert(table::Concurrent *arg)
00288 {
00289   CacheMap::iterator returnable= cache.insert(std::make_pair(arg->getShare()->getCacheKey(), arg));
00290 
00291   return not (returnable == cache.end());
00292 }
00293 
00294 } /* namespace table */
00295 } /* namespace drizzled */