MySQL 5.6.14 Source Code Document
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
my_lock.c
1 /* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
2 
3  This program is free software; you can redistribute it and/or modify
4  it under the terms of the GNU General Public License as published by
5  the Free Software Foundation; version 2 of the License.
6 
7  This program is distributed in the hope that it will be useful,
8  but WITHOUT ANY WARRANTY; without even the implied warranty of
9  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  GNU General Public License for more details.
11 
12  You should have received a copy of the GNU General Public License
13  along with this program; if not, write to the Free Software
14  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
15 
16 #include "mysys_priv.h"
17 #include "mysys_err.h"
18 #include <errno.h>
19 #undef MY_HOW_OFTEN_TO_ALARM
20 #define MY_HOW_OFTEN_TO_ALARM ((int) my_time_to_wait_for_lock)
21 #ifdef NO_ALARM_LOOP
22 #undef NO_ALARM_LOOP
23 #endif
24 #include <my_alarm.h>
25 
26 #ifdef _WIN32
27 #define WIN_LOCK_INFINITE -1
28 #define WIN_LOCK_SLEEP_MILLIS 100
29 
30 static int win_lock(File fd, int locktype, my_off_t start, my_off_t length,
31  int timeout_sec)
32 {
33  LARGE_INTEGER liOffset,liLength;
34  DWORD dwFlags;
35  OVERLAPPED ov= {0};
36  HANDLE hFile= (HANDLE)my_get_osfhandle(fd);
37  DWORD lastError= 0;
38  int i;
39  int timeout_millis= timeout_sec * 1000;
40 
41  DBUG_ENTER("win_lock");
42 
43  liOffset.QuadPart= start;
44  liLength.QuadPart= length;
45 
46  ov.Offset= liOffset.LowPart;
47  ov.OffsetHigh= liOffset.HighPart;
48 
49  if (locktype == F_UNLCK)
50  {
51  if (UnlockFileEx(hFile, 0, liLength.LowPart, liLength.HighPart, &ov))
52  DBUG_RETURN(0);
53  /*
54  For compatibility with fcntl implementation, ignore error,
55  if region was not locked
56  */
57  if (GetLastError() == ERROR_NOT_LOCKED)
58  {
59  SetLastError(0);
60  DBUG_RETURN(0);
61  }
62  goto error;
63  }
64  else if (locktype == F_RDLCK)
65  /* read lock is mapped to a shared lock. */
66  dwFlags= 0;
67  else
68  /* write lock is mapped to an exclusive lock. */
69  dwFlags= LOCKFILE_EXCLUSIVE_LOCK;
70 
71  /*
72  Drop old lock first to avoid double locking.
73  During analyze of Bug#38133 (Myisamlog test fails on Windows)
74  I met the situation that the program myisamlog locked the file
75  exclusively, then additionally shared, then did one unlock, and
76  then blocked on an attempt to lock it exclusively again.
77  Unlocking before every lock fixed the problem.
78  Note that this introduces a race condition. When the application
79  wants to convert an exclusive lock into a shared one, it will now
80  first unlock the file and then lock it shared. A waiting exclusive
81  lock could step in here. For reasons described in Bug#38133 and
82  Bug#41124 (Server hangs on Windows with --external-locking after
83  INSERT...SELECT) and in the review thread at
84  http://lists.mysql.com/commits/60721 it seems to be the better
85  option than not to unlock here.
86  If one day someone notices a way how to do file lock type changes
87  on Windows without unlocking before taking the new lock, please
88  change this code accordingly to fix the race condition.
89  */
90  if (!UnlockFileEx(hFile, 0, liLength.LowPart, liLength.HighPart, &ov) &&
91  (GetLastError() != ERROR_NOT_LOCKED))
92  goto error;
93 
94  if (timeout_sec == WIN_LOCK_INFINITE)
95  {
96  if (LockFileEx(hFile, dwFlags, 0, liLength.LowPart, liLength.HighPart, &ov))
97  DBUG_RETURN(0);
98  goto error;
99  }
100 
101  dwFlags|= LOCKFILE_FAIL_IMMEDIATELY;
102  timeout_millis= timeout_sec * 1000;
103  /* Try lock in a loop, until the lock is acquired or timeout happens */
104  for(i= 0; ;i+= WIN_LOCK_SLEEP_MILLIS)
105  {
106  if (LockFileEx(hFile, dwFlags, 0, liLength.LowPart, liLength.HighPart, &ov))
107  DBUG_RETURN(0);
108 
109  if (GetLastError() != ERROR_LOCK_VIOLATION)
110  goto error;
111 
112  if (i >= timeout_millis)
113  break;
114  Sleep(WIN_LOCK_SLEEP_MILLIS);
115  }
116 
117  /* timeout */
118  errno= EAGAIN;
119  DBUG_RETURN(-1);
120 
121 error:
122  my_osmaperr(GetLastError());
123  DBUG_RETURN(-1);
124 }
125 #endif
126 
127 
128 
129 /*
130  Lock a part of a file
131 
132  RETURN VALUE
133  0 Success
134  -1 An error has occured and 'my_errno' is set
135  to indicate the actual error code.
136 */
137 
138 int my_lock(File fd, int locktype, my_off_t start, my_off_t length,
139  myf MyFlags)
140 {
141 #ifdef HAVE_FCNTL
142  int value;
143  ALARM_VARIABLES;
144 #endif
145 
146  DBUG_ENTER("my_lock");
147  DBUG_PRINT("my",("fd: %d Op: %d start: %ld Length: %ld MyFlags: %d",
148  fd,locktype,(long) start,(long) length,MyFlags));
149  if (my_disable_locking)
150  DBUG_RETURN(0);
151 
152 #if defined(_WIN32)
153  {
154  int timeout_sec;
155  if (MyFlags & MY_DONT_WAIT)
156  timeout_sec= 0;
157  else
158  timeout_sec= WIN_LOCK_INFINITE;
159 
160  if (win_lock(fd, locktype, start, length, timeout_sec) == 0)
161  DBUG_RETURN(0);
162  }
163 #else
164 #if defined(HAVE_FCNTL)
165  {
166  struct flock lock;
167 
168  lock.l_type= (short) locktype;
169  lock.l_whence= SEEK_SET;
170  lock.l_start= (off_t) start;
171  lock.l_len= (off_t) length;
172 
173  if (MyFlags & MY_DONT_WAIT)
174  {
175  if (fcntl(fd,F_SETLK,&lock) != -1) /* Check if we can lock */
176  DBUG_RETURN(0); /* Ok, file locked */
177  DBUG_PRINT("info",("Was locked, trying with alarm"));
178  ALARM_INIT;
179  while ((value=fcntl(fd,F_SETLKW,&lock)) && ! ALARM_TEST &&
180  errno == EINTR)
181  { /* Setup again so we don`t miss it */
182  ALARM_REINIT;
183  }
184  ALARM_END;
185  if (value != -1)
186  DBUG_RETURN(0);
187  if (errno == EINTR)
188  errno=EAGAIN;
189  }
190  else if (fcntl(fd,F_SETLKW,&lock) != -1) /* Wait until a lock */
191  DBUG_RETURN(0);
192  }
193 #else
194  if (MyFlags & MY_SEEK_NOT_DONE)
195  {
196  if (my_seek(fd,start,MY_SEEK_SET,MYF(MyFlags & ~MY_SEEK_NOT_DONE))
197  == MY_FILEPOS_ERROR)
198  {
199  /*
200  If an error has occured in my_seek then we will already
201  have an error code in my_errno; Just return error code.
202  */
203  DBUG_RETURN(-1);
204  }
205  }
206  if (lockf(fd,locktype,length) != -1)
207  DBUG_RETURN(0);
208 #endif /* HAVE_FCNTL */
209 #endif /* HAVE_LOCKING */
210 
211  /* We got an error. We don't want EACCES errors */
212  my_errno=(errno == EACCES) ? EAGAIN : errno ? errno : -1;
213 
214  if (MyFlags & MY_WME)
215  {
216  char errbuf[MYSYS_STRERROR_SIZE];
217  if (locktype == F_UNLCK)
218  my_error(EE_CANTUNLOCK, MYF(ME_BELL+ME_WAITTANG),
219  my_errno, my_strerror(errbuf, sizeof(errbuf), my_errno));
220  else
221  my_error(EE_CANTLOCK, MYF(ME_BELL+ME_WAITTANG),
222  my_errno, my_strerror(errbuf, sizeof(errbuf), my_errno));
223  }
224  DBUG_PRINT("error",("my_errno: %d (%d)",my_errno,errno));
225  DBUG_RETURN(-1);
226 } /* my_lock */