pthread_mutex_unlock实现

我们接着看pthread_mutex_unlock的实现,会发现它跟pthread_mutex_lock形式差不多,并且底层调用futex换乐FUTEX_WAKE而已。

直接贴路径代码:

int
__pthread_mutex_unlock (pthread_mutex_t *mutex)
{
  return __pthread_mutex_unlock_usercnt (mutex, 1);
}
strong_alias (__pthread_mutex_unlock, pthread_mutex_unlock)
hidden_def (__pthread_mutex_unlock)

int
internal_function attribute_hidden
__pthread_mutex_unlock_usercnt (pthread_mutex_t *mutex, int decr)
{
  int type = PTHREAD_MUTEX_TYPE_ELISION (mutex);
  if (__builtin_expect (type &
		~(PTHREAD_MUTEX_KIND_MASK_NP|PTHREAD_MUTEX_ELISION_FLAGS_NP), 0))
    return __pthread_mutex_unlock_full (mutex, decr);

  if (__builtin_expect (type, PTHREAD_MUTEX_TIMED_NP)
      == PTHREAD_MUTEX_TIMED_NP)
    {
      /* Always reset the owner field.  */
    normal:
      mutex->__data.__owner = 0;
      if (decr)
	/* One less user.  */
	--mutex->__data.__nusers;

      /* Unlock.  */
      lll_unlock (mutex->__data.__lock, PTHREAD_MUTEX_PSHARED (mutex));

      LIBC_PROBE (mutex_release, 1, mutex);

      return 0;
    }
  else if (__glibc_likely (type == PTHREAD_MUTEX_TIMED_ELISION_NP))
    {
      /* Don't reset the owner/users fields for elision.  */
      return lll_unlock_elision (mutex->__data.__lock, mutex->__data.__elision,
				      PTHREAD_MUTEX_PSHARED (mutex));
    }
  else if (__builtin_expect (PTHREAD_MUTEX_TYPE (mutex)
			      == PTHREAD_MUTEX_RECURSIVE_NP, 1))
    {
      /* Recursive mutex.  */
      if (mutex->__data.__owner != THREAD_GETMEM (THREAD_SELF, tid))
	return EPERM;

      if (--mutex->__data.__count != 0)
	/* We still hold the mutex.  */
	return 0;
      goto normal;
    }
  else if (__builtin_expect (PTHREAD_MUTEX_TYPE (mutex)
			      == PTHREAD_MUTEX_ADAPTIVE_NP, 1))
    goto normal;
  else
    {
      /* Error checking mutex.  */
      assert (type == PTHREAD_MUTEX_ERRORCHECK_NP);
      if (mutex->__data.__owner != THREAD_GETMEM (THREAD_SELF, tid)
	  || ! lll_islocked (mutex->__data.__lock))
	return EPERM;
      goto normal;
    }
}

这里的:

      mutex->__data.__owner = 0;
      if (decr)
	/* One less user.  */
	--mutex->__data.__nusers;

      /* Unlock.  */
      lll_unlock (mutex->__data.__lock, PTHREAD_MUTEX_PSHARED (mutex));

与pthread_mutex_lock中的

知识简单地把__data中的__owner设置为id,已经++__nusers。从而代表这个锁的使用者人数+1,并且当前有用者为该id的线程。

是相呼应的。

接着的是 lll_unlock:

/* This is an expression rather than a statement even though its value is
   void, so that it can be used in a comma expression or as an expression
   that's cast to void.  */
/* Unconditionally set FUTEX to 0 (not acquired), releasing the lock.  If FUTEX
   was >1 (acquired, possibly with waiters), then wake any waiters.  The waiter
   that acquires the lock will set FUTEX to >1.
   Evaluate PRIVATE before releasing the lock so that we do not violate the
   mutex destruction requirements.  Specifically, we need to ensure that
   another thread can destroy the mutex (and reuse its memory) once it
   acquires the lock and when there will be no further lock acquisitions;
   thus, we must not access the lock after releasing it, or those accesses
   could be concurrent with mutex destruction or reuse of the memory.  */
