-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathsystematic_testing_resources.h
133 lines (115 loc) · 4.66 KB
/
systematic_testing_resources.h
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
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#ifndef SYSTEMATIC_TESTING_RESOURCES_H
#define SYSTEMATIC_TESTING_RESOURCES_H
#include <optional>
#include "systematic_testing.h"
namespace SystematicTesting::Resources
{
// Returns a new resource id, or nullopt if such an id cannot be assigned.
std::optional<size_t> assign_resource_id()
{
std::optional<size_t> id = std::nullopt;
if (auto test_engine = GetTestEngine())
{
id = test_engine->create_next_resource();
}
return id;
}
// Manages a controlled resource that can be used to instrument synchronization primitives
// that can be acquired by controlled operations in a shared or exclusive manner.
class SynchronizedResource final
{
public:
// The status of the resource.
enum class Status
{
Released = 0,
AcquiredExclusive,
AcquiredShared
};
SynchronizedResource(bool is_reentrant = false) noexcept :
m_id(assign_resource_id()),
m_is_reentrant(is_reentrant),
m_status(Status::Released)
{
}
~SynchronizedResource()
{
auto test_engine = GetTestEngine();
if (test_engine && m_id.has_value())
{
test_engine->delete_resource(m_id.value());
}
}
// Acquires the resource in an exclusive or shared manner.
void acquire(bool is_shared = false)
{
auto test_engine = GetTestEngine();
if (test_engine && test_engine->is_resource_attached(m_id))
{
// Loop until the resource can be acquired.
while (true)
{
// The resource can only be acquired in the following cases:
// 1. The resource is released.
// 2. The resource is shared and is being acquired in a shared manner.
// 3. The resource is reentrant, exclusive and is being acquired by the same owner.
if (m_status == Status::Released ||
(is_shared && m_status == Status::AcquiredShared) ||
(m_is_reentrant && m_status == Status::AcquiredExclusive &&
test_engine->is_resource_owned_by_current_operation(m_id.value())))
{
// Acquire the resource and stop retrying.
m_status = is_shared ? Status::AcquiredShared : Status::AcquiredExclusive;
break;
}
// Notify the engine that the current operation is blocked
// until the resource is released.
test_engine->wait_resource(m_id.value());
}
if (test_engine->settings().is_resource_race_checking_enabled())
{
// Introduce an interleaving before the resource is acquired.
test_engine->schedule_next_operation();
}
// Notify the engine that the current operation has acquired the resource.
test_engine->acquire_resource(m_id.value());
}
}
// Releases the resource if it was acquired exclusively or in a shared manner.
void release()
{
auto test_engine = GetTestEngine();
if (test_engine && test_engine->is_resource_attached(m_id))
{
if (m_status != Status::Released)
{
// Notify the engine that the resource has been released by this owner.
if (test_engine->try_release_resource(m_id.value()))
{
m_status = Status::Released;
if (test_engine->settings().is_resource_race_checking_enabled())
{
// Introduce an interleaving after the resource is released.
test_engine->schedule_next_operation();
}
}
}
}
}
// Returns the current status of the resource.
Status status() const noexcept
{
return m_status;
}
private:
// The unique id of this resource.
const std::optional<size_t> m_id;
// True if the resource can be acquired multiple times by the same owner.
const bool m_is_reentrant;
// The status of this resource.
Status m_status;
};
} // namespace SystematicTesting::Resources
#endif // SYSTEMATIC_TESTING_RESOURCES_H