Welcome to mirror list, hosted at ThFree Co, Russian Federation.

cygwin.com/git/newlib-cygwin.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCorinna Vinschen <corinna@vinschen.de>2012-05-23 20:26:34 +0400
committerCorinna Vinschen <corinna@vinschen.de>2012-05-23 20:26:34 +0400
commitbff08077a64cc2894fe7d528a848d81cc00950a0 (patch)
tree638786018ba2f4839dd0ba1eb37e670a7b95069d /winsup/cygwin/thread.cc
parent86b35406f22202a3c382ca06551e5c0fa3a0e628 (diff)
* thread.cc: Add a temporary workaround to help Cygwin along while
newlib doesn't install cleanup handlers. Explain the problem. (class __cygwin_lock_handler): New class. (__cygwin_lock_cleanup): New function. (__cygwin_lock_lock): Push __cygwin_lock_cleanup thread cleanup handler. (__cygwin_lock_trylock): Ditto. (__cygwin_lock_unlock): Pop thread cleanup handler. (pthread::pop_cleanup_handler): Temporarily allow cleanup function to destroy cleanup handler so we can pop in another function than we pushed in.
Diffstat (limited to 'winsup/cygwin/thread.cc')
-rw-r--r--winsup/cygwin/thread.cc76
1 files changed, 75 insertions, 1 deletions
diff --git a/winsup/cygwin/thread.cc b/winsup/cygwin/thread.cc
index e83666bc9..b0da0523b 100644
--- a/winsup/cygwin/thread.cc
+++ b/winsup/cygwin/thread.cc
@@ -95,24 +95,86 @@ __cygwin_lock_fini (_LOCK_T *lock)
pthread_mutex_destroy ((pthread_mutex_t*) lock);
}
+#define WORKAROUND_NEWLIB
+
+#ifdef WORKAROUND_NEWLIB
+/* FIXME:
+
+ This cleanup stuff is necessary to harden Cygwin against thread
+ cancellation. In theory, installing a cleanup handler is the task of
+ the calling function.
+
+ The problem here is that a lot of calling functions are in newlib's
+ stdio implementation. So, the right thing to do would be to change
+ newlib's stdio functions to install the required pthread cleanup
+ handlers.
+
+ This is a bigger task than it sounds, so, as a temporary workaround,
+ what we do here is to install a cleanup handler in the lock function
+ itself and to cleanup in the unlock function. This works around the
+ problem for the time being. */
+class __cygwin_lock_handler : public __pthread_cleanup_handler
+{
+public:
+ pthread_mutex_t *lock;
+
+ __cygwin_lock_handler (__cleanup_routine_type _fn, _LOCK_T *_lock)
+ {
+ function = _fn;
+ arg = this;
+ next = NULL;
+ lock = (pthread_mutex_t *) _lock;
+ }
+ void *operator new (size_t) __attribute__ ((nothrow))
+ {return cmalloc (HEAP_BUF, sizeof (__cygwin_lock_handler));}
+ void operator delete (void *p) { cfree (p); }
+};
+
+static void
+__cygwin_lock_cleanup (void *hdl)
+{
+ __cygwin_lock_handler *cleanup = (__cygwin_lock_handler *) hdl;
+ pthread_mutex_unlock (cleanup->lock);
+ delete cleanup;
+}
+#endif /* WORKAROUND_NEWLIB */
+
extern "C" void
__cygwin_lock_lock (_LOCK_T *lock)
{
paranoid_printf ("threadcount %d. locking", MT_INTERFACE->threadcount);
+#ifdef WORKAROUND_NEWLIB
+ __cygwin_lock_handler *cleanup
+ = new __cygwin_lock_handler (__cygwin_lock_cleanup, lock);
+ pthread::self ()->push_cleanup_handler (cleanup);
+#endif /* WORKAROUND_NEWLIB */
pthread_mutex_lock ((pthread_mutex_t*) lock);
}
extern "C" int
__cygwin_lock_trylock (_LOCK_T *lock)
{
+#ifdef WORKAROUND_NEWLIB
+ __cygwin_lock_handler *cleanup
+ = new __cygwin_lock_handler (__cygwin_lock_cleanup, lock);
+ pthread::self ()->push_cleanup_handler (cleanup);
+ int ret = pthread_mutex_trylock ((pthread_mutex_t*) lock);
+ if (ret)
+ pthread::self ()->pop_cleanup_handler (0);
+ return ret;
+#else
return pthread_mutex_trylock ((pthread_mutex_t*) lock);
+#endif /* WORKAROUND_NEWLIB */
}
-
extern "C" void
__cygwin_lock_unlock (_LOCK_T *lock)
{
+#ifdef WORKAROUND_NEWLIB
+ pthread::self ()->pop_cleanup_handler (1);
+#else
pthread_mutex_unlock ((pthread_mutex_t*) lock);
+#endif /* WORKAROUND_NEWLIB */
paranoid_printf ("threadcount %d. unlocked", MT_INTERFACE->threadcount);
}
@@ -1101,10 +1163,22 @@ pthread::pop_cleanup_handler (int const execute)
if (cleanup_stack != NULL)
{
__pthread_cleanup_handler *handler = cleanup_stack;
+#ifdef WORKAROUND_NEWLIB
+ /* We split out handler->next so we can set cleanup_stack to handler->next
+ without relying on handler still existing. This allows to delete the
+ handler in the handler function. For a description why we need that,
+ at least temporarly, see the comment preceeding the definition of
+ __cygwin_lock_handler earlier in this file. */
+ __pthread_cleanup_handler *next = handler->next;
if (execute)
(*handler->function) (handler->arg);
+ cleanup_stack = next;
+#else
+ if (execute)
+ (*handler->function) (handler->arg);
cleanup_stack = handler->next;
+#endif /* WORKAROUND_NEWLIB */
}
mutex.unlock ();