While looking into some mysterious failures of sem_init() in python, I was
somewhat surprised to find the following comment in python/thread_pthread.h:

> /*
>  * As of February 2002, Cygwin thread implementations mistakenly report error
>  * codes in the return value of the sem_ calls (like the pthread_ functions).
>  * Correct implementations return -1 and put the code in errno. This supports
>  * either.
>  */

While this comment refers to sem_wait() and sem_trywait(), which seem to have
been fixed since [1], it seems that sem_init(), sem_destroy() and sem_close()
are still non-conformant with SUS in that (i) they do not set errno, and (ii)
they don't return -1 on failure, instead returning the value which should be
set as errno.

I'm not sure if (ii) makes much practical difference, as portable code should
arguably be comparing the result against 0, although this is not helped by the
fact that (as the linux man page puts it) "Bizarrely, POSIX.1-2001 does not
specify the value that should be returned by a successful call to sem_init().
 POSIX.1-2008 rectifies this, specifying the zero return on success."

(i) causes the reason for a sem_init() failure to be incorrectly reported as
whatever the previous value of errno is, more on which anon.

Attached is a patch which fixes this conformance issue with these functions.

I did wonder if there were internal uses in the cygwin DLL of these functions
which might be affected by this change. In a quick audit, the only point of
concern I found was semaphore::_terminate(). semaphore::_terminate() is only
used by semaphore::terminate() and does not propagate the result, but now may
be setting errno.  I guess that semaphore::terminate() will now normally be
setting errno, as it attempts to apply semaphore::_terminate() to all
semaphores, not just shared ones.

semaphore::terminate() is only called from close_all_files().  Having this
change errno when used from do_exit() seems safe as we are past the point
where errno might be of significance, but the other uses of close_all_files()
are a bit more mysterious to me.

In what is probably an excess of caution, I've added a save_errno to
semaphore::terminate(), but I could use some help in determining if that is
actually needed.

2011-03-28  Jon TURNEY  <jon.tur...@dronecode.org.uk>

        * thread.cc (semaphore::init, destroy, close): Standards conformance
        fix.  On a failure, return -1 and set errno.
        * thread.h (semaphore::terminate): Save errno since semaphore::close()
        may now modify it.

[1] http://cygwin.com/ml/cygwin/2002-02/msg01379.html
Index: cygwin/thread.cc
===================================================================
RCS file: /cvs/src/src/winsup/cygwin/thread.cc,v
retrieving revision 1.225
diff -u -p -r1.225 thread.cc
--- cygwin/thread.cc    6 Apr 2010 15:27:34 -0000       1.225
+++ cygwin/thread.cc    28 Mar 2011 21:46:56 -0000
@@ -3076,10 +3076,16 @@ semaphore::init (sem_t *sem, int pshared
 {
   /* opengroup calls this undefined */
   if (is_good_object (sem))
-    return EBUSY;
+    {
+      set_errno(EBUSY);
+      return -1;
+    }
 
   if (value > SEM_VALUE_MAX)
-    return EINVAL;
+    {
+      set_errno(EINVAL);
+      return -1;
+    }
 
   *sem = new semaphore (pshared, value);
 
@@ -3087,7 +3093,8 @@ semaphore::init (sem_t *sem, int pshared
     {
       delete (*sem);
       *sem = NULL;
-      return EAGAIN;
+      set_errno(EAGAIN);
+      return -1;
     }
   return 0;
 }
@@ -3096,11 +3103,17 @@ int
 semaphore::destroy (sem_t *sem)
 {
   if (!is_good_object (sem))
-    return EINVAL;
+    {
+      set_errno(EINVAL);
+      return -1;
+    }
 
   /* It's invalid to destroy a semaphore not opened with sem_init. */
   if ((*sem)->fd != -1)
-    return EINVAL;
+    {
+      set_errno(EINVAL);
+      return -1;
+    }
 
   /* FIXME - new feature - test for busy against threads... */
 
@@ -3113,11 +3126,17 @@ int
 semaphore::close (sem_t *sem)
 {
   if (!is_good_object (sem))
-    return EINVAL;
+    {
+      set_errno(EINVAL);
+      return -1;
+    }
 
   /* It's invalid to close a semaphore not opened with sem_open. */
   if ((*sem)->fd == -1)
-    return EINVAL;
+    {
+      set_errno(EINVAL);
+      return -1;
+    }
 
   delete (*sem);
   delete sem;
Index: cygwin/thread.h
===================================================================
RCS file: /cvs/src/src/winsup/cygwin/thread.h,v
retrieving revision 1.114
diff -u -p -r1.114 thread.h
--- cygwin/thread.h     22 Feb 2010 20:36:04 -0000      1.114
+++ cygwin/thread.h     28 Mar 2011 21:46:56 -0000
@@ -21,6 +21,7 @@ details. */
 #include <limits.h>
 #include "security.h"
 #include <errno.h>
+#include "cygerrno.h"
 
 enum cw_sig_wait
 {
@@ -641,6 +642,7 @@ public:
   }
   static void terminate ()
   {
+    save_errno save;
     semaphores.for_each (&semaphore::_terminate);
   }
 

Reply via email to