aboutsummaryrefslogtreecommitdiff
path: root/mysql/mysys/my_lock.c
diff options
context:
space:
mode:
Diffstat (limited to 'mysql/mysys/my_lock.c')
-rw-r--r--mysql/mysys/my_lock.c221
1 files changed, 221 insertions, 0 deletions
diff --git a/mysql/mysys/my_lock.c b/mysql/mysys/my_lock.c
new file mode 100644
index 0000000..1b4336c
--- /dev/null
+++ b/mysql/mysys/my_lock.c
@@ -0,0 +1,221 @@
+/* Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "mysys_priv.h"
+#include "my_sys.h"
+#include "mysys_err.h"
+#include <errno.h>
+#include "my_thread_local.h"
+
+
+#ifndef _WIN32
+#include <signal.h>
+
+static int volatile my_have_got_alarm= 0;
+static uint my_time_to_wait_for_lock= 2; /* In seconds */
+
+void my_set_alarm_variable(int signo MY_ATTRIBUTE((unused)))
+{
+ my_have_got_alarm= 1; /* Tell program that time expired */
+}
+#endif /* !_WIN32 */
+
+#ifdef _WIN32
+#define WIN_LOCK_INFINITE -1
+#define WIN_LOCK_SLEEP_MILLIS 100
+
+static int win_lock(File fd, int locktype, my_off_t start, my_off_t length,
+ int timeout_sec)
+{
+ LARGE_INTEGER liOffset,liLength;
+ DWORD dwFlags;
+ OVERLAPPED ov= {0};
+ HANDLE hFile= (HANDLE)my_get_osfhandle(fd);
+ DWORD lastError= 0;
+ int i;
+ int timeout_millis= timeout_sec * 1000;
+
+ DBUG_ENTER("win_lock");
+
+ liOffset.QuadPart= start;
+ liLength.QuadPart= length;
+
+ ov.Offset= liOffset.LowPart;
+ ov.OffsetHigh= liOffset.HighPart;
+
+ if (locktype == F_UNLCK)
+ {
+ if (UnlockFileEx(hFile, 0, liLength.LowPart, liLength.HighPart, &ov))
+ DBUG_RETURN(0);
+ /*
+ For compatibility with fcntl implementation, ignore error,
+ if region was not locked
+ */
+ if (GetLastError() == ERROR_NOT_LOCKED)
+ {
+ SetLastError(0);
+ DBUG_RETURN(0);
+ }
+ goto error;
+ }
+ else if (locktype == F_RDLCK)
+ /* read lock is mapped to a shared lock. */
+ dwFlags= 0;
+ else
+ /* write lock is mapped to an exclusive lock. */
+ dwFlags= LOCKFILE_EXCLUSIVE_LOCK;
+
+ /*
+ Drop old lock first to avoid double locking.
+ During analyze of Bug#38133 (Myisamlog test fails on Windows)
+ I met the situation that the program myisamlog locked the file
+ exclusively, then additionally shared, then did one unlock, and
+ then blocked on an attempt to lock it exclusively again.
+ Unlocking before every lock fixed the problem.
+ Note that this introduces a race condition. When the application
+ wants to convert an exclusive lock into a shared one, it will now
+ first unlock the file and then lock it shared. A waiting exclusive
+ lock could step in here. For reasons described in Bug#38133 and
+ Bug#41124 (Server hangs on Windows with --external-locking after
+ INSERT...SELECT) and in the review thread at
+ http://lists.mysql.com/commits/60721 it seems to be the better
+ option than not to unlock here.
+ If one day someone notices a way how to do file lock type changes
+ on Windows without unlocking before taking the new lock, please
+ change this code accordingly to fix the race condition.
+ */
+ if (!UnlockFileEx(hFile, 0, liLength.LowPart, liLength.HighPart, &ov) &&
+ (GetLastError() != ERROR_NOT_LOCKED))
+ goto error;
+
+ if (timeout_sec == WIN_LOCK_INFINITE)
+ {
+ if (LockFileEx(hFile, dwFlags, 0, liLength.LowPart, liLength.HighPart, &ov))
+ DBUG_RETURN(0);
+ goto error;
+ }
+
+ dwFlags|= LOCKFILE_FAIL_IMMEDIATELY;
+ timeout_millis= timeout_sec * 1000;
+ /* Try lock in a loop, until the lock is acquired or timeout happens */
+ for(i= 0; ;i+= WIN_LOCK_SLEEP_MILLIS)
+ {
+ if (LockFileEx(hFile, dwFlags, 0, liLength.LowPart, liLength.HighPart, &ov))
+ DBUG_RETURN(0);
+
+ if (GetLastError() != ERROR_LOCK_VIOLATION)
+ goto error;
+
+ if (i >= timeout_millis)
+ break;
+ Sleep(WIN_LOCK_SLEEP_MILLIS);
+ }
+
+ /* timeout */
+ errno= EAGAIN;
+ DBUG_RETURN(-1);
+
+error:
+ my_osmaperr(GetLastError());
+ DBUG_RETURN(-1);
+}
+#endif
+
+
+
+/*
+ Lock a part of a file
+
+ RETURN VALUE
+ 0 Success
+ -1 An error has occured and 'my_errno' is set
+ to indicate the actual error code.
+*/
+
+int my_lock(File fd, int locktype, my_off_t start, my_off_t length,
+ myf MyFlags)
+{
+ DBUG_ENTER("my_lock");
+ DBUG_PRINT("my",("fd: %d Op: %d start: %ld Length: %ld MyFlags: %d",
+ fd,locktype,(long) start,(long) length,MyFlags));
+ if (my_disable_locking)
+ DBUG_RETURN(0);
+
+#if defined(_WIN32)
+ {
+ int timeout_sec;
+ if (MyFlags & MY_DONT_WAIT)
+ timeout_sec= 0;
+ else
+ timeout_sec= WIN_LOCK_INFINITE;
+
+ if (win_lock(fd, locktype, start, length, timeout_sec) == 0)
+ DBUG_RETURN(0);
+ }
+#else
+ {
+ struct flock lock;
+
+ lock.l_type= (short) locktype;
+ lock.l_whence= SEEK_SET;
+ lock.l_start= (off_t) start;
+ lock.l_len= (off_t) length;
+
+ if (MyFlags & MY_DONT_WAIT)
+ {
+ int value;
+ uint alarm_old;
+ sig_return alarm_signal;
+
+ if (fcntl(fd,F_SETLK,&lock) != -1) /* Check if we can lock */
+ DBUG_RETURN(0); /* Ok, file locked */
+ DBUG_PRINT("info",("Was locked, trying with alarm"));
+ my_have_got_alarm= 0;
+ alarm_old= alarm(my_time_to_wait_for_lock);
+ alarm_signal= signal(SIGALRM, my_set_alarm_variable);
+ while ((value= fcntl(fd, F_SETLKW, &lock)) && !my_have_got_alarm &&
+ errno == EINTR)
+ { /* Setup again so we don`t miss it */
+ (void) alarm(my_time_to_wait_for_lock);
+ my_have_got_alarm= 0;
+ }
+ (void) signal(SIGALRM, alarm_signal);
+ (void) alarm(alarm_old);
+ if (value != -1)
+ DBUG_RETURN(0);
+ if (errno == EINTR)
+ errno=EAGAIN;
+ }
+ else if (fcntl(fd,F_SETLKW,&lock) != -1) /* Wait until a lock */
+ DBUG_RETURN(0);
+ }
+#endif /* _WIN32 */
+
+ /* We got an error. We don't want EACCES errors */
+ set_my_errno((errno == EACCES) ? EAGAIN : errno ? errno : -1);
+
+ if (MyFlags & MY_WME)
+ {
+ char errbuf[MYSYS_STRERROR_SIZE];
+ if (locktype == F_UNLCK)
+ my_error(EE_CANTUNLOCK, MYF(0),
+ my_errno(), my_strerror(errbuf, sizeof(errbuf), my_errno()));
+ else
+ my_error(EE_CANTLOCK, MYF(0),
+ my_errno(), my_strerror(errbuf, sizeof(errbuf), my_errno()));
+ }
+ DBUG_PRINT("error",("my_errno: %d (%d)",my_errno(),errno));
+ DBUG_RETURN(-1);
+} /* my_lock */