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