Drizzled Public API Documentation

concurrent.cc
1 /* -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*-
2  * vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
3  *
4  * Copyright (C) 2010 Brian Aker
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19  */
20 
21 #include <config.h>
22 
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <fcntl.h>
26 
27 #include <drizzled/session.h>
28 #include <plugin/myisam/myisam.h>
29 #include <drizzled/open_tables_state.h>
30 #include <drizzled/plugin/transactional_storage_engine.h>
31 #include <drizzled/table/instance.h>
32 #include <drizzled/table.h>
33 #include <drizzled/table_list.h>
34 
35 namespace drizzled {
36 namespace table {
37 
38 /*
39  Open table which is already name-locked by this thread.
40 
41  SYNOPSIS
42  reopen_name_locked_table()
43  session Thread handle
44  table_list TableList object for table to be open, TableList::table
45  member should point to Table object which was used for
46  name-locking.
47  link_in true - if Table object for table to be opened should be
48  linked into Session::open_tables list.
49  false - placeholder used for name-locking is already in
50  this list so we only need to preserve Table::next
51  pointer.
52 
53  NOTE
54  This function assumes that its caller already acquired table::Cache::mutex() mutex.
55 
56  RETURN VALUE
57  false - Success
58  true - Error
59 */
60 
61 bool Concurrent::reopen_name_locked_table(TableList* table_list, Session *session)
62 {
63  safe_mutex_assert_owner(table::Cache::mutex().native_handle());
64 
65  if (session->getKilled())
66  return true;
67 
68  identifier::Table identifier(table_list->getSchemaName(), table_list->getTableName());
69  if (open_unireg_entry(session, table_list->getTableName(), identifier))
70  {
71  intern_close_table();
72  return true;
73  }
74 
75  /*
76  We want to prevent other connections from opening this table until end
77  of statement as it is likely that modifications of table's metadata are
78  not yet finished (for example CREATE TRIGGER have to change .TRG cursor,
79  or we might want to drop table if CREATE TABLE ... SELECT fails).
80  This also allows us to assume that no other connection will sneak in
81  before we will get table-level lock on this table.
82  */
83  getMutableShare()->resetVersion();
84  in_use = session;
85 
86  tablenr= session->open_tables.current_tablenr++;
87  used_fields= 0;
88  const_table= 0;
89  null_row= false;
90  maybe_null= false;
91  force_index= false;
92  status= STATUS_NO_RECORD;
93 
94  return false;
95 }
96 
97 
98 /*
99  Load a table definition from cursor and open unireg table
100 
101  SYNOPSIS
102  open_unireg_entry()
103  session Thread handle
104  entry Store open table definition here
105  table_list TableList with db, table_name
106  alias Alias name
107  cache_key Key for share_cache
108  cache_key_length length of cache_key
109 
110  NOTES
111  Extra argument for open is taken from session->open_options
112  One must have a lock on table::Cache::mutex() when calling this function
113 
114  RETURN
115  0 ok
116 # Error
117 */
118 
119 int table::Concurrent::open_unireg_entry(Session *session,
120  const char *alias,
121  identifier::Table &identifier)
122 {
123  int error;
124  TableShare::shared_ptr share;
125  uint32_t discover_retry_count= 0;
126 
127  safe_mutex_assert_owner(table::Cache::mutex().native_handle());
128 retry:
129  if (not (share= table::instance::Shared::make_shared(session, identifier, error)))
130  {
131  return 1;
132  }
133 
134  while ((error= share->open_table_from_share(session,
135  identifier,
136  alias,
137  (uint32_t) (HA_OPEN_KEYFILE |
138  HA_OPEN_RNDFILE |
139  HA_GET_INDEX |
140  HA_TRY_READ_ONLY),
141  session->open_options, *this)))
142  {
143  if (error == 7) // Table def changed
144  {
145  share->resetVersion(); // Mark share as old
146  if (discover_retry_count++) // Retry once
147  {
148  table::instance::release(share);
149  return 1;
150  }
151 
152  /*
153  TODO->
154  Here we should wait until all threads has released the table.
155  For now we do one retry. This may cause a deadlock if there
156  is other threads waiting for other tables used by this thread.
157 
158  Proper fix would be to if the second retry failed:
159  - Mark that table def changed
160  - Return from open table
161  - Close all tables used by this thread
162  - Start waiting that the share is released
163  - Retry by opening all tables again
164  */
165 
166  /*
167  TO BE FIXED
168  To avoid deadlock, only wait for release if no one else is
169  using the share.
170  */
171  if (share->getTableCount() != 1)
172  {
173  table::instance::release(share);
174  return 1;
175  }
176 
177  /* Free share and wait until it's released by all threads */
178  table::instance::release(share);
179 
180  if (not session->getKilled())
181  {
182  drizzle_reset_errors(*session, true); // Clear warnings
183  session->clear_error(); // Clear error message
184  goto retry;
185  }
186 
187  return 1;
188  }
189 
190  table::instance::release(share);
191 
192  return 1;
193  }
194 
195  return 0;
196 }
197 
198 void table::Concurrent::release(void)
199 {
200  // During an ALTER TABLE we could see the proto go away when the
201  // definition is pushed out of this table object. In this case we would
202  // not release from the cache because we were not in the cache. We just
203  // delete if this happens.
204  if (getShare()->getType() == message::Table::STANDARD)
205  {
206  table::instance::release(getMutableShare());
207  }
208  else
209  {
210  delete _share;
211  }
212  _share= NULL;
213 }
214 
215 } /* namespace table */
216 } /* namespace drizzled */