about summary refs log tree commit diff
path: root/nptl/tst-pthread-timedlock-lockloop.c
blob: bd5ed77a38ad134ce4fd7e67e90ec7a38d18510f (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
/* Make sure pthread_mutex_timedlock doesn't return spurious error codes.

   Copyright (C) 2020 Free Software Foundation, Inc.
   This file is part of the GNU C Library.

   The GNU C Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   The GNU C Library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with the GNU C Library; if not, see
   <https://www.gnu.org/licenses/>.  */

#include <errno.h>
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <support/check.h>
#include <support/timespec.h>
#include <support/xsignal.h>
#include <support/xthread.h>
#include <support/xtime.h>
#include <time.h>
#include <unistd.h>

#define NANO_PER_SEC 1000000000LL
#define TIMEOUT (NANO_PER_SEC / 1000LL)
#define NUM_THREADS 50
#define RETEST_TIMES 100

static pthread_mutex_t mutex;
static int runs;
static clockid_t clockid;

static void
signal_handler (int sig_num)
{
  TEST_COMPARE (sig_num, SIGUSR1);
}

/* Call pthread_mutex_timedlock()/pthread_mutex_unlock() repetitively, hoping
   that one of them returns EAGAIN or EINTR unexpectedly.  */
static void *
worker_timedlock (void *arg)
{
  for (unsigned int run = 0; run < runs; run++)
    {
      struct timespec abs_time = timespec_add (xclock_now (CLOCK_REALTIME),
					       make_timespec (0, 1000000));

      int ret = pthread_mutex_timedlock (&mutex, &abs_time);

      if (ret == 0)
	xpthread_mutex_unlock (&mutex);

      TEST_VERIFY_EXIT (ret == 0 || ret == ETIMEDOUT);
    }
  return NULL;
}

static void *
worker_clocklock (void *arg)
{
  for (unsigned int run = 0; run < runs; run++)
    {
      struct timespec time =
	timespec_add (xclock_now (clockid), make_timespec (0, 1000000));

      int ret = pthread_mutex_clocklock (&mutex, clockid, &time);

      if (ret == 0)
	xpthread_mutex_unlock (&mutex);

      TEST_VERIFY_EXIT (ret == 0 || ret == ETIMEDOUT);
    }
  return NULL;
}

static int
run_test_set (void *(*worker) (void *))
{
  pthread_t workers[NUM_THREADS];

  /* Check if default pthread_mutex_{timed,clock}lock with valid arguments
     returns either 0 or ETIMEDOUT.  Since there is no easy way to force
     the error condition, the test creates multiple threads which in turn
     issues pthread_mutex_timedlock multiple times.  */
  runs = 100;
  for (int run = 0; run < RETEST_TIMES; run++)
    {
      for (int i = 0; i < NUM_THREADS; i++)
	workers[i] = xpthread_create (NULL, worker, NULL);
      for (int i = 0; i < NUM_THREADS; i++)
	xpthread_join (workers[i]);
    }

  /* The idea is similar to previous tests, but we check if
     pthread_mutex_{timed,clock}lock does not return EINTR.  */
  pthread_t thread;
  runs = 1;
  for (int i = 0; i < RETEST_TIMES * 1000; i++)
    {
      xpthread_mutex_lock (&mutex);
      thread = xpthread_create (NULL, worker, NULL);
      /* Sleep just a little bit to reach the lock on the worker thread.  */
      usleep (10);
      pthread_kill (thread, SIGUSR1);
      xpthread_mutex_unlock (&mutex);
      xpthread_join (thread);
    }

  return 0;
}

static int
do_test (void)
{

  xsignal (SIGUSR1, signal_handler);

  xpthread_mutex_init (&mutex, NULL);

  run_test_set (worker_timedlock);
  clockid = CLOCK_REALTIME;
  run_test_set (worker_clocklock);
  clockid = CLOCK_MONOTONIC;
  run_test_set (worker_clocklock);
  return 0;
}

#include <support/test-driver.c>