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