Friday, 9 November 2018

pthread mutux deadlock for cancel handling




Program:

// C program to demonstrates cancellation of self thread
// using thread id
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <string.h>  // For memset
static pthread_mutex_t cfglock = PTHREAD_MUTEX_INITIALIZER;
int is_thread_cancel = 0, is_cleanup_push = 0;
void line_80(char ch)
{
    char str[81] = {'\0'};
    memset(str, ch, sizeof(str));
    str[80] = '\0';
    printf("%s\n", str);
    return;
}
void *calls(void* ptr)
{
    int oldtype = 0;
    printf("\nThread: Entrying into the thread %s \n", __func__);
    line_80('-');

    /* If 2 argument try then do the clean up */
    /* Add setcanceltype & cleanup into the if loop throws below error:
           error: expected ‘while’ before ‘printf’
       Reason is pthread_cleanup_push is not a function, it is a macro it cotain the below.
       So it add the d0 { on start and pthread_cleanup_pop macro contain ending while, so we could not put inside the if condition
             if (is_cleanup_push) {
     pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, &oldtype);
     do { __pthread_unwind_buf_t __cancel_buf; void (*__cancel_routine) (void *) = (pthread_mutex_unlock); void *__cancel_arg = ((void *) &cfglock); int __not_first_call = __sigsetjmp ((struct __jmp_buf_tag *) (void *) __cancel_buf.__cancel_jmp_buf, 0); if (__builtin_expect ((__not_first_call), 0)) { __cancel_routine (__cancel_arg); __pthread_unwind_next (&__cancel_buf); } __pthread_register_cancel (&__cancel_buf); do {;
        }
        pthread_cleanup_pop macro definition is:
        do { } while (0); } while (0); __pthread_unregister_cancel (&__cancel_buf); if (0) __cancel_routine (__cancel_arg); } while (0);
         pthread_setcanceltype(oldtype, ((void *)0));

      */
 //  if (is_cleanup_push) {
        pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, &oldtype);
        pthread_cleanup_push(pthread_mutex_unlock, (void *) &cfglock);
  //  }
    printf("Thread: Going to accquire the lock\n");
    pthread_mutex_lock(&cfglock);
    printf("Thread: Lock is accquired \n");
    printf("Thread: Going to sleep 10 second \n");
    sleep(7);
    printf("Thread: After the sleep of 10 second\n");
    /* If 2 argument try then do the clean up */
  //  if (is_cleanup_push) {
        pthread_mutex_unlock(&cfglock);
        pthread_cleanup_pop(0);
        pthread_setcanceltype(oldtype, NULL);
    //}
    printf("\nThread: Returning from thread %s \n", __func__);
    line_80('-');
    return NULL;
}

int main(int argc, char *argv[])
{
    // NULL when no attribute
    pthread_t thread = 0;
    int ret = 0;
    void *thread_ret = NULL;
    // calls is a function name
    pthread_create(&thread, NULL, calls, NULL);
    /* Argument and his valuel
          Arg 1. if 0 then no cancel, if 1 then it will call pthread_cancel.
                     Scenario: if 0 then, no cancel, thread in lock, process also try to lock, Dead lock
                     scenario 2: if 1 then , cancel kill the thread but lock is not cleaned, so proacess try to lock, hence Dead lock.
          Arg  2. if 0 then no cleanup_push for lock,
                  if 1 then do cleanup_push for lock

      */
    printf("Process: going to sleep for 3  \n");
    sleep(3);
    printf("\t\tProcess: After sleep of 3 second  \n");
    is_thread_cancel = atoi(argv[1]);
    is_cleanup_push = atoi(argv[2]);
    if (is_thread_cancel == 1) {
        /* cancelled threads do not unlock mutexes they hold */
        printf("Process: called the pthread_cancel \n");
        ret = pthread_cancel(thread);
        if (ret == 0) {
            printf("Process: Canceled the thread with ID <%ld> \n", thread);
        } else {
            printf("Process: Error in cancel while try to cancel the thread <%ld>, ret = %d \n",
                    thread, ret);
        }
    }
  //  printf("After cancel \n");

    /* Already taken the lock in thread, so this lock will be blocked */
    printf("Process: Going to acquire the Mutux  \n");
    pthread_mutex_lock(&cfglock);
    printf("\t\tProcess: Lock is acquired \n");
    pthread_mutex_unlock(&cfglock);
    printf("\t\tProcess: Mutux lock is released \n");
    printf("Process: going to sleep for 15  \n");
    sleep(10);
    printf("\t\tProcess: After sleep of 15 second  \n");
    /* The pthread_join() function waits for the thread specified by thread to terminate.
     * If that thread has already terminated, then pthread_join() returns immediately.
     * If the target thread was canceled, then PTHREAD_CANCELED is placed in *retval.
     * As per out PTHREAD_CANCELED value is 0xfffff
       */
    // Waiting for when thread is completed
//    ret = pthread_join(thread, NULL);  // Just  wati
    ret = pthread_join(thread, &thread_ret);  // Just  wati
    if (ret !=0) {
        printf("Process: Error in pthread_join errno = %d \n", ret);
    }
    printf("Process: The return value from thread is = %p \n", thread_ret);
    if (thread_ret == PTHREAD_CANCELED) {
        printf("Process: The thread is already canceled, value of PTHREAD_CANCELED  <%p>\n", PTHREAD_CANCELED);
    }
    return 0;
}

/* Ref:
        For Dedlock release lock, Solution:
                https://stackoverflow.com/questions/14268080/cancelling-a-thread-that-has-a-mutex-locked-does-not-unlock-the-mutex
       Solution method 2: For pthread_mutexattr_setrobust to releace lock:
               http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_mutexattr_setrobust.html
               https://en.wikipedia.org/wiki/Resource_acquisition_is_initialization
        Solution Method 1:
               https://sector7.xray.aps.anl.gov/~dohnarms/programming/glibc/html/Cleanup-Handlers.html
               https://linux.die.net/man/3/pthread_cleanup_push
*/

No comments:

Post a Comment