1  // Copyright (C) 2020-2023 Free Software Foundation, Inc.
       2  //
       3  // This file is part of the GNU ISO C++ Library.  This library is free
       4  // software; you can redistribute it and/or modify it under the
       5  // terms of the GNU General Public License as published by the
       6  // Free Software Foundation; either version 3, or (at your option)
       7  // any later version.
       8  
       9  // This library is distributed in the hope that it will be useful,
      10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
      11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12  // GNU General Public License for more details.
      13  
      14  // You should have received a copy of the GNU General Public License along
      15  // with this library; see the file COPYING3.  If not see
      16  // <http://www.gnu.org/licenses/>.
      17  
      18  #include <atomic>
      19  #include <chrono>
      20  #include <condition_variable>
      21  #include <concepts>
      22  #include <mutex>
      23  #include <thread>
      24  
      25  #include <testsuite_hooks.h>
      26  
      27  #include <iostream>
      28  
      29  template<typename Tp>
      30  Tp check_wait_notify(Tp val1, Tp val2)
      31    requires std::equality_comparable<Tp>
      32  {
      33    using namespace std::literals::chrono_literals;
      34  
      35    std::mutex m;
      36    std::condition_variable cv;
      37    std::unique_lock<std::mutex> l(m);
      38  
      39    std::atomic<Tp> a(val1);
      40    std::thread t([&]
      41  		{
      42  		  {
      43  		    // This ensures we block until cv.wait(l) starts.
      44  		    std::lock_guard<std::mutex> ll(m);
      45  		  }
      46  		  cv.notify_one();
      47  		  a.wait(val1);
      48  		  if (a.load() != val2)
      49  		    a = val1;
      50  		});
      51    cv.wait(l);
      52    std::this_thread::sleep_for(100ms);
      53    a.store(val2);
      54    a.notify_one();
      55    t.join();
      56    return a.load();
      57  }
      58  
      59  template<typename Tp>
      60  Tp check_wait_notify(Tp val1, Tp val2)
      61  {
      62    using namespace std::literals::chrono_literals;
      63  
      64    std::mutex m;
      65    std::condition_variable cv;
      66    std::unique_lock<std::mutex> l(m);
      67  
      68    std::atomic<Tp> a(val1);
      69    std::thread t([&]
      70  		{
      71  		  {
      72  		    // This ensures we block until cv.wait(l) starts.
      73  		    std::lock_guard<std::mutex> ll(m);
      74  		  }
      75  		  cv.notify_one();
      76  		  a.wait(val1);
      77  		  auto v = a.load();
      78  		  // TODO this needs to zero padding bits when we can do that
      79  		  if (__builtin_memcmp(&v, &val2, sizeof(Tp)) != 0)
      80  		    a = val1;
      81  		});
      82    cv.wait(l);
      83    std::this_thread::sleep_for(100ms);
      84    a.store(val2);
      85    a.notify_one();
      86    t.join();
      87    return a.load();
      88  }
      89  
      90  template<typename Tp>
      91  Tp check_atomic_wait_notify(Tp val1, Tp val2)
      92    requires std::equality_comparable<Tp>
      93  {
      94    using namespace std::literals::chrono_literals;
      95  
      96    std::mutex m;
      97    std::condition_variable cv;
      98    std::unique_lock<std::mutex> l(m);
      99  
     100    std::atomic<Tp> a(val1);
     101    std::thread t([&]
     102  		{
     103  		  {
     104  		    // This ensures we block until cv.wait(l) starts.
     105  		    std::lock_guard<std::mutex> ll(m);
     106  		  }
     107  		  cv.notify_one();
     108  		  std::atomic_wait(&a, val1);
     109  		  if (a.load() != val2)
     110  		    a = val1;
     111  		});
     112    cv.wait(l);
     113    std::this_thread::sleep_for(100ms);
     114    a.store(val2);
     115    std::atomic_notify_one(&a);
     116    t.join();
     117    return a.load();
     118  }
     119  
     120  template<typename Tp>
     121  Tp check_atomic_wait_notify(Tp val1, Tp val2)
     122  {
     123    using namespace std::literals::chrono_literals;
     124  
     125    std::mutex m;
     126    std::condition_variable cv;
     127    std::unique_lock<std::mutex> l(m);
     128  
     129    std::atomic<Tp> a(val1);
     130    std::thread t([&]
     131  		{
     132  		  {
     133  		    // This ensures we block until cv.wait(l) starts.
     134  		    std::lock_guard<std::mutex> ll(m);
     135  		  }
     136  		  cv.notify_one();
     137  		  std::atomic_wait(&a, val1);
     138  		  auto v = a.load();
     139  		  // TODO this needs to zero padding bits when we can do that
     140  		  if (__builtin_memcmp(&v, &val2, sizeof(Tp)) != 0)
     141  		    a = val1;
     142  		});
     143    cv.wait(l);
     144    std::this_thread::sleep_for(100ms);
     145    a.store(val2);
     146    std::atomic_notify_one(&a);
     147    t.join();
     148    return a.load();
     149  }
     150  
     151  template<typename Tp>
     152  struct check
     153  {
     154    check(Tp a = 0, Tp b = 42)
     155    {
     156      if constexpr (std::equality_comparable<Tp>)
     157      {
     158        VERIFY( check_wait_notify(a, b) == b);
     159        VERIFY( check_atomic_wait_notify(a, b) == b);
     160      }
     161      else
     162      {
     163        {
     164  	// TODO this needs to zero padding bits when we can do that
     165  	auto v = check_wait_notify(a, b);
     166  	VERIFY( __builtin_memcmp(&v, &b, sizeof(Tp)) == 0 );
     167        }
     168  
     169        {
     170  	// TODO this needs to zero padding bits when we can do that
     171  	auto v = check_atomic_wait_notify(a, b);
     172  	VERIFY( __builtin_memcmp(&v, &b, sizeof(Tp)) == 0);
     173        }
     174      }
     175    }
     176  };