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的實現採用一樣的機制了。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章