summaryrefslogtreecommitdiff
path: root/storage/myisam/mi_locking.c
diff options
context:
space:
mode:
authorbrian@zim.(none) <>2005-04-26 18:19:54 -0700
committerbrian@zim.(none) <>2005-04-26 18:19:54 -0700
commit2a7c71e309337588b3815ea7ff1fe20d734d50d9 (patch)
treedf9016f3d70b4657f89dcddca2ec4e188fc7fbdf /storage/myisam/mi_locking.c
parent8e0eb65f9aaa3dc5a4f713988e58190b82466cde (diff)
downloadmariadb-git-2a7c71e309337588b3815ea7ff1fe20d734d50d9.tar.gz
Changes to create storage directory for storage engines.
Diffstat (limited to 'storage/myisam/mi_locking.c')
-rw-r--r--storage/myisam/mi_locking.c510
1 files changed, 510 insertions, 0 deletions
diff --git a/storage/myisam/mi_locking.c b/storage/myisam/mi_locking.c
new file mode 100644
index 00000000000..789d74680ef
--- /dev/null
+++ b/storage/myisam/mi_locking.c
@@ -0,0 +1,510 @@
+/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/*
+ locking of isam-tables.
+ reads info from a isam-table. Must be first request before doing any furter
+ calls to any isamfunktion. Is used to allow many process use the same
+ isamdatabase.
+*/
+
+#include "myisamdef.h"
+
+ /* lock table by F_UNLCK, F_RDLCK or F_WRLCK */
+
+int mi_lock_database(MI_INFO *info, int lock_type)
+{
+ int error;
+ uint count;
+ MYISAM_SHARE *share=info->s;
+ uint flag;
+ DBUG_ENTER("mi_lock_database");
+ DBUG_PRINT("enter",("lock_type: %d old lock %d r_locks: %u w_locks: %u "
+ "global_changed: %d open_count: %u name: '%s'",
+ lock_type, info->lock_type, share->r_locks,
+ share->w_locks,
+ share->global_changed, share->state.open_count,
+ share->index_file_name));
+
+ if (share->options & HA_OPTION_READ_ONLY_DATA ||
+ info->lock_type == lock_type)
+ DBUG_RETURN(0);
+ if (lock_type == F_EXTRA_LCK) /* Used by TMP tables */
+ {
+ ++share->w_locks;
+ ++share->tot_locks;
+ info->lock_type= lock_type;
+ DBUG_RETURN(0);
+ }
+
+ flag=error=0;
+ pthread_mutex_lock(&share->intern_lock);
+ if (share->kfile >= 0) /* May only be false on windows */
+ {
+ switch (lock_type) {
+ case F_UNLCK:
+ if (info->lock_type == F_RDLCK)
+ count= --share->r_locks;
+ else
+ count= --share->w_locks;
+ --share->tot_locks;
+ if (info->lock_type == F_WRLCK && !share->w_locks &&
+ !share->delay_key_write && flush_key_blocks(share->key_cache,
+ share->kfile,FLUSH_KEEP))
+ {
+ error=my_errno;
+ mi_print_error(info->s, HA_ERR_CRASHED);
+ mi_mark_crashed(info); /* Mark that table must be checked */
+ }
+ if (info->opt_flag & (READ_CACHE_USED | WRITE_CACHE_USED))
+ {
+ if (end_io_cache(&info->rec_cache))
+ {
+ error=my_errno;
+ mi_print_error(info->s, HA_ERR_CRASHED);
+ mi_mark_crashed(info);
+ }
+ }
+ if (!count)
+ {
+ DBUG_PRINT("info",("changed: %u w_locks: %u",
+ (uint) share->changed, share->w_locks));
+ if (share->changed && !share->w_locks)
+ {
+ share->state.process= share->last_process=share->this_process;
+ share->state.unique= info->last_unique= info->this_unique;
+ share->state.update_count= info->last_loop= ++info->this_loop;
+ if (mi_state_info_write(share->kfile, &share->state, 1))
+ error=my_errno;
+ share->changed=0;
+ if (myisam_flush)
+ {
+ if (my_sync(share->kfile, MYF(0)))
+ error= my_errno;
+ if (my_sync(info->dfile, MYF(0)))
+ error= my_errno;
+ }
+ else
+ share->not_flushed=1;
+ if (error)
+ {
+ mi_print_error(info->s, HA_ERR_CRASHED);
+ mi_mark_crashed(info);
+ }
+ }
+ if (info->lock_type != F_EXTRA_LCK)
+ {
+ if (share->r_locks)
+ { /* Only read locks left */
+ flag=1;
+ if (my_lock(share->kfile,F_RDLCK,0L,F_TO_EOF,
+ MYF(MY_WME | MY_SEEK_NOT_DONE)) && !error)
+ error=my_errno;
+ }
+ else if (!share->w_locks)
+ { /* No more locks */
+ flag=1;
+ if (my_lock(share->kfile,F_UNLCK,0L,F_TO_EOF,
+ MYF(MY_WME | MY_SEEK_NOT_DONE)) && !error)
+ error=my_errno;
+ }
+ }
+ }
+ info->opt_flag&= ~(READ_CACHE_USED | WRITE_CACHE_USED);
+ info->lock_type= F_UNLCK;
+ break;
+ case F_RDLCK:
+ if (info->lock_type == F_WRLCK)
+ {
+ /*
+ Change RW to READONLY
+
+ mysqld does not turn write locks to read locks,
+ so we're never here in mysqld.
+ */
+ if (share->w_locks == 1)
+ {
+ flag=1;
+ if (my_lock(share->kfile,lock_type,0L,F_TO_EOF,
+ MYF(MY_SEEK_NOT_DONE)))
+ {
+ error=my_errno;
+ break;
+ }
+ }
+ share->w_locks--;
+ share->r_locks++;
+ info->lock_type=lock_type;
+ break;
+ }
+ if (!share->r_locks && !share->w_locks)
+ {
+ flag=1;
+ if (my_lock(share->kfile,lock_type,0L,F_TO_EOF,
+ info->lock_wait | MY_SEEK_NOT_DONE))
+ {
+ error=my_errno;
+ break;
+ }
+ if (mi_state_info_read_dsk(share->kfile, &share->state, 1))
+ {
+ error=my_errno;
+ VOID(my_lock(share->kfile,F_UNLCK,0L,F_TO_EOF,MYF(MY_SEEK_NOT_DONE)));
+ my_errno=error;
+ break;
+ }
+ }
+ VOID(_mi_test_if_changed(info));
+ share->r_locks++;
+ share->tot_locks++;
+ info->lock_type=lock_type;
+ break;
+ case F_WRLCK:
+ if (info->lock_type == F_RDLCK)
+ { /* Change READONLY to RW */
+ if (share->r_locks == 1)
+ {
+ flag=1;
+ if (my_lock(share->kfile,lock_type,0L,F_TO_EOF,
+ MYF(info->lock_wait | MY_SEEK_NOT_DONE)))
+ {
+ error=my_errno;
+ break;
+ }
+ share->r_locks--;
+ share->w_locks++;
+ info->lock_type=lock_type;
+ break;
+ }
+ }
+ if (!(share->options & HA_OPTION_READ_ONLY_DATA))
+ {
+ if (!share->w_locks)
+ {
+ flag=1;
+ if (my_lock(share->kfile,lock_type,0L,F_TO_EOF,
+ info->lock_wait | MY_SEEK_NOT_DONE))
+ {
+ error=my_errno;
+ break;
+ }
+ if (!share->r_locks)
+ {
+ if (mi_state_info_read_dsk(share->kfile, &share->state, 1))
+ {
+ error=my_errno;
+ VOID(my_lock(share->kfile,F_UNLCK,0L,F_TO_EOF,
+ info->lock_wait | MY_SEEK_NOT_DONE));
+ my_errno=error;
+ break;
+ }
+ }
+ }
+ }
+ VOID(_mi_test_if_changed(info));
+ info->lock_type=lock_type;
+ info->invalidator=info->s->invalidator;
+ share->w_locks++;
+ share->tot_locks++;
+ break;
+ default:
+ break; /* Impossible */
+ }
+ }
+ pthread_mutex_unlock(&share->intern_lock);
+#if defined(FULL_LOG) || defined(_lint)
+ lock_type|=(int) (flag << 8); /* Set bit to set if real lock */
+ myisam_log_command(MI_LOG_LOCK,info,(byte*) &lock_type,sizeof(lock_type),
+ error);
+#endif
+ DBUG_RETURN(error);
+} /* mi_lock_database */
+
+
+/****************************************************************************
+ The following functions are called by thr_lock() in threaded applications
+****************************************************************************/
+
+void mi_get_status(void* param)
+{
+ MI_INFO *info=(MI_INFO*) param;
+ DBUG_ENTER("mi_get_status");
+ DBUG_PRINT("info",("key_file: %ld data_file: %ld",
+ (long) info->s->state.state.key_file_length,
+ (long) info->s->state.state.data_file_length));
+#ifndef DBUG_OFF
+ if (info->state->key_file_length > info->s->state.state.key_file_length ||
+ info->state->data_file_length > info->s->state.state.data_file_length)
+ DBUG_PRINT("warning",("old info: key_file: %ld data_file: %ld",
+ (long) info->state->key_file_length,
+ (long) info->state->data_file_length));
+#endif
+ info->save_state=info->s->state.state;
+ info->state= &info->save_state;
+ DBUG_VOID_RETURN;
+}
+
+void mi_update_status(void* param)
+{
+ MI_INFO *info=(MI_INFO*) param;
+ /*
+ Because someone may have closed the table we point at, we only
+ update the state if its our own state. This isn't a problem as
+ we are always pointing at our own lock or at a read lock.
+ (This is enforced by thr_multi_lock.c)
+ */
+ if (info->state == &info->save_state)
+ {
+#ifndef DBUG_OFF
+ DBUG_PRINT("info",("updating status: key_file: %ld data_file: %ld",
+ (long) info->state->key_file_length,
+ (long) info->state->data_file_length));
+ if (info->state->key_file_length < info->s->state.state.key_file_length ||
+ info->state->data_file_length < info->s->state.state.data_file_length)
+ DBUG_PRINT("warning",("old info: key_file: %ld data_file: %ld",
+ (long) info->s->state.state.key_file_length,
+ (long) info->s->state.state.data_file_length));
+#endif
+ info->s->state.state= *info->state;
+ info->state= &info->s->state.state;
+ }
+
+ /*
+ We have to flush the write cache here as other threads may start
+ reading the table before mi_lock_database() is called
+ */
+ if (info->opt_flag & WRITE_CACHE_USED)
+ {
+ if (end_io_cache(&info->rec_cache))
+ {
+ mi_print_error(info->s, HA_ERR_CRASHED);
+ mi_mark_crashed(info);
+ }
+ info->opt_flag&= ~WRITE_CACHE_USED;
+ }
+}
+
+void mi_copy_status(void* to,void *from)
+{
+ ((MI_INFO*) to)->state= &((MI_INFO*) from)->save_state;
+}
+
+
+/*
+ Check if should allow concurrent inserts
+
+ IMPLEMENTATION
+ Don't allow concurrent inserts if we have a hole in the table.
+
+ NOTES
+ Rtree indexes are disabled in mi_open()
+
+ RETURN
+ 0 ok to use concurrent inserts
+ 1 not ok
+*/
+
+my_bool mi_check_status(void* param)
+{
+ MI_INFO *info=(MI_INFO*) param;
+ return (my_bool) (info->s->state.dellink != HA_OFFSET_ERROR);
+}
+
+
+/****************************************************************************
+ ** functions to read / write the state
+****************************************************************************/
+
+int _mi_readinfo(register MI_INFO *info, int lock_type, int check_keybuffer)
+{
+ DBUG_ENTER("_mi_readinfo");
+
+ if (info->lock_type == F_UNLCK)
+ {
+ MYISAM_SHARE *share=info->s;
+ if (!share->tot_locks)
+ {
+ if (my_lock(share->kfile,lock_type,0L,F_TO_EOF,
+ info->lock_wait | MY_SEEK_NOT_DONE))
+ DBUG_RETURN(1);
+ if (mi_state_info_read_dsk(share->kfile, &share->state, 1))
+ {
+ int error=my_errno ? my_errno : -1;
+ VOID(my_lock(share->kfile,F_UNLCK,0L,F_TO_EOF,
+ MYF(MY_SEEK_NOT_DONE)));
+ my_errno=error;
+ DBUG_RETURN(1);
+ }
+ }
+ if (check_keybuffer)
+ VOID(_mi_test_if_changed(info));
+ info->invalidator=info->s->invalidator;
+ }
+ else if (lock_type == F_WRLCK && info->lock_type == F_RDLCK)
+ {
+ my_errno=EACCES; /* Not allowed to change */
+ DBUG_RETURN(-1); /* when have read_lock() */
+ }
+ DBUG_RETURN(0);
+} /* _mi_readinfo */
+
+
+/*
+ Every isam-function that uppdates the isam-database MUST end with this
+ request
+*/
+
+int _mi_writeinfo(register MI_INFO *info, uint operation)
+{
+ int error,olderror;
+ MYISAM_SHARE *share=info->s;
+ DBUG_ENTER("_mi_writeinfo");
+ DBUG_PRINT("info",("operation: %u tot_locks: %u", operation,
+ share->tot_locks));
+
+ error=0;
+ if (share->tot_locks == 0)
+ {
+ olderror=my_errno; /* Remember last error */
+ if (operation)
+ { /* Two threads can't be here */
+ share->state.process= share->last_process= share->this_process;
+ share->state.unique= info->last_unique= info->this_unique;
+ share->state.update_count= info->last_loop= ++info->this_loop;
+ if ((error=mi_state_info_write(share->kfile, &share->state, 1)))
+ olderror=my_errno;
+#ifdef __WIN__
+ if (myisam_flush)
+ {
+ _commit(share->kfile);
+ _commit(info->dfile);
+ }
+#endif
+ }
+ if (!(operation & WRITEINFO_NO_UNLOCK) &&
+ my_lock(share->kfile,F_UNLCK,0L,F_TO_EOF,
+ MYF(MY_WME | MY_SEEK_NOT_DONE)) && !error)
+ DBUG_RETURN(1);
+ my_errno=olderror;
+ }
+ else if (operation)
+ share->changed= 1; /* Mark keyfile changed */
+ DBUG_RETURN(error);
+} /* _mi_writeinfo */
+
+
+ /* Test if someone has changed the database */
+ /* (Should be called after readinfo) */
+
+int _mi_test_if_changed(register MI_INFO *info)
+{
+ MYISAM_SHARE *share=info->s;
+ if (share->state.process != share->last_process ||
+ share->state.unique != info->last_unique ||
+ share->state.update_count != info->last_loop)
+ { /* Keyfile has changed */
+ DBUG_PRINT("info",("index file changed"));
+ if (share->state.process != share->this_process)
+ VOID(flush_key_blocks(share->key_cache, share->kfile, FLUSH_RELEASE));
+ share->last_process=share->state.process;
+ info->last_unique= share->state.unique;
+ info->last_loop= share->state.update_count;
+ info->update|= HA_STATE_WRITTEN; /* Must use file on next */
+ info->data_changed= 1; /* For mi_is_changed */
+ return 1;
+ }
+ return (!(info->update & HA_STATE_AKTIV) ||
+ (info->update & (HA_STATE_WRITTEN | HA_STATE_DELETED |
+ HA_STATE_KEY_CHANGED)));
+} /* _mi_test_if_changed */
+
+
+/*
+ Put a mark in the .MYI file that someone is updating the table
+
+
+ DOCUMENTATION
+
+ state.open_count in the .MYI file is used the following way:
+ - For the first change of the .MYI file in this process open_count is
+ incremented by mi_mark_file_change(). (We have a write lock on the file
+ when this happens)
+ - In mi_close() it's decremented by _mi_decrement_open_count() if it
+ was incremented in the same process.
+
+ This mean that if we are the only process using the file, the open_count
+ tells us if the MYISAM file wasn't properly closed. (This is true if
+ my_disable_locking is set).
+*/
+
+
+int _mi_mark_file_changed(MI_INFO *info)
+{
+ char buff[3];
+ register MYISAM_SHARE *share=info->s;
+ DBUG_ENTER("_mi_mark_file_changed");
+
+ if (!(share->state.changed & STATE_CHANGED) || ! share->global_changed)
+ {
+ share->state.changed|=(STATE_CHANGED | STATE_NOT_ANALYZED |
+ STATE_NOT_OPTIMIZED_KEYS);
+ if (!share->global_changed)
+ {
+ share->global_changed=1;
+ share->state.open_count++;
+ }
+ if (!share->temporary)
+ {
+ mi_int2store(buff,share->state.open_count);
+ buff[2]=1; /* Mark that it's changed */
+ DBUG_RETURN(my_pwrite(share->kfile,buff,sizeof(buff),
+ sizeof(share->state.header),
+ MYF(MY_NABP)));
+ }
+ }
+ DBUG_RETURN(0);
+}
+
+
+/*
+ This is only called by close or by extra(HA_FLUSH) if the OS has the pwrite()
+ call. In these context the following code should be safe!
+ */
+
+int _mi_decrement_open_count(MI_INFO *info)
+{
+ char buff[2];
+ register MYISAM_SHARE *share=info->s;
+ int lock_error=0,write_error=0;
+ if (share->global_changed)
+ {
+ uint old_lock=info->lock_type;
+ share->global_changed=0;
+ lock_error=mi_lock_database(info,F_WRLCK);
+ /* Its not fatal even if we couldn't get the lock ! */
+ if (share->state.open_count > 0)
+ {
+ share->state.open_count--;
+ mi_int2store(buff,share->state.open_count);
+ write_error=my_pwrite(share->kfile,buff,sizeof(buff),
+ sizeof(share->state.header),
+ MYF(MY_NABP));
+ }
+ if (!lock_error)
+ lock_error=mi_lock_database(info,old_lock);
+ }
+ return test(lock_error || write_error);
+}