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