ltp测试套件-pthread_rwlock_rdlock_2-1分析记录

测试pthread_rwlock_rdlock_2-1.c用例失败。log如下:

[83#yuchen@ubuntu ltp]# gcc 2-1.c -o 21 -pthread
[84#yuchen@ubuntu ltp]# ./21
main: has priority: 3
main: attempt read lock
main: acquired read lock
main: create wr_thread, with priority: 2
wr_thread: attempt write lock
main: create rd_thread, with priority: 1
rd_thread: attempt read lock
rd_thread: acquired read lock
rd_thread: unlock read lock
Test FAILED: rd_thread did not block on read lock, when a reader owns the lock, and a higher priority writer is waiting for the lock
[85#yuchen@ubuntu ltp]#

 这个测试用例的流程:

Steps:
 * We have three threads, main(also a reader), writer, reader
 *
 * 1.  Main thread set its shcedule policy as "SCHED_FIFO", with highest priority
 *     the three: sched_get_priority_min()+2.
 * 2.  Main thread read lock 'rwlock'
 * 3.  Create a writer thread, with schedule policy as "SCHED_FIFO", and priority
 *     using sched_get_priority_min()+1.
 * 4.  The thread write lock 'rwlock', should block.
 * 5.  Main thread create a reader thread, with schedule policy as "SCHED_FIFO", and
 *     priority sched_get_priority_min()
 * 6.  Reader thread read lock 'rwlock', should block, since there is a higher priority
 *     writer blocked on 'rwlock'
 * 7.  Main thread release the 'rwlock', the writer should get the lock first
 */

用例失败的原因:

rwlock是读写锁,读者是可以递归调用的,因为多个读者不存在竞争问题,如果写者先拿到了锁,则后面来的读者必须阻塞。读和写之间必然会造成竞争,但是读和读之间没有竞争。读写锁的实现比较复杂,基于futex实现(用户态和内核态混合同步机制)。在glibc库有很多分支判断,如果对futex机制不了解的话,是比较难理解的。针对这个用例,简单说下流程:

默认情况下,如果没有写者持有锁,并且锁的属性设置了读优先(默认情况下rwlock就是读优先的),那么读者可以重复占用锁(读和读没有竞争)。代码见nptl/pthread_rwlock_rdlock.c,注意140行。那么在该测试用例中有2个读者,读者1先拿到了锁,读者2在去拿锁,是不会被阻塞的,因为写者并没有持有锁。持有锁的是一个读者(main主程序)。

但是测试用例期望,在有高优先级写者竞争锁的时候,读者要阻塞。从c库代码上看,要想让读者阻塞,必须设置rwlock写者优先属性,也就是让pthread_rwlock_rdlock.c的140行的条件判断为假。从而让代码走到slowfast,在pthread_rwlock_rdlock_slow函数中会挂起当前线程,等待其他任务unlock时才有可能被唤醒。

119 __pthread_rwlock_rdlock (pthread_rwlock_t *rwlock)
120 {
121   int result = 0;
122   bool wake = false;
123   int futex_shared =
124       rwlock->__data.__shared == LLL_PRIVATE ? FUTEX_PRIVATE : FUTEX_SHARED;
125 
126   LIBC_PROBE (rdlock_entry, 1, rwlock);
127 
128   if (ELIDE_LOCK (rwlock->__data.__rwelision,
129                   rwlock->__data.__lock == 0
130                   && rwlock->__data.__writer == 0
131                   && rwlock->__data.__nr_readers == 0))
132     return 0;
133 
134   /* Make sure we are alone.  */
135   lll_lock (rwlock->__data.__lock, rwlock->__data.__shared);
136 
137   /* Get the rwlock if there is no writer...  */
138   if (rwlock->__data.__writer == 0
139       /* ...and if either no writer is waiting or we prefer readers.  */
140       && (!rwlock->__data.__nr_writers_queued
141           || PTHREAD_RWLOCK_PREFER_READER_P (rwlock)))
142     {
143       /* Increment the reader counter.  Avoid overflow.  */
144       if (__glibc_unlikely (++rwlock->__data.__nr_readers == 0))
145         {
146           /* Overflow on number of readers.      */
147           --rwlock->__data.__nr_readers;
148           result = EAGAIN;
149         }
150       else
151         {
152           LIBC_PROBE (rdlock_acquire_read, 1, rwlock);
153           /* If we are the first reader, and there are blocked readers and
154              writers (which we don't prefer, see above), then it can be the
155              case that we stole the lock from a writer that was already woken
156              to acquire it.  That means that we need to take over the writer's
157              responsibility to wake all readers (see pthread_rwlock_unlock).
158              Thus, wake all readers in this case.  */
159           if (rwlock->__data.__nr_readers == 1
160               && rwlock->__data.__nr_readers_queued > 0
161               && rwlock->__data.__nr_writers_queued > 0)
162             {
163               ++rwlock->__data.__readers_wakeup;
164               wake = true;
165             }
166         }
167 
168       /* We are done, free the lock.  */
169       lll_unlock (rwlock->__data.__lock, rwlock->__data.__shared);
170 
171       if (wake)
172         futex_wake (&rwlock->__data.__readers_wakeup, INT_MAX, futex_shared);
173 
174       return result;
175     }
176 
177   return __pthread_rwlock_rdlock_slow (rwlock);
178 }

解决方法:

在初始化rwlock的时候,要设置一下写者优先属性。代码如下:

pthread_rwlockattr_t attr;
pthread_rwlockattr_init(&attr);
pthread_rwlockattr_setkind_np(&attr, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP);

//初始化rwlock时,设置写者优先属性。
pthread_rwlock_t rwlock;
pthread_rwlock_init(&rwlock, &attr);

测试用例代码: 

/*
 * Copyright (c) 2002, Intel Corporation. All rights reserved.
 * This file is licensed under the GPL license.  For the full content
 * of this license, see the COPYING file at the top level of this
 * source tree.
 * Test that pthread_rwlock_rdlock(pthread_rwlock_t *rwlock)
 *
 * 	If the Thread Execution Scheduling option is supported,
 *	and the threads involved in the lock are executing with the
 *	scheduling policies SCHED_FIFO or SCHED_RR, the calling thread shall not
 *	acquire the lock if a writer holds the lock or if writers of
 *	higher or equal priority are blocked on the lock;
 *	otherwise, the calling thread shall acquire the lock.
 *
 * In this case, we test "higher priority writer block"
 *
 Steps:
 * We have three threads, main(also a reader), writer, reader
 *
 * 1.  Main thread set its shcedule policy as "SCHED_FIFO", with highest priority
 *     the three: sched_get_priority_min()+2.
 * 2.  Main thread read lock 'rwlock'
 * 3.  Create a writer thread, with schedule policy as "SCHED_FIFO", and priority
 * 	using sched_get_priority_min()+1.
 * 4.  The thread write lock 'rwlock', should block.
 * 5.  Main thread create a reader thread, with schedule policy as "SCHED_FIFO", and
 *     priority sched_get_priority_min()
 * 6.  Reader thread read lock 'rwlock', should block, since there is a higher priority
 *     writer blocked on 'rwlock'
 * 7.  Main thread release the 'rwlock', the writer should get the lock first
 */

/* NOTE: The test result is UNSUPPORTED if Thread Execution Scheduling option
 * 	 is not supported.
 */

#define _XOPEN_SOURCE 600
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sched.h>
#include "posixtest.h"

#define TRD_POLICY SCHED_FIFO

static pthread_rwlock_t rwlock;
static int rd_thread_state;
static int wr_thread_state;

/* thread states:
	1: not in child thread yet;
	2: just enter child thread ;
	3: just before child thread exit;
*/

#define NOT_CREATED_THREAD 1
#define ENTERED_THREAD 2
#define EXITING_THREAD 3

static int set_priority(pthread_t pid, unsigned policy, unsigned prio)
{
	struct sched_param sched_param;
	memset(&sched_param, 0, sizeof(sched_param));
	sched_param.sched_priority = prio;
	if (pthread_setschedparam(pid, policy, &sched_param) == -1) {
		printf("Can't set policy to %d and prio to %d\n", policy, prio);
		exit(PTS_UNRESOLVED);
	}
	return 0;
}

static void *fn_rd(void *arg)
{
	int rc = 0;
	int priority;
	rd_thread_state = ENTERED_THREAD;

	priority = (long)arg;
	set_priority(pthread_self(), TRD_POLICY, priority);

	printf("rd_thread: attempt read lock\n");
	rc = pthread_rwlock_rdlock(&rwlock);
	if (rc != 0) {
		printf
		    ("Test FAILED: rd_thread failed to get read lock, Error code:%d\n",
		     rc);
		exit(PTS_FAIL);
	} else
		printf("rd_thread: acquired read lock\n");

	sleep(1);

	printf("rd_thread: unlock read lock\n");
	if (pthread_rwlock_unlock(&rwlock) != 0) {
		printf("rd_thread: Error at pthread_rwlock_unlock()\n");
		exit(PTS_UNRESOLVED);
	}
	rd_thread_state = EXITING_THREAD;
	pthread_exit(0);
	return NULL;
}

static void *fn_wr(void *arg)
{
	int rc = 0;
	int priority;
	wr_thread_state = ENTERED_THREAD;

	priority = (long)arg;
	set_priority(pthread_self(), TRD_POLICY, priority);

	printf("wr_thread: attempt write lock\n");
	rc = pthread_rwlock_wrlock(&rwlock);
	if (rc != 0) {
		printf
		    ("Error: wr_thread failed to get write lock, Error code:%d\n",
		     rc);
		exit(PTS_UNRESOLVED);
	} else
		printf("wr_thread: acquired write lock\n");

	sleep(1);

	printf("wr_thread: unlock write lock\n");
	if (pthread_rwlock_unlock(&rwlock) != 0) {
		printf("wr_thread: Error at pthread_rwlock_unlock()\n");
		exit(PTS_UNRESOLVED);
	}
	wr_thread_state = EXITING_THREAD;
	pthread_exit(0);
	return NULL;
}

int main(void)
{
#ifndef _POSIX_THREAD_PRIORITY_SCHEDULING
	printf("Posix Thread Execution Scheduling not supported\n");
	return PTS_UNSUPPORTED;
#endif

	int cnt = 0;
	pthread_t rd_thread, wr_thread;
	int priority;

	/* main thread needs to have the highest priority */
	priority = sched_get_priority_min(TRD_POLICY) + 2;
	set_priority(pthread_self(), TRD_POLICY, priority);
	printf("main: has priority: %d\n", priority);

	if (pthread_rwlock_init(&rwlock, NULL) != 0) {
		printf("main: Error at pthread_rwlock_init()\n");
		return PTS_UNRESOLVED;
	}

	printf("main: attempt read lock\n");
	/* This read lock should succeed */
	if (pthread_rwlock_rdlock(&rwlock) != 0) {
		printf
		    ("Test FAILED: main cannot get read lock when no one owns the lock\n");
		return PTS_FAIL;
	} else
		printf("main: acquired read lock\n");

	wr_thread_state = NOT_CREATED_THREAD;
	priority = sched_get_priority_min(TRD_POLICY) + 1;
	printf("main: create wr_thread, with priority: %d\n", priority);
	if (pthread_create(&wr_thread, NULL, fn_wr, (void *)(long)priority) !=
	    0) {
		printf("main: Error at 1st pthread_create()\n");
		return PTS_UNRESOLVED;
	}

	/* If the shared data is not altered by child after 3 seconds,
	   we regard it as blocked */

	/* We expect the wr_thread to block */
	cnt = 0;
	do {
		sleep(1);
	} while (wr_thread_state != EXITING_THREAD && cnt++ < 3);

	if (wr_thread_state == EXITING_THREAD) {
		printf
		    ("wr_thread did not block on write lock, when a reader owns the lock\n");
		exit(PTS_UNRESOLVED);
	} else if (wr_thread_state != ENTERED_THREAD) {
		printf("Unexpected wr_thread state: %d\n", wr_thread_state);
		exit(PTS_UNRESOLVED);
	}

	rd_thread_state = 1;
	priority = sched_get_priority_min(TRD_POLICY);
	printf("main: create rd_thread, with priority: %d\n", priority);
	if (pthread_create(&rd_thread, NULL, fn_rd, (void *)(long)priority) !=
	    0) {
		printf("main: failed at creating rd_thread\n");
		return PTS_UNRESOLVED;
	}

	/* We expect the rd_thread to block */
	cnt = 0;
	do {
		sleep(1);
	} while (rd_thread_state != EXITING_THREAD && cnt++ < 3);

	if (rd_thread_state == EXITING_THREAD) {
		printf
		    ("Test FAILED: rd_thread did not block on read lock, when a reader owns the lock, and a higher priority writer is waiting for the lock\n");
		exit(PTS_FAIL);
	} else if (rd_thread_state != ENTERED_THREAD) {
		printf("Unexpected rd_thread state: %d\n", rd_thread_state);
		exit(PTS_UNRESOLVED);
	}

	printf("main: unlock read lock\n");
	if (pthread_rwlock_unlock(&rwlock) != 0) {
		printf("main: failed to unlock read lock\n");
		exit(PTS_UNRESOLVED);
	}

	/* we expect the writer get the lock */
	cnt = 0;
	do {
		sleep(1);
	} while (wr_thread_state != EXITING_THREAD && cnt++ < 3);

	if (wr_thread_state == ENTERED_THREAD) {
		printf
		    ("Test FAILED: higher priority wr_thread still blocked on write lock, when a reader release the lock\n");
		exit(PTS_FAIL);
	} else if (wr_thread_state != EXITING_THREAD) {
		printf("Unexpected wr_thread state: %d\n", wr_thread_state);
		exit(PTS_UNRESOLVED);
	}

	if (pthread_join(wr_thread, NULL) != 0) {
		printf("main: Error at 1st pthread_join()\n");
		exit(PTS_UNRESOLVED);
	}
	/* we expect the reader get the lock when writer has unlocked the lock */
	cnt = 0;
	do {
		sleep(1);
	} while (rd_thread_state != EXITING_THREAD && cnt++ < 3);

	if (rd_thread_state == ENTERED_THREAD) {
		printf
		    ("Test FAILED: rd_thread still block on read lock when the lock has no owner\n");
		exit(PTS_FAIL);
	} else if (rd_thread_state != EXITING_THREAD) {
		printf("Unexpected rd_thread state\n");
		exit(PTS_UNRESOLVED);
	}

	if (pthread_join(rd_thread, NULL) != 0) {
		printf("main: Error at 2nd pthread_join()\n");
		exit(PTS_UNRESOLVED);
	}

	if (pthread_rwlock_destroy(&rwlock) != 0) {
		printf("Error at pthread_rwlockattr_destroy()");
		return PTS_UNRESOLVED;
	}

	printf("Test PASSED\n");
	return PTS_PASS;
}

猜你喜欢

转载自blog.csdn.net/y33988979/article/details/82229249