brintos

brintos / llvm-project-archived public Read only

0
0
Text · 5.2 KiB · 1c52065 Raw
219 lines · cpp
1//===----------------------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9// <thread>10 11// class thread12 13// template <class F, class ...Args> thread(F&& f, Args&&... args);14 15// UNSUPPORTED: no-threads16// UNSUPPORTED: sanitizer-new-delete17 18#include <thread>19#include <new>20#include <atomic>21#include <cstdlib>22#include <cassert>23#include <vector>24 25#include "test_macros.h"26 27std::atomic<unsigned> throw_one(0xFFFF);28std::atomic<unsigned> outstanding_new(0);29 30 31void* operator new(std::size_t s) TEST_THROW_SPEC(std::bad_alloc)32{33    unsigned expected = throw_one;34    do {35        if (expected == 0) TEST_THROW(std::bad_alloc());36    } while (!throw_one.compare_exchange_weak(expected, expected - 1));37    ++outstanding_new;38    void* ret = std::malloc(s);39    if (!ret) {40      std::abort(); // placate MSVC's unchecked malloc warning (assert() won't silence it)41    }42    return ret;43}44 45void  operator delete(void* p) TEST_NOEXCEPT46{47    if (!p) return;48    --outstanding_new;49    std::free(p);50}51 52bool f_run = false;53 54struct F {55    std::vector<int> v_;  // so f's copy-ctor calls operator new56    explicit F() : v_(10) {}57    void operator()() const { f_run = true; }58};59F f;60 61class G62{63    int alive_;64public:65    static int n_alive;66    static bool op_run;67 68    G() : alive_(1) {++n_alive;}69    G(const G& g) : alive_(g.alive_) {++n_alive;}70    ~G() {alive_ = 0; --n_alive;}71 72    void operator()()73    {74        assert(alive_ == 1);75        assert(n_alive >= 1);76        op_run = true;77    }78 79    void operator()(int i, double j)80    {81        assert(alive_ == 1);82        assert(n_alive >= 1);83        assert(i == 5);84        assert(j == 5.5);85        op_run = true;86    }87};88 89int G::n_alive = 0;90bool G::op_run = false;91 92#if TEST_STD_VER >= 1193 94class MoveOnly95{96    MoveOnly(const MoveOnly&);97public:98    MoveOnly() {}99    MoveOnly(MoveOnly&&) {}100 101    void operator()(MoveOnly&&)102    {103    }104};105 106#endif107 108// Test throwing std::bad_alloc109//-----------------------------110// Concerns:111//  A Each allocation performed during thread construction should be performed112//    in the parent thread so that std::terminate is not called if113//    std::bad_alloc is thrown by new.114//  B std::thread's constructor should properly handle exceptions and not leak115//    memory.116// Plan:117//  1 Create a thread and count the number of allocations, 'numAllocs', it118//    performs.119//  2 For each allocation performed run a test where that allocation throws.120//    2.1 check that the exception can be caught in the parent thread.121//    2.2 Check that the functor has not been called.122//    2.3 Check that no memory allocated by the creation of the thread is leaked.123//  3 Finally check that a thread runs successfully if we throw after124//    'numAllocs + 1' allocations.125 126int numAllocs;127 128void test_throwing_new_during_thread_creation() {129#ifndef TEST_HAS_NO_EXCEPTIONS130    throw_one = 0xFFF;131    {132        std::thread t(f);133        t.join();134    }135    numAllocs = 0xFFF - throw_one;136    // i <= numAllocs means the last iteration is expected not to throw.137    for (int i=0; i <= numAllocs; ++i) {138        throw_one = i;139        f_run = false;140        unsigned old_outstanding = outstanding_new;141        try {142            std::thread t(f);143            assert(i == numAllocs); // Only final iteration will not throw.144            t.join();145            assert(f_run);146        } catch (std::bad_alloc const&) {147            assert(i < numAllocs);148            assert(!f_run); // (2.2)149        }150        ASSERT_WITH_LIBRARY_INTERNAL_ALLOCATIONS(old_outstanding == outstanding_new); // (2.3)151    }152    f_run = false;153    throw_one = 0xFFF;154#endif155}156 157int main(int, char**)158{159    test_throwing_new_during_thread_creation();160    {161        std::thread t(f);162        t.join();163        assert(f_run == true);164    }165 166    {167        assert(G::n_alive == 0);168        assert(!G::op_run);169        {170            G g;171            std::thread t(g);172            t.join();173        }174        assert(G::n_alive == 0);175        assert(G::op_run);176    }177    G::op_run = false;178#ifndef TEST_HAS_NO_EXCEPTIONS179    // The test below expects `std::thread` to call `new`, which may not be the180    // case for all implementations.181    LIBCPP_ASSERT(numAllocs > 0); // libc++ should call new.182    if (numAllocs > 0) {183        try184        {185            throw_one = 0;186            assert(G::n_alive == 0);187            assert(!G::op_run);188            std::thread t((G()));189            assert(false);190        }191        catch (std::bad_alloc const&)192        {193            throw_one = 0xFFFF;194            assert(G::n_alive == 0);195            assert(!G::op_run);196        }197    }198#endif199#if TEST_STD_VER >= 11200    {201        assert(G::n_alive == 0);202        assert(!G::op_run);203        {204            G g;205            std::thread t(g, 5, 5.5);206            t.join();207        }208        assert(G::n_alive == 0);209        assert(G::op_run);210    }211    {212        std::thread t = std::thread(MoveOnly(), MoveOnly());213        t.join();214    }215#endif216 217    return 0;218}219