Re: notation for documenting multithread-safety

2019-12-01 Thread Bruno Haible
Here's the proposed macro.

The file glibc/manual/macros.texi contains documentation annotations for
MT-safety, along similar lines. I didn't use this nomenclature because it
is based on what the functions internally do; I prefer a nomenclature which
is about what the caller of the function can expect or must guarantee.


2019-12-01  Bruno Haible  

Add infrastructure for documenting multi-thread safety.
* m4/gnulib-common.m4 (gl_COMMON_BODY): Declare C macros _GL_MT_SAFE,
_GL_NOT_MT_SAFE.

diff --git a/m4/gnulib-common.m4 b/m4/gnulib-common.m4
index 479c9de..ca0d400 100644
--- a/m4/gnulib-common.m4
+++ b/m4/gnulib-common.m4
@@ -1,4 +1,4 @@
-# gnulib-common.m4 serial 45
+# gnulib-common.m4 serial 46
 dnl Copyright (C) 2007-2019 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
@@ -115,6 +115,48 @@ AC_DEFUN([gl_COMMON_BODY], [
errno.  */
 #define _GL_ASYNC_SAFE
 ])
+  AH_VERBATIM([multithread_safe],
+[/* A _GL_MT_SAFE macro invocation should be attached to functions that are
+multi-thread safe, possibly with specific constraints:
+
+  _GL_MT_SAFE( IF_DIFFERENT(argument...)
+   // means that it is MT-safe as long as different threads
+   // call the functions with different values for the
+   // specified argument(s), not the same one.
+
+   IF_MT_SAFE(argument...)
+   // means that it is MT-safe as if the argument, a function
+   // pointer, is multi-thread safe.
+
+   IF_LOCKED(global_variable...)
+   // means that it is MT-safe as long as all invocations
+   // happen with the specified global variable(s) locked:
+   // pthread_mutex_t instances - locked.
+   // pthread_rwlock_t instances - wrlocked.
+
+   IF_RDLOCKED(global_variable...)
+   // means that it is MT-safe as long as all invocations
+   // happen with the specified global variable(s) locked:
+   // pthread_rwlock_t instances - rdlocked.
+
+   IF_FIXED(global_variable...)
+   // means that it is MT-safe as long as the specified
+   // global variable(s), and the memory they point to
+   // (directly or indirectly), are guaranteed not to be
+   // modified.
+
+   IF_NOT_CALLED(global_function...)
+   // means that it is MT-safe as long as the specified
+   // global function(s) are guaranteed not to be called.
+ )
+
+  _GL_MT_SAFE() // means that it is MT-safe in the absolute sense.
+
+A _GL_NOT_MT_SAFE macro invocation should be attached to functions that
+are not multi-thread safe.  */
+#define _GL_MT_SAFE(...)
+#define _GL_NOT_MT_SAFE()
+])
   dnl Hint which direction to take regarding cross-compilation guesses:
   dnl When a user installs a program on a platform they are not intimately
   dnl familiar with, --enable-cross-guesses=conservative is the appropriate




Re: notation for documenting multithread-safety

2019-11-30 Thread Bruno Haible
Hi Ben,

> Details:
> https://clang.llvm.org/docs/ThreadSafetyAnalysis.html

Thank you very much for the pointer!

This convinces me to use a macro that may expand into __attribute__(...)
annotations at some point, rather than a comment.

There is also the 'race' annotation in the glibc manual, discussed in
https://sourceware.org/bugzilla/show_bug.cgi?id=25158

Regarding the POSIX functions, there's also a blog post by Simon:
https://blog.josefsson.org/2009/06/23/thread-safe-functions/

Bruno




Re: notation for documenting multithread-safety - clang's warnings

2019-11-30 Thread Bruno Haible
Ben Pfaff wrote:
> The compiler analyzes the code and reports violations of the
> annotations where possible.
> 
> Details:
> https://clang.llvm.org/docs/ThreadSafetyAnalysis.html
> 
> The Clang docs mostly talk about C++ but it also supports C.

Indeed, this is a very nice feature. Find attached a test case, where
the compiler warns about locking violations:


$ clang -O -Wthread-safety mt-warnings.c
mt-warnings.c:72:47: warning: reading variable 'account_balance' requires 
holding mutex 'account_lock' [-Wthread-safety-analysis]
  printf ("Final balance unlocked = %0.2f\n", account_balance);
  ^
1 warning generated.


To make this easier to use on glibc systems, a couple of annotations in glibc's
 would be needed. Reported as
  https://sourceware.org/bugzilla/show_bug.cgi?id=25238

Bruno
/*
 * Exercise multithread-safety warnings from clang 9.0
 * http://releases.llvm.org/9.0.0/tools/clang/docs/ThreadSafetyAnalysis.html
 */
#include 
#include 

typedef pthread_mutex_t mutex_t __attribute__((capability("mutex")));

