aboutsummaryrefslogtreecommitdiffstats
path: root/vespalib/src/tests/latch/latch_test.cpp
blob: 4f5092a6f26cd0bbb8dd3d83dfbfb2763b8c656a (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
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.

#include <vespa/vespalib/testkit/test_kit.h>
#include <vespa/vespalib/testkit/time_bomb.h>
#include <vespa/vespalib/util/gate.h>
#include <vespa/vespalib/util/latch.h>

using namespace vespalib;

TEST("require that write then read works") {
    Latch<int> latch;
    EXPECT_TRUE(!latch.has_value());
    latch.write(42);
    EXPECT_TRUE(latch.has_value());
    EXPECT_EQUAL(latch.read(), 42);
    EXPECT_TRUE(!latch.has_value());
}

TEST_MT_FFF("require that read waits for write", 2, Latch<int>(), Gate(), TimeBomb(60)) {
    if (thread_id == 0) {
        EXPECT_TRUE(!f2.await(10ms));
        f1.write(123);
        EXPECT_TRUE(f2.await(60s));
    } else {
        EXPECT_EQUAL(f1.read(), 123);
        f2.countDown();
    }
}

TEST_MT_FFF("require that write waits for read", 2, Latch<int>(), Gate(), TimeBomb(60)) {
    if (thread_id == 0) {
        f1.write(123);
        f1.write(456);
        f2.countDown();
    } else {
        EXPECT_TRUE(!f2.await(10ms));
        EXPECT_EQUAL(f1.read(), 123);
        EXPECT_TRUE(f2.await(60s));
        EXPECT_EQUAL(f1.read(), 456);
    }
}

struct MyInt {
    int value;
    MyInt(int value_in) : value(value_in) {}
    MyInt(MyInt &&rhs) = default;
    MyInt(const MyInt &rhs) = delete;
    MyInt &operator=(const MyInt &rhs) = delete;
    MyInt &operator=(MyInt &&rhs) = delete;
};

TEST("require that un-assignable non-default-constructable move-only objects can be used") {
    Latch<MyInt> latch;
    latch.write(MyInt(1337));
    EXPECT_EQUAL(latch.read().value, 1337);
}

struct MyObj {
    static int total;
    int *with_state;
    MyObj(int &with_state_in) : with_state(&with_state_in) {}
    MyObj(MyObj &&rhs) {
        with_state = rhs.with_state;
        rhs.with_state = nullptr;
    }
    void detach() { with_state = nullptr; }
    ~MyObj() {
        ++total;
        if (with_state) {
            ++(*with_state);
        }
    }
    MyObj(const MyObj &rhs) = delete;
    MyObj &operator=(const MyObj &rhs) = delete;
    MyObj &operator=(MyObj &&rhs) = delete;
};
int MyObj::total = 0;

TEST("require that latched objects are appropriately destructed") {
    int with_state = 0;
    int total_sample = 0;
    {
        Latch<MyObj> latch1;
        Latch<MyObj> latch2;
        Latch<MyObj> latch3;
        latch2.write(MyObj(with_state));
        latch3.write(MyObj(with_state));
        latch2.read().detach();
        EXPECT_TRUE(!latch1.has_value());
        EXPECT_TRUE(!latch2.has_value());
        EXPECT_TRUE(latch3.has_value());
        EXPECT_EQUAL(with_state, 0);
        EXPECT_GREATER_EQUAL(MyObj::total, 1);
        total_sample = MyObj::total;
    }
    EXPECT_EQUAL(MyObj::total, total_sample + 1);
    EXPECT_EQUAL(with_state, 1);
}

TEST_MAIN() { TEST_RUN_ALL(); }