| Summary: | behavior of pthread_cleanup_pop does not behave as described | ||
|---|---|---|---|
| Product: | uClibc | Reporter: | Mark Gondree <gondree> |
| Component: | Threads | Assignee: | unassigned |
| Status: | NEW --- | ||
| Severity: | normal | CC: | gondree, uclibc-cvs |
| Priority: | P5 | ||
| Version: | 0.9.31 | ||
| Target Milestone: | 0.9.32 | ||
| Hardware: | Other | ||
| OS: | Other | ||
| Host: | Target: | ||
| Build: | |||
According to its description, pthread_cleanup_pop: "shall remove the routine at the top of the calling thread's cancellation cleanup stack and optionally invoke it (if execute is non-zero)." However, this implementation executes and then removes it from the cleanup stack (order is reversed). This alternate behavior has the potential to cause some code to deadlock. Example is below. If you agree this is problematic, the solution is quite simple: in, libpthread/linuxthread/cancel.c void _pthread_cleanup_pop(struct _pthread_cleanup_buffer * buffer, int execute) { pthread_descr self = thread_self(); if (execute) buffer->__routine(buffer->__arg); THREAD_SETMEM(self, p_cleanup, buffer->__prev); } s/b THREAD_SETMEM(self, p_cleanup, buffer->__prev); if (execute) buffer->__routine(buffer->__arg); Similar changes may be required in _pthread_cleanup_pop_restore ======= Notional example of deadlocking code: If a thread is cancelled at point A, cancellation-handler ch1() is executed for a second time (this time with the mutex m acquired) which causes a deadlock. void foo(void) { ... pthread_mutex_lock(&m); pthread_cleanup_push(&ch2, NULL); ... while(condition) { .... pthread_mutex_unlock(&m); pthread_cleanup_push(&ch1, NULL); .... pthread_cleanup_pop(1); } pthread_cleanup_pop(1); } void *ch1(void *p) { pthread_mutex_lock(&m); .... (A) .... } void *ch2(void *p) { pthread_mutex_unlock(&m); .... }