Drizzled Public API Documentation

concurrent.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 #include <drizzled/session.h>
00028 #include <plugin/myisam/myisam.h>
00029 #include <drizzled/open_tables_state.h>
00030 #include <drizzled/plugin/transactional_storage_engine.h>
00031 #include <drizzled/table/instance.h>
00032 #include <drizzled/table.h>
00033 #include <drizzled/table_list.h>
00034 
00035 namespace drizzled {
00036 namespace table {
00037 
00038 /*
00039   Open table which is already name-locked by this thread.
00040 
00041   SYNOPSIS
00042   reopen_name_locked_table()
00043   session         Thread handle
00044   table_list  TableList object for table to be open, TableList::table
00045   member should point to Table object which was used for
00046   name-locking.
00047   link_in     true  - if Table object for table to be opened should be
00048   linked into Session::open_tables list.
00049   false - placeholder used for name-locking is already in
00050   this list so we only need to preserve Table::next
00051   pointer.
00052 
00053   NOTE
00054   This function assumes that its caller already acquired table::Cache::mutex() mutex.
00055 
00056   RETURN VALUE
00057   false - Success
00058   true  - Error
00059 */
00060 
00061 bool Concurrent::reopen_name_locked_table(TableList* table_list, Session *session)
00062 {
00063   safe_mutex_assert_owner(table::Cache::mutex().native_handle());
00064 
00065   if (session->getKilled())
00066     return true;
00067 
00068   identifier::Table identifier(table_list->getSchemaName(), table_list->getTableName());
00069   if (open_unireg_entry(session, table_list->getTableName(), identifier))
00070   {
00071     intern_close_table();
00072     return true;
00073   }
00074 
00075   /*
00076     We want to prevent other connections from opening this table until end
00077     of statement as it is likely that modifications of table's metadata are
00078     not yet finished (for example CREATE TRIGGER have to change .TRG cursor,
00079     or we might want to drop table if CREATE TABLE ... SELECT fails).
00080     This also allows us to assume that no other connection will sneak in
00081     before we will get table-level lock on this table.
00082   */
00083   getMutableShare()->resetVersion();
00084   in_use = session;
00085 
00086   tablenr= session->open_tables.current_tablenr++;
00087   used_fields= 0;
00088   const_table= 0;
00089   null_row= false;
00090   maybe_null= false;
00091   force_index= false;
00092   status= STATUS_NO_RECORD;
00093 
00094   return false;
00095 }
00096 
00097 
00098 /*
00099   Load a table definition from cursor and open unireg table
00100 
00101   SYNOPSIS
00102   open_unireg_entry()
00103   session     Thread handle
00104   entry   Store open table definition here
00105   table_list    TableList with db, table_name
00106   alias   Alias name
00107   cache_key   Key for share_cache
00108   cache_key_length  length of cache_key
00109 
00110   NOTES
00111   Extra argument for open is taken from session->open_options
00112   One must have a lock on table::Cache::mutex() when calling this function
00113 
00114   RETURN
00115   0 ok
00116 # Error
00117 */
00118 
00119 int table::Concurrent::open_unireg_entry(Session *session,
00120                                          const char *alias,
00121                                          identifier::Table &identifier)
00122 {
00123   int error;
00124   TableShare::shared_ptr share;
00125   uint32_t discover_retry_count= 0;
00126 
00127   safe_mutex_assert_owner(table::Cache::mutex().native_handle());
00128 retry:
00129   if (not (share= table::instance::Shared::make_shared(session, identifier, error)))
00130   {
00131     return 1;
00132   }
00133 
00134   while ((error= share->open_table_from_share(session,
00135                                               identifier,
00136                                               alias,
00137                                               (uint32_t) (HA_OPEN_KEYFILE |
00138                                                           HA_OPEN_RNDFILE |
00139                                                           HA_GET_INDEX |
00140                                                           HA_TRY_READ_ONLY),
00141                                               session->open_options, *this)))
00142   {
00143     if (error == 7)                             // Table def changed
00144     {
00145       share->resetVersion();                        // Mark share as old
00146       if (discover_retry_count++)               // Retry once
00147       {
00148         table::instance::release(share);
00149         return 1;
00150       }
00151 
00152       /*
00153         TODO->
00154         Here we should wait until all threads has released the table.
00155         For now we do one retry. This may cause a deadlock if there
00156         is other threads waiting for other tables used by this thread.
00157 
00158         Proper fix would be to if the second retry failed:
00159         - Mark that table def changed
00160         - Return from open table
00161         - Close all tables used by this thread
00162         - Start waiting that the share is released
00163         - Retry by opening all tables again
00164       */
00165 
00166       /*
00167         TO BE FIXED
00168         To avoid deadlock, only wait for release if no one else is
00169         using the share.
00170       */
00171       if (share->getTableCount() != 1)
00172       {
00173         table::instance::release(share);
00174         return 1;
00175       }
00176 
00177       /* Free share and wait until it's released by all threads */
00178       table::instance::release(share);
00179 
00180       if (not session->getKilled())
00181       {
00182         drizzle_reset_errors(*session, true);         // Clear warnings
00183         session->clear_error();                 // Clear error message
00184         goto retry;
00185       }
00186 
00187       return 1;
00188     }
00189 
00190     table::instance::release(share);
00191 
00192     return 1;
00193   }
00194 
00195   return 0;
00196 }
00197 
00198 void table::Concurrent::release(void)
00199 {
00200   // During an ALTER TABLE we could see the proto go away when the
00201   // definition is pushed out of this table object. In this case we would
00202   // not release from the cache because we were not in the cache. We just
00203   // delete if this happens.
00204   if (getShare()->getType() == message::Table::STANDARD)
00205   {
00206     table::instance::release(getMutableShare());
00207   }
00208   else
00209   {
00210     delete _share;
00211   }
00212   _share= NULL;
00213 }
00214 
00215 } /* namespace table */
00216 } /* namespace drizzled */