extern int pthread_mutex_lock (mutex_t *arg) __attribute__((acquire_capability(*arg)));
extern int pthread_mutex_unlock (mutex_t *arg) __attribute__((release_capability(*arg)));


static mutex_t account_lock = PTHREAD_MUTEX_INITIALIZER;

static double account_balance __attribute__((guarded_by(account_lock))) = 0.0;

static void account_add (double delta) /*__attribute__((locks_excluded(account_lock)))*/
{
  pthread_mutex_lock (_lock);
  account_balance += delta;
  pthread_mutex_unlock (_lock);
}

static double get_balance (void) /*__attribute__((locks_excluded(account_lock))) */
{
  pthread_mutex_lock (_lock);
  double value = account_balance;
  pthread_mutex_unlock (_lock);
  return value;
}


static mutex_t purse_lock = PTHREAD_MUTEX_INITIALIZER;

static double purse_contents __attribute__((guarded_by(purse_lock))) = 0.0;

static void withdraw (double delta)
{
  pthread_mutex_lock (_lock);
  pthread_mutex_lock (_lock);
  account_balance -= delta;
  purse_contents += delta;
  pthread_mutex_unlock (_lock);
  pthread_mutex_unlock (_lock);
}

static void spend (double amount)
{
  pthread_mutex_lock (_lock);
  purse_contents -= amount;
  pthread_mutex_unlock (_lock);
}

static double peek_into_purse (void)
{
  pthread_mutex_lock (_lock);
  double value = purse_contents;
  pthread_mutex_unlock (_lock);
  return value;
}

int main ()
{
  account_add (37.02);
  withdraw (10.00);
  account_add (12.07);
  spend (1.99);

  printf ("Final balance = %0.2f\n", get_balance ());

  printf ("Final balance unlocked = %0.2f\n", account_balance);

  printf ("Purse contents = %0.2f\n", peek_into_purse ());
}

/*
 * Compile-command: ~/inst-clang/9.0.0/bin/clang -O -Wthread-safety mt-warnings.c
 */


Re: notation for documenting multithread-safety

2019-11-26 Thread Ben Pfaff
On Tue, Nov 26, 2019 at 9:59 AM Bruno Haible  wrote:
> Is anyone aware of a notation that allows to specify, unambiguously, under
> which calls to a C function are multithread-safe?
>
> I would like to start documenting the multithread-safety of the functions in
> gnulib and other libraries (libunistring, libgettextpo, ...).

The only thing I know of that is close to this is the thread-safety annotations
that Clang supports, in which one can mark a function as requiring a particular
mutex to be taken, or that a particular mutex must not be taken, or that the
function acquires the mutex and then returns holding it, and various other
helpful things. The compiler analyzes the code and reports violations of the
annotations where possible.

Details:
https://clang.llvm.org/docs/ThreadSafetyAnalysis.html

The Clang docs mostly talk about C++ but it also supports C.

The "sparse" code analyzer has something a little like this but it is weaker.

This only covers one of your cases, however.



notation for documenting multithread-safety

2019-11-26 Thread Bruno Haible
Hi all,

Is anyone aware of a notation that allows to specify, unambiguously, under
which calls to a C function are multithread-safe?

I would like to start documenting the multithread-safety of the functions in
gnulib and other libraries (libunistring, libgettextpo, ...).

A simple annotation
  /* mt-safe */
won't do it, because many functions are only mt-safe in specific conditions.

I'm thinking of a more elaborated annotation
  /* mt-safe [if_different(argument ...)]
 [if_fixed(global_variable ...)]
 [if_locked(global_variable ...)]
 [if_mt_safe(argument ...)]
 [if_not_called(function ...)] */

with the following examples:

1)
  /* mt-safe if_different(cd) */
  size_t iconv (iconv_t cd,
char **inbuf, size_t *inbytesleft,
char **outbuf, size_t *outbytesleft);

  means that different threads can call iconv() at the same time on
  different iconv_t descriptors, but not on the same one.

2)
  /* mt-safe if_fixed(program_name) */
  void error (int status, int errnum, const char *message, ...);

  means that different threads can call error(), as long as no thread
  modifies or frees the program_name variable.

3)
  /* mt-safe if_locked(gettext_lock) */
  char * some_gettext_internal_function (...);

  means that different threads can call the some_gettext_internal_function,
  if all take care to lock gettext_lock during the call.

4)
  /* mt-safe if_mt_safe(func) */
  int pthread_once (pthread_once_t *once_control, void (*func) (void));

  means that if func is mt-safe, the calls to pthread_once with this
  function as argument are mt-safe as well. (It's a bad example: In this case,
  func will typically have side effects. What we actually want to express
  is that pthread_once does not have more side effects than func has.)

5)
  /* mt-safe if_not_called(setlocale) */
  char *nl_langinfo (nl_item item);

  means that different threads can call nl_langinfo(), as long as no thread
  invokes setlocale.

But before I reinvent the wheel, maybe it has already been invented?

Bruno