Chinaunix首页 | 论坛 | 博客
  • 博客访问: 800768
  • 博文数量: 247
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 501
  • 用 户 组: 普通用户
  • 注册时间: 2013-07-12 21:53
个人简介

系统未建立

文章分类

全部博文(247)

文章存档

2021年(1)

2020年(3)

2019年(5)

2018年(3)

2017年(44)

2016年(75)

2015年(52)

2014年(63)

2013年(1)

我的朋友

分类: LINUX

2015-09-16 12:48:09

原文地址:pthread_once 作者:flw2

 pthread_once - once-only initialization


pthread_once_t once_control = 0;

int pthread_once(pthread_once_t *once_control, void (*init_routine) (void));


pthread_once函数在用于仅仅调用一次初始化
它的代码比想象中的复杂一些
本来直接用一个原子的test and set bit操作就可以防止执行多次,但是pthread_once
的一个重要功能就是让其它线程睡眠等待而不是直接不等待init_routine执行完就继续
还要考虑正在执行的时候被fork或者被cancel的问题


  1. .globl    __pthread_once
  2.     .type    __pthread_once,@function
  3.     .align    16
  4. __pthread_once:
  5.     movl    4(%esp), %ecx
  6.    
  7.   只有执行过init_routine之后bit 1才会被设置
  8.     testl    $2, (%ecx)
  9.     jz    1f
  10.     xorl    %eax, %eax
  11.     ret

  12. 1:    pushl    %ebx
  13.     pushl    %esi
  14.     movl    %ecx, %ebx
  15.     xorl    %esi, %esi

  16.     /* Not yet initialized or initialization in progress.
  17.    Get the fork generation counter now. */

  18. 6:    movl    (%ebx), %eax
  19. 5:    movl    %eax, %edx
  20.             

  21.     testl    $2, %eax
  22.     jnz    4f
  23.    
  24.   __fork_generation在fork的时候会被 +4
  25.     andl    $3, %edx
  26.     orl    __fork_generation, %edx
  27.     orl    $1, %edx
  28.   原子地设置并交换bit 0,该位表示有线程正在执行
  29.     LOCK
  30.     cmpxchgl %edx, (%ebx)
  31.     jnz    5b
  32.    
  33.    
  34.     /* Check whether another thread already runs the initializer. */
  35.     testl    $1, %eax
  36.     jz    3f    /* No -> do it.  *
  37.  

  38.     fork的问题, 如果正在执行init_routine的时候fork, 其子进程
  39.     调用pthread_once就会到这,这样会执行init_routine,下面那个例子可以看出来
  40.    
  41.     xorl    %edx, %eax
  42.     testl    $0xfffffffc, %eax
  43.     jnz    3f    /* Different for generation -> run initializer. */
  44.  
  45.     /* Somebody else got here first. Wait. */


  1.     等待别的线程执行init_routine然后唤醒
  2.     movl    $FUTEX_WAIT|FUTEX_PRIVATE_FLAG, %ecx
  3.     movl    $SYS_futex, %eax
  4.     ENTER_KERNEL
    
   
 
     之所以要重新检查,是因为init_routine在执行的时候
   它可能被杀死

  1.     jmp    6b

  2. 3:   
  3.     subl    $UNWINDBUFSIZE+8, %esp
  4.     movl    %ecx, %ebx        /* PIC register value. */
  5.      
  6.     set
  7.     leal    8+UWJMPBUF(%esp), %eax
  8.     movl    $0, 4(%esp)
  9.     movl    %eax, (%esp)
  10.     call    __sigsetjmp@PLT
  11.     testl    %eax, %eax
  12.     jne    7f

  13.   setjmp == 0,注册cancel jmpbuf
  14.   这样,线程在退出的时候会到这里来,上面那条
  15.   jne 7f会处理这种情况

  16.     leal    8(%esp), %eax
  17.     call    HIDDEN_JUMPTARGET(__pthread_register_cancel)

  18.     /* Call the user-provided initialization function. */
  19.     call    *24+UNWINDBUFSIZE(%esp)

  20.     /* Pop the cleanup handler. */
  21.     leal    8(%esp), %eax
  22.     call    HIDDEN_JUMPTARGET(__pthread_unregister_cancel)
  23.     addl    $UNWINDBUFSIZE+8, %esp

  24.     /* Sucessful run of the initializer. Signal that we are done. */
  25.     movl    12(%esp), %ebx
  函数执行完毕!
  1.     LOCK
  2.     addl    $1, (%ebx)

  3.     /* Wake up all other threads. */
  4.     movl    $0x7fffffff, %edx
  5.  
  6.     movl    $FUTEX_WAKE|FUTEX_PRIVATE_FLAG, %ecx
  7.  
  8.     movl    $SYS_futex, %eax
  9.     ENTER_KERNEL

  10. 4:    popl    %esi
  11.     popl    %ebx
  12.     xorl    %eax, %eax
  13.     ret

  14. 7:    /* __sigsetjmp returned for the second time. */
  15.     这表明执行init_routine的时候线程被取消
  16.     这需要唤醒别的线程
  17.     但是并不设置执行完毕的bit 1位
  18.     movl    20+UNWINDBUFSIZE(%esp), %ebx
  19.     movl    $0, (%ebx)

  20.     movl    $0x7fffffff, %edx
  21.  
  22.     movl    $FUTEX_WAKE|FUTEX_PRIVATE_FLAG, %ecx
  23.  
  24.     movl    $SYS_futex, %eax
  25.     ENTER_KERNEL
  26.     
  27.    
  28.     leal    8(%esp), %eax
  29.     线程准备结束,有关线程如何结束,可以参考
  30.      pthread_create
  31.     call    HIDDEN_JUMPTARGET (__pthread_unwind_next)
  32.     /* NOTREACHED */
  33.     hlt


fork的例子
  1. #include <pthread.h>
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <unistd.h>
  5. #include <sys/wait.h>

  6. #define NUMTHREADS 3

  7. int number = 0;

  8. pthread_once_t onceControl = PTHREAD_ONCE_INIT;

  9. void initRoutine(void)
  10. {
  11.     ++number;
  12.     sleep(2);
  13.     ++number;
  14. }

  15. void *threadfunc(void *parm)
  16. {
  17.     pthread_once(&onceControl, initRoutine);
  18.     return 0;
  19. }

  20. int main(int argc, char **argv)
  21. {
  22.     pthread_t thread[NUMTHREADS];
  23.     int rc = 0;
  24.     int i = NUMTHREADS;
  25.     int is_child;

  26.     for (i=0; i < NUMTHREADS; ++i) {
  27.         rc = pthread_create(&thread[i], NULL, threadfunc, NULL);
  28.     }

  29.     sleep(1);

  30.     is_child = !fork();
  31.     if (is_child)
  32.     {
  33.         printf("onceControl: %d\n", onceControl);
  34.         pthread_once(&onceControl, initRoutine);
  35.     } else {
  36.         for (i = 0; i < NUMTHREADS; ++i) {
  37.             rc = pthread_join(thread[i], 0);
  38.         }
  39.         wait(0);
  40.     }

  41.     printf("%s: number = %d\n", is_child?"child":"parent", number);
  42.     printf("onceControl: %d\n", onceControl);

  43.     return 0;
  44. }









阅读(937) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~