text: // Copyright 2019 The SwiftShader Authors. All Rights Reserved.
  //
  // Licensed under the Apache License, Version 2.0 (the "License");
  // you may not use this file except in compliance with the License.
  // You may obtain a copy of the License at
  //
  //    http://www.apache.org/licenses/LICENSE-2.0
  //
  // Unless required by applicable law or agreed to in writing, software
  // distributed under the License is distributed on an "AS IS" BASIS,
  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  // See the License for the specific language governing permissions and
  // limitations under the License.
  
  #ifndef VK_SEMAPHORE_EXTERNAL_LINUX_H_
  #define VK_SEMAPHORE_EXTERNAL_LINUX_H_
  
  #include "System/Linux/MemFd.hpp"
  #include "System/Memory.hpp"
  
  #include <errno.h>
  #include <pthread.h>
  #include <string.h>
  #include <sys/mman.h>
  
  // An external semaphore implementation for Linux, that uses memfd-backed
  // shared memory regions as the underlying implementation. The region contains
  // a single SharedSemaphore instance, which is a reference-counted semaphore
  // implementation based on a pthread process-shared mutex + condition variable
  // pair.
  //
  // This implementation works on any Linux system with at least kernel 3.17
  // (which should be sufficient for any not-so-recent Android system) and doesn't
  // require special libraries installed on the system.
  //
  // NOTE: This is not interoperable with other Linux ICDs that use Linux kernel
  // sync file objects (which correspond to
  // VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT) instead.
  //
  
  // A process-shared semaphore implementation that can be stored in
  // a process-shared memory region. It also includes a reference count to
  // ensure it is only destroyed when the last reference to it is dropped.
  class SharedSemaphore
  {
  public:
  	SharedSemaphore(bool initialValue)
  	    : signaled(initialValue)
  	{
  		pthread_mutexattr_t mattr;
  		pthread_mutexattr_init(&mattr);
  		pthread_mutexattr_setpshared(&mattr, PTHREAD_PROCESS_SHARED);
  		pthread_mutex_init(&mutex, &mattr);
  		pthread_mutexattr_destroy(&mattr);
  
  		pthread_condattr_t cattr;
  		pthread_condattr_init(&cattr);
  		pthread_condattr_setpshared(&cattr, PTHREAD_PROCESS_SHARED);
  		pthread_cond_init(&cond, &cattr);
  		pthread_condattr_destroy(&cattr);
  	}
  
  	~SharedSemaphore()
  	{
  		pthread_cond_destroy(&cond);
  		pthread_mutex_destroy(&mutex);
  	}
  
  	// Increment reference count.
  	void addRef()
  	{
  		pthread_mutex_lock(&mutex);
  		ref_count++;
  		pthread_mutex_unlock(&mutex);
  	}
  
  	// Decrement reference count and returns true iff it reaches 0.
  	bool deref()
  	{
  		pthread_mutex_lock(&mutex);
  		bool result = (--ref_count == 0);
  		pthread_mutex_unlock(&mutex);
  		return result;
  	}
  
  	void wait()
  	{
  		pthread_mutex_lock(&mutex);
  		while(!signaled)
  		{
  			pthread_cond_wait(&cond, &mutex);
  		}
  		// From Vulkan 1.1.119 spec, section 6.4.2:
  		// Unlike fences or events, the act of waiting for a semaphore also
  		// unsignals that semaphore.
  		signaled = false;
  		pthread_mutex_unlock(&mutex);
  	}
  
  	// Just like wait() but never blocks. Returns true if the semaphore
  	// was signaled (and reset by the function), or false otherwise.
  	// Used to avoid using a background thread for waiting in the case
  	// where the semaphore is already signaled.
  	bool tryWait()
  	{
  		pthread_mutex_lock(&mutex);
  		bool result = signaled;
  		if(result)
  		{
  			signaled = false;
  		}
  		pthread_mutex_unlock(&mutex);
  		return result;
  	}
  
  	void signal()
  	{
  		pthread_mutex_lock(&mutex);
  		signaled = true;
  		pthread_cond_broadcast(&cond);
  		pthread_mutex_unlock(&mutex);
  	}
  
  private:
  	pthread_mutex_t mutex;
  	pthread_cond_t cond;
  	int ref_count = 1;
  	bool signaled = false;
  };
  
  namespace vk {
  
  class OpaqueFdExternalSemaphore : public Semaphore::External
  {
  public:
  	~OpaqueFdExternalSemaphore() { unmapRegion(); }
  
  	// Initialize instance by creating a new shared memory region.
  	VkResult init(bool initialState) override
  	{
  		// Allocate or import the region's file descriptor.
  		const size_t size = sw::memoryPageSize();
  		// To be exportable, the PosixSemaphore must be stored in a shared
  		// memory region.
  		static int counter = 0;
  		char name[40];
  		snprintf(name, sizeof(name), "SwiftShader.Semaphore.%d", ++counter);
  		if(!memfd.allocate(name, size))
  		{
  			TRACE("memfd.allocate() returned %s", strerror(errno));
  			return VK_ERROR_INITIALIZATION_FAILED;
  		}
  		if(!mapRegion(size, true, initialState))
  			return VK_ERROR_INITIALIZATION_FAILED;
  
  		return VK_SUCCESS;
  	}
  
  	// Import an existing semaphore through its file descriptor.
  	VkResult importOpaqueFd(int fd) override
  	{
  		unmapRegion();
  		memfd.importFd(fd);
  		if(!mapRegion(sw::memoryPageSize(), false, false))
  			return VK_ERROR_INITIALIZATION_FAILED;
  
  		return VK_SUCCESS;
  	}
  
  	// Export the current semaphore as a duplicated file descriptor to the same
  	// region. This can be consumed by importFd() running in a different
  	// process.
  	VkResult exportOpaqueFd(int *pFd) override
  	{
  		int fd = memfd.exportFd();
  		if(fd < 0)
  		{
  			return VK_ERROR_INVALID_EXTERNAL_HANDLE;
  		}
  		*pFd = fd;
  		return VK_SUCCESS;
  	}
  
  	void wait() override
  	{
  		semaphore->wait();
  	}
  
  	bool tryWait() override
  	{
  		return semaphore->tryWait();
  	}
  
  	void signal() override
  	{
  		semaphore->signal();
  	}
  
  private:
  	void unmapRegion()
  	{
  		if(semaphore)
  		{
  			if(semaphore->deref())
  			{
  				semaphore->~SharedSemaphore();
  			}
  			memfd.unmap(semaphore, sw::memoryPageSize());
  			memfd.close();
  			semaphore = nullptr;
  		}
  	}
  
  	// Remap the shared region and setup the semaphore or increment its reference count.
  	bool mapRegion(size_t size, bool needsInitialization, bool initialValue)
  	{
  		// Map the region into memory and point the semaphore to it.
  		void *addr = memfd.mapReadWrite(0, size);
  		if(!addr)
  		{
  			TRACE("mmap() failed: %s", strerror(errno));
  			return false;
  		}
  		semaphore = reinterpret_cast<SharedSemaphore *>(addr);
  		if(needsInitialization)
  		{
  			new(semaphore) SharedSemaphore(initialValue);
  		}
  		else
  		{
  			semaphore->addRef();
  		}
  		return true;
  	}
  
  	LinuxMemFd memfd;
  	SharedSemaphore *semaphore = nullptr;
  };
  
  }  // namespace vk
  
  #endif  // VK_SEMAPHORE_EXTERNAL_LINUX_H_
  