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的问题
- .globl __pthread_once
-
.type __pthread_once,@function
-
.align 16
-
__pthread_once:
-
movl 4(%esp), %ecx
-
- 只有执行过init_routine之后bit 1才会被设置
-
testl $2, (%ecx)
-
jz 1f
-
xorl %eax, %eax
-
ret
-
-
1: pushl %ebx
-
pushl %esi
-
movl %ecx, %ebx
-
xorl %esi, %esi
-
-
/* Not yet initialized or initialization in progress.
-
Get the fork generation counter now. */
-
6: movl (%ebx), %eax
-
5: movl %eax, %edx
-
-
-
testl $2, %eax
-
jnz 4f
-
- __fork_generation在fork的时候会被 +4
-
andl $3, %edx
-
orl __fork_generation, %edx
-
orl $1, %edx
- 原子地设置并交换bit 0,该位表示有线程正在执行
-
LOCK
-
cmpxchgl %edx, (%ebx)
-
jnz 5b
-
-
-
/* Check whether another thread already runs the initializer. */
-
testl $1, %eax
-
jz 3f /* No -> do it. */
-
- fork的问题, 如果正在执行init_routine的时候fork, 其子进程
- 调用pthread_once就会到这,这样会执行init_routine,下面那个例子可以看出来
-
-
xorl %edx, %eax
-
testl $0xfffffffc, %eax
-
jnz 3f /* Different for generation -> run initializer. */
-
-
/* Somebody else got here first. Wait. */
-
等待别的线程执行init_routine然后唤醒
-
movl $FUTEX_WAIT|FUTEX_PRIVATE_FLAG, %ecx
-
movl $SYS_futex, %eax
-
ENTER_KERNEL
之所以要重新检查,是因为init_routine在执行的时候
它可能被杀死-
jmp 6b
-
-
3:
-
subl $UNWINDBUFSIZE+8, %esp
-
movl %ecx, %ebx /* PIC register value. */
-
- set
-
leal 8+UWJMPBUF(%esp), %eax
-
movl $0, 4(%esp)
-
movl %eax, (%esp)
-
call __sigsetjmp@PLT
-
testl %eax, %eax
-
jne 7f
-
- setjmp == 0,注册cancel jmpbuf
- 这样,线程在退出的时候会到这里来,上面那条
- jne 7f会处理这种情况
-
leal 8(%esp), %eax
-
call HIDDEN_JUMPTARGET(__pthread_register_cancel)
-
-
/* Call the user-provided initialization function. */
-
call *24+UNWINDBUFSIZE(%esp)
-
-
/* Pop the cleanup handler. */
-
leal 8(%esp), %eax
-
call HIDDEN_JUMPTARGET(__pthread_unregister_cancel)
-
addl $UNWINDBUFSIZE+8, %esp
-
-
/* Sucessful run of the initializer. Signal that we are done. */
-
movl 12(%esp), %ebx
函数执行完毕!-
LOCK
-
addl $1, (%ebx)
-
-
/* Wake up all other threads. */
-
movl $0x7fffffff, %edx
-
-
movl $FUTEX_WAKE|FUTEX_PRIVATE_FLAG, %ecx
-
movl $SYS_futex, %eax
-
ENTER_KERNEL
-
-
4: popl %esi
-
popl %ebx
-
xorl %eax, %eax
-
ret
-
-
7: /* __sigsetjmp returned for the second time. */
- 这表明执行init_routine的时候线程被取消
- 这需要唤醒别的线程
- 但是并不设置执行完毕的bit 1位
-
movl 20+UNWINDBUFSIZE(%esp), %ebx
-
movl $0, (%ebx)
-
-
movl $0x7fffffff, %edx
-
-
movl $FUTEX_WAKE|FUTEX_PRIVATE_FLAG, %ecx
-
-
movl $SYS_futex, %eax
-
ENTER_KERNEL
-
-
-
leal 8(%esp), %eax
- 线程准备结束,有关线程如何结束,可以参考
- pthread_create
-
call HIDDEN_JUMPTARGET (__pthread_unwind_next)
-
/* NOTREACHED */
- hlt
fork的例子
- #include <pthread.h>
-
#include <stdio.h>
-
#include <stdlib.h>
-
#include <unistd.h>
-
#include <sys/wait.h>
-
-
#define NUMTHREADS 3
-
-
int number = 0;
-
-
pthread_once_t onceControl = PTHREAD_ONCE_INIT;
-
-
void initRoutine(void)
-
{
-
++number;
-
sleep(2);
-
++number;
-
}
-
-
void *threadfunc(void *parm)
-
{
-
pthread_once(&onceControl, initRoutine);
-
return 0;
-
}
-
-
int main(int argc, char **argv)
-
{
-
pthread_t thread[NUMTHREADS];
-
int rc = 0;
-
int i = NUMTHREADS;
-
int is_child;
-
-
for (i=0; i < NUMTHREADS; ++i) {
-
rc = pthread_create(&thread[i], NULL, threadfunc, NULL);
-
}
-
-
sleep(1);
-
-
is_child = !fork();
-
if (is_child)
-
{
-
printf("onceControl: %d\n", onceControl);
-
pthread_once(&onceControl, initRoutine);
-
} else {
-
for (i = 0; i < NUMTHREADS; ++i) {
-
rc = pthread_join(thread[i], 0);
-
}
-
wait(0);
-
}
-
-
printf("%s: number = %d\n", is_child?"child":"parent", number);
-
printf("onceControl: %d\n", onceControl);
-
-
return 0;
-
}
阅读(957) | 评论(0) | 转发(0) |