1 // Copyright 2021 The SwiftShader Authors. All Rights Reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #ifndef VK_TIMELINE_SEMAPHORE_HPP_
16 #define VK_TIMELINE_SEMAPHORE_HPP_
17
18 #include "VkConfig.hpp"
19 #include "VkObject.hpp"
20 #include "VkSemaphore.hpp"
21
22 #include "marl/conditionvariable.h"
23 #include "marl/mutex.h"
24
25 #include "System/Synchronization.hpp"
26
27 #include <chrono>
28
29 namespace vk {
30
31 struct Shared;
32
33 // Timeline Semaphores track a 64-bit payload instead of a binary payload.
34 //
35 // A timeline does not have a "signaled" and "unsignalled" state. Threads instead wait
36 // for the payload to become a certain value. When a thread signals the timeline, it provides
37 // a new payload that is greater than the current payload.
38 //
39 // There is no way to reset a timeline or to decrease the payload's value. A user must instead
40 // create a new timeline with a new initial payload if they desire this behavior.
41 class TimelineSemaphore : public Semaphore, public Object<TimelineSemaphore, VkSemaphore>
42 {
43 public:
44 TimelineSemaphore(const VkSemaphoreCreateInfo *pCreateInfo, void *mem, const VkAllocationCallbacks *pAllocator);
45 TimelineSemaphore();
46
47 static size_t ComputeRequiredAllocationSize(const VkSemaphoreCreateInfo *pCreateInfo);
48
49 // Block until this semaphore is signaled with the specified value;
50 void wait(uint64_t value);
51
52 // Wait until a certain amount of time has passed or until the specified value is signaled.
53 template<class CLOCK, class DURATION>
54 VkResult wait(uint64_t value, const std::chrono::time_point<CLOCK, DURATION> end_ns);
55
56 // Set the payload to the specified value and signal all waiting threads.
57 void signal(uint64_t value);
58
59 // Retrieve the current payload. This should not be used to make thread execution decisions
60 // as there's no guarantee that the value returned here matches the actual payload's value.
61 uint64_t getCounterValue();
62
63 // Dependent timeline semaphores allow an 'any' semaphore to be created that can wait on the
64 // state of multiple other timeline semaphores and be signaled like a binary semaphore
65 // if any of its parent semaphores are signaled with a certain value.
66 //
67 // Since a timeline semaphore can be signalled with nearly any value, but threads waiting
68 // on a timeline semaphore only unblock when a specific value is signaled, dependents can't
69 // naively become signaled whenever their parent semaphores are signaled with a new value.
70 // Instead, the dependent semaphore needs to wait for its parent semaphore to be signaled
71 // with a specific value as well. This specific value may differ for each parent semaphore.
72 //
73 // So this function adds other as a dependent semaphore, and tells it to only become unsignaled
74 // by this semaphore when this semaphore is signaled with waitValue.
75 void addDependent(TimelineSemaphore &other, uint64_t waitValue);
76 void addDependency(int id, uint64_t waitValue);
77
78 // Tells this semaphore to become signaled as part of a dependency chain when the parent semaphore
79 // with the specified id is signaled with the specified waitValue.
80 void addToWaitMap(int parentId, uint64_t waitValue);
81
82 // Clean up any allocated resources
83 void destroy(const VkAllocationCallbacks *pAllocator);
84
85 private:
86 // Track the 64-bit payload. Timeline Semaphores have a shared_ptr<Shared>
87 // that they can pass to other Timeline Semaphores to create dependency chains.
88 struct Shared
89 {
90 private:
91 // Guards access to all the resources that may be accessed by other threads.
92 // No clang Thread Safety Analysis is used on variables guarded by mutex
93 // as there is an issue with TSA. Despite instrumenting everything properly,
94 // compilation will fail when a lambda function uses a guarded resource.
95 marl::mutex mutex;
96
97 static std::atomic<int> nextId;
98
99 public:
100 Shared(marl::Allocator *allocator, uint64_t initialState);
101
102 // Block until this semaphore is signaled with the specified value;
103 void wait(uint64_t value);
104 // Wait until a certain amount of time has passed or until the specified value is signaled.
105 template<class CLOCK, class DURATION>
106 VkResult wait(uint64_t value, const std::chrono::time_point<CLOCK, DURATION> end_ns);
107
108 // Pass a signal down to a dependent.
109 void signal(int parentId, uint64_t value);
110 // Set the payload to the specified value and signal all waiting threads.
111 void signal(uint64_t value);
112
113 // Retrieve the current payload. This should not be used to make thread execution decisions
114 // as there's no guarantee that the value returned here matches the actual payload's value.
115 uint64_t getCounterValue();
116
117 // Add the other semaphore's Shared to deps.
118 void addDependent(TimelineSemaphore &other);
119
120 // Add {id, waitValue} as a key-value pair to waitMap.
121 void addDependency(int id, uint64_t waitValue);
122
123 // Entry point to the marl threading library that handles blocking and unblocking.
124 marl::ConditionVariable cv;
125
126 // TODO(b/181683382) -- Add Thread Safety Analysis instrumentation when it can properly
127 // analyze lambdas.
128 // The 64-bit payload.
129 uint64_t counter;
130
131 // A list of this semaphore's dependents.
132 marl::containers::vector<std::shared_ptr<Shared>, 1> deps;
133
134 // A map of {parentId: waitValue} pairs that tracks when this semaphore should unblock if it's
135 // signaled as a dependent by another semaphore.
136 std::map<int, uint64_t> waitMap;
137
138 // An ID that's unique for each instance of Shared
139 const int id;
140 };
141
142 std::shared_ptr<Shared> shared;
143 };
144
145 template<typename Clock, typename Duration>
wait(uint64_t value,const std::chrono::time_point<Clock,Duration> timeout)146 VkResult TimelineSemaphore::wait(uint64_t value,
147 const std::chrono::time_point<Clock, Duration> timeout)
148 {
149 return shared->wait(value, timeout);
150 }
151
152 template<typename Clock, typename Duration>
wait(uint64_t value,const std::chrono::time_point<Clock,Duration> timeout)153 VkResult TimelineSemaphore::Shared::wait(uint64_t value,
154 const std::chrono::time_point<Clock, Duration> timeout)
155 {
156 marl::lock lock(mutex);
157 if(!cv.wait_until(lock, timeout, [&]() { return counter == value; }))
158 {
159 return VK_TIMEOUT;
160 }
161 return VK_SUCCESS;
162 }
163
164 } // namespace vk
165
166 #endif // VK_TIMELINE_SEMAPHORE_HPP_
167