Allow arbitrary timeouts with dotlock.

This commit is contained in:
Werner Koch 2011-09-28 11:47:40 +02:00
parent 8a033fecfb
commit 567a31c2a0
2 changed files with 87 additions and 37 deletions

View File

@ -1,3 +1,8 @@
2011-09-28 Werner Koch <wk@g10code.com>
* dotlock.c (dotlock_take, dotlock_take_unix, dotlock_take_w32):
Implement arbitrary timeout values.
2011-09-27 Werner Koch <wk@g10code.com> 2011-09-27 Werner Koch <wk@g10code.com>
* dotlock.c (dotlock_take_unix): Check only the link count and not * dotlock.c (dotlock_take_unix): Check only the link count and not

View File

@ -87,8 +87,10 @@
This function will wait until the lock is acquired. If an This function will wait until the lock is acquired. If an
unexpected error occurs if will return non-zero and set ERRNO. If unexpected error occurs if will return non-zero and set ERRNO. If
you pass (0) instead of (-1) the function does not wait if the file you pass (0) instead of (-1) the function does not wait in case the
is already locked but returns -1 and sets ERRNO to EACCES. file is already locked but returns -1 and sets ERRNO to EACCES.
Any other positive value for the second parameter is considered a
timeout valuie in milliseconds.
To release the lock you call: To release the lock you call:
@ -805,15 +807,20 @@ dotlock_destroy (dotlock_t h)
#ifdef HAVE_POSIX_SYSTEM #ifdef HAVE_POSIX_SYSTEM
/* Unix specific code of make_dotlock. Returns 0 on success, -1 on /* Unix specific code of make_dotlock. Returns 0 on success and -1 on
error and 1 to try again. */ error. */
static int static int
dotlock_take_unix (dotlock_t h, long timeout, int *backoff) dotlock_take_unix (dotlock_t h, long timeout)
{ {
int pid; int wtime = 0;
int sumtime = 0;
int pid;
int lastpid = -1;
int ownerchanged;
const char *maybe_dead=""; const char *maybe_dead="";
int same_node; int same_node;
again:
if (h->use_o_excl) if (h->use_o_excl)
{ {
/* No hardlink support - use open(O_EXCL). */ /* No hardlink support - use open(O_EXCL). */
@ -889,7 +896,7 @@ dotlock_take_unix (dotlock_t h, long timeout, int *backoff)
return -1; return -1;
} }
my_info_0 ("lockfile disappeared\n"); my_info_0 ("lockfile disappeared\n");
return 1; /* Try again. */ goto again;
} }
else if ( pid == getpid() && same_node ) else if ( pid == getpid() && same_node )
{ {
@ -904,24 +911,49 @@ dotlock_take_unix (dotlock_t h, long timeout, int *backoff)
of the stale file tries to lock right at the same time as we. */ of the stale file tries to lock right at the same time as we. */
my_info_1 (_("removing stale lockfile (created by %d)\n"), pid); my_info_1 (_("removing stale lockfile (created by %d)\n"), pid);
unlink (h->lockname); unlink (h->lockname);
return 1; /* Try again. */ goto again;
} }
if ( timeout == -1 ) if (lastpid == -1)
lastpid = pid;
ownerchanged = (pid != lastpid);
if (timeout)
{ {
/* Wait until lock has been released. */
struct timeval tv; struct timeval tv;
my_info_3 (_("waiting for lock (held by %d%s) %s...\n"), /* Wait until lock has been released. We use increasing retry
pid, maybe_dead, maybe_deadlock(h)? _("(deadlock?) "):""); intervals of 50ms, 100ms, 200ms, 400ms, 800ms, 2s, 4s and 8s
but reset it if the lock owner meanwhile changed. */
if (!wtime || ownerchanged)
wtime = 50;
else if (wtime < 800)
wtime *= 2;
else if (wtime == 800)
wtime = 2000;
else if (wtime < 8000)
wtime *= 2;
/* We can't use sleep, cause signals may be blocked. */ if (timeout > 0)
tv.tv_sec = 1 + *backoff; {
tv.tv_usec = 0; if (wtime > timeout)
wtime = timeout;
timeout -= wtime;
}
sumtime += wtime;
if (sumtime >= 1500)
{
sumtime = 0;
my_info_3 (_("waiting for lock (held by %d%s) %s...\n"),
pid, maybe_dead, maybe_deadlock(h)? _("(deadlock?) "):"");
}
tv.tv_sec = wtime / 1000;
tv.tv_usec = (wtime % 1000) * 1000;
select (0, NULL, NULL, NULL, &tv); select (0, NULL, NULL, NULL, &tv);
if ( *backoff < 10 ) goto again;
++*backoff;
return 1; /* Try again. */
} }
jnlib_set_errno (EACCES); jnlib_set_errno (EACCES);
@ -931,14 +963,16 @@ dotlock_take_unix (dotlock_t h, long timeout, int *backoff)
#ifdef HAVE_DOSISH_SYSTEM #ifdef HAVE_DOSISH_SYSTEM
/* Windows specific code of make_dotlock. Returns 0 on success, -1 on /* Windows specific code of make_dotlock. Returns 0 on success and -1 on
error and 1 to try again. */ error. */
static int static int
dotlock_take_w32 (dotlock_t h, long timeout, int *backoff) dotlock_take_w32 (dotlock_t h, long timeout)
{ {
int wtime = 0;
int w32err; int w32err;
OVERLAPPED ovl; OVERLAPPED ovl;
again:
/* Lock one byte at offset 0. The offset is given by OVL. */ /* Lock one byte at offset 0. The offset is given by OVL. */
memset (&ovl, 0, sizeof ovl); memset (&ovl, 0, sizeof ovl);
if (LockFileEx (h->lockhd, (LOCKFILE_EXCLUSIVE_LOCK if (LockFileEx (h->lockhd, (LOCKFILE_EXCLUSIVE_LOCK
@ -956,14 +990,31 @@ dotlock_take_w32 (dotlock_t h, long timeout, int *backoff)
return -1; return -1;
} }
if ( timeout == -1 ) if (timeout)
{ {
/* Wait until lock has been released. */ /* Wait until lock has been released. We use retry intervals of
my_info_1 (_("waiting for lock %s...\n"), h->lockname); 50ms, 100ms, 200ms, 400ms, 800ms, 2s, 4s and 8s. */
Sleep ((1 + *backoff)*1000); if (!wtime)
if ( *backoff < 10 ) wtime = 50;
++*backoff; else if (wtime < 800)
return 1; /* Try again. */ wtime *= 2;
else if (wtime == 800)
wtime = 2000;
else if (wtime < 8000)
wtime *= 2;
if (timeout > 0)
{
if (wtime > timeout)
wtime = timeout;
timeout -= wtime;
}
if (wtime >= 800)
my_info_1 (_("waiting for lock %s...\n"), h->lockname);
Sleep (wtime);
goto again;
} }
return -1; return -1;
@ -973,12 +1024,10 @@ dotlock_take_w32 (dotlock_t h, long timeout, int *backoff)
/* Take a lock on H. A value of 0 for TIMEOUT returns immediately if /* Take a lock on H. A value of 0 for TIMEOUT returns immediately if
the lock can't be taked, -1 waits forever (hopefully not), other the lock can't be taked, -1 waits forever (hopefully not), other
values are reserved (planned to be timeouts in milliseconds). values wait for TIMEOUT milliseconds. Returns: 0 on success */
Returns: 0 on success */
int int
dotlock_take (dotlock_t h, long timeout) dotlock_take (dotlock_t h, long timeout)
{ {
int backoff = 0;
int ret; int ret;
if ( h->disable ) if ( h->disable )
@ -990,15 +1039,11 @@ dotlock_take (dotlock_t h, long timeout)
return 0; return 0;
} }
do
{
#ifdef HAVE_DOSISH_SYSTEM #ifdef HAVE_DOSISH_SYSTEM
ret = dotlock_take_w32 (h, timeout, &backoff); ret = dotlock_take_w32 (h, timeout);
#else /*!HAVE_DOSISH_SYSTEM*/ #else /*!HAVE_DOSISH_SYSTEM*/
ret = dotlock_take_unix (h, timeout, &backoff); ret = dotlock_take_unix (h, timeout);
#endif /*!HAVE_DOSISH_SYSTEM*/ #endif /*!HAVE_DOSISH_SYSTEM*/
}
while (ret == 1);
return ret; return ret;
} }