1 // Copyright 2019 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_SEMAPHORE_EXTERNAL_LINUX_H_
16 #define VK_SEMAPHORE_EXTERNAL_LINUX_H_
17 
18 #include "System/Linux/MemFd.hpp"
19 #include "System/Memory.hpp"
20 
21 #include <errno.h>
22 #include <pthread.h>
23 #include <string.h>
24 #include <sys/mman.h>
25 
26 // An external semaphore implementation for Linux, that uses memfd-backed
27 // shared memory regions as the underlying implementation. The region contains
28 // a single SharedSemaphore instance, which is a reference-counted semaphore
29 // implementation based on a pthread process-shared mutex + condition variable
30 // pair.
31 //
32 // This implementation works on any Linux system with at least kernel 3.17
33 // (which should be sufficient for any not-so-recent Android system) and doesn't
34 // require special libraries installed on the system.
35 //
36 // NOTE: This is not interoperable with other Linux ICDs that use Linux kernel
37 // sync file objects (which correspond to
38 // VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT) instead.
39 //
40 
41 // A process-shared semaphore implementation that can be stored in
42 // a process-shared memory region. It also includes a reference count to
43 // ensure it is only destroyed when the last reference to it is dropped.
44 class SharedSemaphore
45 {
46 public:
SharedSemaphore(bool initialValue)47 	SharedSemaphore(bool initialValue)
48 	    : signaled(initialValue)
49 	{
50 		pthread_mutexattr_t mattr;
51 		pthread_mutexattr_init(&mattr);
52 		pthread_mutexattr_setpshared(&mattr, PTHREAD_PROCESS_SHARED);
53 		pthread_mutex_init(&mutex, &mattr);
54 		pthread_mutexattr_destroy(&mattr);
55 
56 		pthread_condattr_t cattr;
57 		pthread_condattr_init(&cattr);
58 		pthread_condattr_setpshared(&cattr, PTHREAD_PROCESS_SHARED);
59 		pthread_cond_init(&cond, &cattr);
60 		pthread_condattr_destroy(&cattr);
61 	}
62 
~SharedSemaphore()63 	~SharedSemaphore()
64 	{
65 		pthread_cond_destroy(&cond);
66 		pthread_mutex_destroy(&mutex);
67 	}
68 
69 	// Increment reference count.
addRef()70 	void addRef()
71 	{
72 		pthread_mutex_lock(&mutex);
73 		ref_count++;
74 		pthread_mutex_unlock(&mutex);
75 	}
76 
77 	// Decrement reference count and returns true iff it reaches 0.
deref()78 	bool deref()
79 	{
80 		pthread_mutex_lock(&mutex);
81 		bool result = (--ref_count == 0);
82 		pthread_mutex_unlock(&mutex);
83 		return result;
84 	}
85 
wait()86 	void wait()
87 	{
88 		pthread_mutex_lock(&mutex);
89 		while(!signaled)
90 		{
91 			pthread_cond_wait(&cond, &mutex);
92 		}
93 		// From Vulkan 1.1.119 spec, section 6.4.2:
94 		// Unlike fences or events, the act of waiting for a semaphore also
95 		// unsignals that semaphore.
96 		signaled = false;
97 		pthread_mutex_unlock(&mutex);
98 	}
99 
100 	// Just like wait() but never blocks. Returns true if the semaphore
101 	// was signaled (and reset by the function), or false otherwise.
102 	// Used to avoid using a background thread for waiting in the case
103 	// where the semaphore is already signaled.
tryWait()104 	bool tryWait()
105 	{
106 		pthread_mutex_lock(&mutex);
107 		bool result = signaled;
108 		if(result)
109 		{
110 			signaled = false;
111 		}
112 		pthread_mutex_unlock(&mutex);
113 		return result;
114 	}
115 
signal()116 	void signal()
117 	{
118 		pthread_mutex_lock(&mutex);
119 		signaled = true;
120 		pthread_cond_broadcast(&cond);
121 		pthread_mutex_unlock(&mutex);
122 	}
123 
124 private:
125 	pthread_mutex_t mutex;
126 	pthread_cond_t cond;
127 	int ref_count = 1;
128 	bool signaled = false;
129 };
130 
131 namespace vk {
132 
133 class OpaqueFdExternalSemaphore : public BinarySemaphore::External
134 {
135 public:
~OpaqueFdExternalSemaphore()136 	~OpaqueFdExternalSemaphore() { unmapRegion(); }
137 
138 	// Initialize instance by creating a new shared memory region.
init(bool initialState)139 	VkResult init(bool initialState) override
140 	{
141 		// Allocate or import the region's file descriptor.
142 		const size_t size = sw::memoryPageSize();
143 		// To be exportable, the PosixSemaphore must be stored in a shared
144 		// memory region.
145 		static int counter = 0;
146 		char name[40];
147 		snprintf(name, sizeof(name), "SwiftShader.Semaphore.%d", ++counter);
148 		if(!memfd.allocate(name, size))
149 		{
150 			TRACE("memfd.allocate() returned %s", strerror(errno));
151 			return VK_ERROR_INITIALIZATION_FAILED;
152 		}
153 		if(!mapRegion(size, true, initialState))
154 			return VK_ERROR_INITIALIZATION_FAILED;
155 
156 		return VK_SUCCESS;
157 	}
158 
159 	// Import an existing semaphore through its file descriptor.
importOpaqueFd(int fd)160 	VkResult importOpaqueFd(int fd) override
161 	{
162 		unmapRegion();
163 		memfd.importFd(fd);
164 		if(!mapRegion(sw::memoryPageSize(), false, false))
165 			return VK_ERROR_INITIALIZATION_FAILED;
166 
167 		return VK_SUCCESS;
168 	}
169 
170 	// Export the current semaphore as a duplicated file descriptor to the same
171 	// region. This can be consumed by importFd() running in a different
172 	// process.
exportOpaqueFd(int * pFd)173 	VkResult exportOpaqueFd(int *pFd) override
174 	{
175 		int fd = memfd.exportFd();
176 		if(fd < 0)
177 		{
178 			return VK_ERROR_INVALID_EXTERNAL_HANDLE;
179 		}
180 		*pFd = fd;
181 		return VK_SUCCESS;
182 	}
183 
wait()184 	void wait() override
185 	{
186 		semaphore->wait();
187 	}
188 
tryWait()189 	bool tryWait() override
190 	{
191 		return semaphore->tryWait();
192 	}
193 
signal()194 	void signal() override
195 	{
196 		semaphore->signal();
197 	}
198 
199 private:
unmapRegion()200 	void unmapRegion()
201 	{
202 		if(semaphore)
203 		{
204 			if(semaphore->deref())
205 			{
206 				semaphore->~SharedSemaphore();
207 			}
208 			memfd.unmap(semaphore, sw::memoryPageSize());
209 			memfd.close();
210 			semaphore = nullptr;
211 		}
212 	}
213 
214 	// Remap the shared region and setup the semaphore or increment its reference count.
mapRegion(size_t size,bool needsInitialization,bool initialValue)215 	bool mapRegion(size_t size, bool needsInitialization, bool initialValue)
216 	{
217 		// Map the region into memory and point the semaphore to it.
218 		void *addr = memfd.mapReadWrite(0, size);
219 		if(!addr)
220 		{
221 			TRACE("mmap() failed: %s", strerror(errno));
222 			return false;
223 		}
224 		semaphore = reinterpret_cast<SharedSemaphore *>(addr);
225 		if(needsInitialization)
226 		{
227 			new(semaphore) SharedSemaphore(initialValue);
228 		}
229 		else
230 		{
231 			semaphore->addRef();
232 		}
233 		return true;
234 	}
235 
236 	LinuxMemFd memfd;
237 	SharedSemaphore *semaphore = nullptr;
238 };
239 
240 }  // namespace vk
241 
242 #endif  // VK_SEMAPHORE_EXTERNAL_LINUX_H_
243