Drizzled Public API Documentation

mi_locking.cc
00001 /* Copyright (C) 2000-2006 MySQL AB
00002 
00003    This program is free software; you can redistribute it and/or modify
00004    it under the terms of the GNU General Public License as published by
00005    the Free Software Foundation; version 2 of the License.
00006 
00007    This program is distributed in the hope that it will be useful,
00008    but WITHOUT ANY WARRANTY; without even the implied warranty of
00009    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00010    GNU General Public License for more details.
00011 
00012    You should have received a copy of the GNU General Public License
00013    along with this program; if not, write to the Free Software
00014    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA */
00015 
00016 /*
00017   locking of isam-tables.
00018   reads info from a isam-table. Must be first request before doing any furter
00019   calls to any isamfunktion.  Is used to allow many process use the same
00020   isamdatabase.
00021 */
00022 
00023 #include "myisam_priv.h"
00024 #include <drizzled/charset_info.h>
00025 #include <drizzled/util/test.h>
00026 
00027 using namespace std;
00028 using namespace drizzled;
00029 
00030   /* lock table by F_UNLCK, F_RDLCK or F_WRLCK */
00031 
00032 int mi_lock_database(MI_INFO *info, int lock_type)
00033 {
00034   int error;
00035   uint32_t count;
00036   MYISAM_SHARE *share=info->s;
00037   uint32_t flag;
00038 
00039   if (!info->s->in_use)
00040     info->s->in_use= new list<Session *>;
00041 
00042   if (lock_type == F_EXTRA_LCK)                 /* Used by TMP tables */
00043   {
00044     ++share->w_locks;
00045     ++share->tot_locks;
00046     info->lock_type= lock_type;
00047     info->s->in_use->push_front(info->in_use);
00048     return(0);
00049   }
00050 
00051   flag=error=0;
00052   if (share->kfile >= 0)    /* May only be false on windows */
00053   {
00054     switch (lock_type) {
00055     case F_UNLCK:
00056       if (info->lock_type == F_RDLCK)
00057   count= --share->r_locks;
00058       else
00059   count= --share->w_locks;
00060       --share->tot_locks;
00061       if (info->lock_type == F_WRLCK && !share->w_locks &&
00062     !share->delay_key_write && flush_key_blocks(share->getKeyCache(),
00063                   share->kfile,FLUSH_KEEP))
00064       {
00065   error=errno;
00066         mi_print_error(info->s, HA_ERR_CRASHED);
00067   mi_mark_crashed(info);    /* Mark that table must be checked */
00068       }
00069       if (info->opt_flag & (READ_CACHE_USED | WRITE_CACHE_USED))
00070       {
00071   if (info->rec_cache.end_io_cache())
00072   {
00073     error=errno;
00074           mi_print_error(info->s, HA_ERR_CRASHED);
00075     mi_mark_crashed(info);
00076   }
00077       }
00078       if (!count)
00079       {
00080   if (share->changed && !share->w_locks)
00081   {
00082     if ((info->s->mmaped_length != info->s->state.state.data_file_length) &&
00083         (info->s->nonmmaped_inserts > MAX_NONMAPPED_INSERTS))
00084     {
00085       mi_remap_file(info, info->s->state.state.data_file_length);
00086       info->s->nonmmaped_inserts= 0;
00087     }
00088     share->state.process= share->last_process=share->this_process;
00089     share->state.unique=   info->last_unique=  info->this_unique;
00090     share->state.update_count= info->last_loop= ++info->this_loop;
00091           if (mi_state_info_write(share->kfile, &share->state, 1))
00092       error=errno;
00093     share->changed=0;
00094           share->not_flushed=1;
00095     if (error)
00096           {
00097             mi_print_error(info->s, HA_ERR_CRASHED);
00098       mi_mark_crashed(info);
00099           }
00100   }
00101   if (info->lock_type != F_EXTRA_LCK)
00102   {
00103     if (share->r_locks)
00104     {         /* Only read locks left */
00105       flag=1;
00106     }
00107     else if (!share->w_locks)
00108     {         /* No more locks */
00109       flag=1;
00110     }
00111   }
00112       }
00113       info->opt_flag&= ~(READ_CACHE_USED | WRITE_CACHE_USED);
00114       info->lock_type= F_UNLCK;
00115       info->s->in_use->remove(info->in_use);
00116       break;
00117     case F_RDLCK:
00118       if (info->lock_type == F_WRLCK)
00119       {
00120         /*
00121           Change RW to READONLY
00122 
00123           mysqld does not turn write locks to read locks,
00124           so we're never here in mysqld.
00125         */
00126   if (share->w_locks == 1)
00127   {
00128     flag=1;
00129   }
00130   share->w_locks--;
00131   share->r_locks++;
00132   info->lock_type=lock_type;
00133   break;
00134       }
00135       if (!share->r_locks && !share->w_locks)
00136       {
00137   flag=1;
00138   if (mi_state_info_read_dsk(share->kfile, &share->state, 1))
00139   {
00140     error=errno;
00141     break;
00142   }
00143   if (mi_state_info_read_dsk(share->kfile, &share->state, 1))
00144   {
00145     error=errno;
00146     errno=error;
00147     break;
00148   }
00149       }
00150       _mi_test_if_changed(info);
00151       share->r_locks++;
00152       share->tot_locks++;
00153       info->lock_type=lock_type;
00154       info->s->in_use->push_front(info->in_use);
00155       break;
00156     case F_WRLCK:
00157       if (info->lock_type == F_RDLCK)
00158       {           /* Change READONLY to RW */
00159   if (share->r_locks == 1)
00160   {
00161     flag=1;
00162     share->r_locks--;
00163     share->w_locks++;
00164     info->lock_type=lock_type;
00165     break;
00166   }
00167       }
00168       if (!(share->options & HA_OPTION_READ_ONLY_DATA))
00169       {
00170   if (!share->w_locks)
00171   {
00172     flag=1;
00173     if (!share->r_locks)
00174     {
00175       if (mi_state_info_read_dsk(share->kfile, &share->state, 1))
00176       {
00177         error=errno;
00178         errno=error;
00179         break;
00180       }
00181     }
00182   }
00183       }
00184       _mi_test_if_changed(info);
00185 
00186       info->lock_type=lock_type;
00187       share->w_locks++;
00188       share->tot_locks++;
00189       info->s->in_use->push_front(info->in_use);
00190       break;
00191     default:
00192       break;        /* Impossible */
00193     }
00194   }
00195 #ifdef __WIN__
00196   else
00197   {
00198     /*
00199        Check for bad file descriptors if this table is part
00200        of a merge union. Failing to capture this may cause
00201        a crash on windows if the table is renamed and
00202        later on referenced by the merge table.
00203      */
00204     if( info->owned_by_merge && (info->s)->kfile < 0 )
00205     {
00206       error = HA_ERR_NO_SUCH_TABLE;
00207     }
00208   }
00209 #endif
00210 #if defined(FULL_LOG) || defined(_lint)
00211   lock_type|=(int) (flag << 8);   /* Set bit to set if real lock */
00212   myisam_log_command(MI_LOG_LOCK,info,(unsigned char*) &lock_type,sizeof(lock_type),
00213          error);
00214 #endif
00215   return(error);
00216 } /* mi_lock_database */
00217 
00218 
00219 /****************************************************************************
00220  ** functions to read / write the state
00221 ****************************************************************************/
00222 
00223 int _mi_readinfo(register MI_INFO *info, int lock_type, int check_keybuffer)
00224 {
00225   if (info->lock_type == F_UNLCK)
00226   {
00227     MYISAM_SHARE *share=info->s;
00228     if (!share->tot_locks)
00229     {
00230       if (mi_state_info_read_dsk(share->kfile, &share->state, 1))
00231       {
00232   int error=errno ? errno : -1;
00233   errno=error;
00234   return(1);
00235       }
00236     }
00237     if (check_keybuffer)
00238       _mi_test_if_changed(info);
00239   }
00240   else if (lock_type == F_WRLCK && info->lock_type == F_RDLCK)
00241   {
00242     errno=EACCES;       /* Not allowed to change */
00243     return(-1);       /* when have read_lock() */
00244   }
00245   return(0);
00246 } /* _mi_readinfo */
00247 
00248 
00249 /*
00250   Every isam-function that uppdates the isam-database MUST end with this
00251   request
00252 */
00253 
00254 int _mi_writeinfo(register MI_INFO *info, uint32_t operation)
00255 {
00256   int error,olderror;
00257   MYISAM_SHARE *share=info->s;
00258 
00259   error=0;
00260   if (share->tot_locks == 0)
00261   {
00262     olderror=errno;     /* Remember last error */
00263     if (operation)
00264     {         /* Two threads can't be here */
00265       share->state.process= share->last_process=   share->this_process;
00266       share->state.unique=  info->last_unique=     info->this_unique;
00267       share->state.update_count= info->last_loop= ++info->this_loop;
00268       if ((error=mi_state_info_write(share->kfile, &share->state, 1)))
00269   olderror=errno;
00270     }
00271     errno=olderror;
00272   }
00273   else if (operation)
00274     share->changed= 1;      /* Mark keyfile changed */
00275   return(error);
00276 } /* _mi_writeinfo */
00277 
00278 
00279   /* Test if someone has changed the database */
00280   /* (Should be called after readinfo) */
00281 
00282 int _mi_test_if_changed(register MI_INFO *info)
00283 {
00284   MYISAM_SHARE *share=info->s;
00285   if (share->state.process != share->last_process ||
00286       share->state.unique  != info->last_unique ||
00287       share->state.update_count != info->last_loop)
00288   {           /* Keyfile has changed */
00289     if (share->state.process != share->this_process)
00290       flush_key_blocks(share->getKeyCache(), share->kfile, FLUSH_RELEASE);
00291     share->last_process=share->state.process;
00292     info->last_unique=  share->state.unique;
00293     info->last_loop=  share->state.update_count;
00294     info->update|=  HA_STATE_WRITTEN; /* Must use file on next */
00295     info->data_changed= 1;      /* For mi_is_changed */
00296     return 1;
00297   }
00298   return (!(info->update & HA_STATE_AKTIV) ||
00299     (info->update & (HA_STATE_WRITTEN | HA_STATE_DELETED |
00300          HA_STATE_KEY_CHANGED)));
00301 } /* _mi_test_if_changed */
00302 
00303 
00304 /*
00305   Put a mark in the .MYI file that someone is updating the table
00306 
00307 
00308   DOCUMENTATION
00309 
00310   state.open_count in the .MYI file is used the following way:
00311   - For the first change of the .MYI file in this process open_count is
00312     incremented by mi_mark_file_change(). (We have a write lock on the file
00313     when this happens)
00314   - In mi_close() it's decremented by _mi_decrement_open_count() if it
00315     was incremented in the same process.
00316 
00317   This mean that if we are the only process using the file, the open_count
00318   tells us if the MYISAM file wasn't properly closed.*/
00319 
00320 
00321 int _mi_mark_file_changed(MI_INFO *info)
00322 {
00323   unsigned char buff[3];
00324   register MYISAM_SHARE *share=info->s;
00325 
00326   if (!(share->state.changed & STATE_CHANGED) || ! share->global_changed)
00327   {
00328     share->state.changed|=(STATE_CHANGED | STATE_NOT_ANALYZED |
00329          STATE_NOT_OPTIMIZED_KEYS);
00330     if (!share->global_changed)
00331     {
00332       share->global_changed=1;
00333       share->state.open_count++;
00334     }
00335     if (!share->temporary)
00336     {
00337       mi_int2store(buff,share->state.open_count);
00338       buff[2]=1;        /* Mark that it's changed */
00339       return(my_pwrite(share->kfile,buff,sizeof(buff),
00340                             sizeof(share->state.header),
00341                             MYF(MY_NABP)));
00342     }
00343   }
00344   return(0);
00345 }
00346 
00347 
00348 /*
00349   This is only called by close or by extra(HA_FLUSH) if the OS has the pwrite()
00350   call.  In these context the following code should be safe!
00351  */
00352 
00353 int _mi_decrement_open_count(MI_INFO *info)
00354 {
00355   unsigned char buff[2];
00356   register MYISAM_SHARE *share=info->s;
00357   int lock_error=0,write_error=0;
00358   if (share->global_changed)
00359   {
00360     uint32_t old_lock=info->lock_type;
00361     share->global_changed=0;
00362     lock_error=mi_lock_database(info,F_WRLCK);
00363     /* Its not fatal even if we couldn't get the lock ! */
00364     if (share->state.open_count > 0)
00365     {
00366       share->state.open_count--;
00367       mi_int2store(buff,share->state.open_count);
00368       write_error=my_pwrite(share->kfile,buff,sizeof(buff),
00369           sizeof(share->state.header),
00370           MYF(MY_NABP));
00371     }
00372     if (!lock_error)
00373       lock_error=mi_lock_database(info,old_lock);
00374   }
00375   return test(lock_error || write_error);
00376 }