Bug 1195 - behavior of pthread_cleanup_pop does not behave as described
Summary: behavior of pthread_cleanup_pop does not behave as described
Status: NEW
Alias: None
Product: uClibc
Classification: Unclassified
Component: Threads (show other bugs)
Version: 0.9.31
Hardware: Other Other
: P5 normal
Target Milestone: 0.9.32
Assignee: unassigned
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2010-02-27 03:03 UTC by Mark Gondree
Modified: 2010-03-31 15:30 UTC (History)
2 users (show)

See Also:
Host:
Target:
Build:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Mark Gondree 2010-02-27 03:03:28 UTC
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);
    ....
}