mirror of
git://git.gnupg.org/gnupg.git
synced 2025-03-28 22:49:59 +01:00
Make dotlock.c thread-safe on pthread systems.
This is achieved by passing the define DOTLOCK_USE_PTHREAD.
This commit is contained in:
parent
ed8e267859
commit
bf3d5beb71
@ -1,3 +1,10 @@
|
|||||||
|
2011-09-29 Werner Koch <wk@g10code.com>
|
||||||
|
|
||||||
|
* dotlock.c (DOTLOCK_USE_PTHREAD): New macro.
|
||||||
|
[DOTLOCK_USE_PTHREAD] (all_lockfiles_mutex): New.
|
||||||
|
(LOCK_all_lockfiles, UNLOCK_all_lockfiles): New. Use them to
|
||||||
|
protect access to all_lockfiles.
|
||||||
|
|
||||||
2011-09-28 Werner Koch <wk@g10code.com>
|
2011-09-28 Werner Koch <wk@g10code.com>
|
||||||
|
|
||||||
* dotlock.c (dotlock_take, dotlock_take_unix, dotlock_take_w32):
|
* dotlock.c (dotlock_take, dotlock_take_unix, dotlock_take_w32):
|
||||||
|
@ -57,7 +57,8 @@
|
|||||||
|
|
||||||
This installs an atexit handler and may also initialize mutex etc.
|
This installs an atexit handler and may also initialize mutex etc.
|
||||||
It is optional for non-threaded applications. Only the first call
|
It is optional for non-threaded applications. Only the first call
|
||||||
has an effect.
|
has an effect. This needs to be done before any extra threads are
|
||||||
|
started.
|
||||||
|
|
||||||
To create a lock file (which prepares it but does not take the
|
To create a lock file (which prepares it but does not take the
|
||||||
lock) you do:
|
lock) you do:
|
||||||
@ -71,12 +72,14 @@
|
|||||||
It is important to handle the error. For example on a read-only
|
It is important to handle the error. For example on a read-only
|
||||||
file system a lock can't be created (but is usually not needed).
|
file system a lock can't be created (but is usually not needed).
|
||||||
FNAME is the file you want to lock; the actual lockfile is that
|
FNAME is the file you want to lock; the actual lockfile is that
|
||||||
name with the suffix ".lock" appended. This call creates a unique
|
name with the suffix ".lock" appended. On success a handle to be
|
||||||
file temporary file (".#lk*") in the same directory as FNAME and
|
used with the other functions is returned or NULL on error. Note
|
||||||
returns a handle for further operations. The module keeps track of
|
that the handle shall only be used by one thread at a time. This
|
||||||
theses unique files so that they will be unlinked using the atexit
|
function creates a unique file temporary file (".#lk*") in the same
|
||||||
handler. If you don't need the lock file anymore, you may also
|
directory as FNAME and returns a handle for further operations.
|
||||||
explicitly remove it with a call to:
|
The module keeps track of theses unique files so that they will be
|
||||||
|
unlinked using the atexit handler. If you don't need the lock file
|
||||||
|
anymore, you may also explicitly remove it with a call to:
|
||||||
|
|
||||||
dotlock_destroy (h);
|
dotlock_destroy (h);
|
||||||
|
|
||||||
@ -124,6 +127,8 @@
|
|||||||
to pass -DHAVE_CONFIG_H to the compiler. Macros used by this
|
to pass -DHAVE_CONFIG_H to the compiler. Macros used by this
|
||||||
module are:
|
module are:
|
||||||
|
|
||||||
|
DOTLOCK_USE_PTHREAD - Define if POSIX threads are in use.
|
||||||
|
|
||||||
DOTLOCK_GLIB_LOGGING - Define this to use Glib logging functions.
|
DOTLOCK_GLIB_LOGGING - Define this to use Glib logging functions.
|
||||||
|
|
||||||
GNUPG_MAJOR_VERSION - Defined when used by GnuPG.
|
GNUPG_MAJOR_VERSION - Defined when used by GnuPG.
|
||||||
@ -150,6 +155,12 @@
|
|||||||
it on the command line, remember to pass -D_FILE_OFFSET_BITS=64
|
it on the command line, remember to pass -D_FILE_OFFSET_BITS=64
|
||||||
|
|
||||||
|
|
||||||
|
Bugs:
|
||||||
|
=====
|
||||||
|
|
||||||
|
On Windows this module is not yet thread-safe.
|
||||||
|
|
||||||
|
|
||||||
Miscellaneous notes:
|
Miscellaneous notes:
|
||||||
====================
|
====================
|
||||||
|
|
||||||
@ -227,6 +238,9 @@
|
|||||||
#ifdef HAVE_SIGNAL_H
|
#ifdef HAVE_SIGNAL_H
|
||||||
# include <signal.h>
|
# include <signal.h>
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef DOTLOCK_USE_PTHREAD
|
||||||
|
# include <pthread.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef DOTLOCK_GLIB_LOGGING
|
#ifdef DOTLOCK_GLIB_LOGGING
|
||||||
# include <glib.h>
|
# include <glib.h>
|
||||||
@ -285,6 +299,7 @@
|
|||||||
# define my_error_1(a,b) log_error ((a), (b))
|
# define my_error_1(a,b) log_error ((a), (b))
|
||||||
# define my_error_2(a,b,c) log_error ((a), (b), (c))
|
# define my_error_2(a,b,c) log_error ((a), (b), (c))
|
||||||
# define my_debug_1(a,b) log_debug ((a), (b))
|
# define my_debug_1(a,b) log_debug ((a), (b))
|
||||||
|
# define my_fatal_0(a) log_fatal ((a))
|
||||||
#elif defined (DOTLOCK_GLIB_LOGGING)
|
#elif defined (DOTLOCK_GLIB_LOGGING)
|
||||||
# define my_info_0(a) g_message ((a))
|
# define my_info_0(a) g_message ((a))
|
||||||
# define my_info_1(a,b) g_message ((a), (b))
|
# define my_info_1(a,b) g_message ((a), (b))
|
||||||
@ -294,6 +309,7 @@
|
|||||||
# define my_error_1(a,b) g_warning ((a), (b))
|
# define my_error_1(a,b) g_warning ((a), (b))
|
||||||
# define my_error_2(a,b,c g_warning ((a), (b), (c))
|
# define my_error_2(a,b,c g_warning ((a), (b), (c))
|
||||||
# define my_debug_1(a,b) g_debug ((a), (b))
|
# define my_debug_1(a,b) g_debug ((a), (b))
|
||||||
|
# define my_fatal_0(a) g_error ((a))
|
||||||
#else
|
#else
|
||||||
# define my_info_0(a) fprintf (stderr, (a))
|
# define my_info_0(a) fprintf (stderr, (a))
|
||||||
# define my_info_1(a,b) fprintf (stderr, (a), (b))
|
# define my_info_1(a,b) fprintf (stderr, (a), (b))
|
||||||
@ -303,6 +319,8 @@
|
|||||||
# define my_error_1(a,b) fprintf (stderr, (a), (b))
|
# define my_error_1(a,b) fprintf (stderr, (a), (b))
|
||||||
# define my_error_2(a,b,c) fprintf (stderr, (a), (b), (c))
|
# define my_error_2(a,b,c) fprintf (stderr, (a), (b), (c))
|
||||||
# define my_debug_1(a,b) fprintf (stderr, (a), (b))
|
# define my_debug_1(a,b) fprintf (stderr, (a), (b))
|
||||||
|
# define my_fatal_0(a) do { fprintf (stderr,(a)); fflush (stderr); \
|
||||||
|
abort (); } while (0)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
@ -331,11 +349,27 @@ struct dotlock_handle
|
|||||||
/* A list of of all lock handles. The volatile attribute might help
|
/* A list of of all lock handles. The volatile attribute might help
|
||||||
if used in an atexit handler. */
|
if used in an atexit handler. */
|
||||||
static volatile dotlock_t all_lockfiles;
|
static volatile dotlock_t all_lockfiles;
|
||||||
|
#ifdef DOTLOCK_USE_PTHREAD
|
||||||
|
static pthread_mutex_t all_lockfiles_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
# define LOCK_all_lockfiles() do { \
|
||||||
|
if (pthread_mutex_lock (&all_lockfiles_mutex)) \
|
||||||
|
my_fatal_0 ("locking all_lockfiles_mutex failed\n"); \
|
||||||
|
} while (0)
|
||||||
|
# define UNLOCK_all_lockfiles() do { \
|
||||||
|
if (pthread_mutex_unlock (&all_lockfiles_mutex)) \
|
||||||
|
my_fatal_0 ("unlocking all_lockfiles_mutex failed\n"); \
|
||||||
|
} while (0)
|
||||||
|
#else /*!DOTLOCK_USE_PTHREAD*/
|
||||||
|
# define LOCK_all_lockfiles() do { } while (0)
|
||||||
|
# define UNLOCK_all_lockfiles() do { } while (0)
|
||||||
|
#endif /*!DOTLOCK_USE_PTHREAD*/
|
||||||
|
|
||||||
/* If this has the value true all locking is disabled. */
|
/* If this has the value true all locking is disabled. */
|
||||||
static int never_lock;
|
static int never_lock;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Entirely disable all locking. This function should be called
|
/* Entirely disable all locking. This function should be called
|
||||||
before any locking is done. It may be called right at startup of
|
before any locking is done. It may be called right at startup of
|
||||||
@ -352,13 +386,19 @@ static int
|
|||||||
maybe_deadlock (dotlock_t h)
|
maybe_deadlock (dotlock_t h)
|
||||||
{
|
{
|
||||||
dotlock_t r;
|
dotlock_t r;
|
||||||
|
int res = 0;
|
||||||
|
|
||||||
for ( r=all_lockfiles; r; r = r->next )
|
LOCK_all_lockfiles ();
|
||||||
|
for (r=all_lockfiles; r; r = r->next)
|
||||||
{
|
{
|
||||||
if ( r != h && r->locked )
|
if ( r != h && r->locked )
|
||||||
return 1;
|
{
|
||||||
|
res = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
UNLOCK_all_lockfiles ();
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
#endif /*HAVE_POSIX_SYSTEM*/
|
#endif /*HAVE_POSIX_SYSTEM*/
|
||||||
|
|
||||||
@ -528,9 +568,7 @@ dotlock_create_unix (dotlock_t h, const char *file_to_lock)
|
|||||||
dirpart = file_to_lock;
|
dirpart = file_to_lock;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef _REENTRANT
|
LOCK_all_lockfiles ();
|
||||||
/* fixme: aquire mutex on all_lockfiles */
|
|
||||||
#endif
|
|
||||||
h->next = all_lockfiles;
|
h->next = all_lockfiles;
|
||||||
all_lockfiles = h;
|
all_lockfiles = h;
|
||||||
|
|
||||||
@ -539,6 +577,7 @@ dotlock_create_unix (dotlock_t h, const char *file_to_lock)
|
|||||||
if (!h->tname)
|
if (!h->tname)
|
||||||
{
|
{
|
||||||
all_lockfiles = h->next;
|
all_lockfiles = h->next;
|
||||||
|
UNLOCK_all_lockfiles ();
|
||||||
jnlib_free (h);
|
jnlib_free (h);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@ -560,6 +599,7 @@ dotlock_create_unix (dotlock_t h, const char *file_to_lock)
|
|||||||
if ( fd == -1 )
|
if ( fd == -1 )
|
||||||
{
|
{
|
||||||
all_lockfiles = h->next;
|
all_lockfiles = h->next;
|
||||||
|
UNLOCK_all_lockfiles ();
|
||||||
my_error_2 (_("failed to create temporary file `%s': %s\n"),
|
my_error_2 (_("failed to create temporary file `%s': %s\n"),
|
||||||
h->tname, strerror(errno));
|
h->tname, strerror(errno));
|
||||||
jnlib_free (h->tname);
|
jnlib_free (h->tname);
|
||||||
@ -590,19 +630,18 @@ dotlock_create_unix (dotlock_t h, const char *file_to_lock)
|
|||||||
goto write_failed;
|
goto write_failed;
|
||||||
}
|
}
|
||||||
|
|
||||||
# ifdef _REENTRANT
|
|
||||||
/* release mutex */
|
|
||||||
# endif
|
|
||||||
h->lockname = jnlib_malloc (strlen (file_to_lock) + 6 );
|
h->lockname = jnlib_malloc (strlen (file_to_lock) + 6 );
|
||||||
if (!h->lockname)
|
if (!h->lockname)
|
||||||
{
|
{
|
||||||
all_lockfiles = h->next;
|
all_lockfiles = h->next;
|
||||||
|
UNLOCK_all_lockfiles ();
|
||||||
unlink (h->tname);
|
unlink (h->tname);
|
||||||
jnlib_free (h->tname);
|
jnlib_free (h->tname);
|
||||||
jnlib_free (h);
|
jnlib_free (h);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
strcpy (stpcpy (h->lockname, file_to_lock), EXTSEP_S "lock");
|
strcpy (stpcpy (h->lockname, file_to_lock), EXTSEP_S "lock");
|
||||||
|
UNLOCK_all_lockfiles ();
|
||||||
if (h->use_o_excl)
|
if (h->use_o_excl)
|
||||||
my_debug_1 ("locking for `%s' done via O_EXCL\n", h->lockname);
|
my_debug_1 ("locking for `%s' done via O_EXCL\n", h->lockname);
|
||||||
|
|
||||||
@ -610,9 +649,7 @@ dotlock_create_unix (dotlock_t h, const char *file_to_lock)
|
|||||||
|
|
||||||
write_failed:
|
write_failed:
|
||||||
all_lockfiles = h->next;
|
all_lockfiles = h->next;
|
||||||
# ifdef _REENTRANT
|
UNLOCK_all_lockfiles ();
|
||||||
/* fixme: release mutex */
|
|
||||||
# endif
|
|
||||||
my_error_2 (_("error writing to `%s': %s\n"), h->tname, strerror (errno));
|
my_error_2 (_("error writing to `%s': %s\n"), h->tname, strerror (errno));
|
||||||
close (fd);
|
close (fd);
|
||||||
unlink (h->tname);
|
unlink (h->tname);
|
||||||
@ -632,6 +669,7 @@ dotlock_create_unix (dotlock_t h, const char *file_to_lock)
|
|||||||
static dotlock_t
|
static dotlock_t
|
||||||
dotlock_create_w32 (dotlock_t h, const char *file_to_lock)
|
dotlock_create_w32 (dotlock_t h, const char *file_to_lock)
|
||||||
{
|
{
|
||||||
|
LOCK_all_lockfiles ();
|
||||||
h->next = all_lockfiles;
|
h->next = all_lockfiles;
|
||||||
all_lockfiles = h;
|
all_lockfiles = h;
|
||||||
|
|
||||||
@ -639,6 +677,7 @@ dotlock_create_w32 (dotlock_t h, const char *file_to_lock)
|
|||||||
if (!h->lockname)
|
if (!h->lockname)
|
||||||
{
|
{
|
||||||
all_lockfiles = h->next;
|
all_lockfiles = h->next;
|
||||||
|
UNLOCK_all_lockfiles ();
|
||||||
jnlib_free (h);
|
jnlib_free (h);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@ -673,8 +712,9 @@ dotlock_create_w32 (dotlock_t h, const char *file_to_lock)
|
|||||||
}
|
}
|
||||||
if (h->lockhd == INVALID_HANDLE_VALUE)
|
if (h->lockhd == INVALID_HANDLE_VALUE)
|
||||||
{
|
{
|
||||||
my_error_2 (_("can't create `%s': %s\n"), h->lockname, w32_strerror (-1));
|
|
||||||
all_lockfiles = h->next;
|
all_lockfiles = h->next;
|
||||||
|
UNLOCK_all_lockfiles ();
|
||||||
|
my_error_2 (_("can't create `%s': %s\n"), h->lockname, w32_strerror (-1));
|
||||||
jnlib_free (h->lockname);
|
jnlib_free (h->lockname);
|
||||||
jnlib_free (h);
|
jnlib_free (h);
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -733,11 +773,10 @@ dotlock_create (const char *file_to_lock, unsigned int flags)
|
|||||||
if (never_lock)
|
if (never_lock)
|
||||||
{
|
{
|
||||||
h->disable = 1;
|
h->disable = 1;
|
||||||
#ifdef _REENTRANT
|
LOCK_all_lockfiles ();
|
||||||
/* fixme: aquire mutex on all_lockfiles */
|
|
||||||
#endif
|
|
||||||
h->next = all_lockfiles;
|
h->next = all_lockfiles;
|
||||||
all_lockfiles = h;
|
all_lockfiles = h;
|
||||||
|
UNLOCK_all_lockfiles ();
|
||||||
return h;
|
return h;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -791,6 +830,7 @@ dotlock_destroy (dotlock_t h)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
/* First remove the handle from our global list of all locks. */
|
/* First remove the handle from our global list of all locks. */
|
||||||
|
LOCK_all_lockfiles ();
|
||||||
for (hprev=NULL, htmp=all_lockfiles; htmp; hprev=htmp, htmp=htmp->next)
|
for (hprev=NULL, htmp=all_lockfiles; htmp; hprev=htmp, htmp=htmp->next)
|
||||||
if (htmp == h)
|
if (htmp == h)
|
||||||
{
|
{
|
||||||
@ -801,6 +841,7 @@ dotlock_destroy (dotlock_t h)
|
|||||||
h->next = NULL;
|
h->next = NULL;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
UNLOCK_all_lockfiles ();
|
||||||
|
|
||||||
/* Then destroy the lock. */
|
/* Then destroy the lock. */
|
||||||
if (!h->disable)
|
if (!h->disable)
|
||||||
@ -1123,7 +1164,10 @@ dotlock_release (dotlock_t h)
|
|||||||
any locks left. It might happen that another atexit handler
|
any locks left. It might happen that another atexit handler
|
||||||
tries to release the lock while the atexit handler of this module
|
tries to release the lock while the atexit handler of this module
|
||||||
already ran and thus H is undefined. */
|
already ran and thus H is undefined. */
|
||||||
if (!all_lockfiles)
|
LOCK_all_lockfiles ();
|
||||||
|
ret = !all_lockfiles;
|
||||||
|
UNLOCK_all_lockfiles ();
|
||||||
|
if (ret)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if ( h->disable )
|
if ( h->disable )
|
||||||
@ -1148,7 +1192,7 @@ dotlock_release (dotlock_t h)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Remove all lockfiles. This is usually called by the atexit handler
|
/* Remove all lockfiles. This is called by the atexit handler
|
||||||
installed by this module but may also be called by other
|
installed by this module but may also be called by other
|
||||||
termination handlers. */
|
termination handlers. */
|
||||||
void
|
void
|
||||||
@ -1156,8 +1200,13 @@ dotlock_remove_lockfiles (void)
|
|||||||
{
|
{
|
||||||
dotlock_t h, h2;
|
dotlock_t h, h2;
|
||||||
|
|
||||||
|
/* First set the lockfiles list to NULL so that for example
|
||||||
|
dotlock_release is ware that this fucntion is currently
|
||||||
|
running. */
|
||||||
|
LOCK_all_lockfiles ();
|
||||||
h = all_lockfiles;
|
h = all_lockfiles;
|
||||||
all_lockfiles = NULL;
|
all_lockfiles = NULL;
|
||||||
|
UNLOCK_all_lockfiles ();
|
||||||
|
|
||||||
while ( h )
|
while ( h )
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user