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.
44class SharedSemaphore
45{
46public:
47 SharedSemaphore()
48 {
49 pthread_mutexattr_t mattr;
50 pthread_mutexattr_init(&mattr);
51 pthread_mutexattr_setpshared(&mattr, PTHREAD_PROCESS_SHARED);
52 pthread_mutex_init(&mutex, &mattr);
53 pthread_mutexattr_destroy(&mattr);
54
55 pthread_condattr_t cattr;
56 pthread_condattr_init(&cattr);
57 pthread_condattr_setpshared(&cattr, PTHREAD_PROCESS_SHARED);
58 pthread_cond_init(&cond, &cattr);
59 pthread_condattr_destroy(&cattr);
60 }
61
62 ~SharedSemaphore()
63 {
64 pthread_cond_destroy(&cond);
65 pthread_mutex_destroy(&mutex);
66 }
67
68 // Increment reference count.
69 void addRef()
70 {
71 pthread_mutex_lock(&mutex);
72 ref_count++;
73 pthread_mutex_unlock(&mutex);
74 }
75
76 // Decrement reference count and returns true iff it reaches 0.
77 bool deref()
78 {
79 pthread_mutex_lock(&mutex);
80 bool result = (--ref_count == 0);
81 pthread_mutex_unlock(&mutex);
82 return result;
83 }
84
85 void wait()
86 {
87 pthread_mutex_lock(&mutex);
88 while (!signaled)
89 {
90 pthread_cond_wait(&cond, &mutex);
91 }
92 // From Vulkan 1.1.119 spec, section 6.4.2:
93 // Unlike fences or events, the act of waiting for a semaphore also
94 // unsignals that semaphore.
95 signaled = false;
96 pthread_mutex_unlock(&mutex);
97 }
98
99 // Just like wait() but never blocks. Returns true if the semaphore
100 // was signaled (and reset by the function), or false otherwise.
101 // Used to avoid using a background thread for waiting in the case
102 // where the semaphore is already signaled.
103 bool tryWait()
104 {
105 pthread_mutex_lock(&mutex);
106 bool result = signaled;
107 if (result)
108 {
109 signaled = false;
110 }
111 pthread_mutex_unlock(&mutex);
112 return result;
113 }
114
115 void signal()
116 {
117 pthread_mutex_lock(&mutex);
118 signaled = true;
119 pthread_cond_broadcast(&cond);
120 pthread_mutex_unlock(&mutex);
121 }
122
123private:
124 pthread_mutex_t mutex;
125 pthread_cond_t cond;
126 int ref_count = 1;
127 bool signaled = false;
128};
129
130namespace vk
131{
132
133class Semaphore::External {
134public:
135 // The type of external semaphore handle types supported by this implementation.
136 static const VkExternalSemaphoreHandleTypeFlags kExternalSemaphoreHandleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT;
137
138 // Default constructor. Note that one should call either init() or
139 // importFd() before any call to wait() or signal().
140 External() = default;
141
142 ~External() { close(); }
143
144 // Initialize instance by creating a new shared memory region.
145 void init()
146 {
147 // Allocate or import the region's file descriptor.
148 const size_t size = sw::memoryPageSize();
149 // To be exportable, the PosixSemaphore must be stored in a shared
150 // memory region.
151 static int counter = 0;
152 char name[40];
153 snprintf(name, sizeof(name), "SwiftShader.Semaphore.%d", ++counter);
154 if (!memfd.allocate(name, size))
155 {
156 ABORT("memfd.allocate() returned %s", strerror(errno));
157 }
158 mapRegion(size, true);
159 }
160
161 // Import an existing semaphore through its file descriptor.
162 VkResult importFd(int fd)
163 {
164 close();
165 memfd.importFd(fd);
166 mapRegion(sw::memoryPageSize(), false);
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.
173 VkResult exportFd(int* pFd) const
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
184 void wait()
185 {
186 semaphore->wait();
187 }
188
189 bool tryWait()
190 {
191 return semaphore->tryWait();
192 }
193
194 void signal()
195 {
196 semaphore->signal();
197 }
198
199private:
200 // Unmap the semaphore if needed and close its file descriptor.
201 void close()
202 {
203 if (semaphore)
204 {
205 if (semaphore->deref())
206 {
207 semaphore->~SharedSemaphore();
208 }
209 memfd.unmap(semaphore, sw::memoryPageSize());
210 memfd.close();
211 semaphore = nullptr;
212 }
213 }
214
215 // Remap the shared region and setup the semaphore or increment its reference count.
216 void mapRegion(size_t size, bool needInitialization)
217 {
218 // Map the region into memory and point the semaphore to it.
219 void* addr = memfd.mapReadWrite(0, size);
220 if (!addr)
221 {
222 ABORT("mmap() failed: %s", strerror(errno));
223 }
224 semaphore = reinterpret_cast<SharedSemaphore *>(addr);
225 if (needInitialization)
226 {
227 new (semaphore) SharedSemaphore();
228 }
229 else
230 {
231 semaphore->addRef();
232 }
233 }
234
235 LinuxMemFd memfd;
236 SharedSemaphore* semaphore = nullptr;
237};
238
239} // namespace vk
240
241#endif // VK_SEMAPHORE_EXTERNAL_LINUX_H_
242