• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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