File: notify_one.pass.cpp

package info (click to toggle)
llvm-toolchain-15 1%3A15.0.6-4
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 1,554,644 kB
  • sloc: cpp: 5,922,452; ansic: 1,012,136; asm: 674,362; python: 191,568; objc: 73,855; f90: 42,327; lisp: 31,913; pascal: 11,973; javascript: 10,144; sh: 9,421; perl: 7,447; ml: 5,527; awk: 3,523; makefile: 2,520; xml: 885; cs: 573; fortran: 567
file content (130 lines) | stat: -rw-r--r-- 3,057 bytes parent folder | download | duplicates (5)
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
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// UNSUPPORTED: no-threads

// <condition_variable>

// class condition_variable;

// void notify_one();


// NOTE: `notify_one` is just a wrapper around pthread_cond_signal, but
// POSIX does not guarantee that one and only one thread will be woken:
//
// https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_cond_signal.html
//
// Quote:
//     Multiple Awakenings by Condition Signal
//     On a multi-processor, it may be impossible for an implementation of
//     pthread_cond_signal() to avoid the unblocking of more than one thread
//     blocked on a condition variable. For example...



// NOTE: In previous versions of this test, `notify_one` was called WITHOUT
// holding the lock but POSIX says (in the aforementioned URL) that:
//     ...if predictable scheduling behavior is required, then that mutex shall
//     be locked by the thread calling pthread_cond_broadcast() or
//     pthread_cond_signal().


#include <condition_variable>
#include <atomic>
#include <mutex>
#include <thread>
#include <cassert>

#include "make_test_thread.h"
#include "test_macros.h"


std::condition_variable cv;
std::mutex mut;

std::atomic_int test1(0);
std::atomic_int test2(0);
std::atomic_int ready(2);
std::atomic_int which(0);

void f1()
{
  std::unique_lock<std::mutex> lk(mut);
  assert(test1 == 0);
  --ready;
  while (test1 == 0)
    cv.wait(lk);
  which = 1;
  assert(test1 == 1);
  test1 = 2;
}

void f2()
{
  std::unique_lock<std::mutex> lk(mut);
  assert(test2 == 0);
  --ready;
  while (test2 == 0)
    cv.wait(lk);
  which = 2;
  assert(test2 == 1);
  test2 = 2;
}

int main(int, char**)
{
  std::thread t1 = support::make_test_thread(f1);
  std::thread t2 = support::make_test_thread(f2);
  {
    while (ready > 0)
      std::this_thread::yield();
    // At this point:
    // 1) Both f1 and f2 have entered their condition variable wait.
    // 2) Either f1 or f2 has the mutex locked and is about to wait.
    std::unique_lock<std::mutex> lk(mut);
    test1 = 1;
    test2 = 1;
    ready = 1;
    cv.notify_one();
  }
  {
    while (which == 0)
      std::this_thread::yield();
    std::unique_lock<std::mutex> lk(mut);
    if (test1 == 2) {
      assert(test2 == 1);
      t1.join();
      test1 = 0;
    } else {
      assert(test1 == 1);
      assert(test2 == 2);
      t2.join();
      test2 = 0;
    }
    which = 0;
    cv.notify_one();
  }
  {
    while (which == 0)
      std::this_thread::yield();
    std::unique_lock<std::mutex> lk(mut);
    if (test1 == 2) {
      assert(test2 == 0);
      t1.join();
      test1 = 0;
    } else {
      assert(test1 == 0);
      assert(test2 == 2);
      t2.join();
      test2 = 0;
    }
  }

  return 0;
}