#define __lll_unlock(futex, private)                    \
  ((void)                                               \
   ({                                                   \
     int *__futex = (futex);                            \
     int __private = (private);                         \
     int __oldval = atomic_exchange_rel (__futex, 0);   \
     if (__glibc_unlikely (__oldval > 1))               \
       lll_futex_wake (__futex, 1, __private);          \
   }))
#define lll_unlock(futex, private)	\
  __lll_unlock (&(futex), private)

接着看lll_futex_wake的实现:

#define lll_futex_wake(futexp, nr, private)                             \
  lll_futex_syscall (4, futexp,                                         \
		     __lll_private_flag (FUTEX_WAKE, private), nr, 0)

所以这里是同样是lll_futex_syscall的调用,这与pthread_mutex_lock中的调用一致,除了接口名FUTEX_WAKE,以及这里的参数nr是1代表唤醒一个线程(之前是值2,代表如果futexp值为2则阻塞),0之前为NULL.



我们接着尝试将之前采用FUTEX_WAKE那部分的代码也换掉。

之前的代码:

#include <stdio.h>
#include <pthread.h>
#include <linux/futex.h>
#include <syscall.h>
#include <unistd.h>
#include <sys/time.h>


#define NUM 1000

#define INTERNAL_SYSCALL_NCS(a1, a2, a3, a4)  \
  ({                                          \
    unsigned long long int resultvar;          \
    long int __arg4 = (long int) (a4);                         \
    long int __arg3 = (long int) (a3);                         \
    long int __arg2 = (long int) (a2);                         \
    long int __arg1 = (long int) (a1);                         \    
    register long int _a4 asm ("r10") = __arg4;                    \
    register long int _a3 asm ("rdx") = __arg3;                    \
    register long int _a2 asm ("rsi") = __arg2;                    \
    register long int _a1 asm ("rdi") = __arg1;                    \
    asm volatile ( \
    "syscall\n\t"                                 \
    : "=a" (resultvar)                                \
    : "0" (SYS_futex), "r" (_a1), "r" (_a2), "r" (_a3), "r" (_a4) : "memory", "cc", "r11", "cx");   \
    (long long int) resultvar; })


int num = 0;
int futex_addr = 0;

int futex_wait(void* addr, int val){
//  return syscall(SYS_futex, addr, FUTEX_WAIT, val, NULL, NULL, 0);
    return INTERNAL_SYSCALL_NCS(addr, FUTEX_WAIT, val, NULL);
}
int futex_wake(void* addr, int val){
//  return syscall(SYS_futex, addr, FUTEX_WAKE, val, NULL, NULL, 0);
    return INTERNAL_SYSCALL_NCS(addr, FUTEX_WAKE, val, 0);
}

void* thread_f(void* par){
        int id = (int) par;

    /*go to sleep*/
for(int i = 0; i < 1000; ++i){
    while(21 == __sync_val_compare_and_swap(&futex_addr, 0, 21) ){
        futex_wait(&futex_addr,21);
    }
    ++num;
    futex_addr = 0;
    futex_wake(&futex_addr, 1);
}
  //      printf("Thread %d starting to work!\n",id);
        return NULL;
}

int main(){
        pthread_t threads[NUM];
        int i;

        printf("Everyone go...\n");
        float time_use=0;
        struct timeval start;
        struct timeval end;
        gettimeofday(&start,NULL);



        for (i=0;i<NUM;i++){
                pthread_create(&threads[i],NULL,thread_f,(void *)i);
        }

    /*wake threads*/

    /*give the threads time to complete their tasks*/
        for (i=0;i<NUM;i++){
                pthread_join(*(threads + i), NULL);
        }


    printf("Main is quitting...\n");
    printf("and num is %d\n", num);

    gettimeofday(&end,NULL);
    time_use=(end.tv_sec-start.tv_sec)+(end.tv_usec-start.tv_usec) / 1000000.0;//微秒
    printf("time_use is %f \n",time_use);
    return 0;
}

执行如上代码,输出为:

Everyone go…
Main is quitting…
and num is 1000000
time_use is 0.327365 

执行正确。


并且我们现在的这个锁的调用在用户态已经完全和pthreadmutex的实现采用一样的机制了。


点赞