1/*
2   Check that a thread which yields with pause (rep;nop) makes less
3   progress against a pure spinner.
4 */
5#include <pthread.h>
6#include <stdio.h>
7#include <stdlib.h>
8#include <unistd.h>
9
10static pthread_mutex_t m_go = PTHREAD_MUTEX_INITIALIZER;
11static pthread_cond_t c_go = PTHREAD_COND_INITIALIZER;
12static pthread_cond_t c_running = PTHREAD_COND_INITIALIZER;
13
14static volatile int alive, running;
15
16static int spin;
17static int rep_nop;
18
19static void *spinner(void *v)
20{
21	pthread_mutex_lock(&m_go);
22	while(!alive)
23		pthread_cond_wait(&c_go, &m_go);
24	running++;
25	pthread_cond_signal(&c_running);
26	pthread_mutex_unlock(&m_go);
27
28	while(alive)
29		spin++;
30
31	return 0;
32}
33
34static void *rep_nopper(void *v)
35{
36	pthread_mutex_lock(&m_go);
37	while(!alive)
38		pthread_cond_wait(&c_go, &m_go);
39	running++;
40	pthread_cond_signal(&c_running);
41	pthread_mutex_unlock(&m_go);
42
43	while(alive) {
44		rep_nop++;
45                // This gives a hint to a P4, telling it to pause
46                // (ie. we're in a spin-wait loop)
47		asm volatile ("rep; nop" : : : "memory");
48	}
49
50	return 0;
51}
52
53int main()
54{
55	pthread_t a, b;
56
57	pthread_create(&a, NULL, spinner, NULL);
58	pthread_create(&b, NULL, rep_nopper, NULL);
59
60	/* make sure both threads start at the same time */
61	pthread_mutex_lock(&m_go);
62	alive = 1;
63	pthread_cond_broadcast(&c_go);
64
65	/* make sure they both get started */
66	while(running < 2)
67		pthread_cond_wait(&c_running, &m_go);
68	pthread_mutex_unlock(&m_go);
69
70	sleep(2);
71
72	alive = 0;
73	pthread_join(a, NULL);
74	pthread_join(b, NULL);
75
76	if (0)
77		printf("spin=%d rep_nop=%d rep_nop:spin ratio: %g\n",
78		       spin, rep_nop, (float)rep_nop / spin);
79
80	if (spin > rep_nop)
81		printf("PASS\n");
82	else
83		printf("FAIL spin=%d rep_nop=%d rep_nop:spin ratio: %g\n",
84		       spin, rep_nop, (float)rep_nop / spin);
85
86	return 0;
87}
88