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