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