* 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.
This commit is contained in:
Corinna Vinschen 2012-05-23 16:26:34 +00:00
parent 86b35406f2
commit bff08077a6
2 changed files with 89 additions and 1 deletions

View File

@ -1,3 +1,17 @@
2012-05-23 Corinna Vinschen <corinna@vinschen.de>
* 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.
2012-05-23 Corinna Vinschen <corinna@vinschen.de>
* thread.cc (pthread::cancel): Only allow asynchronous cancellation

View File

@ -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 ();