/***********************************************************************
THIS FILE IS AUTOMATICALLY GENERATED. DO NOT MODIFY
DEVELOPER: Zihan Chen(vczh)
***********************************************************************/
#include "VlppOS.Linux.h"

/***********************************************************************
.\FILESYSTEM.LINUX.CPP
***********************************************************************/
/***********************************************************************
Author: Zihan Chen (vczh)
Licensed under https://github.com/vczh-libraries/License
***********************************************************************/

#include <sys/stat.h>
#include <dirent.h>
#include <unistd.h>

#ifndef VCZH_GCC
static_assert(false, "Do not build this file for Windows applications.");
#endif

namespace vl
{
	namespace stream
	{
		extern Ptr<IFileStreamImpl>		CreateOSFileStreamImpl(const WString& fileName, FileStream::AccessRight accessRight);
	}

	namespace filesystem
	{
		using namespace collections;
		using namespace stream;

/***********************************************************************
LinuxFileSystemImpl
***********************************************************************/

		class LinuxFileSystemImpl : public feature_injection::FeatureImpl<IFileSystemImpl>
		{
		public:
			// FilePath operations implementation
			wchar_t GetPathDelimiter() const override
			{
				return L'/';
			}

			const wchar_t* GetCompatibleDelimiters() const override
			{
				return L"";
			}

			WString ConcatPath(const WString& fullPath, const WString& relativePath) const override
			{
				auto delimiter = WString::FromChar(GetPathDelimiter());
				if (IsRoot(fullPath))
				{
					return delimiter + relativePath;
				}

				return fullPath + delimiter + relativePath;
			}

			void Initialize(WString& fullPath) const override
			{
				{
					Array<wchar_t> buffer(fullPath.Length() + 1);
					wcscpy(&buffer[0], fullPath.Buffer());
					FilePath::NormalizeDelimiters(buffer);
					fullPath = &buffer[0];
				}

				if (fullPath.Length() == 0)
					fullPath = WString::FromChar(GetPathDelimiter());

				if (fullPath[0] != GetPathDelimiter())
				{
					char buffer[PATH_MAX] = { 0 };
					getcwd(buffer, PATH_MAX);
					fullPath = atow(AString(buffer)) + WString::FromChar(GetPathDelimiter()) + fullPath;
				}

				{
					collections::List<WString> components;
					FilePath::GetPathComponents(fullPath, components);
					for (int i = 0; i < components.Count(); i++)
					{
						if (components[i] == L".")
						{
							components.RemoveAt(i);
							i--;
						}
						else if (components[i] == L"..")
						{
							if (i > 0)
							{
								components.RemoveAt(i);
								components.RemoveAt(i - 1);
								i -= 2;
							}
							else
							{
								throw ArgumentException(L"Illegal path.");
							}
						}
					}

					fullPath = FilePath::ComponentsToPath(components);
				}

				FilePath::TrimLastDelimiter(fullPath);
			}

			bool IsFile(const WString& fullPath) const override
			{
				struct stat info;
				AString path = wtoa(fullPath);
				int result = stat(path.Buffer(), &info);
				if(result != 0) return false;
				else return S_ISREG(info.st_mode);
			}

			bool IsFolder(const WString& fullPath) const override
			{
				struct stat info;
				AString path = wtoa(fullPath);
				int result = stat(path.Buffer(), &info);
				if(result != 0) return false;
				else return S_ISDIR(info.st_mode);
			}

			bool IsRoot(const WString& fullPath) const override
			{
				return fullPath == WString::FromChar(GetPathDelimiter());
			}

			WString GetRelativePathFor(const WString& fromPath, const WString& toPath) const override
			{
				if (fromPath.Length() == 0 || toPath.Length() == 0 || fromPath[0] != toPath[0])
				{
					return toPath;
				}

				collections::List<WString> srcComponents, tgtComponents, resultComponents;
				FilePath::GetPathComponents(IsFolder(fromPath) ? fromPath : FilePath(fromPath).GetFolder().GetFullPath(), srcComponents);
				FilePath::GetPathComponents(toPath, tgtComponents);

				int minLength = srcComponents.Count() <= tgtComponents.Count() ? srcComponents.Count() : tgtComponents.Count();
				int lastCommonComponent = 0;
				for (int i = 0; i < minLength; i++)
				{
					if (srcComponents[i] == tgtComponents[i])
					{
						lastCommonComponent = i;
					}
					else
						break;
				}

				for (int i = lastCommonComponent + 1; i < srcComponents.Count(); i++)
				{
					resultComponents.Add(L"..");
				}

				for (int i = lastCommonComponent + 1; i < tgtComponents.Count(); i++)
				{
					resultComponents.Add(tgtComponents[i]);
				}

				return FilePath::ComponentsToPath(resultComponents);
			}

			// File operations implementation
			bool FileDelete(const FilePath& filePath) const override
			{
				AString path = wtoa(filePath.GetFullPath());
				return unlink(path.Buffer()) == 0;
			}

			bool FileRename(const FilePath& filePath, const WString& newName) const override
			{
				AString oldFileName = wtoa(filePath.GetFullPath());
				AString newFileName = wtoa((filePath.GetFolder() / newName).GetFullPath());
				return rename(oldFileName.Buffer(), newFileName.Buffer()) == 0;
			}

			// Folder operations implementation
			bool GetFolders(const FilePath& folderPath, collections::List<Folder>& folders) const override
			{
				DIR *dir;
				AString searchPath = wtoa(folderPath.GetFullPath());

				if ((dir = opendir(searchPath.Buffer())) == NULL)
				{
					return false;
				}

				struct dirent* entry;
				while ((entry = readdir(dir)) != NULL)
				{
					WString childName = atow(AString(entry->d_name));
					FilePath childFullPath = folderPath / childName;
					if (childName != L"." && childName != L".." && childFullPath.IsFolder())
					{
						folders.Add(Folder(childFullPath));
					}
				}

				if (closedir(dir) != 0)
				{
					return false;
				}

				return true;
			}

			bool GetFiles(const FilePath& folderPath, collections::List<File>& files) const override
			{
				DIR* dir;
				AString searchPath = wtoa(folderPath.GetFullPath());

				if ((dir = opendir(searchPath.Buffer())) == NULL)
				{
					return false;
				}

				struct dirent* entry;
				while ((entry = readdir(dir)) != NULL)
				{
					FilePath childFullPath = folderPath / (atow(AString(entry->d_name)));
					if (childFullPath.IsFile())
					{
						files.Add(File(childFullPath));
					}
				}

				if (closedir(dir) != 0)
				{
					return false;
				}

				return true;
			}

			bool CreateFolder(const FilePath& folderPath) const override
			{
				AString path = wtoa(folderPath.GetFullPath());
				return mkdir(path.Buffer(), 0777) == 0;
			}

			bool DeleteFolder(const FilePath& folderPath) const override
			{
				AString path = wtoa(folderPath.GetFullPath());
				return rmdir(path.Buffer()) == 0;
			}

			bool FolderRename(const FilePath& folderPath, const WString& newName) const override
			{
				AString oldFileName = wtoa(folderPath.GetFullPath());
				AString newFileName = wtoa((folderPath.GetFolder() / newName).GetFullPath());
				return rename(oldFileName.Buffer(), newFileName.Buffer()) == 0;
			}

			Ptr<stream::IFileStreamImpl> GetFileStreamImpl(const WString& fileName, stream::FileStream::AccessRight accessRight) const override
			{
				return stream::CreateOSFileStreamImpl(fileName, accessRight);
			}
		};

/***********************************************************************
Global FileSystem Implementation
***********************************************************************/

		IFileSystemImpl* GetOSFileSystemImpl()
		{
			static LinuxFileSystemImpl osFileSystemImpl;
			return &osFileSystemImpl;
		}
	}
}


/***********************************************************************
.\LOCALE.LINUX.CPP
***********************************************************************/
/***********************************************************************
Author: Zihan Chen (vczh)
Licensed under https://github.com/vczh-libraries/License
***********************************************************************/


#ifndef VCZH_GCC
static_assert(false, "Do not build this file for Windows applications.");
#endif

namespace vl
{
	ILocaleImpl* GetOSLocaleImpl()
	{
		static EnUsLocaleImpl linuxLocaleImpl;
		return &linuxLocaleImpl;
	}
}


/***********************************************************************
.\THREADING.LINUX.CPP
***********************************************************************/
/***********************************************************************
Author: Zihan Chen (vczh)
Licensed under https://github.com/vczh-libraries/License
***********************************************************************/

#include <pthread.h>
#include <fcntl.h>
#include <semaphore.h>
#include <errno.h>
#include <time.h>
#if defined VCZH_APPLE
#include <CoreFoundation/CoreFoundation.h>
#endif

#ifndef VCZH_GCC
static_assert(false, "Do not build this file for Windows applications.");
#endif

namespace vl
{
	using namespace threading_internal;
	using namespace collections;


/***********************************************************************
Thread
***********************************************************************/

	namespace threading_internal
	{
		struct ThreadData
		{
			pthread_t					id;
			EventObject					ev;
			bool						deleteAfterStopped = false;
		};

		class ProceduredThread : public Thread
		{
		private:
			Thread::ThreadProcedure		procedure;
			void*						argument;

		protected:
			void Run()
			{
				procedure(this, argument);
			}
		public:
			ProceduredThread(Thread::ThreadProcedure _procedure, void* _argument, bool _deleteAfterStopped)
				:procedure(_procedure)
				,argument(_argument)
			{
				internalData->deleteAfterStopped = _deleteAfterStopped;
			}
		};

		class LambdaThread : public Thread
		{
		private:
			Func<void()>				procedure;

		protected:
			void Run()
			{
				procedure();
			}
		public:
			LambdaThread(const Func<void()>& _procedure, bool _deleteAfterStopped)
				:procedure(_procedure)
			{
				internalData->deleteAfterStopped = _deleteAfterStopped;
			}
		};
	}

	void InternalThreadProc(Thread* thread)
	{
		auto deleteAfterStopped = thread->internalData->deleteAfterStopped;
		ThreadLocalStorage::FixStorages();
		try
		{
			thread->Run();
			ThreadLocalStorage::ClearStorages();
			thread->threadState=Thread::Stopped;
			thread->internalData->ev.Signal();
		}
		catch (...)
		{
			ThreadLocalStorage::ClearStorages();
			throw;
		}
		if(deleteAfterStopped)
		{
			delete thread;
		}
	}

	void* InternalThreadProcWrapper(void* lpParameter)
	{
		InternalThreadProc((Thread*)lpParameter);
		return 0;
	}

	Thread::Thread()
	{
		internalData=new ThreadData;
		internalData->ev.CreateManualUnsignal(false);
		threadState=Thread::NotStarted;
	}

	Thread::~Thread()
	{
		if (internalData)
		{
			Stop();
			if (threadState!=Thread::NotStarted)
			{
				pthread_detach(internalData->id);
			}
			delete internalData;
		}
	}

	Thread* Thread::CreateAndStart(ThreadProcedure procedure, void* argument, bool deleteAfterStopped)
	{
		if(procedure)
		{
			Thread* thread=new ProceduredThread(procedure, argument, deleteAfterStopped);
			if(thread->Start())
			{
				return thread;
			}
			else
			{
				delete thread;
			}
		}
		return 0;
	}

	Thread* Thread::CreateAndStart(const Func<void()>& procedure, bool deleteAfterStopped)
	{
		Thread* thread=new LambdaThread(procedure, deleteAfterStopped);
		if(thread->Start())
		{
			return thread;
		}
		else
		{
			delete thread;
		}
		return 0;
	}

	void Thread::Sleep(vint ms)
	{
		if (ms >= 1000)
		{
			sleep(ms / 1000);
		}
		if (ms % 1000)
		{
			usleep((ms % 1000) * 1000);
		}
	}
	
	vint Thread::GetCPUCount()
	{
		return (vint)sysconf(_SC_NPROCESSORS_ONLN);
	}

	vint Thread::GetCurrentThreadId()
	{
		return (vint)::pthread_self();
	}

	bool Thread::Start()
	{
		if(threadState==Thread::NotStarted)
		{
			threadState=Thread::Running;
			if(pthread_create(&internalData->id, nullptr, &InternalThreadProcWrapper, this)==0)
			{
				return true;
			}
			threadState=Thread::NotStarted;
		}
		return false;
	}

	bool Thread::Wait()
	{
		return internalData->ev.Wait();
	}

	bool Thread::Stop()
	{
		if (threadState==Thread::Running)
		{
			if(pthread_cancel(internalData->id)==0)
			{
				threadState=Thread::Stopped;
				internalData->ev.Signal();
				return true;
			}
		}
		return false;
	}

	Thread::ThreadState Thread::GetState()
	{
		return threadState;
	}

/***********************************************************************
Mutex
***********************************************************************/

	namespace threading_internal
	{
		struct MutexData
		{
			Semaphore			sem;
		};
	};

	Mutex::Mutex()
	{
		internalData = new MutexData;
	}

	Mutex::~Mutex()
	{
		delete internalData;
	}

	bool Mutex::Create(bool owned, const WString& name)
	{
		return internalData->sem.Create(owned ? 0 : 1, 1, name);
	}

	bool Mutex::Open(bool inheritable, const WString& name)
	{
		return internalData->sem.Open(inheritable, name);
	}

	bool Mutex::Release()
	{
		return internalData->sem.Release();
	}

	bool Mutex::Wait()
	{
		return internalData->sem.Wait();
	}

/***********************************************************************
Semaphore
***********************************************************************/

	namespace threading_internal
	{
		struct SemaphoreData
		{
			sem_t			semUnnamed;
			sem_t*			semNamed = nullptr;
		};

#if defined VCZH_APPLE
		void FailOnUnnamedSemaphore()
		{
			CHECK_FAIL(L"vl::Semaphore::~Semaphore()#Unnamed semaphores are not supported on macOS.");
		}
#endif
	}

	Semaphore::Semaphore()
		:internalData(0)
	{
	}

	Semaphore::~Semaphore()
	{
		if (internalData)
		{
			if (internalData->semNamed)
			{
				sem_close(internalData->semNamed);
			}
			else
			{
#if defined VCZH_APPLE
				threading_internal::FailOnUnnamedSemaphore();
#else
				sem_destroy(&internalData->semUnnamed);
#endif
			}
			delete internalData;
		}
	}

	bool Semaphore::Create(vint initialCount, vint maxCount, const WString& name)
	{
		if (internalData) return false;
		if (initialCount > maxCount) return false;

		internalData = new SemaphoreData;
#if defined VCZH_APPLE

		AString auuid;
		if(name.Length() == 0)
		{
			CFUUIDRef cfuuid = CFUUIDCreate(kCFAllocatorDefault);
			CFStringRef cfstr = CFUUIDCreateString(kCFAllocatorDefault, cfuuid);
			auuid = CFStringGetCStringPtr(cfstr, kCFStringEncodingASCII);

			CFRelease(cfstr);
			CFRelease(cfuuid);
		}
		auuid = auuid.Insert(0, "/");
		// OSX SEM_NAME_LENGTH = 31
		if(auuid.Length() >= 30)
			auuid = auuid.Sub(0, 30);
        
		if ((internalData->semNamed = sem_open(auuid.Buffer(), O_CREAT, O_RDWR, initialCount)) == SEM_FAILED)
		{
			delete internalData;
			internalData = 0;
			return false;
		}
        
#else
		if (name == L"")
		{
			if(sem_init(&internalData->semUnnamed, 0, (int)initialCount) == -1)
			{
				delete internalData;
				internalData = 0;
				return false;
			}
		}
		else
		{
			AString astr = wtoa(name);

			if ((internalData->semNamed = sem_open(astr.Buffer(), O_CREAT, 0777, initialCount)) == SEM_FAILED)
			{
				delete internalData;
				internalData = 0;
				return false;
			}
		}
#endif

		Release(initialCount);
		return true;
	}

	bool Semaphore::Open(bool inheritable, const WString& name)
	{
		if (internalData) return false;
		if (inheritable) return false;

		internalData = new SemaphoreData;
		if (!(internalData->semNamed = sem_open(wtoa(name).Buffer(), 0)))
		{
            delete internalData;
            internalData = 0;
			return false;
		}

		return true;
	}

	bool Semaphore::Release()
	{
		return Release(1);
	}

	vint Semaphore::Release(vint count)
	{
		for (vint i = 0; i < count; i++)
		{
			if (internalData->semNamed)
			{
				sem_post(internalData->semNamed);
			}
			else
			{
				sem_post(&internalData->semUnnamed);
			}
		}
		return true;
	}

	bool Semaphore::Wait()
	{
		if (internalData->semNamed)
		{
			return sem_wait(internalData->semNamed) == 0;
		}
		else
		{
			return sem_wait(&internalData->semUnnamed) == 0;
		}
	}

/***********************************************************************
EventObject
***********************************************************************/

	namespace threading_internal
	{
		struct EventData
		{
			bool				autoReset;
			volatile bool		signaled;
			CriticalSection		mutex;
			ConditionVariable	cond;
			atomic_vint			counter = 0;
			vint				releasedWaiters = 0;
			vuint64_t			signalVersion = 0;
		};
	}

	EventObject::EventObject()
	{
		internalData = nullptr;
	}

	EventObject::~EventObject()
	{
		if (internalData)
		{
			delete internalData;
		}
	}

	bool EventObject::CreateAutoUnsignal(bool signaled, const WString& name)
	{
		if (name!=L"") return false;
		if (internalData) return false;

		internalData = new EventData;
		internalData->autoReset = true;
		internalData->signaled = signaled;
		return true;
	}

	bool EventObject::CreateManualUnsignal(bool signaled, const WString& name)
	{
		if (name!=L"") return false;
		if (internalData) return false;

		internalData = new EventData;
		internalData->autoReset = false;
		internalData->signaled = signaled;
		return true;
	}

	bool EventObject::Signal()
	{
		if (!internalData) return false;

		internalData->mutex.Enter();
		if (internalData->autoReset)
		{
			if (internalData->counter > internalData->releasedWaiters)
			{
				internalData->releasedWaiters++;
				internalData->cond.WakeOnePending();
			}
			else
			{
				internalData->signaled = true;
			}
		}
		else
		{
			internalData->signaled = true;
			internalData->signalVersion++;
			if (internalData->counter)
			{
				internalData->cond.WakeAllPendings();
			}
		}
		internalData->mutex.Leave();
		return true;
	}

	bool EventObject::Unsignal()
	{
		if (!internalData) return false;

		internalData->mutex.Enter();
		internalData->signaled = false;
		internalData->mutex.Leave();
		return true;
	}

	bool EventObject::Wait()
	{
		if (!internalData) return false;

		bool result = true;
		internalData->mutex.Enter();
		if (internalData->signaled)
		{
			if (internalData->autoReset)
			{
				internalData->signaled = false;
			}
		}
		else
		{
			auto signalVersion = internalData->signalVersion;
			INCRC(&internalData->counter);
			while (internalData->autoReset
				? internalData->releasedWaiters == 0
				: !internalData->signaled && internalData->signalVersion == signalVersion)
			{
				if (!internalData->cond.SleepWith(internalData->mutex))
				{
					result = false;
					break;
				}
			}
			if (result && internalData->autoReset)
			{
				internalData->releasedWaiters--;
			}
			DECRC(&internalData->counter);
		}
		internalData->mutex.Leave();
		return result;
	}

	bool EventObject::WaitForTime(vint ms)
	{
		if (!internalData) return false;

		bool result = true;
		internalData->mutex.Enter();
		if (internalData->signaled)
		{
			if (internalData->autoReset)
			{
				internalData->signaled = false;
			}
		}
		else
		{
			auto signalVersion = internalData->signalVersion;
			timespec now;
#if defined VCZH_APPLE
			constexpr auto waitClock = CLOCK_REALTIME;
#else
			constexpr auto waitClock = CLOCK_MONOTONIC;
#endif
			if (ms <= 0 || clock_gettime(waitClock, &now) != 0)
			{
				internalData->mutex.Leave();
				return false;
			}
			auto deadline = (vuint64_t)now.tv_sec * 1000000000 + now.tv_nsec + (vuint64_t)ms * 1000000;
			auto remaining = ms;
			INCRC(&internalData->counter);
			while (internalData->autoReset
				? internalData->releasedWaiters == 0
				: !internalData->signaled && internalData->signalVersion == signalVersion)
			{
				if (!internalData->cond.SleepWithForTime(internalData->mutex, remaining))
				{
					result = false;
					break;
				}
				if (clock_gettime(waitClock, &now) != 0)
				{
					result = false;
					break;
				}
				auto current = (vuint64_t)now.tv_sec * 1000000000 + now.tv_nsec;
				if (current >= deadline)
				{
					result = false;
					break;
				}
				remaining = (vint)((deadline - current + 999999) / 1000000);
			}
			if (internalData->autoReset && internalData->releasedWaiters > 0)
			{
				internalData->releasedWaiters--;
				result = true;
			}
			else if (!internalData->autoReset && (internalData->signaled || internalData->signalVersion != signalVersion))
			{
				result = true;
			}
			DECRC(&internalData->counter);
		}
		internalData->mutex.Leave();
		return result;
	}

/***********************************************************************
ThreadPoolLite
***********************************************************************/

	namespace threading_internal
	{
		struct ThreadPoolTask
		{
			Func<void()>			task;
			Ptr<ThreadPoolTask>		next;
		};

		struct ThreadPoolData
		{
			Semaphore				semaphore;
			EventObject				taskFinishEvent;
			Ptr<ThreadPoolTask>		taskBegin;
			Ptr<ThreadPoolTask>*	taskEnd = nullptr;
			volatile bool			stopping = false;
			List<Thread*>			taskThreads;
		};

		SpinLock					threadPoolLock;
		ThreadPoolData*				threadPoolData = nullptr;

		void ThreadPoolProc(Thread* thread, void* argument)
		{
			while (true)
			{
				Ptr<ThreadPoolTask> task;

				threadPoolData->semaphore.Wait();
				SPIN_LOCK(threadPoolLock)
				{
					if (threadPoolData->taskBegin)
					{
						task = threadPoolData->taskBegin;
						threadPoolData->taskBegin = task->next;
					}

					if (!threadPoolData->taskBegin)
					{
						threadPoolData->taskEnd = &threadPoolData->taskBegin;
						threadPoolData->taskFinishEvent.Signal();
					}
				}

				if (task)
				{
					ThreadLocalStorage::FixStorages();
					try
					{
						task->task();
						ThreadLocalStorage::ClearStorages();
					}
					catch (...)
					{
						ThreadLocalStorage::ClearStorages();
					}
				}
				else if (threadPoolData->stopping)
				{
					return;
				}
			}
		}

		bool ThreadPoolQueue(const Func<void()>& proc)
		{
			SPIN_LOCK(threadPoolLock)
			{
				if (!threadPoolData)
				{
					threadPoolData = new ThreadPoolData;
					threadPoolData->semaphore.Create(0, 65536);
					threadPoolData->taskFinishEvent.CreateManualUnsignal(false);
					threadPoolData->taskEnd = &threadPoolData->taskBegin;

					for (vint i = 0; i < Thread::GetCPUCount() * 4; i++)
					{
						threadPoolData->taskThreads.Add(Thread::CreateAndStart(&ThreadPoolProc, nullptr, false));
					}
				}

				if (threadPoolData)
				{
					if (threadPoolData->stopping)
					{
						return false;
					}

					auto task = Ptr(new ThreadPoolTask);
					task->task = proc;
					*threadPoolData->taskEnd = task;
					threadPoolData->taskEnd = &task->next;
					threadPoolData->semaphore.Release();
					threadPoolData->taskFinishEvent.Unsignal();
				}
			}
			return true;
		}

		bool ThreadPoolStop(bool discardPendingTasks)
		{
			SPIN_LOCK(threadPoolLock)
			{
				if (!threadPoolData) return false;
				if (threadPoolData->stopping) return false;

				threadPoolData->stopping = true;
				if (discardPendingTasks)
				{
					threadPoolData->taskEnd = &threadPoolData->taskBegin;
					threadPoolData->taskBegin = nullptr;
				}

				threadPoolData->semaphore.Release(threadPoolData->taskThreads.Count());
			}

			threadPoolData->taskFinishEvent.Wait();
			// TODO: (enumerable) foreach
			for (vint i = 0; i < threadPoolData->taskThreads.Count(); i++)
			{
				auto thread = threadPoolData->taskThreads[i];
				thread->Wait();
				delete thread;
			}
			threadPoolData->taskThreads.Clear();

			SPIN_LOCK(threadPoolLock)
			{
				delete threadPoolData;
				threadPoolData = nullptr;
			}
			return true;
		}
	}

	ThreadPoolLite::ThreadPoolLite()
	{
	}

	ThreadPoolLite::~ThreadPoolLite()
	{
	}

	bool ThreadPoolLite::Queue(void(*proc)(void*), void* argument)
	{
		return ThreadPoolQueue([proc, argument](){proc(argument);});
	}

	bool ThreadPoolLite::Queue(const Func<void()>& proc)
	{
		return ThreadPoolQueue(proc);
	}

	bool ThreadPoolLite::Stop(bool discardPendingTasks)
	{
		return ThreadPoolStop(discardPendingTasks);
	}

/***********************************************************************
CriticalSection
***********************************************************************/

	namespace threading_internal
	{
		struct CriticalSectionData
		{
			pthread_mutex_t		mutex;
		};
	}

	CriticalSection::CriticalSection()
	{
		internalData = new CriticalSectionData;
		pthread_mutex_init(&internalData->mutex, nullptr);
	}

	CriticalSection::~CriticalSection()
	{
		pthread_mutex_destroy(&internalData->mutex);
		delete internalData;
	}

	bool CriticalSection::TryEnter()
	{
		return pthread_mutex_trylock(&internalData->mutex) == 0;
	}

	void CriticalSection::Enter()
	{
		pthread_mutex_lock(&internalData->mutex);
	}

	void CriticalSection::Leave()
	{
		pthread_mutex_unlock(&internalData->mutex);
	}

	CriticalSection::Scope::Scope(CriticalSection& _criticalSection)
		:criticalSection(&_criticalSection)
	{
		criticalSection->Enter();
	}

	CriticalSection::Scope::~Scope()
	{
		criticalSection->Leave();
	}

/***********************************************************************
ReaderWriterLock
***********************************************************************/

	namespace threading_internal
	{
		struct ReaderWriterLockData
		{
			pthread_rwlock_t			rwlock;
		};
	}

	ReaderWriterLock::ReaderWriterLock()
	{
		internalData = new ReaderWriterLockData;
		pthread_rwlock_init(&internalData->rwlock, nullptr);
	}

	ReaderWriterLock::~ReaderWriterLock()
	{
		pthread_rwlock_destroy(&internalData->rwlock);
		delete internalData;
	}

	bool ReaderWriterLock::TryEnterReader()
	{
		return pthread_rwlock_tryrdlock(&internalData->rwlock) == 0;
	}

	void ReaderWriterLock::EnterReader()
	{
		pthread_rwlock_rdlock(&internalData->rwlock);
	}

	void ReaderWriterLock::LeaveReader()
	{
		pthread_rwlock_unlock(&internalData->rwlock);
	}

	bool ReaderWriterLock::TryEnterWriter()
	{
		return pthread_rwlock_trywrlock(&internalData->rwlock) == 0;
	}

	void ReaderWriterLock::EnterWriter()
	{
		pthread_rwlock_wrlock(&internalData->rwlock);
	}

	void ReaderWriterLock::LeaveWriter()
	{
		pthread_rwlock_unlock(&internalData->rwlock);
	}

	ReaderWriterLock::ReaderScope::ReaderScope(ReaderWriterLock& _lock)
		:lock(&_lock)
	{
		lock->EnterReader();
	}

	ReaderWriterLock::ReaderScope::~ReaderScope()
	{
		lock->LeaveReader();
	}

	ReaderWriterLock::WriterScope::WriterScope(ReaderWriterLock& _lock)
		:lock(&_lock)
	{
		lock->EnterWriter();
	}

	ReaderWriterLock::WriterScope::~WriterScope()
	{
		lock->LeaveReader();
	}

/***********************************************************************
ConditionVariable
***********************************************************************/

	namespace threading_internal
	{
		struct ConditionVariableData
		{
			pthread_cond_t			cond;
		};
	}

	ConditionVariable::ConditionVariable()
	{
#define ERROR_MESSAGE_PREFIX L"vl::ConditionVariable::ConditionVariable()#"
		internalData = new ConditionVariableData;
#if defined VCZH_APPLE
		auto initResult = pthread_cond_init(&internalData->cond, nullptr);
#else
		pthread_condattr_t attributes;
		auto attributeResult = pthread_condattr_init(&attributes);
		if (attributeResult != 0)
		{
			delete internalData;
			internalData = nullptr;
			CHECK_FAIL(ERROR_MESSAGE_PREFIX L"Failed to initialize condition-variable attributes.");
		}
		auto clockResult = pthread_condattr_setclock(&attributes, CLOCK_MONOTONIC);
		auto initResult = clockResult == 0 ? pthread_cond_init(&internalData->cond, &attributes) : clockResult;
		pthread_condattr_destroy(&attributes);
#endif
		if (initResult != 0)
		{
			delete internalData;
			internalData = nullptr;
			CHECK_FAIL(ERROR_MESSAGE_PREFIX L"Failed to initialize the condition variable.");
		}
#undef ERROR_MESSAGE_PREFIX
	}

	ConditionVariable::~ConditionVariable()
	{
		pthread_cond_destroy(&internalData->cond);
		delete internalData;
	}

	bool ConditionVariable::SleepWith(CriticalSection& cs)
	{
		return pthread_cond_wait(&internalData->cond, &cs.internalData->mutex) == 0;
	}

	bool ConditionVariable::SleepWithForTime(CriticalSection& cs, vint ms)
	{
		if (ms < 0) return false;

		timespec timeout;
#if defined VCZH_APPLE
		constexpr auto waitClock = CLOCK_REALTIME;
#else
		constexpr auto waitClock = CLOCK_MONOTONIC;
#endif
		if (clock_gettime(waitClock, &timeout) != 0)
		{
			return false;
		}

		timeout.tv_sec += ms / 1000;
		timeout.tv_nsec += (ms % 1000) * 1000000;
		if (timeout.tv_nsec >= 1000000000)
		{
			timeout.tv_sec++;
			timeout.tv_nsec -= 1000000000;
		}
		else if (timeout.tv_nsec < 0)
		{
			timeout.tv_sec--;
			timeout.tv_nsec += 1000000000;
		}

		return pthread_cond_timedwait(&internalData->cond, &cs.internalData->mutex, &timeout) == 0;
	}

	void ConditionVariable::WakeOnePending()
	{
		pthread_cond_signal(&internalData->cond);
	}

	void ConditionVariable::WakeAllPendings()
	{
		pthread_cond_broadcast(&internalData->cond);
	}

/***********************************************************************
ThreadLocalStorage
***********************************************************************/

#define KEY ((pthread_key_t&)key)

	ThreadLocalStorage::ThreadLocalStorage(Destructor _destructor)
		:destructor(_destructor)
	{
		static_assert(sizeof(key) >= sizeof(pthread_key_t), "ThreadLocalStorage's key storage is not large enouth.");
		PushStorage(this);
		auto error = pthread_key_create(&KEY, destructor);
		CHECK_ERROR(error != EAGAIN && error != ENOMEM, L"vl::ThreadLocalStorage::ThreadLocalStorage()#Failed to create a thread local storage index.");
	}

	ThreadLocalStorage::~ThreadLocalStorage()
	{
		pthread_key_delete(KEY);
	}

	void* ThreadLocalStorage::Get()
	{
		CHECK_ERROR(!disposed, L"vl::ThreadLocalStorage::Get()#Cannot access a disposed ThreadLocalStorage.");
		return pthread_getspecific(KEY);
	}

	void ThreadLocalStorage::Set(void* data)
	{
		CHECK_ERROR(!disposed, L"vl::ThreadLocalStorage::Set()#Cannot access a disposed ThreadLocalStorage.");
		pthread_setspecific(KEY, data);
	}

#undef KEY
}


/***********************************************************************
.\ENCODING\CHARFORMAT\CHARFORMAT.LINUX.CPP
***********************************************************************/
/***********************************************************************
Author: Zihan Chen (vczh)
Licensed under https://github.com/vczh-libraries/License
***********************************************************************/

#include <string.h>

#ifndef VCZH_GCC
static_assert(false, "Do not build this file for Windows applications.");
#endif

namespace vl
{
	namespace stream
	{
		using namespace vl::encoding;

		bool IsMbcsLeadByte(char c)
		{
			return (vint8_t)c < 0;
		}

		void MbcsToWChar(wchar_t* wideBuffer, vint wideChars, vint wideReaded, char* mbcsBuffer, vint mbcsChars)
		{
			AString a = AString::CopyFrom(mbcsBuffer, mbcsChars);
			WString w = atow(a);
			memcpy(wideBuffer, w.Buffer(), wideReaded * sizeof(wchar_t));
		}

/***********************************************************************
MbcsEncoder
***********************************************************************/

		vint MbcsEncoder::WriteString(wchar_t* _buffer, vint chars)
		{
			WString w = WString::CopyFrom(_buffer, chars);
			AString a = wtoa(w);
			vint length = a.Length();
			vint result = stream->Write((void*)a.Buffer(), length);

			if (result != length)
			{
				Close();
				return 0;
			}
			return chars;
		}

/***********************************************************************
Helper Functions
***********************************************************************/

		extern bool CanBeMbcs(unsigned char* buffer, vint size);
		extern bool CanBeUtf8(unsigned char* buffer, vint size);
		extern bool CanBeUtf16(unsigned char* buffer, vint size, bool& hitSurrogatePairs);
		extern bool CanBeUtf16BE(unsigned char* buffer, vint size, bool& hitSurrogatePairs);
		
/***********************************************************************
TestEncoding
***********************************************************************/

		extern void TestEncodingInternal(
			unsigned char* buffer,
			vint size,
			BomEncoder::Encoding& encoding,
			bool containsBom,
			bool utf16HitSurrogatePairs,
			bool utf16BEHitSurrogatePairs,
			bool roughMbcs,
			bool roughUtf8,
			bool roughUtf16,
			bool roughUtf16BE
			)
		{
			if (roughUtf16 && roughUtf16BE && !roughUtf8)
			{
				if (utf16BEHitSurrogatePairs && !utf16HitSurrogatePairs)
				{
					encoding = BomEncoder::Utf16BE;
				}
				else
				{
					encoding = BomEncoder::Utf16;
				}
			}
			else
			{
				encoding = BomEncoder::Utf8;
			}
		}
	}
}


/***********************************************************************
.\INTERPROCESS\ASYNCSOCKET\ASYNCSOCKET.LINUX.CPP
***********************************************************************/
#if defined VCZH_GCC && !defined VCZH_APPLE

#include <liburing.h>
#include <arpa/inet.h>
#include <cerrno>
#include <cstdlib>
#include <cstring>
#include <netinet/in.h>
#include <sys/socket.h>

namespace vl::inter_process::async_tcp_socket::linux_socket
{
	using namespace collections;

	class RingOperation;
	class RingRuntime;
	class ConnectionState;
	class ServerState;
	class AsyncSocketConnection;

	static thread_local RingRuntime* currentRingRuntime = nullptr;

	struct ConnectionCallbackFrame
	{
		ConnectionState*					connection = nullptr;
		ConnectionCallbackFrame*			previous = nullptr;
	};

	struct ServerCallbackFrame
	{
		ServerState*						server = nullptr;
		ServerCallbackFrame*				previous = nullptr;
	};

	static thread_local ConnectionCallbackFrame* currentConnectionCallbackFrame = nullptr;
	static thread_local ServerCallbackFrame* currentServerCallbackFrame = nullptr;

	WString LinuxSocketErrorMessage(const wchar_t* operation, vint error)
	{
		return WString::Unmanaged(operation)
			+ L" failed with Linux error "
			+ itow(error)
			+ L" ("
			+ atow(AString::Unmanaged(strerror((int)error)))
			+ L").";
	}

	void CloseFileDescriptor(vint fileDescriptor)
	{
		if (fileDescriptor >= 0)
		{
			// Do not retry close after EINTR: the descriptor number can already be reused.
			close((int)fileDescriptor);
		}
	}

/***********************************************************************
OwnedFileDescriptor
***********************************************************************/

	class OwnedFileDescriptor
	{
	private:
		vint								fileDescriptor = -1;

	public:
		OwnedFileDescriptor(vint _fileDescriptor)
			: fileDescriptor(_fileDescriptor)
		{
		}

		OwnedFileDescriptor(const OwnedFileDescriptor&) = delete;
		OwnedFileDescriptor& operator=(const OwnedFileDescriptor&) = delete;

		~OwnedFileDescriptor()
		{
			CloseFileDescriptor(fileDescriptor);
		}

		vint Get()
		{
			return fileDescriptor;
		}

		vint Detach()
		{
			auto result = fileDescriptor;
			fileDescriptor = -1;
			return result;
		}
	};

/***********************************************************************
OperationDrain
***********************************************************************/

	class OperationDrain : public Object
	{
	private:
		CriticalSection					lockDrain;
		vint							pendingOperations = 0;
		EventObject						eventDrained;

	public:
		OperationDrain()
		{
#define ERROR_MESSAGE_PREFIX L"vl::inter_process::async_tcp_socket::linux_socket::OperationDrain::OperationDrain()#"
			CHECK_ERROR(eventDrained.CreateManualUnsignal(true), ERROR_MESSAGE_PREFIX L"Failed to create an operation drain event.");
#undef ERROR_MESSAGE_PREFIX
		}

		void Begin()
		{
			CS_LOCK(lockDrain)
			{
				if (pendingOperations++ == 0)
				{
					eventDrained.Unsignal();
				}
			}
		}

		void End()
		{
#define ERROR_MESSAGE_PREFIX L"vl::inter_process::async_tcp_socket::linux_socket::OperationDrain::End()#"
			CS_LOCK(lockDrain)
			{
				CHECK_ERROR(pendingOperations > 0, ERROR_MESSAGE_PREFIX L"Operation drain bookkeeping became unbalanced.");
				if (--pendingOperations == 0)
				{
					eventDrained.Signal();
				}
			}
#undef ERROR_MESSAGE_PREFIX
		}

		void Wait()
		{
			eventDrained.Wait();
		}
	};

/***********************************************************************
RingOperationOwner
***********************************************************************/

	class RingOperationOwner : public Object
	{
		friend class RingRuntime;
	protected:
		Ptr<OperationDrain>				operationDrain;

		RingOperationOwner()
			: operationDrain(Ptr(new OperationDrain))
		{
		}

		virtual void EndTargetOperation() = 0;
		virtual void HandleOperationFailure(Ptr<RingOperationOwner> retainedOwner) = 0;
	};

/***********************************************************************
RingOperation
***********************************************************************/

	class RingOperation
	{
		friend class RingRuntime;
	private:
		Ptr<RingOperationOwner>			operationOwner;
		bool								targetOperation = false;

	public:
		vuint64_t							id = 0;

		RingOperation(vuint64_t _id, Ptr<RingOperationOwner> _operationOwner = nullptr, bool _targetOperation = false)
			: id(_id)
			, operationOwner(_operationOwner)
			, targetOperation(_targetOperation)
		{
		}

		virtual ~RingOperation() = default;
		virtual void Prepare(io_uring_sqe* sqe) noexcept = 0;
		virtual void Handle(vint result) = 0;
	};

/***********************************************************************
RuntimeWakeOperation
***********************************************************************/

	class RuntimeWakeOperation : public RingOperation
	{
	public:
		RuntimeWakeOperation(vuint64_t id)
			: RingOperation(id)
		{
		}

		void Prepare(io_uring_sqe* sqe) noexcept override
		{
			io_uring_prep_nop(sqe);
		}

		void Handle(vint) override
		{
		}
	};

/***********************************************************************
RingRuntime
***********************************************************************/

	class RingRuntime : public Object
	{
	private:
		enum class FlushResult
		{
			Done,
			ConsumeCompletion,
		};

		io_uring							ring = {};
		CriticalSection					lockRuntime;
		ConditionVariable				cvRuntimeProgress;
		Dictionary<vuint64_t, RingOperation*>	operations;
		vuint64_t							nextOperationId = 1;
		bool								ringInitialized = false;
		bool								workerStarted = false;
		bool								stopRequested = false;
		EventObject							eventWorkerStopped;

		vuint64_t ReserveOperationIdLocked()
		{
			auto result = nextOperationId++;
			if (result == 0)
			{
				result = nextOperationId++;
			}
			return result;
		}

		FlushResult FlushPendingSubmissionsLocked()
		{
			while (io_uring_sq_ready(&ring))
			{
				vint submitResult = 0;
				do
				{
					submitResult = io_uring_submit(&ring);
				} while (submitResult == -EINTR);
				if (submitResult == -EAGAIN)
				{
					std::abort();
				}
				if (submitResult == -EBUSY)
				{
					return FlushResult::ConsumeCompletion;
				}
				if (submitResult <= 0)
				{
					std::abort();
				}
			}
			return FlushResult::Done;
		}

		void FlushPendingSubmissionsCore(bool completionWorker)
		{
			while (true)
			{
				FlushResult result = FlushResult::Done;
				bool waited = true;
				lockRuntime.Enter();
				if (!ringInitialized)
				{
					lockRuntime.Leave();
					return;
				}
				result = FlushPendingSubmissionsLocked();
				if (result == FlushResult::ConsumeCompletion && !completionWorker)
				{
					waited = cvRuntimeProgress.SleepWith(lockRuntime);
				}
				lockRuntime.Leave();
				if (!waited)
				{
					std::abort();
				}
				if (result == FlushResult::Done)
				{
					return;
				}
				if (completionWorker)
				{
					return;
				}
			}
		}

		bool SubmitLocked(RingOperation* operation, bool allowStopping)
		{
#define ERROR_MESSAGE_PREFIX L"vl::inter_process::async_tcp_socket::linux_socket::RingRuntime::SubmitLocked(RingOperation*, bool)#"
			if (!ringInitialized || (!allowStopping && stopRequested) || operation->id == 0 || operations.Keys().Contains(operation->id))
			{
				delete operation;
				return false;
			}

			try
			{
				operations.Add(operation->id, operation);
			}
			catch (...)
			{
				delete operation;
				return false;
			}
			auto sqe = io_uring_get_sqe(&ring);
			if (!sqe)
			{
				operations.Remove(operation->id);
				delete operation;
				return false;
			}

			operation->Prepare(sqe);
			io_uring_sqe_set_data64(sqe, operation->id);
			vint submitResult = 0;
			do
			{
				submitResult = io_uring_submit(&ring);
			} while (submitResult == -EINTR);
			if (submitResult == -EAGAIN || (submitResult <= 0 && submitResult != -EBUSY))
			{
				std::abort();
			}
#undef ERROR_MESSAGE_PREFIX
			return true;
		}

		void WorkerLoop()
		{
#define ERROR_MESSAGE_PREFIX L"vl::inter_process::async_tcp_socket::linux_socket::RingRuntime::WorkerLoop()#"
			currentRingRuntime = this;
			while (true)
			{
				io_uring_cqe* cqe = nullptr;
				vint waitResult = 0;
				do
				{
					waitResult = io_uring_wait_cqe(&ring, &cqe);
				} while (waitResult == -EINTR);
				if (waitResult != 0 || !cqe)
				{
					std::abort();
				}

				auto operationId = io_uring_cqe_get_data64(cqe);
				RingOperation* operation = nullptr;
				CS_LOCK(lockRuntime)
				{
					if (operations.Keys().Contains(operationId))
					{
						operation = operations[operationId];
					}
				}
				if (!operation)
				{
					std::abort();
				}

				bool operationFailed = false;
				try
				{
					operation->Handle((vint)cqe->res);
				}
				catch (...)
				{
					operationFailed = true;
				}
				io_uring_cqe_seen(&ring, cqe);

				CS_LOCK(lockRuntime)
				{
					operations.Remove(operationId);
					cvRuntimeProgress.WakeAllPendings();
				}
				auto operationOwner = operation->operationOwner;
				auto operationDrain = operationOwner ? operationOwner->operationDrain : nullptr;
				auto targetOperation = operation->targetOperation;
				delete operation;
				if (operationOwner && targetOperation)
				{
					operationOwner->EndTargetOperation();
				}
				if (operationOwner && operationFailed)
				{
					try
					{
						operationOwner->HandleOperationFailure(operationOwner);
					}
					catch (...)
					{
					}
				}
				operationOwner = nullptr;
				if (operationDrain)
				{
					operationDrain->End();
				}

				FlushPendingSubmissionsCore(true);

				bool shouldStop = false;
				CS_LOCK(lockRuntime)
				{
					shouldStop = stopRequested && operations.Count() == 0;
				}
				if (shouldStop)
				{
					break;
				}
			}

			CS_LOCK(lockRuntime)
			{
				io_uring_queue_exit(&ring);
				ringInitialized = false;
				cvRuntimeProgress.WakeAllPendings();
			}
			currentRingRuntime = nullptr;
			eventWorkerStopped.Signal();
#undef ERROR_MESSAGE_PREFIX
		}

	public:
		RingRuntime()
		{
#define ERROR_MESSAGE_PREFIX L"vl::inter_process::async_tcp_socket::linux_socket::RingRuntime::RingRuntime()#"
			CHECK_ERROR(eventWorkerStopped.CreateManualUnsignal(false), ERROR_MESSAGE_PREFIX L"Failed to create the io_uring worker drain event.");

			io_uring_params parameters = {};
			auto initResult = io_uring_queue_init_params(1024, &ring, &parameters);
			CHECK_ERROR(initResult == 0, ERROR_MESSAGE_PREFIX L"Failed to initialize io_uring.");
			ringInitialized = true;

			auto probe = io_uring_get_probe_ring(&ring);
			if (!probe)
			{
				io_uring_queue_exit(&ring);
				ringInitialized = false;
				CHECK_FAIL(ERROR_MESSAGE_PREFIX L"Failed to probe io_uring operations.");
			}
			auto supported =
				io_uring_opcode_supported(probe, IORING_OP_NOP) &&
				io_uring_opcode_supported(probe, IORING_OP_ACCEPT) &&
				io_uring_opcode_supported(probe, IORING_OP_CONNECT) &&
				io_uring_opcode_supported(probe, IORING_OP_RECV) &&
				io_uring_opcode_supported(probe, IORING_OP_SEND) &&
				io_uring_opcode_supported(probe, IORING_OP_ASYNC_CANCEL) &&
				io_uring_opcode_supported(probe, IORING_OP_TIMEOUT);
			io_uring_free_probe(probe);
			if (!supported)
			{
				io_uring_queue_exit(&ring);
				ringInitialized = false;
				CHECK_FAIL(ERROR_MESSAGE_PREFIX L"Requires io_uring accept, connect, receive, send, async-cancel, timeout, and nop operations.");
			}
#undef ERROR_MESSAGE_PREFIX
		}

		~RingRuntime()
		{
			Stop();
		}

		static Ptr<RingRuntime> Create(bool startWorker)
		{
			auto result = Ptr(new RingRuntime);
			if (startWorker)
			{
				result->Start(result);
			}
			return result;
		}

		void Start(Ptr<RingRuntime> retainedRuntime)
		{
#define ERROR_MESSAGE_PREFIX L"vl::inter_process::async_tcp_socket::linux_socket::RingRuntime::Start(Ptr<RingRuntime>)#"
			CS_LOCK(lockRuntime)
			{
				CHECK_ERROR(!workerStarted && !stopRequested && ringInitialized, ERROR_MESSAGE_PREFIX L"The io_uring runtime can only be started once before stopping.");
				auto worker = Thread::CreateAndStart(Func<void()>([retainedRuntime]()
				{
					retainedRuntime->WorkerLoop();
				}), true);
				CHECK_ERROR(worker != nullptr, ERROR_MESSAGE_PREFIX L"Failed to start the io_uring completion worker.");
				workerStarted = true;
			}
#undef ERROR_MESSAGE_PREFIX
		}

		vuint64_t ReserveOperationId()
		{
			vuint64_t result = 0;
			CS_LOCK(lockRuntime)
			{
				result = ReserveOperationIdLocked();
			}
			return result;
		}

		bool Submit(RingOperation* operation)
		{
			bool result = false;
			CS_LOCK(lockRuntime)
			{
				result = SubmitLocked(operation, false);
			}
			return result;
		}

		void FlushPendingSubmissions()
		{
			if (currentRingRuntime != this)
			{
				FlushPendingSubmissionsCore(false);
			}
		}

		void Stop();
	};

	void RingRuntime::Stop()
	{
#define ERROR_MESSAGE_PREFIX L"vl::inter_process::async_tcp_socket::linux_socket::RingRuntime::Stop()#"
		bool waitForWorker = false;
		bool signalWithoutWorker = false;
		bool wakeFailed = false;
		bool wakeSubmitted = false;
		CS_LOCK(lockRuntime)
		{
			if (!stopRequested)
			{
				if (workerStarted)
				{
					if (currentRingRuntime == this)
					{
						stopRequested = true;
					}
					else
					{
						auto operation = new RuntimeWakeOperation(ReserveOperationIdLocked());
						if (SubmitLocked(operation, true))
						{
							stopRequested = true;
							wakeSubmitted = true;
						}
						else
						{
							wakeFailed = true;
						}
					}
				}
				else
				{
					stopRequested = true;
					if (ringInitialized)
					{
						io_uring_queue_exit(&ring);
						ringInitialized = false;
						cvRuntimeProgress.WakeAllPendings();
					}
					signalWithoutWorker = true;
				}
			}
			waitForWorker = stopRequested && workerStarted && currentRingRuntime != this;
		}
		CHECK_ERROR(!wakeFailed, ERROR_MESSAGE_PREFIX L"Failed to wake the stopping io_uring worker.");
		if (wakeSubmitted)
		{
			FlushPendingSubmissions();
		}

		if (signalWithoutWorker)
		{
			eventWorkerStopped.Signal();
		}
		if (waitForWorker)
		{
			eventWorkerStopped.Wait();
		}
#undef ERROR_MESSAGE_PREFIX
	}

/***********************************************************************
ConnectionState
***********************************************************************/

	class ConnectionState : public RingOperationOwner
	{
		friend class AsyncSocketConnection;
	private:
		class ReceiveOperation;
		class WriteOperation;
		class ConnectOperation;
		class RetryOperation;
		class CancelOperation;

		Ptr<RingRuntime>					runtime;
		IAsyncSocketConnection*			owner = nullptr;

		// covers all fields below, callback counts, and target operation counts
		CriticalSection					lockState;
		ConditionVariable				cvCallbacks;
		vint								fileDescriptor = -1;
		IAsyncSocketCallback*				callback = nullptr;
		bool								connected = false;
		bool								stopping = false;
		bool								reading = false;
		bool								writePending = false;
		bool								terminalPending = false;
		bool								disconnectedNotified = false;
		vint								activeCallbacks = 0;

		vuint64_t						readOperationId = 0;
		vuint64_t						writeOperationId = 0;
		vuint64_t						connectOperationId = 0;
		vuint64_t						retryOperationId = 0;
		bool								readCancelRequested = false;
		bool								writeCancelRequested = false;
		bool								connectCancelRequested = false;
		bool								retryCancelRequested = false;
		vint								targetOperations = 0;

		bool								clientMode = false;
		vint								clientPort = 0;
		ClientStatus						clientStatus = ClientStatus::Ready;
		vint								clientAttempts = 0;
		EventObject							eventWaitForServer;

		void BeginTargetLocked()
		{
			targetOperations++;
			operationDrain->Begin();
		}

		void BeginCancelLocked()
		{
			operationDrain->Begin();
		}

		void RollbackTargetLocked()
		{
#define ERROR_MESSAGE_PREFIX L"vl::inter_process::async_tcp_socket::linux_socket::ConnectionState::RollbackTargetLocked()#"
			CHECK_ERROR(targetOperations > 0, ERROR_MESSAGE_PREFIX L"Target-operation bookkeeping became unbalanced.");
			targetOperations--;
			operationDrain->End();
#undef ERROR_MESSAGE_PREFIX
		}

		void RollbackCancelLocked()
		{
			operationDrain->End();
		}

		void EndTargetOperation() override
		{
			CS_LOCK(lockState)
			{
				if (targetOperations <= 0)
				{
					std::abort();
				}
				targetOperations--;
				if (stopping && targetOperations == 0 && fileDescriptor >= 0)
				{
					CloseFileDescriptor(fileDescriptor);
					fileDescriptor = -1;
				}
			}
		}

		void HandleOperationFailure(Ptr<RingOperationOwner> retainedOwner) override
		{
			auto retainedState = retainedOwner.Cast<ConnectionState>();
			if (!retainedState)
			{
				std::abort();
			}
			try
			{
				BeginTerminal(retainedState, EIO, true);
			}
			catch (...)
			{
				Stop(retainedState);
			}
		}

		vint CurrentCallbackDepth()
		{
			vint result = 0;
			for (auto frame = currentConnectionCallbackFrame; frame; frame = frame->previous)
			{
				if (frame->connection == this)
				{
					result++;
				}
			}
			return result;
		}

		IAsyncSocketCallback* BeginCallback(bool terminal)
		{
			IAsyncSocketCallback* result = nullptr;
			CS_LOCK(lockState)
			{
				if (callback && (terminal || (!stopping && !terminalPending)))
				{
					result = callback;
					activeCallbacks++;
				}
			}
			return result;
		}

		void EndCallback()
		{
			CS_LOCK(lockState)
			{
				activeCallbacks--;
				cvCallbacks.WakeAllPendings();
			}
		}

		template<typename TCallback>
		bool InvokeCallback(bool terminal, TCallback&& invoke)
		{
			auto installed = BeginCallback(terminal);
			if (!installed)
			{
				return false;
			}

			ConnectionCallbackFrame frame{ this, currentConnectionCallbackFrame };
			currentConnectionCallbackFrame = &frame;
			try
			{
				invoke(installed);
			}
			catch (...)
			{
			}
			currentConnectionCallbackFrame = frame.previous;
			EndCallback();
			return true;
		}

		void PostRead(Ptr<ConnectionState> retainedState);
		void BeginTerminal(Ptr<ConnectionState> retainedState, vint error, bool reportError);
		void StartConnectAttempt(Ptr<ConnectionState> retainedState);
		void DeliverConnectFailure(Ptr<ConnectionState> retainedState, vint error, bool fatal);
		void ScheduleRetry(Ptr<ConnectionState> retainedState);

	public:
		ConnectionState(Ptr<RingRuntime> _runtime, bool _clientMode, vint _clientPort)
			: runtime(_runtime)
			, clientMode(_clientMode)
			, clientPort(_clientPort)
		{
#define ERROR_MESSAGE_PREFIX L"vl::inter_process::async_tcp_socket::linux_socket::ConnectionState::ConnectionState(Ptr<RingRuntime>, bool, vint)#"
			CHECK_ERROR(eventWaitForServer.CreateManualUnsignal(false), ERROR_MESSAGE_PREFIX L"Failed to create the client wait event.");
#undef ERROR_MESSAGE_PREFIX
		}

		ConnectionState(Ptr<RingRuntime> _runtime, vint _fileDescriptor)
			: runtime(_runtime)
			, fileDescriptor(_fileDescriptor)
			, connected(true)
		{
#define ERROR_MESSAGE_PREFIX L"vl::inter_process::async_tcp_socket::linux_socket::ConnectionState::ConnectionState(Ptr<RingRuntime>, vint)#"
			CHECK_ERROR(eventWaitForServer.CreateManualUnsignal(false), ERROR_MESSAGE_PREFIX L"Failed to create the client wait event.");
#undef ERROR_MESSAGE_PREFIX
		}

		void InstallCallback(Ptr<ConnectionState> retainedState, IAsyncSocketCallback* value);
		void BeginReading(Ptr<ConnectionState> retainedState);
		void Write(Ptr<ConnectionState> retainedState, Ptr<AsyncSocketBuffer> buffer);
		void Stop(Ptr<ConnectionState> retainedState);
		void WaitForServer(Ptr<ConnectionState> retainedState);
		ClientStatus GetStatus();
	};

/***********************************************************************
ConnectionState::CancelOperation
***********************************************************************/

	class ConnectionState::CancelOperation : public RingOperation
	{
	public:
		Ptr<ConnectionState>					connection;
		vuint64_t							targetId = 0;

		CancelOperation(vuint64_t id, Ptr<ConnectionState> _connection, vuint64_t _targetId)
			: RingOperation(id, _connection, false)
			, connection(_connection)
			, targetId(_targetId)
		{
		}

		void Prepare(io_uring_sqe* sqe) noexcept override
		{
			io_uring_prep_cancel64(sqe, targetId, 0);
		}

		void Handle(vint) override
		{
			CS_LOCK(connection->lockState)
			{
			}
		}
	};

/***********************************************************************
ConnectionState::ReceiveOperation
***********************************************************************/

	class ConnectionState::ReceiveOperation : public RingOperation
	{
	public:
		Ptr<ConnectionState>					connection;
		vint								fileDescriptor = -1;
		Array<vuint8_t>						buffer;

		ReceiveOperation(vuint64_t id, Ptr<ConnectionState> _connection, vint _fileDescriptor)
			: RingOperation(id, _connection, true)
			, connection(_connection)
			, fileDescriptor(_fileDescriptor)
		{
			buffer.Resize(65536);
		}

		void Prepare(io_uring_sqe* sqe) noexcept override
		{
			io_uring_prep_recv(sqe, (int)fileDescriptor, &buffer[0], (size_t)buffer.Count(), 0);
		}

		void Handle(vint result) override
		{
			bool active = false;
			CS_LOCK(connection->lockState)
			{
				if (connection->readOperationId == id)
				{
					connection->readOperationId = 0;
					connection->readCancelRequested = false;
					active = !connection->stopping && connection->connected && connection->reading;
				}
			}
			if (!active)
			{
				return;
			}

			if (result > 0 && result <= buffer.Count())
			{
				auto invoked = connection->InvokeCallback(false, [&](IAsyncSocketCallback* installed)
				{
					installed->OnRead(&buffer[0], result);
				});
				if (invoked)
				{
					connection->PostRead(connection);
				}
			}
			else if (result == 0)
			{
				connection->BeginTerminal(connection, 0, false);
			}
			else
			{
				auto error = result < 0 ? -result : EIO;
				connection->BeginTerminal(connection, error, true);
			}
		}
	};

/***********************************************************************
ConnectionState::WriteOperation
***********************************************************************/

	class ConnectionState::WriteOperation : public RingOperation
	{
	public:
		Ptr<ConnectionState>					connection;
		vint								fileDescriptor = -1;
		Ptr<AsyncSocketBuffer>				buffer;
		vint								offset = 0;
		bool								empty = false;

		WriteOperation(vuint64_t id, Ptr<ConnectionState> _connection, vint _fileDescriptor, Ptr<AsyncSocketBuffer> _buffer, vint _offset)
			: RingOperation(id, _connection, true)
			, connection(_connection)
			, fileDescriptor(_fileDescriptor)
			, buffer(_buffer)
			, offset(_offset)
			, empty(_buffer->data.Count() == 0)
		{
		}

		void Prepare(io_uring_sqe* sqe) noexcept override
		{
			if (empty)
			{
				io_uring_prep_nop(sqe);
			}
			else
			{
				io_uring_prep_send(
					sqe,
					(int)fileDescriptor,
					&buffer->data[offset],
					(size_t)(buffer->data.Count() - offset),
					MSG_NOSIGNAL
				);
			}
		}

		void Handle(vint result) override
		{
			if (empty)
			{
				bool deliver = false;
				CS_LOCK(connection->lockState)
				{
					if (connection->writeOperationId == id)
					{
						connection->writeOperationId = 0;
						connection->writeCancelRequested = false;
						if (!connection->stopping && connection->connected && result == 0)
						{
							connection->writePending = false;
							deliver = true;
						}
					}
				}
				if (deliver)
				{
					connection->InvokeCallback(false, [&](IAsyncSocketCallback* installed)
					{
						installed->OnWriteCompleted(buffer);
					});
				}
				else if (result < 0 && result != -ECANCELED)
				{
					connection->BeginTerminal(connection, -result, true);
				}
				return;
			}

			if (result > 0 && result <= buffer->data.Count() - offset)
			{
				auto nextOffset = offset + result;
				if (nextOffset == buffer->data.Count())
				{
					bool deliver = false;
					CS_LOCK(connection->lockState)
					{
						if (connection->writeOperationId == id)
						{
							connection->writeOperationId = 0;
							connection->writeCancelRequested = false;
							if (!connection->stopping && connection->connected)
							{
								connection->writePending = false;
								deliver = true;
							}
						}
					}
					if (deliver)
					{
						connection->InvokeCallback(false, [&](IAsyncSocketCallback* installed)
						{
							installed->OnWriteCompleted(buffer);
						});
					}
				}
				else
				{
					auto nextId = connection->runtime->ReserveOperationId();
					auto next = new WriteOperation(nextId, connection, fileDescriptor, buffer, nextOffset);
					bool attempted = false;
					bool submissionFailed = false;
					CS_LOCK(connection->lockState)
					{
						if (connection->writeOperationId == id && !connection->stopping && connection->connected)
						{
							attempted = true;
							connection->writeOperationId = nextId;
							connection->writeCancelRequested = false;
							connection->BeginTargetLocked();
							if (!connection->runtime->Submit(next))
							{
								connection->writeOperationId = 0;
								connection->RollbackTargetLocked();
								submissionFailed = true;
							}
						}
					}
					if (attempted && !submissionFailed)
					{
						connection->runtime->FlushPendingSubmissions();
					}
					if (!attempted)
					{
						delete next;
					}
					else if (submissionFailed)
					{
						connection->BeginTerminal(connection, EIO, true);
					}
				}
			}
			else
			{
				bool active = false;
				CS_LOCK(connection->lockState)
				{
					if (connection->writeOperationId == id)
					{
						connection->writeOperationId = 0;
						connection->writeCancelRequested = false;
						active = !connection->stopping && connection->connected;
					}
				}
				if (active)
				{
					auto error = result < 0 ? -result : EPIPE;
					connection->BeginTerminal(connection, error, true);
				}
			}
		}
	};

/***********************************************************************
ConnectionState::ConnectOperation
***********************************************************************/

	class ConnectionState::ConnectOperation : public RingOperation
	{
	public:
		Ptr<ConnectionState>					connection;
		OwnedFileDescriptor					ownedSocket;
		sockaddr_in							address = {};

		ConnectOperation(vuint64_t id, Ptr<ConnectionState> _connection, vint _fileDescriptor, vint port)
			: RingOperation(id, _connection, true)
			, connection(_connection)
			, ownedSocket(_fileDescriptor)
		{
			address.sin_family = AF_INET;
			address.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
			address.sin_port = htons((vuint16_t)port);
		}

		void Prepare(io_uring_sqe* sqe) noexcept override
		{
			io_uring_prep_connect(sqe, (int)ownedSocket.Get(), (sockaddr*)&address, sizeof(address));
		}

		void Handle(vint result) override
		{
			bool accepted = false;
			bool failed = false;
			bool fatal = false;
			CS_LOCK(connection->lockState)
			{
				if (connection->connectOperationId == id)
				{
					connection->connectOperationId = 0;
					connection->connectCancelRequested = false;
					if (!connection->stopping && connection->clientStatus == ClientStatus::WaitingForServer)
					{
						if (result == 0)
						{
							connection->connected = true;
							connection->clientStatus = ClientStatus::Connected;
							ownedSocket.Detach();
							accepted = true;
						}
						else
						{
							connection->fileDescriptor = -1;
							failed = true;
							fatal = connection->clientAttempts >= AsyncSocketClientRetryCount;
						}
					}
					else
					{
						if (connection->fileDescriptor == ownedSocket.Get())
						{
							connection->fileDescriptor = -1;
						}
					}
				}
			}

			if (accepted)
			{
				connection->InvokeCallback(false, [](IAsyncSocketCallback* installed)
				{
					installed->OnConnected();
				});
				connection->eventWaitForServer.Signal();
			}
			else if (failed)
			{
				connection->DeliverConnectFailure(connection, result < 0 ? -result : EIO, fatal);
			}
		}
	};

/***********************************************************************
ConnectionState::RetryOperation
***********************************************************************/

	class ConnectionState::RetryOperation : public RingOperation
	{
	public:
		Ptr<ConnectionState>					connection;
		__kernel_timespec						timeout = {};

		RetryOperation(vuint64_t id, Ptr<ConnectionState> _connection)
			: RingOperation(id, _connection, true)
			, connection(_connection)
		{
			timeout.tv_sec = AsyncSocketClientRetryDelay / 1000;
			timeout.tv_nsec = (AsyncSocketClientRetryDelay % 1000) * 1000000;
		}

		void Prepare(io_uring_sqe* sqe) noexcept override
		{
			io_uring_prep_timeout(sqe, &timeout, 0, 0);
		}

		void Handle(vint result) override
		{
			bool active = false;
			CS_LOCK(connection->lockState)
			{
				if (connection->retryOperationId == id)
				{
					connection->retryOperationId = 0;
					connection->retryCancelRequested = false;
					active = !connection->stopping && connection->clientStatus == ClientStatus::WaitingForServer;
				}
			}
			if (!active)
			{
				return;
			}

			if (result == -ETIME)
			{
				connection->StartConnectAttempt(connection);
			}
			else if (result != -ECANCELED)
			{
				connection->BeginTerminal(connection, result < 0 ? -result : EIO, true);
			}
		}
	};

/***********************************************************************
ConnectionState
***********************************************************************/

	void ConnectionState::InstallCallback(Ptr<ConnectionState>, IAsyncSocketCallback* value)
	{
#define ERROR_MESSAGE_PREFIX L"vl::inter_process::async_tcp_socket::linux_socket::ConnectionState::InstallCallback(Ptr<ConnectionState>, IAsyncSocketCallback*)#"
		auto callbackDepth = CurrentCallbackDepth();
		if (!value)
		{
			CS_LOCK(lockState)
			{
				callback = nullptr;
				while (activeCallbacks > callbackDepth)
				{
					cvCallbacks.SleepWith(lockState);
				}
			}
			return;
		}

		bool canInstall = false;
		CS_LOCK(lockState)
		{
			canInstall = callback == nullptr && !stopping && owner != nullptr;
			if (canInstall)
			{
				callback = value;
				activeCallbacks++;
			}
		}
		CHECK_ERROR(canInstall, ERROR_MESSAGE_PREFIX L"Cannot replace a callback or install one on a stopped connection.");

		ConnectionCallbackFrame frame{ this, currentConnectionCallbackFrame };
		currentConnectionCallbackFrame = &frame;
		try
		{
			value->OnInstalled(owner);
		}
		catch (...)
		{
		}
		currentConnectionCallbackFrame = frame.previous;
		EndCallback();
#undef ERROR_MESSAGE_PREFIX
	}

	void ConnectionState::PostRead(Ptr<ConnectionState> retainedState)
	{
		auto operationId = runtime->ReserveOperationId();
		ReceiveOperation* operation = nullptr;
		bool submitted = false;
		bool submissionFailed = false;
		CS_LOCK(lockState)
		{
			if (connected && !stopping && !terminalPending && reading && readOperationId == 0)
			{
				operation = new ReceiveOperation(operationId, retainedState, fileDescriptor);
				readOperationId = operationId;
				readCancelRequested = false;
				BeginTargetLocked();
				submitted = runtime->Submit(operation);
				if (!submitted)
				{
					readOperationId = 0;
					RollbackTargetLocked();
					submissionFailed = true;
				}
			}
		}
		if (submitted)
		{
			runtime->FlushPendingSubmissions();
		}
		if (submissionFailed)
		{
			BeginTerminal(retainedState, EIO, true);
		}
	}

	void ConnectionState::BeginReading(Ptr<ConnectionState> retainedState)
	{
#define ERROR_MESSAGE_PREFIX L"vl::inter_process::async_tcp_socket::linux_socket::ConnectionState::BeginReading(Ptr<ConnectionState>)#"
		CS_LOCK(lockState)
		{
			CHECK_ERROR(connected && !stopping && !terminalPending, ERROR_MESSAGE_PREFIX L"Requires a connected connection.");
			CHECK_ERROR(callback != nullptr, ERROR_MESSAGE_PREFIX L"Requires an installed callback.");
			CHECK_ERROR(!reading, ERROR_MESSAGE_PREFIX L"Can only be called once.");
			reading = true;
		}
		PostRead(retainedState);
#undef ERROR_MESSAGE_PREFIX
	}

	void ConnectionState::Write(Ptr<ConnectionState> retainedState, Ptr<AsyncSocketBuffer> buffer)
	{
#define ERROR_MESSAGE_PREFIX L"vl::inter_process::async_tcp_socket::linux_socket::ConnectionState::Write(Ptr<ConnectionState>, Ptr<AsyncSocketBuffer>)#"
		CHECK_ERROR(buffer, ERROR_MESSAGE_PREFIX L"Requires a buffer.");
		auto operationId = runtime->ReserveOperationId();
		bool submitted = false;
		CS_LOCK(lockState)
		{
			CHECK_ERROR(connected && !stopping && !terminalPending && !writePending, ERROR_MESSAGE_PREFIX L"Requires a connected connection with no outstanding write.");
			auto operation = new WriteOperation(operationId, retainedState, fileDescriptor, buffer, 0);
			writePending = true;
			writeOperationId = operationId;
			writeCancelRequested = false;
			BeginTargetLocked();
			submitted = runtime->Submit(operation);
			if (!submitted)
			{
				writePending = false;
				writeOperationId = 0;
				RollbackTargetLocked();
			}
		}
		if (submitted)
		{
			runtime->FlushPendingSubmissions();
		}
		CHECK_ERROR(submitted, ERROR_MESSAGE_PREFIX L"Failed to submit the write operation.");
#undef ERROR_MESSAGE_PREFIX
	}

	void ConnectionState::BeginTerminal(Ptr<ConnectionState> retainedState, vint error, bool reportError)
	{
		bool claimed = false;
		CS_LOCK(lockState)
		{
			if (!stopping && !terminalPending)
			{
				terminalPending = true;
				claimed = true;
			}
		}
		if (!claimed)
		{
			return;
		}

		if (reportError)
		{
			InvokeCallback(true, [&](IAsyncSocketCallback* installed)
			{
				installed->OnError(LinuxSocketErrorMessage(L"Asynchronous socket operation", error), true);
			});
		}
		Stop(retainedState);
	}

	void ConnectionState::StartConnectAttempt(Ptr<ConnectionState> retainedState)
	{
		bool active = false;
		bool fatal = false;
		CS_LOCK(lockState)
		{
			if (clientMode && !stopping && clientStatus == ClientStatus::WaitingForServer && connectOperationId == 0 && retryOperationId == 0)
			{
				clientAttempts++;
				fatal = clientAttempts >= AsyncSocketClientRetryCount;
				active = true;
			}
		}
		if (!active)
		{
			return;
		}

		OwnedFileDescriptor createdSocket(socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, IPPROTO_TCP));
		if (createdSocket.Get() < 0)
		{
			DeliverConnectFailure(retainedState, errno, fatal);
			return;
		}
		vint reuseAddress = 1;
		if (setsockopt(createdSocket.Get(), SOL_SOCKET, SO_REUSEADDR, &reuseAddress, sizeof(reuseAddress)) != 0)
		{
			DeliverConnectFailure(retainedState, errno, fatal);
			return;
		}

		auto operationId = runtime->ReserveOperationId();
		auto createdSocketValue = createdSocket.Get();
		auto operation = new ConnectOperation(operationId, retainedState, createdSocketValue, clientPort);
		createdSocket.Detach();
		bool attempted = false;
		bool submissionFailed = false;
		CS_LOCK(lockState)
		{
			if (!stopping && clientStatus == ClientStatus::WaitingForServer && connectOperationId == 0 && retryOperationId == 0)
			{
				attempted = true;
				fileDescriptor = createdSocketValue;
				connectOperationId = operationId;
				connectCancelRequested = false;
				BeginTargetLocked();
				if (!runtime->Submit(operation))
				{
					fileDescriptor = -1;
					connectOperationId = 0;
					RollbackTargetLocked();
					submissionFailed = true;
				}
			}
		}
		if (attempted && !submissionFailed)
		{
			runtime->FlushPendingSubmissions();
		}
		if (!attempted)
		{
			delete operation;
		}
		else if (submissionFailed)
		{
			DeliverConnectFailure(retainedState, EIO, fatal);
		}
	}

	void ConnectionState::DeliverConnectFailure(Ptr<ConnectionState> retainedState, vint error, bool fatal)
	{
		bool deliver = false;
		CS_LOCK(lockState)
		{
			deliver = !stopping && clientStatus == ClientStatus::WaitingForServer;
		}
		if (!deliver)
		{
			return;
		}

		InvokeCallback(false, [&](IAsyncSocketCallback* installed)
		{
			installed->OnError(LinuxSocketErrorMessage(L"io_uring connect", error), fatal);
		});
		if (fatal)
		{
			Stop(retainedState);
		}
		else
		{
			ScheduleRetry(retainedState);
		}
	}

	void ConnectionState::ScheduleRetry(Ptr<ConnectionState> retainedState)
	{
		auto operationId = runtime->ReserveOperationId();
		auto operation = new RetryOperation(operationId, retainedState);
		bool attempted = false;
		bool submissionFailed = false;
		CS_LOCK(lockState)
		{
			if (!stopping && clientStatus == ClientStatus::WaitingForServer && retryOperationId == 0 && connectOperationId == 0)
			{
				attempted = true;
				retryOperationId = operationId;
				retryCancelRequested = false;
				BeginTargetLocked();
				if (!runtime->Submit(operation))
				{
					retryOperationId = 0;
					RollbackTargetLocked();
					submissionFailed = true;
				}
			}
		}
		if (attempted && !submissionFailed)
		{
			runtime->FlushPendingSubmissions();
		}
		if (!attempted)
		{
			delete operation;
		}
		else if (submissionFailed)
		{
			BeginTerminal(retainedState, EIO, true);
		}
	}

	void ConnectionState::Stop(Ptr<ConnectionState> retainedState)
	{
		bool firstStop = false;
		bool cancellationSubmitted = false;
		CS_LOCK(lockState)
		{
			if (!stopping)
			{
				stopping = true;
				firstStop = true;
				connected = false;
				reading = false;
				writePending = false;
				terminalPending = false;
				if (clientMode)
				{
					clientStatus = ClientStatus::Disconnected;
				}
			}

			auto collect = [&](vuint64_t operationId, bool& cancelRequested)
			{
				if (operationId != 0 && !cancelRequested)
				{
					auto operation = new CancelOperation(runtime->ReserveOperationId(), retainedState, operationId);
					cancelRequested = true;
					BeginCancelLocked();
					if (!runtime->Submit(operation))
					{
						cancelRequested = false;
						RollbackCancelLocked();
					}
					else
					{
						cancellationSubmitted = true;
					}
				}
			};
			collect(readOperationId, readCancelRequested);
			collect(writeOperationId, writeCancelRequested);
			collect(connectOperationId, connectCancelRequested);
			collect(retryOperationId, retryCancelRequested);

			if (firstStop && fileDescriptor >= 0)
			{
				shutdown((int)fileDescriptor, SHUT_RDWR);
				if (targetOperations == 0)
				{
					CloseFileDescriptor(fileDescriptor);
					fileDescriptor = -1;
				}
			}
		}
		if (cancellationSubmitted)
		{
			runtime->FlushPendingSubmissions();
		}

		if (currentRingRuntime != runtime.Obj())
		{
			operationDrain->Wait();
		}

		auto callbackDepth = CurrentCallbackDepth();
		IAsyncSocketCallback* installed = nullptr;
		lockState.Enter();
		while (activeCallbacks > callbackDepth)
		{
			cvCallbacks.SleepWith(lockState);
		}
		if (!disconnectedNotified)
		{
			disconnectedNotified = true;
			if (callback)
			{
				installed = callback;
				activeCallbacks++;
			}
		}
		lockState.Leave();

		if (installed)
		{
			ConnectionCallbackFrame frame{ this, currentConnectionCallbackFrame };
			currentConnectionCallbackFrame = &frame;
			try
			{
				installed->OnDisconnected();
			}
			catch (...)
			{
			}
			currentConnectionCallbackFrame = frame.previous;
			EndCallback();
		}

		if (callbackDepth == 0)
		{
			CS_LOCK(lockState)
			{
				while (activeCallbacks > 0)
				{
					cvCallbacks.SleepWith(lockState);
				}
			}
		}
		if (clientMode)
		{
			eventWaitForServer.Signal();
		}
	}

	void ConnectionState::WaitForServer(Ptr<ConnectionState> retainedState)
	{
#define ERROR_MESSAGE_PREFIX L"vl::inter_process::async_tcp_socket::linux_socket::ConnectionState::WaitForServer(Ptr<ConnectionState>)#"
		bool begin = false;
		CS_LOCK(lockState)
		{
			if (clientMode && clientStatus == ClientStatus::Ready && !stopping)
			{
				clientStatus = ClientStatus::WaitingForServer;
				begin = true;
			}
		}
		CHECK_ERROR(begin, ERROR_MESSAGE_PREFIX L"Can only be called once while the client is ready.");
		StartConnectAttempt(retainedState);
		eventWaitForServer.Wait();
#undef ERROR_MESSAGE_PREFIX
	}

	ClientStatus ConnectionState::GetStatus()
	{
		ClientStatus result;
		CS_LOCK(lockState)
		{
			result = clientStatus;
		}
		return result;
	}

/***********************************************************************
AsyncSocketConnection
***********************************************************************/

	class AsyncSocketConnection : public Object, public virtual IAsyncSocketConnection
	{
	private:
		Ptr<ConnectionState>					state;

	public:
		AsyncSocketConnection(Ptr<ConnectionState> _state)
			: state(_state)
		{
			state->owner = this;
		}

		~AsyncSocketConnection()
		{
			state->Stop(state);
			state->owner = nullptr;
		}

		void InstallCallback(IAsyncSocketCallback* callback) override
		{
			state->InstallCallback(state, callback);
		}

		void BeginReadingLoopUnsafe() override
		{
			state->BeginReading(state);
		}

		void WriteAsync(Ptr<AsyncSocketBuffer> buffer) override
		{
			state->Write(state, buffer);
		}

		void Stop() override
		{
			state->Stop(state);
		}
	};

/***********************************************************************
ServerState
***********************************************************************/

	class ServerState : public RingOperationOwner
	{
	private:
		class AcceptOperation;
		class CancelOperation;

		Ptr<RingRuntime>					runtime;
		vint								port = 0;

		// covers all fields below, callback counts, and target operation counts
		CriticalSection					lockState;
		ConditionVariable				cvCallbacks;
		IAsyncSocketServerCallback*		callback = nullptr;
		vint								listener = -1;
		bool								startCalled = false;
		bool								starting = false;
		bool								started = false;
		bool								stopping = false;
		bool								stopped = false;
		bool								unexpectedStopNotified = false;
		EventObject						eventStartFinished;
		vuint64_t						acceptOperationId = 0;
		bool								acceptCancelRequested = false;
		vint								targetOperations = 0;
		vint								activeCallbacks = 0;
		List<Ptr<AsyncSocketConnection>>		connections;

		void FinishStart()
		{
			CS_LOCK(lockState)
			{
				starting = false;
				eventStartFinished.Signal();
			}
		}

		class StartScope
		{
		private:
			ServerState*						server = nullptr;

		public:
			StartScope(ServerState* _server)
				: server(_server)
			{
			}

			~StartScope()
			{
				server->FinishStart();
			}
		};

		void BeginTargetLocked()
		{
			targetOperations++;
			operationDrain->Begin();
		}

		void BeginCancelLocked()
		{
			operationDrain->Begin();
		}

		void RollbackTargetLocked()
		{
#define ERROR_MESSAGE_PREFIX L"vl::inter_process::async_tcp_socket::linux_socket::ServerState::RollbackTargetLocked()#"
			CHECK_ERROR(targetOperations > 0, ERROR_MESSAGE_PREFIX L"Target-operation bookkeeping became unbalanced.");
			targetOperations--;
			operationDrain->End();
#undef ERROR_MESSAGE_PREFIX
		}

		void RollbackCancelLocked()
		{
			operationDrain->End();
		}

		void EndTargetOperation() override
		{
			CS_LOCK(lockState)
			{
				if (targetOperations <= 0)
				{
					std::abort();
				}
				targetOperations--;
				if (stopping && targetOperations == 0 && listener >= 0)
				{
					CloseFileDescriptor(listener);
					listener = -1;
				}
			}
		}

		void HandleOperationFailure(Ptr<RingOperationOwner> retainedOwner) override
		{
			auto retainedState = retainedOwner.Cast<ServerState>();
			if (!retainedState)
			{
				std::abort();
			}
			HandleUnexpectedStop(retainedState);
		}

		void HandleUnexpectedStop(Ptr<ServerState> retainedState)
		{
			IAsyncSocketServerCallback* installedCallback = nullptr;
			CS_LOCK(lockState)
			{
				if (started && !stopping && !unexpectedStopNotified)
				{
					unexpectedStopNotified = true;
					installedCallback = callback;
					if (installedCallback)
					{
						activeCallbacks++;
					}
				}
			}
			if (installedCallback)
			{
				ServerCallbackFrame frame{ this, currentServerCallbackFrame };
				currentServerCallbackFrame = &frame;
				try
				{
					installedCallback->OnServerStopped();
				}
				catch (...)
				{
				}
				currentServerCallbackFrame = frame.previous;
				EndCallback();
			}
			Stop(retainedState);
		}

		vint CurrentCallbackDepth()
		{
			vint result = 0;
			for (auto frame = currentServerCallbackFrame; frame; frame = frame->previous)
			{
				if (frame->server == this)
				{
					result++;
				}
			}
			return result;
		}

		void EndCallback()
		{
			CS_LOCK(lockState)
			{
				activeCallbacks--;
				cvCallbacks.WakeAllPendings();
			}
		}

		bool PostAccept(Ptr<ServerState> retainedState);

	public:
		ServerState(Ptr<RingRuntime> _runtime, vint _port)
			: runtime(_runtime)
			, port(_port)
		{
#define ERROR_MESSAGE_PREFIX L"vl::inter_process::async_tcp_socket::linux_socket::ServerState::ServerState(Ptr<RingRuntime>, vint)#"
			CHECK_ERROR(eventStartFinished.CreateManualUnsignal(true), ERROR_MESSAGE_PREFIX L"Failed to create the server startup drain event.");
#undef ERROR_MESSAGE_PREFIX
		}

		void Start(Ptr<ServerState> retainedState, IAsyncSocketServerCallback* value);
		void Stop(Ptr<ServerState> retainedState);
		bool IsStopped();
	};

/***********************************************************************
ServerState::CancelOperation
***********************************************************************/

	class ServerState::CancelOperation : public RingOperation
	{
	public:
		Ptr<ServerState>						server;
		vuint64_t							targetId = 0;

		CancelOperation(vuint64_t id, Ptr<ServerState> _server, vuint64_t _targetId)
			: RingOperation(id, _server, false)
			, server(_server)
			, targetId(_targetId)
		{
		}

		void Prepare(io_uring_sqe* sqe) noexcept override
		{
			io_uring_prep_cancel64(sqe, targetId, 0);
		}

		void Handle(vint) override
		{
			CS_LOCK(server->lockState)
			{
			}
		}
	};

/***********************************************************************
ServerState::AcceptOperation
***********************************************************************/

	class ServerState::AcceptOperation : public RingOperation
	{
	public:
		Ptr<ServerState>						server;
		vint								listener = -1;
		sockaddr_storage						address = {};
		socklen_t							addressSize = sizeof(address);

		AcceptOperation(vuint64_t id, Ptr<ServerState> _server, vint _listener)
			: RingOperation(id, _server, true)
			, server(_server)
			, listener(_listener)
		{
		}

		void Prepare(io_uring_sqe* sqe) noexcept override
		{
			io_uring_prep_accept(sqe, (int)listener, (sockaddr*)&address, &addressSize, SOCK_CLOEXEC);
		}

		void Handle(vint result) override
		{
			bool running = false;
			CS_LOCK(server->lockState)
			{
				if (server->acceptOperationId == id)
				{
					server->acceptOperationId = 0;
					server->acceptCancelRequested = false;
					running = server->started && !server->stopping;
				}
			}

			if (result < 0)
			{
				bool recoverable = false;
				switch (-result)
				{
				case EINTR:
				case EAGAIN:
				case ECONNABORTED:
				case EPROTO:
				case ENETDOWN:
				case ENOPROTOOPT:
				case EHOSTDOWN:
				case ENONET:
				case EHOSTUNREACH:
				case EOPNOTSUPP:
				case ENETUNREACH:
					recoverable = true;
				}
				if (running && recoverable)
				{
					if (!server->PostAccept(server))
					{
						server->HandleUnexpectedStop(server);
					}
				}
				else if (running && result != -ECANCELED)
				{
					server->HandleUnexpectedStop(server);
				}
				return;
			}

			OwnedFileDescriptor acceptedSocket(result);
			if (!running)
			{
				return;
			}

			// Keep exactly one accept pending, and rearm before invoking user code.
			if (!server->PostAccept(server))
			{
				server->HandleUnexpectedStop(server);
				return;
			}
			auto connectionState = Ptr(new ConnectionState(server->runtime, acceptedSocket.Get()));
			auto connection = Ptr(new AsyncSocketConnection(connectionState));
			acceptedSocket.Detach();
			IAsyncSocketServerCallback* installedCallback = nullptr;
			CS_LOCK(server->lockState)
			{
				if (server->started && !server->stopping && server->callback)
				{
					server->connections.Add(connection);
					installedCallback = server->callback;
					server->activeCallbacks++;
				}
			}
			if (!installedCallback)
			{
				connection->Stop();
				return;
			}

			WaitForClientResult acceptResult = WaitForClientResult::Reject;
			ServerCallbackFrame frame{ server.Obj(), currentServerCallbackFrame };
			currentServerCallbackFrame = &frame;
			try
			{
				acceptResult = installedCallback->OnClientConnected(connection.Obj());
			}
			catch (...)
			{
			}
			currentServerCallbackFrame = frame.previous;
			bool accepted = false;
			CS_LOCK(server->lockState)
			{
				accepted = acceptResult == WaitForClientResult::Accept && server->started && !server->stopping;
				if (!accepted)
				{
					server->connections.Remove(connection.Obj());
				}
			}
			server->EndCallback();
			if (!accepted)
			{
				connection->Stop();
			}
		}
	};

/***********************************************************************
ServerState
***********************************************************************/

	bool ServerState::PostAccept(Ptr<ServerState> retainedState)
	{
		auto operationId = runtime->ReserveOperationId();
		AcceptOperation* operation = nullptr;
		bool submitted = false;
		bool submissionFailed = false;
		CS_LOCK(lockState)
		{
			if (started && !stopping && listener >= 0 && acceptOperationId == 0)
			{
				operation = new AcceptOperation(operationId, retainedState, listener);
				acceptOperationId = operationId;
				acceptCancelRequested = false;
				BeginTargetLocked();
				submitted = runtime->Submit(operation);
				if (!submitted)
				{
					acceptOperationId = 0;
					RollbackTargetLocked();
					submissionFailed = true;
				}
			}
		}
		if (submitted)
		{
			runtime->FlushPendingSubmissions();
		}
		return !submissionFailed;
	}

	void ServerState::Start(Ptr<ServerState> retainedState, IAsyncSocketServerCallback* value)
	{
#define ERROR_MESSAGE_PREFIX L"vl::inter_process::async_tcp_socket::linux_socket::ServerState::Start(Ptr<ServerState>, IAsyncSocketServerCallback*)#"
		CHECK_ERROR(value != nullptr, ERROR_MESSAGE_PREFIX L"Requires a callback.");
		CS_LOCK(lockState)
		{
			CHECK_ERROR(!startCalled && !stopping, ERROR_MESSAGE_PREFIX L"Can only be called once before stopping.");
			startCalled = true;
			starting = true;
			callback = value;
			eventStartFinished.Unsignal();
		}
		StartScope startScope(this);

		OwnedFileDescriptor createdListener(socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, IPPROTO_TCP));
		vint setupError = createdListener.Get() < 0 ? errno : 0;
		if (setupError == 0)
		{
			vint reuseAddress = 1;
			if (setsockopt(createdListener.Get(), SOL_SOCKET, SO_REUSEADDR, &reuseAddress, sizeof(reuseAddress)) != 0)
			{
				setupError = errno;
			}
		}

		sockaddr_in address = {};
		address.sin_family = AF_INET;
		address.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
		address.sin_port = htons((vuint16_t)port);
		if (setupError == 0 && bind(createdListener.Get(), (sockaddr*)&address, sizeof(address)) != 0)
		{
			setupError = errno;
		}
		if (setupError == 0 && listen(createdListener.Get(), SOMAXCONN) != 0)
		{
			setupError = errno;
		}
		if (setupError != 0)
		{
			CS_LOCK(lockState)
			{
				stopping = true;
				stopped = true;
				callback = nullptr;
			}
			runtime->Stop();
			auto failure = setupError == EADDRINUSE ? AsyncSocketServerStartFailure::AddressInUse : AsyncSocketServerStartFailure::Other;
			throw AsyncSocketServerStartException(failure, LinuxSocketErrorMessage(L"AsyncSocketServer listener setup", setupError));
		}

		bool committed = false;
		try
		{
			CS_LOCK(lockState)
			{
				if (!stopping)
				{
					runtime->Start(runtime);
					listener = createdListener.Detach();
					started = true;
					committed = true;
				}
			}
		}
		catch (...)
		{
			CS_LOCK(lockState)
			{
				started = false;
				stopping = true;
				stopped = true;
				callback = nullptr;
			}
			runtime->Stop();
			throw AsyncSocketServerStartException(AsyncSocketServerStartFailure::Other, ERROR_MESSAGE_PREFIX L"Failed to start the listener runtime.");
		}
		if (!committed)
		{
			throw AsyncSocketServerStartException(AsyncSocketServerStartFailure::Other, ERROR_MESSAGE_PREFIX L"The listener was stopped during startup.");
		}
		if (!PostAccept(retainedState))
		{
			CS_LOCK(lockState)
			{
				started = false;
				stopping = true;
				stopped = true;
				callback = nullptr;
				if (listener >= 0)
				{
					shutdown((int)listener, SHUT_RDWR);
					if (targetOperations == 0)
					{
						CloseFileDescriptor(listener);
						listener = -1;
					}
				}
			}
			runtime->Stop();
			throw AsyncSocketServerStartException(AsyncSocketServerStartFailure::Other, ERROR_MESSAGE_PREFIX L"Failed to submit the first accept operation.");
		}
#undef ERROR_MESSAGE_PREFIX
	}

	void ServerState::Stop(Ptr<ServerState> retainedState)
	{
		bool waitForStart = false;
		bool cancellationSubmitted = false;
		CS_LOCK(lockState)
		{
			if (!stopping)
			{
				stopping = true;
				stopped = true;
				started = false;
			}
			waitForStart = starting;
		}
		if (waitForStart)
		{
			eventStartFinished.Wait();
		}

		CS_LOCK(lockState)
		{
			if (acceptOperationId != 0 && !acceptCancelRequested)
			{
				auto operation = new CancelOperation(runtime->ReserveOperationId(), retainedState, acceptOperationId);
				acceptCancelRequested = true;
				BeginCancelLocked();
				if (!runtime->Submit(operation))
				{
					acceptCancelRequested = false;
					RollbackCancelLocked();
				}
				else
				{
					cancellationSubmitted = true;
				}
			}
			if (listener >= 0)
			{
				shutdown((int)listener, SHUT_RDWR);
				if (targetOperations == 0)
				{
					CloseFileDescriptor(listener);
					listener = -1;
				}
			}
		}
		if (cancellationSubmitted)
		{
			runtime->FlushPendingSubmissions();
		}

		if (currentRingRuntime != runtime.Obj())
		{
			operationDrain->Wait();
		}

		auto callbackDepth = CurrentCallbackDepth();
		CS_LOCK(lockState)
		{
			while (activeCallbacks > callbackDepth)
			{
				cvCallbacks.SleepWith(lockState);
			}
		}

		List<Ptr<AsyncSocketConnection>> retainedConnections;
		CS_LOCK(lockState)
		{
			for (auto connection : connections)
			{
				retainedConnections.Add(connection);
			}
		}
		for (auto connection : retainedConnections)
		{
			connection->Stop();
		}
		runtime->Stop();
		if (callbackDepth == 0)
		{
			CS_LOCK(lockState)
			{
				callback = nullptr;
			}
		}
	}

	bool ServerState::IsStopped()
	{
		bool result = false;
		CS_LOCK(lockState)
		{
			result = stopped;
		}
		return result;
	}

/***********************************************************************
AsyncSocketServer::Impl
***********************************************************************/

	class AsyncSocketServer::Impl : public Object
	{
	private:
		vint								port = 0;
		Ptr<RingRuntime>					runtime;
		Ptr<ServerState>					state;

	public:
		Impl(vint _port)
			: port(_port)
			, runtime(RingRuntime::Create(false))
			, state(Ptr(new ServerState(runtime, _port)))
		{
		}

		~Impl()
		{
			Stop();
		}

		vint GetPort()
		{
			return port;
		}

		void Start(IAsyncSocketServerCallback* callback)
		{
			state->Start(state, callback);
		}

		void Stop()
		{
			state->Stop(state);
		}

		bool IsStopped()
		{
			return state->IsStopped();
		}
	};

/***********************************************************************
AsyncSocketServer
***********************************************************************/

	AsyncSocketServer::AsyncSocketServer(vint port)
	{
#define ERROR_MESSAGE_PREFIX L"vl::inter_process::async_tcp_socket::linux_socket::AsyncSocketServer::AsyncSocketServer(vint)#"
		CHECK_ERROR(1 <= port && port <= 65535, ERROR_MESSAGE_PREFIX L"The port must be in 1..65535.");
#undef ERROR_MESSAGE_PREFIX
		impl = new Impl(port);
	}

	AsyncSocketServer::~AsyncSocketServer()
	{
		delete impl;
	}

	vint AsyncSocketServer::GetPort()
	{
		return impl->GetPort();
	}

	void AsyncSocketServer::Start(IAsyncSocketServerCallback* callback)
	{
		impl->Start(callback);
	}

	void AsyncSocketServer::Stop()
	{
		impl->Stop();
	}

	bool AsyncSocketServer::IsStopped()
	{
		return impl->IsStopped();
	}

/***********************************************************************
AsyncSocketClient::Impl
***********************************************************************/

	class AsyncSocketClient::Impl : public Object
	{
	private:
		vint								port = 0;
		Ptr<RingRuntime>					runtime;
		Ptr<ConnectionState>				state;
		Ptr<AsyncSocketConnection>		connection;

	public:
		Impl(vint _port)
			: port(_port)
			, runtime(RingRuntime::Create(true))
			, state(Ptr(new ConnectionState(runtime, true, _port)))
			, connection(Ptr(new AsyncSocketConnection(state)))
		{
		}

		~Impl()
		{
			Stop();
		}

		vint GetPort()
		{
			return port;
		}

		void Stop()
		{
			connection->Stop();
			runtime->Stop();
		}

		IAsyncSocketConnection* GetConnection()
		{
			return connection.Obj();
		}

		void WaitForServer()
		{
			state->WaitForServer(state);
		}

		ClientStatus GetStatus()
		{
			return state->GetStatus();
		}
	};

/***********************************************************************
AsyncSocketClient
***********************************************************************/

	AsyncSocketClient::AsyncSocketClient(vint port)
	{
#define ERROR_MESSAGE_PREFIX L"vl::inter_process::async_tcp_socket::linux_socket::AsyncSocketClient::AsyncSocketClient(vint)#"
		CHECK_ERROR(1 <= port && port <= 65535, ERROR_MESSAGE_PREFIX L"The port must be in 1..65535.");
#undef ERROR_MESSAGE_PREFIX
		impl = new Impl(port);
	}

	AsyncSocketClient::~AsyncSocketClient()
	{
		delete impl;
	}

	vint AsyncSocketClient::GetPort()
	{
		return impl->GetPort();
	}

	Ptr<IAsyncSocketClient> AsyncSocketClient::CreateSameEndpointClient()
	{
		return Ptr(new AsyncSocketClient(GetPort()));
	}

	IAsyncSocketConnection* AsyncSocketClient::GetConnection()
	{
		return impl->GetConnection();
	}

	void AsyncSocketClient::WaitForServer()
	{
		impl->WaitForServer();
	}

	ClientStatus AsyncSocketClient::GetStatus()
	{
		return impl->GetStatus();
	}

}

namespace vl::inter_process::async_tcp_socket
{
	Ptr<IAsyncSocketServer> CreateDefaultAsyncSocketServer(vint port)
	{
		return Ptr(new linux_socket::AsyncSocketServer(port));
	}

	Ptr<IAsyncSocketClient> CreateDefaultAsyncSocketClient(vint port)
	{
		return Ptr(new linux_socket::AsyncSocketClient(port));
	}
}

#endif


/***********************************************************************
.\INTERPROCESS\ASYNCSOCKET\ASYNCSOCKET.MACOS.CPP
***********************************************************************/
#if defined VCZH_GCC && defined VCZH_APPLE

#include <Network/Network.h>
#include <dispatch/dispatch.h>

#if !defined VCZH_GCC || !defined VCZH_APPLE
static_assert(false, "Do not build this file for non-macOS applications.");
#endif

namespace vl::inter_process::async_tcp_socket::macos_socket
{
	using namespace collections;

	class ConnectionState;
	class AsyncSocketConnection;

	struct CallbackFrame
	{
		ConnectionState*					connection = nullptr;
		CallbackFrame*					previous = nullptr;
	};

	static thread_local CallbackFrame* currentCallbackFrame = nullptr;
	static char connectionQueueKey;
	static char serverQueueKey;

/***********************************************************************
NativeConnectionContext
***********************************************************************/

	class NativeConnectionContext : public Object
	{
		friend class ConnectionState;
	private:
		Ptr<ConnectionState>				state;
		nw_connection_t					connection = nullptr;
		vint							generation = 0;
		bool							started = false;
		bool							cancelRequested = false;
		bool							waitingHandled = false;
		bool							cancelledHandled = false;

	public:
		NativeConnectionContext(Ptr<ConnectionState> _state, nw_connection_t _connection);
		~NativeConnectionContext();
	};

	NativeConnectionContext::NativeConnectionContext(Ptr<ConnectionState> _state, nw_connection_t _connection)
		: state(_state)
		, connection(_connection)
	{
	}

	NativeConnectionContext::~NativeConnectionContext()
	{
		if (connection)
		{
			nw_release(connection);
		}
	}

/***********************************************************************
ConnectionState
***********************************************************************/

	class ConnectionState : public Object
	{
		friend class NativeConnectionContext;
		friend class AsyncSocketConnection;
	private:
		// covers all fields below
		CriticalSection					lockState;
		ConditionVariable				cvState;
		dispatch_queue_t				queue = nullptr;
		IAsyncSocketConnection*			owner = nullptr;
		IAsyncSocketCallback*			callback = nullptr;
		bool							clientMode = false;
		vint							port = 0;
		bool							logicalConnected = false;
		bool							nativeReady = false;
		bool							stopping = false;
		bool							terminalPending = false;
		bool							disconnectedNotified = false;
		vint							activeCallbacks = 0;
		bool							callbackExecuting = false;
		bool							readingRequested = false;
		bool							receivePending = false;
		bool							receiveHandling = false;
		vint							receiveGeneration = -1;
		Ptr<AsyncSocketBuffer>			pendingReadBuffer;
		bool							writePending = false;
		bool							writeIssued = false;
		vint							writeGeneration = -1;
		Ptr<AsyncSocketBuffer>			writeBuffer;
		dispatch_data_t					writeData = nullptr;
		vint							pendingLocalTasks = 0;
		Ptr<NativeConnectionContext>		nativeContext;
		vint							pendingNativeContexts = 0;
		vint							nextGeneration = 0;
		ClientStatus					clientStatus = ClientStatus::Ready;
		vint							clientAttempts = 0;
		vint							retryAfterGeneration = -1;
		dispatch_source_t				retryTimer = nullptr;
		vint							retryTimerGeneration = -1;
		bool							retryTimerFired = false;
		bool							retryTimerCancelRequested = false;
		vint							pendingRetryTimers = 0;
		EventObject						eventWaitForServer;

		vint CountCurrentCallbackFrames()
		{
			vint count = 0;
			for (auto frame = currentCallbackFrame; frame; frame = frame->previous)
			{
				if (frame->connection == this)
				{
					count++;
				}
			}
			return count;
		}

		bool IsOnQueue()
		{
			return dispatch_get_specific(&connectionQueueKey) == this;
		}

		enum class CallbackType
		{
			Ordinary,
			FatalError,
			Disconnected,
		};

		bool CanInvokeCallbackLocked(CallbackType type)
		{
			if (!callback)
			{
				return false;
			}
			switch (type)
			{
			case CallbackType::Ordinary:
				return !stopping && !terminalPending;
			case CallbackType::FatalError:
				return !stopping;
			case CallbackType::Disconnected:
				return true;
			default:
				return false;
			}
		}

		IAsyncSocketCallback* BeginCallback(CallbackType type, bool& ownsExecution)
		{
			IAsyncSocketCallback* installed = nullptr;
			auto nested = CountCurrentCallbackFrames() > 0;
			ownsExecution = false;
			lockState.Enter();
			while (!nested && callbackExecuting && CanInvokeCallbackLocked(type))
			{
				cvState.SleepWith(lockState);
			}
			if (CanInvokeCallbackLocked(type))
			{
				installed = callback;
				activeCallbacks++;
				if (!nested)
				{
					callbackExecuting = true;
					ownsExecution = true;
				}
			}
			lockState.Leave();
			return installed;
		}

		void EndCallback(bool ownsExecution)
		{
			CS_LOCK(lockState)
			{
				activeCallbacks--;
				if (ownsExecution)
				{
					callbackExecuting = false;
				}
				cvState.WakeAllPendings();
			}
		}

		template<typename TCallback>
		bool InvokeCallback(CallbackType type, TCallback&& invoke)
		{
			bool ownsExecution = false;
			auto installed = BeginCallback(type, ownsExecution);
			if (!installed)
			{
				return false;
			}

			CallbackFrame frame{ this, currentCallbackFrame };
			currentCallbackFrame = &frame;
			try
			{
				invoke(installed);
			}
			catch (...)
			{
			}
			currentCallbackFrame = frame.previous;
			EndCallback(ownsExecution);
			return true;
		}

		void WaitForOtherCallbacks(vint currentFrames)
		{
			lockState.Enter();
			while (activeCallbacks > currentFrames)
			{
				cvState.SleepWith(lockState);
			}
			lockState.Leave();
		}

		void NotifyDisconnected()
		{
			bool notify = false;
			lockState.Enter();
			if (!disconnectedNotified)
			{
				disconnectedNotified = true;
				notify = callback != nullptr;
			}
			lockState.Leave();

			if (notify)
			{
				InvokeCallback(CallbackType::Disconnected, [](IAsyncSocketCallback* installed)
				{
					installed->OnDisconnected();
				});
			}
		}

		void WaitForDrain()
		{
			lockState.Enter();
			while (
				activeCallbacks > 0 ||
				pendingNativeContexts > 0 ||
				pendingRetryTimers > 0 ||
				pendingLocalTasks > 0
				)
			{
				cvState.SleepWith(lockState);
			}
			lockState.Leave();
		}

		void RequestNativeCancelLocked(Ptr<NativeConnectionContext> context)
		{
			if (!context || !context->connection || context->cancelRequested)
			{
				return;
			}
			if (!context->started)
			{
				context->started = true;
				nw_connection_start(context->connection);
			}
			context->cancelRequested = true;
			nw_connection_cancel(context->connection);
		}

		void RequestRetryTimerCancelLocked()
		{
			if (retryTimer && !retryTimerCancelRequested)
			{
				retryTimerCancelRequested = true;
				dispatch_source_cancel(retryTimer);
			}
		}

		WString NetworkErrorMessage(const wchar_t* operation, nw_error_t error)
		{
			if (!error)
			{
				return WString(operation) + L" failed without a Network.framework error.";
			}
			return WString(operation) + L" failed with Network.framework domain "
				+ itow((vint)nw_error_get_error_domain(error)) + L", code "
				+ itow((vint)nw_error_get_error_code(error)) + L".";
		}

		void ConfigureNativeLocked(Ptr<NativeConnectionContext> context, bool start)
		{
			nativeContext = context;
			pendingNativeContexts++;
			context->generation = ++nextGeneration;
			nw_connection_set_queue(context->connection, queue);

			auto retainedContext = context;
			nw_connection_set_state_changed_handler(context->connection, ^(nw_connection_state_t state, nw_error_t error)
			{
				retainedContext->state->OnNativeState(retainedContext, state, error);
			});
			if (start)
			{
				context->started = true;
				nw_connection_start(context->connection);
			}
		}

		void IssueReceiveLocked(Ptr<NativeConnectionContext> context)
		{
			if (
				!context || context != nativeContext || !context->connection ||
				!logicalConnected || !nativeReady || stopping || terminalPending ||
				!callback || !readingRequested || receivePending || receiveHandling || pendingReadBuffer
				)
			{
				return;
			}

			receivePending = true;
			receiveGeneration = context->generation;
			auto retainedContext = context;
			nw_connection_receive(context->connection, 1, 65536, ^(dispatch_data_t content, nw_content_context_t, bool isComplete, nw_error_t error)
			{
				retainedContext->state->OnReceive(retainedContext, content, isComplete, error);
			});
		}

		void IssueWriteLocked(Ptr<NativeConnectionContext> context)
		{
			if (
				!context || context != nativeContext || !context->connection ||
				!logicalConnected || !nativeReady || stopping || terminalPending ||
				!writePending || writeIssued
				)
			{
				return;
			}

			writeIssued = true;
			writeGeneration = context->generation;
			if (writeBuffer->data.Count() == 0)
			{
				pendingLocalTasks++;
				auto retainedState = context->state;
				auto generation = context->generation;
				dispatch_async(queue, ^
				{
					retainedState->CompleteEmptyWrite(generation);
				});
			}
			else
			{
				auto retainedContext = context;
				nw_connection_send(
					context->connection,
					writeData,
					NW_CONNECTION_DEFAULT_MESSAGE_CONTEXT,
					true,
					^(nw_error_t error)
					{
						retainedContext->state->OnSendCompleted(retainedContext, error);
					}
				);
			}
		}

		void EnterTerminal(Ptr<NativeConnectionContext> context, const WString& error, bool reportError)
		{
			bool claim = false;
			CS_LOCK(lockState)
			{
				if (!stopping && !terminalPending && (!context || context == nativeContext))
				{
					terminalPending = true;
					claim = true;
				}
			}
			if (!claim)
			{
				return;
			}

			if (reportError)
			{
				InvokeCallback(CallbackType::FatalError, [&](IAsyncSocketCallback* installed)
				{
					installed->OnError(error, true);
				});
			}
			Stop();
		}

		void StartClientAttempt(Ptr<ConnectionState> retainedState)
		{
			bool canStart = false;
			CS_LOCK(lockState)
			{
				canStart = clientMode && !stopping && clientStatus == ClientStatus::WaitingForServer && !nativeContext;
			}
			if (!canStart)
			{
				return;
			}

			auto portText = itoa(port);
			auto endpoint = nw_endpoint_create_host("127.0.0.1", portText.Buffer());
			auto parameters = nw_parameters_create_secure_tcp(
				NW_PARAMETERS_DISABLE_PROTOCOL,
				NW_PARAMETERS_DEFAULT_CONFIGURATION
			);
			nw_connection_t connection = nullptr;
			if (endpoint && parameters)
			{
				connection = nw_connection_create(endpoint, parameters);
			}
			if (endpoint)
			{
				nw_release(endpoint);
			}
			if (parameters)
			{
				nw_release(parameters);
			}

			if (!connection)
			{
				EnterTerminal(nullptr, L"AsyncSocketClient failed to create a Network.framework connection.", true);
				return;
			}

			auto context = Ptr(new NativeConnectionContext(retainedState, connection));
			bool installed = false;
			lockState.Enter();
			if (clientMode && !stopping && clientStatus == ClientStatus::WaitingForServer && !nativeContext)
			{
				clientAttempts++;
				ConfigureNativeLocked(context, true);
				installed = true;
			}
			lockState.Leave();
			if (!installed)
			{
				return;
			}
		}

		void ScheduleRetry(Ptr<ConnectionState> retainedState, vint generation)
		{
			auto source = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
			if (!source)
			{
				EnterTerminal(nullptr, L"AsyncSocketClient failed to create its retry timer.", true);
				return;
			}

			auto retainedForEvent = retainedState;
			dispatch_source_set_event_handler(source, ^
			{
				bool cancel = false;
				retainedForEvent->lockState.Enter();
				if (retainedForEvent->retryTimer == source && !retainedForEvent->retryTimerCancelRequested)
				{
					retainedForEvent->retryTimerFired = true;
					retainedForEvent->retryTimerCancelRequested = true;
					cancel = true;
				}
				retainedForEvent->lockState.Leave();
				if (cancel)
				{
					dispatch_source_cancel(source);
				}
			});

			auto retainedForCancel = retainedState;
			dispatch_source_set_cancel_handler(source, ^
			{
				auto retainedForSentinel = retainedForCancel;
				dispatch_async(retainedForCancel->queue, ^
				{
					retainedForSentinel->FinishRetryTimer(retainedForSentinel, source, generation);
				});
			});
			dispatch_source_set_timer(
				source,
				dispatch_time(DISPATCH_TIME_NOW, (int64_t)AsyncSocketClientRetryDelay * NSEC_PER_MSEC),
				DISPATCH_TIME_FOREVER,
				0
			);

			bool installed = false;
			lockState.Enter();
			if (
				clientMode && !stopping && clientStatus == ClientStatus::WaitingForServer &&
				retryAfterGeneration == generation && !retryTimer
				)
			{
				retryTimer = source;
				retryTimerGeneration = generation;
				retryTimerFired = false;
				retryTimerCancelRequested = false;
				pendingRetryTimers++;
				installed = true;
			}
			lockState.Leave();

			if (installed)
			{
				dispatch_activate(source);
			}
			else
			{
				dispatch_source_set_event_handler(source, nullptr);
				dispatch_source_set_cancel_handler(source, nullptr);
				dispatch_activate(source);
				dispatch_source_cancel(source);
				dispatch_release(source);
			}
		}

		void FinishRetryTimer(Ptr<ConnectionState> retainedState, dispatch_source_t source, vint generation)
		{
			bool start = false;
			lockState.Enter();
			if (retryTimer == source)
			{
				start = retryTimerFired && !stopping && clientStatus == ClientStatus::WaitingForServer && retryTimerGeneration == generation;
				retryTimer = nullptr;
				retryTimerGeneration = -1;
				retryTimerFired = false;
				retryTimerCancelRequested = false;
			}
			lockState.Leave();

			dispatch_release(source);
			if (start)
			{
				StartClientAttempt(retainedState);
			}

			CS_LOCK(lockState)
			{
				pendingRetryTimers--;
				cvState.WakeAllPendings();
			}
		}

		void OnNativeReady(Ptr<NativeConnectionContext> context)
		{
			bool notifyConnected = false;
			lockState.Enter();
			if (
				context == nativeContext && !context->cancelRequested &&
				retryAfterGeneration != context->generation &&
				!stopping && !terminalPending
				)
			{
				nativeReady = true;
				logicalConnected = true;
				if (clientMode && clientStatus == ClientStatus::WaitingForServer)
				{
					clientStatus = ClientStatus::Connected;
					notifyConnected = true;
				}
			}
			lockState.Leave();

			if (notifyConnected)
			{
				InvokeCallback(CallbackType::Ordinary, [](IAsyncSocketCallback* installed)
				{
					installed->OnConnected();
				});
				eventWaitForServer.Signal();
			}

			lockState.Enter();
			if (context == nativeContext && nativeReady && logicalConnected && !stopping && !terminalPending)
			{
				IssueReceiveLocked(context);
				IssueWriteLocked(context);
			}
			lockState.Leave();
		}

		void OnNativeWaiting(Ptr<NativeConnectionContext> context, nw_error_t error)
		{
			bool retryable = false;
			bool exhausted = false;
			lockState.Enter();
			if (
				clientMode && context == nativeContext && !context->waitingHandled &&
				!stopping && clientStatus == ClientStatus::WaitingForServer
				)
			{
				context->waitingHandled = true;
				exhausted = clientAttempts >= AsyncSocketClientRetryCount;
				retryable = !exhausted;
				if (retryable)
				{
					retryAfterGeneration = context->generation;
				}
			}
			lockState.Leave();
			if (!retryable && !exhausted)
			{
				return;
			}

			auto message = NetworkErrorMessage(L"AsyncSocketClient connection attempt", error);
			if (exhausted)
			{
				EnterTerminal(context, message, true);
				return;
			}

			InvokeCallback(CallbackType::Ordinary, [&](IAsyncSocketCallback* installed)
			{
				installed->OnError(message, false);
			});

			lockState.Enter();
			if (
				context == nativeContext && retryAfterGeneration == context->generation &&
				!stopping && clientStatus == ClientStatus::WaitingForServer
				)
			{
				RequestNativeCancelLocked(context);
			}
			lockState.Leave();
		}

		void OnNativeCancelled(Ptr<NativeConnectionContext> context)
		{
			bool schedule = false;
			lockState.Enter();
			if (!context->cancelledHandled)
			{
				context->cancelledHandled = true;
				if (context->connection)
				{
					nw_connection_set_state_changed_handler(context->connection, nullptr);
					nw_release(context->connection);
					context->connection = nullptr;
				}
				schedule = true;
			}
			lockState.Leave();

			if (schedule)
			{
				auto retainedContext = context;
				dispatch_async(queue, ^
				{
					retainedContext->state->FinishNativeCancellation(retainedContext);
				});
			}
		}

		void FinishNativeCancellation(Ptr<NativeConnectionContext> context)
		{
			bool retry = false;
			lockState.Enter();
			if (nativeContext == context)
			{
				retry = clientMode && !stopping && clientStatus == ClientStatus::WaitingForServer && retryAfterGeneration == context->generation;
				nativeContext = nullptr;
				nativeReady = false;
				receivePending = false;
				receiveGeneration = -1;
			}
			lockState.Leave();

			if (retry)
			{
				ScheduleRetry(context->state, context->generation);
			}

			CS_LOCK(lockState)
			{
				pendingNativeContexts--;
				cvState.WakeAllPendings();
			}
		}

		void OnNativeState(Ptr<NativeConnectionContext> context, nw_connection_state_t state, nw_error_t error)
		{
			switch (state)
			{
			case nw_connection_state_waiting:
				OnNativeWaiting(context, error);
				break;
			case nw_connection_state_ready:
				OnNativeReady(context);
				break;
			case nw_connection_state_failed:
				{
					bool intentionalCancellation = false;
					CS_LOCK(lockState)
					{
						intentionalCancellation =
							context != nativeContext || context->cancelRequested ||
							retryAfterGeneration == context->generation || stopping;
					}
					if (!intentionalCancellation)
					{
						EnterTerminal(context, NetworkErrorMessage(L"Asynchronous socket connection", error), true);
					}
				}
				break;
			case nw_connection_state_cancelled:
				OnNativeCancelled(context);
				break;
			default:
				break;
			}
		}

		void ResumeReading()
		{
			while (true)
			{
				Ptr<AsyncSocketBuffer> buffered;
				lockState.Enter();
				if (
					pendingReadBuffer && callback && !receiveHandling &&
					logicalConnected && !stopping && !terminalPending
					)
				{
					buffered = pendingReadBuffer;
					pendingReadBuffer = nullptr;
					receiveHandling = true;
				}
				else
				{
					IssueReceiveLocked(nativeContext);
				}
				lockState.Leave();

				if (!buffered)
				{
					return;
				}

				auto invoked = InvokeCallback(CallbackType::Ordinary, [&](IAsyncSocketCallback* installed)
				{
					installed->OnRead(&buffered->data[0], buffered->data.Count());
				});

				bool continueReading = false;
				lockState.Enter();
				if (
					!invoked && !pendingReadBuffer && logicalConnected &&
					!stopping && !terminalPending
					)
				{
					pendingReadBuffer = buffered;
				}
				receiveHandling = false;
				continueReading = invoked && logicalConnected && !stopping && !terminalPending;
				lockState.Leave();
				if (!continueReading)
				{
					return;
				}
			}
		}

		void OnReceive(Ptr<NativeConnectionContext> context, dispatch_data_t content, bool isComplete, nw_error_t error)
		{
			bool claimed = false;
			bool deliver = false;
			lockState.Enter();
			if (context == nativeContext && receivePending && receiveGeneration == context->generation)
			{
				receivePending = false;
				receiveHandling = true;
				receiveGeneration = -1;
				claimed = true;
				deliver = !stopping && !terminalPending && logicalConnected;
			}
			lockState.Leave();

			if (!deliver)
			{
				if (claimed)
				{
					CS_LOCK(lockState)
					{
						receiveHandling = false;
					}
				}
				return;
			}

			auto undelivered = Ptr(new AsyncSocketBuffer);
			bool buffering = false;
			auto bufferingRef = &buffering;
			if (content)
			{
				dispatch_data_apply(content, ^bool(dispatch_data_t, size_t, const void* buffer, size_t size)
				{
					if (size == 0)
					{
						return true;
					}
					if (!*bufferingRef)
					{
						auto invoked = InvokeCallback(CallbackType::Ordinary, [&](IAsyncSocketCallback* installed)
						{
							installed->OnRead((const vuint8_t*)buffer, (vint)size);
						});
						if (invoked)
						{
							return true;
						}
						*bufferingRef = true;
					}

					auto oldSize = undelivered->data.Count();
					undelivered->data.Resize(oldSize + (vint)size);
					std::memcpy(&undelivered->data[oldSize], buffer, size);
					return true;
				});
			}

			if (error)
			{
				EnterTerminal(context, NetworkErrorMessage(L"Asynchronous socket receive", error), true);
			}
			else if (isComplete)
			{
				EnterTerminal(context, WString::Empty, false);
			}

			bool resume = false;
			lockState.Enter();
			if (
				!error && !isComplete && context == nativeContext &&
				!stopping && !terminalPending && logicalConnected
				)
			{
				if (undelivered->data.Count() > 0)
				{
					pendingReadBuffer = undelivered;
				}
				resume = true;
			}
			receiveHandling = false;
			lockState.Leave();
			if (resume)
			{
				ResumeReading();
			}
		}

		void OnSendCompleted(Ptr<NativeConnectionContext> context, nw_error_t error)
		{
			Ptr<AsyncSocketBuffer> completedBuffer;
			dispatch_data_t completedData = nullptr;
			bool reportCompletion = false;
			bool reportError = false;
			lockState.Enter();
			if (writePending && writeIssued && writeGeneration == context->generation)
			{
				completedBuffer = writeBuffer;
				completedData = writeData;
				writeBuffer = nullptr;
				writeData = nullptr;
				writePending = false;
				writeIssued = false;
				writeGeneration = -1;
				reportCompletion = !error && !stopping && !terminalPending && context == nativeContext;
				reportError = error && !stopping && !terminalPending && context == nativeContext;
			}
			lockState.Leave();

			if (completedData)
			{
				dispatch_release(completedData);
			}
			if (reportCompletion)
			{
				InvokeCallback(CallbackType::Ordinary, [&](IAsyncSocketCallback* installed)
				{
					installed->OnWriteCompleted(completedBuffer);
				});
			}
			else if (reportError)
			{
				EnterTerminal(context, NetworkErrorMessage(L"Asynchronous socket send", error), true);
			}
		}

		void CompleteEmptyWrite(vint generation)
		{
			Ptr<AsyncSocketBuffer> completedBuffer;
			bool reportCompletion = false;
			lockState.Enter();
			if (writePending && writeIssued && writeGeneration == generation)
			{
				completedBuffer = writeBuffer;
				writeBuffer = nullptr;
				writePending = false;
				writeIssued = false;
				writeGeneration = -1;
				reportCompletion = !stopping && !terminalPending && nativeContext && nativeContext->generation == generation;
			}
			lockState.Leave();

			if (reportCompletion)
			{
				InvokeCallback(CallbackType::Ordinary, [&](IAsyncSocketCallback* installed)
				{
					installed->OnWriteCompleted(completedBuffer);
				});
			}

			CS_LOCK(lockState)
			{
				pendingLocalTasks--;
				cvState.WakeAllPendings();
			}
		}

	public:
		ConnectionState(bool _clientMode, vint _port)
			: clientMode(_clientMode)
			, port(_port)
		{
			queue = dispatch_queue_create("vlppos.async-socket.connection", DISPATCH_QUEUE_SERIAL);
			CHECK_ERROR(queue != nullptr, L"IAsyncSocketConnection failed to create its dispatch queue.");
			try
			{
				CHECK_ERROR(eventWaitForServer.CreateManualUnsignal(false), L"IAsyncSocketClient failed to create its wait event.");
				dispatch_queue_set_specific(queue, &connectionQueueKey, this, nullptr);
			}
			catch (...)
			{
				dispatch_release(queue);
				queue = nullptr;
				throw;
			}
		}

		~ConnectionState()
		{
			if (writeData)
			{
				dispatch_release(writeData);
			}
			dispatch_queue_set_specific(queue, &connectionQueueKey, nullptr, nullptr);
			dispatch_release(queue);
		}

		void AttachOwner(IAsyncSocketConnection* value)
		{
			CS_LOCK(lockState)
			{
				owner = value;
			}
		}

		void DetachOwner(IAsyncSocketConnection* value)
		{
			CS_LOCK(lockState)
			{
				if (owner == value)
				{
					owner = nullptr;
				}
			}
		}

		bool RequiresDeferredDrain()
		{
			return CountCurrentCallbackFrames() > 0 || IsOnQueue();
		}

		void ConfigureServerNative(Ptr<ConnectionState> retainedState, nw_connection_t connection)
		{
			auto context = Ptr(new NativeConnectionContext(retainedState, connection));
			CS_LOCK(lockState)
			{
				CHECK_ERROR(!clientMode && !nativeContext && !stopping, L"The accepted async socket connection is already configured.");
				logicalConnected = true;
				ConfigureNativeLocked(context, false);
			}
		}

		void StartAcceptedConnection()
		{
			CS_LOCK(lockState)
			{
				if (nativeContext && !nativeContext->started && !stopping)
				{
					nativeContext->started = true;
					nw_connection_start(nativeContext->connection);
				}
			}
		}

		void InstallCallback(IAsyncSocketCallback* value)
		{
			if (!value)
			{
				auto currentFrames = CountCurrentCallbackFrames();
				lockState.Enter();
				callback = nullptr;
				cvState.WakeAllPendings();
				while (activeCallbacks > currentFrames)
				{
					cvState.SleepWith(lockState);
				}
				lockState.Leave();
				return;
			}

			IAsyncSocketConnection* installedOwner = nullptr;
			bool canInstall = false;
			bool ownsExecution = false;
			auto nested = CountCurrentCallbackFrames() > 0;
			lockState.Enter();
			while (!nested && callbackExecuting && !stopping)
			{
				cvState.SleepWith(lockState);
			}
			if (!callback && !stopping && owner)
			{
				callback = value;
				installedOwner = owner;
				activeCallbacks++;
				if (!nested)
				{
					callbackExecuting = true;
					ownsExecution = true;
				}
				canInstall = true;
			}
			lockState.Leave();
			CHECK_ERROR(canInstall, L"IAsyncSocketConnection::InstallCallback cannot replace a callback or install one on a stopped connection.");

			CallbackFrame frame{ this, currentCallbackFrame };
			currentCallbackFrame = &frame;
			try
			{
				value->OnInstalled(installedOwner);
			}
			catch (...)
			{
			}
			currentCallbackFrame = frame.previous;
			EndCallback(ownsExecution);
			ResumeReading();
		}

		void BeginReading()
		{
			CS_LOCK(lockState)
			{
				CHECK_ERROR(logicalConnected && !stopping && !terminalPending, L"IAsyncSocketConnection::BeginReadingLoopUnsafe requires a connected connection.");
				CHECK_ERROR(callback != nullptr, L"IAsyncSocketConnection::BeginReadingLoopUnsafe requires an installed callback.");
				CHECK_ERROR(!readingRequested, L"IAsyncSocketConnection::BeginReadingLoopUnsafe can only be called once.");
				readingRequested = true;
				IssueReceiveLocked(nativeContext);
			}
		}

		void Write(Ptr<AsyncSocketBuffer> buffer)
		{
			CHECK_ERROR(buffer, L"IAsyncSocketConnection::WriteAsync requires a buffer.");

			dispatch_data_t data = nullptr;
			if (buffer->data.Count() > 0)
			{
				auto bytes = std::malloc((size_t)buffer->data.Count());
				CHECK_ERROR(bytes != nullptr, L"IAsyncSocketConnection::WriteAsync failed to allocate native write storage.");
				std::memcpy(bytes, &buffer->data[0], (size_t)buffer->data.Count());
				data = dispatch_data_create(bytes, (size_t)buffer->data.Count(), nullptr, DISPATCH_DATA_DESTRUCTOR_FREE);
				if (!data)
				{
					std::free(bytes);
				}
				CHECK_ERROR(data != nullptr, L"IAsyncSocketConnection::WriteAsync failed to create native write data.");
			}

			bool canWrite = false;
			lockState.Enter();
			if (logicalConnected && !stopping && !terminalPending && !writePending)
			{
				writePending = true;
				writeBuffer = buffer;
				writeData = data;
				canWrite = true;
				IssueWriteLocked(nativeContext);
			}
			lockState.Leave();
			if (!canWrite && data)
			{
				dispatch_release(data);
			}
			CHECK_ERROR(canWrite, L"IAsyncSocketConnection::WriteAsync requires a connected connection with no outstanding write.");
		}

		void Stop()
		{
			auto currentFrames = CountCurrentCallbackFrames();
			auto deferredDrain = RequiresDeferredDrain();
			Ptr<AsyncSocketBuffer> cancelledBuffer;
			dispatch_data_t cancelledData = nullptr;

			lockState.Enter();
			if (!stopping)
			{
				stopping = true;
				terminalPending = true;
				logicalConnected = false;
				nativeReady = false;
				readingRequested = false;
				pendingReadBuffer = nullptr;
				if (clientMode)
				{
					clientStatus = ClientStatus::Disconnected;
				}
				if (writePending && !writeIssued)
				{
					cancelledBuffer = writeBuffer;
					cancelledData = writeData;
					writeBuffer = nullptr;
					writeData = nullptr;
					writePending = false;
					writeGeneration = -1;
				}
			}
			RequestRetryTimerCancelLocked();
			RequestNativeCancelLocked(nativeContext);
			cvState.WakeAllPendings();
			lockState.Leave();

			if (cancelledData)
			{
				dispatch_release(cancelledData);
			}
			if (clientMode)
			{
				eventWaitForServer.Signal();
			}

			WaitForOtherCallbacks(currentFrames);
			NotifyDisconnected();
			if (!deferredDrain)
			{
				WaitForDrain();
			}
		}

		void WaitForServer(Ptr<ConnectionState> retainedState)
		{
			bool begin = false;
			CS_LOCK(lockState)
			{
				if (clientMode && clientStatus == ClientStatus::Ready && !stopping)
				{
					clientStatus = ClientStatus::WaitingForServer;
					begin = true;
				}
			}
			CHECK_ERROR(begin, L"IAsyncSocketClient::WaitForServer can only be called once while the client is ready.");
			StartClientAttempt(retainedState);
			eventWaitForServer.Wait();
		}

		ClientStatus GetStatus()
		{
			ClientStatus result;
			CS_LOCK(lockState)
			{
				result = clientStatus;
			}
			return result;
		}
	};

/***********************************************************************
AsyncSocketConnection
***********************************************************************/

	class AsyncSocketConnection : public Object, public virtual IAsyncSocketConnection
	{
	private:
		Ptr<ConnectionState>					state;

	public:
		AsyncSocketConnection(Ptr<ConnectionState> _state)
			: state(_state)
		{
			state->AttachOwner(this);
		}

		~AsyncSocketConnection()
		{
			state->Stop();
			state->DetachOwner(this);
		}

		Ptr<ConnectionState> GetState()
		{
			return state;
		}

		void StartAcceptedConnection()
		{
			state->StartAcceptedConnection();
		}

		bool RequiresDeferredDrain()
		{
			return state->RequiresDeferredDrain();
		}

		void InstallCallback(IAsyncSocketCallback* callback) override
		{
			state->InstallCallback(callback);
		}

		void BeginReadingLoopUnsafe() override
		{
			state->BeginReading();
		}

		void WriteAsync(Ptr<AsyncSocketBuffer> buffer) override
		{
			state->Write(buffer);
		}

		void Stop() override
		{
			state->Stop();
		}
	};

/***********************************************************************
ServerState
***********************************************************************/

	class ServerState : public Object
	{
	private:
		// covers all fields below
		CriticalSection					lockState;
		ConditionVariable				cvState;
		dispatch_queue_t				queue = nullptr;
		IAsyncSocketServerCallback*		callback = nullptr;
		vint							port = 0;
		bool							startCalled = false;
		bool							starting = false;
		bool							startupResolved = false;
		bool							startupReady = false;
		AsyncSocketServerStartFailure	startupFailure = AsyncSocketServerStartFailure::Other;
		vint							startupError = 0;
		bool							started = false;
		bool							stopping = false;
		bool							stopped = false;
		bool							unexpectedStopNotified = false;
		bool							stopFinalizing = false;
		bool							stopCompleted = false;
		nw_listener_t					listener = nullptr;
		bool							listenerCancelRequested = false;
		bool							listenerCancelledHandled = false;
		vint							pendingListener = 0;
		List<Ptr<AsyncSocketConnection>>	connections;

		bool IsOnQueue()
		{
			return dispatch_get_specific(&serverQueueKey) == this;
		}

		void RequestListenerCancelLocked()
		{
			if (listener && !listenerCancelRequested)
			{
				listenerCancelRequested = true;
				nw_listener_cancel(listener);
			}
		}

		void OnListenerCancelled(Ptr<ServerState> retainedState)
		{
			bool schedule = false;
			lockState.Enter();
			if (!listenerCancelledHandled)
			{
				listenerCancelledHandled = true;
				if (listener)
				{
					nw_listener_set_new_connection_handler(listener, nullptr);
					nw_listener_set_state_changed_handler(listener, nullptr);
					nw_release(listener);
					listener = nullptr;
				}
				schedule = true;
			}
			lockState.Leave();

			if (schedule)
			{
				dispatch_async(queue, ^
				{
					retainedState->FinishListenerCancellation();
				});
			}
		}

		void FinishListenerCancellation()
		{
			CS_LOCK(lockState)
			{
				pendingListener = 0;
				cvState.WakeAllPendings();
			}
		}

		void OnListenerState(Ptr<ServerState> retainedState, nw_listener_state_t state, nw_error_t error)
		{
			switch (state)
			{
			case nw_listener_state_waiting:
				{
					bool startupFailed = false;
					CS_LOCK(lockState)
					{
						if (starting && !startupResolved)
						{
							starting = false;
							startupResolved = true;
							startupReady = false;
							startupError = error ? (vint)nw_error_get_error_code(error) : 0;
							startupFailure = error && nw_error_get_error_domain(error) == nw_error_domain_posix && startupError == EADDRINUSE
								? AsyncSocketServerStartFailure::AddressInUse
								: AsyncSocketServerStartFailure::Other;
							startupFailed = true;
							cvState.WakeAllPendings();
						}
					}
					if (startupFailed)
					{
						Stop();
					}
				}
				break;
			case nw_listener_state_ready:
				CS_LOCK(lockState)
				{
					if (starting && !startupResolved && !stopping)
					{
						starting = false;
						startupResolved = true;
						startupReady = true;
						started = true;
						cvState.WakeAllPendings();
					}
				}
				break;
			case nw_listener_state_failed:
				{
					IAsyncSocketServerCallback* installedCallback = nullptr;
					lockState.Enter();
					if (starting && !startupResolved)
					{
						starting = false;
						startupResolved = true;
						startupReady = false;
						startupError = error ? (vint)nw_error_get_error_code(error) : 0;
						startupFailure = error && nw_error_get_error_domain(error) == nw_error_domain_posix && startupError == EADDRINUSE
							? AsyncSocketServerStartFailure::AddressInUse
							: AsyncSocketServerStartFailure::Other;
						cvState.WakeAllPendings();
					}
					else if (started && !stopping && !unexpectedStopNotified)
					{
						unexpectedStopNotified = true;
						installedCallback = callback;
					}
					lockState.Leave();

					if (installedCallback)
					{
						try
						{
							installedCallback->OnServerStopped();
						}
						catch (...)
						{
						}
					}
				}
				Stop();
				break;
			case nw_listener_state_cancelled:
				CS_LOCK(lockState)
				{
					if (starting && !startupResolved)
					{
						starting = false;
						startupResolved = true;
						startupReady = false;
						startupFailure = AsyncSocketServerStartFailure::Other;
						cvState.WakeAllPendings();
					}
				}
				OnListenerCancelled(retainedState);
				break;
			default:
				break;
			}
		}

		Ptr<AsyncSocketConnection> CreateConnection(nw_connection_t connection)
		{
			nw_retain(connection);
			auto connectionState = Ptr(new ConnectionState(false, 0));
			auto wrapper = Ptr(new AsyncSocketConnection(connectionState));
			connectionState->ConfigureServerNative(connectionState, connection);
			return wrapper;
		}

		void OnNewConnection(nw_connection_t connection)
		{
			auto wrapper = CreateConnection(connection);
			IAsyncSocketServerCallback* installedCallback = nullptr;
			CS_LOCK(lockState)
			{
				if (started && !stopping)
				{
					installedCallback = callback;
				}
			}

			WaitForClientResult result = WaitForClientResult::Reject;
			if (installedCallback)
			{
				try
				{
					result = installedCallback->OnClientConnected(wrapper.Obj());
				}
				catch (...)
				{
				}
			}

			bool accepted = false;
			lockState.Enter();
			if (result == WaitForClientResult::Accept && started && !stopping)
			{
				connections.Add(wrapper);
				accepted = true;
			}
			lockState.Leave();

			if (accepted)
			{
				wrapper->StartAcceptedConnection();
			}
			else
			{
				wrapper->Stop();
			}
		}

	public:
		ServerState(vint _port)
			: port(_port)
		{
			queue = dispatch_queue_create("vlppos.async-socket.listener", DISPATCH_QUEUE_SERIAL);
			CHECK_ERROR(queue != nullptr, L"AsyncSocketServer failed to create its dispatch queue.");
			dispatch_queue_set_specific(queue, &serverQueueKey, this, nullptr);
		}

		~ServerState()
		{
			dispatch_queue_set_specific(queue, &serverQueueKey, nullptr, nullptr);
			dispatch_release(queue);
		}

		void Start(Ptr<ServerState> retainedState, IAsyncSocketServerCallback* value)
		{
#define ERROR_MESSAGE_PREFIX L"vl::inter_process::async_tcp_socket::macos_socket::ServerState::Start(Ptr<ServerState>, IAsyncSocketServerCallback*)#"
			CHECK_ERROR(value != nullptr, ERROR_MESSAGE_PREFIX L"Requires a callback.");
			bool begin = false;
			CS_LOCK(lockState)
			{
				if (!startCalled && !stopping)
				{
					startCalled = true;
					callback = value;
					begin = true;
				}
			}
			CHECK_ERROR(begin, ERROR_MESSAGE_PREFIX L"Can only be called once before stopping.");

			auto portText = itoa(port);
			auto endpoint = nw_endpoint_create_host("127.0.0.1", portText.Buffer());
			auto parameters = nw_parameters_create_secure_tcp(
				NW_PARAMETERS_DISABLE_PROTOCOL,
				NW_PARAMETERS_DEFAULT_CONFIGURATION
			);
			nw_listener_t createdListener = nullptr;
			if (endpoint && parameters)
			{
				nw_parameters_set_local_endpoint(parameters, endpoint);
				createdListener = nw_listener_create(parameters);
			}
			if (endpoint)
			{
				nw_release(endpoint);
			}
			if (parameters)
			{
				nw_release(parameters);
			}

			if (!createdListener)
			{
				CS_LOCK(lockState)
				{
					stopping = true;
					stopped = true;
					callback = nullptr;
				}
				throw AsyncSocketServerStartException(AsyncSocketServerStartFailure::Other, ERROR_MESSAGE_PREFIX L"Failed to create the Network.framework listener.");
			}

			bool installed = false;
			lockState.Enter();
			if (!stopping)
			{
				listener = createdListener;
				pendingListener = 1;
				starting = true;
				nw_listener_set_queue(listener, queue);

				auto retainedForState = retainedState;
				nw_listener_set_state_changed_handler(listener, ^(nw_listener_state_t state, nw_error_t error)
				{
					retainedForState->OnListenerState(retainedForState, state, error);
				});

				auto retainedForConnection = retainedState;
				nw_listener_set_new_connection_handler(listener, ^(nw_connection_t connection)
				{
					retainedForConnection->OnNewConnection(connection);
				});
				nw_listener_start(listener);
				installed = true;
			}
			lockState.Leave();
			if (!installed)
			{
				nw_release(createdListener);
				throw AsyncSocketServerStartException(AsyncSocketServerStartFailure::Other, ERROR_MESSAGE_PREFIX L"The listener was stopped during startup.");
			}

			AsyncSocketServerStartFailure failure = AsyncSocketServerStartFailure::Other;
			vint error = 0;
			bool ready = false;
			lockState.Enter();
			while (!startupResolved)
			{
				cvState.SleepWith(lockState);
			}
			ready = startupReady;
			failure = startupFailure;
			error = startupError;
			lockState.Leave();
			if (!ready)
			{
				Stop();
				throw AsyncSocketServerStartException(failure, ERROR_MESSAGE_PREFIX L"Network.framework listener startup failed with error " + itow(error) + L".");
			}
#undef ERROR_MESSAGE_PREFIX
		}

		void Stop()
		{
			auto deferredDrain = IsOnQueue();
			List<Ptr<AsyncSocketConnection>> stoppingConnections;
			lockState.Enter();
			if (!stopping)
			{
				stopping = true;
				started = false;
				stopped = true;
				if (starting && !startupResolved)
				{
					starting = false;
					startupResolved = true;
					startupReady = false;
					startupFailure = AsyncSocketServerStartFailure::Other;
					cvState.WakeAllPendings();
				}
			}
			RequestListenerCancelLocked();
			for (auto connection : connections)
			{
				stoppingConnections.Add(connection);
			}
			lockState.Leave();

			if (!deferredDrain)
			{
				for (auto connection : stoppingConnections)
				{
					if (connection->RequiresDeferredDrain())
					{
						deferredDrain = true;
						break;
					}
				}
			}

			if (deferredDrain)
			{
				for (auto connection : stoppingConnections)
				{
					connection->Stop();
				}
				return;
			}

			bool finalizeHere = false;
			lockState.Enter();
			if (!stopCompleted && !stopFinalizing)
			{
				stopFinalizing = true;
				finalizeHere = true;
			}
			while (!finalizeHere && !stopCompleted)
			{
				cvState.SleepWith(lockState);
			}
			lockState.Leave();
			if (!finalizeHere)
			{
				return;
			}

			lockState.Enter();
			while (pendingListener > 0)
			{
				cvState.SleepWith(lockState);
			}
			lockState.Leave();

			for (auto connection : stoppingConnections)
			{
				connection->Stop();
			}

			CS_LOCK(lockState)
			{
				connections.Clear();
				callback = nullptr;
				stopFinalizing = false;
				stopCompleted = true;
				cvState.WakeAllPendings();
			}
		}

		bool IsStopped()
		{
			bool result = false;
			CS_LOCK(lockState)
			{
				result = stopped;
			}
			return result;
		}
	};

/***********************************************************************
AsyncSocketServer::Impl
***********************************************************************/

	class AsyncSocketServer::Impl : public Object
	{
	private:
		vint								port = 0;
		Ptr<ServerState>					state;

	public:
		Impl(vint _port)
			: port(_port)
			, state(Ptr(new ServerState(_port)))
		{
		}

		~Impl()
		{
			state->Stop();
		}

		vint GetPort()
		{
			return port;
		}

		void Start(IAsyncSocketServerCallback* callback)
		{
			state->Start(state, callback);
		}

		void Stop()
		{
			state->Stop();
		}

		bool IsStopped()
		{
			return state->IsStopped();
		}
	};

/***********************************************************************
AsyncSocketServer
***********************************************************************/

	AsyncSocketServer::AsyncSocketServer(vint port)
	{
		CHECK_ERROR(1 <= port && port <= 65535, L"AsyncSocketServer requires a port in 1..65535.");
		impl = new Impl(port);
	}

	AsyncSocketServer::~AsyncSocketServer()
	{
		delete impl;
	}

	vint AsyncSocketServer::GetPort()
	{
		return impl->GetPort();
	}

	void AsyncSocketServer::Start(IAsyncSocketServerCallback* callback)
	{
		impl->Start(callback);
	}

	void AsyncSocketServer::Stop()
	{
		impl->Stop();
	}

	bool AsyncSocketServer::IsStopped()
	{
		return impl->IsStopped();
	}

/***********************************************************************
AsyncSocketClient::Impl
***********************************************************************/

	class AsyncSocketClient::Impl : public Object
	{
	private:
		vint								port = 0;
		Ptr<ConnectionState>					state;
		Ptr<AsyncSocketConnection>			connection;

	public:
		Impl(vint _port)
			: port(_port)
			, state(Ptr(new ConnectionState(true, _port)))
			, connection(Ptr(new AsyncSocketConnection(state)))
		{
		}

		~Impl()
		{
			connection->Stop();
		}

		vint GetPort()
		{
			return port;
		}

		IAsyncSocketConnection* GetConnection()
		{
			return connection.Obj();
		}

		void WaitForServer()
		{
			state->WaitForServer(state);
		}

		ClientStatus GetStatus()
		{
			return state->GetStatus();
		}
	};

/***********************************************************************
AsyncSocketClient
***********************************************************************/

	AsyncSocketClient::AsyncSocketClient(vint port)
	{
		CHECK_ERROR(1 <= port && port <= 65535, L"AsyncSocketClient requires a port in 1..65535.");
		impl = new Impl(port);
	}

	AsyncSocketClient::~AsyncSocketClient()
	{
		delete impl;
	}

	vint AsyncSocketClient::GetPort()
	{
		return impl->GetPort();
	}

	Ptr<IAsyncSocketClient> AsyncSocketClient::CreateSameEndpointClient()
	{
		return Ptr(new AsyncSocketClient(GetPort()));
	}

	IAsyncSocketConnection* AsyncSocketClient::GetConnection()
	{
		return impl->GetConnection();
	}

	void AsyncSocketClient::WaitForServer()
	{
		impl->WaitForServer();
	}

	ClientStatus AsyncSocketClient::GetStatus()
	{
		return impl->GetStatus();
	}

}

namespace vl::inter_process::async_tcp_socket
{
	Ptr<IAsyncSocketServer> CreateDefaultAsyncSocketServer(vint port)
	{
		return Ptr(new macos_socket::AsyncSocketServer(port));
	}

	Ptr<IAsyncSocketClient> CreateDefaultAsyncSocketClient(vint port)
	{
		return Ptr(new macos_socket::AsyncSocketClient(port));
	}
}
#endif


/***********************************************************************
.\TUI\TUI.LINUX.CPP
***********************************************************************/
/***********************************************************************
Author: Zihan Chen (vczh)
Licensed under https://github.com/vczh-libraries/License
***********************************************************************/


#ifdef VCZH_GCC

#include <csignal>
#include <poll.h>
#include <string>
#include <sys/ioctl.h>
#include <termios.h>
#include <wchar.h>

using namespace vl;
using namespace vl::collections;

namespace vl
{
	namespace console
	{
		vint TUI::MeasureChar(char32_t code)
		{
			if (!tui_internal::IsScalar(code)) return 0;
			auto width = wcwidth((wchar_t)code);
			return width < 0 ? 0 : width;
		}

		namespace tui_internal
		{
			int resizePipe[2] = { -1, -1 };

			void ResizeSignalHandler(int)
			{
				auto savedError = errno;
				char byte = 1;
				while (write(resizePipe[1], &byte, 1) == -1 && errno == EINTR)
				{
				}
				errno = savedError;
			}

			void EnsureResizePipe()
			{
				if (resizePipe[0] != -1) return;
				CHECK_ERROR(pipe(resizePipe) == 0, L"vl::console::TUI POSIX backend failed to create its resize pipe.");
				for (vint i = 0; i < 2; i++)
				{
					auto descriptorFlags = fcntl(resizePipe[i], F_GETFD);
					auto statusFlags = fcntl(resizePipe[i], F_GETFL);
					CHECK_ERROR(descriptorFlags != -1 && statusFlags != -1, L"vl::console::TUI POSIX backend failed to query resize-pipe flags.");
					CHECK_ERROR(fcntl(resizePipe[i], F_SETFD, descriptorFlags | FD_CLOEXEC) != -1, L"vl::console::TUI POSIX backend failed to make the resize pipe close-on-exec.");
					CHECK_ERROR(fcntl(resizePipe[i], F_SETFL, statusFlags | O_NONBLOCK) != -1, L"vl::console::TUI POSIX backend failed to make the resize pipe nonblocking.");
				}
			}

			void WriteAll(int descriptor, const char* text, vint length)
			{
				vint written = 0;
				while (written < length)
				{
					auto count = write(descriptor, text + written, (size_t)(length - written));
					if (count == -1 && errno == EINTR) continue;
					CHECK_ERROR(count > 0, L"vl::console::TUI POSIX backend failed while writing terminal output.");
					written += count;
				}
			}

			void AppendUtf8(std::string& output, char32_t code)
			{
				if (code <= 0x7F)
				{
					output.push_back((char)code);
				}
				else if (code <= 0x7FF)
				{
					output.push_back((char)(0xC0 | (code >> 6)));
					output.push_back((char)(0x80 | (code & 0x3F)));
				}
				else if (code <= 0xFFFF)
				{
					output.push_back((char)(0xE0 | (code >> 12)));
					output.push_back((char)(0x80 | ((code >> 6) & 0x3F)));
					output.push_back((char)(0x80 | (code & 0x3F)));
				}
				else
				{
					output.push_back((char)(0xF0 | (code >> 18)));
					output.push_back((char)(0x80 | ((code >> 12) & 0x3F)));
					output.push_back((char)(0x80 | ((code >> 6) & 0x3F)));
					output.push_back((char)(0x80 | (code & 0x3F)));
				}
			}

			void AppendNumber(std::string& output, vint value)
			{
				output += std::to_string((long long)value);
			}

			class PosixTuiBackend : public unittest::ITuiBackend
			{
			private:
				termios						savedTermios = {};
				struct sigaction				savedAction = {};
				sigset_t					savedMask = {};
				List<vuint8_t>				inputBytes;
				List<unittest::TuiBackendEvent> pendingEvents;
				vuint64_t					escapeDeadline = 0;
				vuint64_t					lastClickTime = 0;
				vint						lastClickX = -1;
				vint						lastClickY = -1;
				TuiMouseButton				lastClickButton = TuiMouseButton::Left;
				bool						left = false;
				bool						middle = false;
				bool						right = false;
				bool						started = false;
				bool						termiosChanged = false;
				bool						signalInstalled = false;

				void QueueChar(wchar_t code, bool alt = false)
				{
					unittest::TuiBackendEvent event;
					event.type = unittest::TuiBackendEventType::Char;
					event.charInfo.code = code;
					event.charInfo.alt = alt;
					pendingEvents.Add(event);
				}

				void DrainResizePipe()
				{
					char buffer[64];
					for (;;)
					{
						auto count = read(resizePipe[0], buffer, sizeof(buffer));
						if (count > 0) continue;
						if (count == -1 && errno == EINTR) continue;
						break;
					}
				}

				void QueueResize()
				{
					vint width = 0;
					vint height = 0;
					if (TryGetConsoleSize(width, height))
					{
						unittest::TuiBackendEvent event;
						event.type = unittest::TuiBackendEventType::Resize;
						event.width = width;
						event.height = height;
						pendingEvents.Add(event);
					}
				}

				bool TryParseMouse()
				{
					if (inputBytes.Count() < 4 || inputBytes[0] != 0x1B || inputBytes[1] != '[' || inputBytes[2] != '<') return false;
					vint end = -1;
					for (vint i = 3; i < inputBytes.Count(); i++)
					{
						if (inputBytes[i] == 'M' || inputBytes[i] == 'm')
						{
							end = i;
							break;
						}
						if (!(inputBytes[i] == ';' || (inputBytes[i] >= '0' && inputBytes[i] <= '9')))
						{
							inputBytes.RemoveRange(0, i + 1);
							QueueChar(L'\uFFFD');
							return true;
						}
					}
					if (end == -1) return false;

					vint values[3] = {};
					vint valueIndex = 0;
					for (vint i = 3; i < end; i++)
					{
						if (inputBytes[i] == ';')
						{
							valueIndex++;
							if (valueIndex >= 3) break;
						}
						else
						{
							values[valueIndex] = values[valueIndex] * 10 + inputBytes[i] - '0';
						}
					}
					auto final = inputBytes[end];
					inputBytes.RemoveRange(0, end + 1);
					escapeDeadline = 0;
					if (valueIndex != 2) return true;

					auto cb = values[0];
					TuiMouseInfo info;
					info.x = values[1] - 1;
					info.y = values[2] - 1;
					info.shift = (cb & 4) != 0;
					info.alt = (cb & 8) != 0;
					info.ctrl = (cb & 16) != 0;
					auto motion = (cb & 32) != 0;
					auto base = cb & ~(4 | 8 | 16 | 32);
					info.left = left;
					info.middle = middle;
					info.right = right;

					unittest::TuiBackendEvent event;
					event.mouseInfo = info;
					if (base >= 64 && base <= 67)
					{
						if (base == 64 || base == 65)
						{
							event.type = unittest::TuiBackendEventType::MouseVerticalWheel;
							event.mouseInfo.wheel = base == 64 ? 120 : -120;
						}
						else
						{
							event.type = unittest::TuiBackendEventType::MouseHorizontalWheel;
							event.mouseInfo.wheel = base == 66 ? 120 : -120;
						}
						pendingEvents.Add(event);
						return true;
					}
					if (motion)
					{
						event.type = unittest::TuiBackendEventType::MouseMove;
						pendingEvents.Add(event);
						return true;
					}

					auto released = final == 'm' || base == 3;
					auto button = base == 0 ? TuiMouseButton::Left : base == 1 ? TuiMouseButton::Middle : TuiMouseButton::Right;
					event.mouseButton = button;
					if (released)
					{
						event.type = unittest::TuiBackendEventType::MouseUp;
						if (button == TuiMouseButton::Left) left = false;
						if (button == TuiMouseButton::Middle) middle = false;
						if (button == TuiMouseButton::Right) right = false;
					}
					else
					{
						auto now = GetMonotonicTime();
						auto doubleClick = lastClickTime != 0 && now - lastClickTime <= 500 && lastClickX == info.x && lastClickY == info.y && lastClickButton == button;
						event.type = doubleClick ? unittest::TuiBackendEventType::MouseDoubleClick : unittest::TuiBackendEventType::MouseDown;
						if (button == TuiMouseButton::Left) left = true;
						if (button == TuiMouseButton::Middle) middle = true;
						if (button == TuiMouseButton::Right) right = true;
						if (doubleClick)
						{
							lastClickTime = 0;
						}
						else
						{
							lastClickTime = now;
							lastClickX = info.x;
							lastClickY = info.y;
							lastClickButton = button;
						}
					}
					event.mouseInfo.left = left;
					event.mouseInfo.middle = middle;
					event.mouseInfo.right = right;
					pendingEvents.Add(event);
					return true;
				}

				bool TryConsumeSpecialSequence()
				{
					if (inputBytes.Count() < 2 || inputBytes[0] != 0x1B || inputBytes[1] != '[') return false;
					for (vint i = 2; i < inputBytes.Count(); i++)
					{
						auto byte = inputBytes[i];
						if (byte >= 0x40 && byte <= 0x7E)
						{
							inputBytes.RemoveRange(0, i + 1);
							escapeDeadline = 0;
							return true;
						}
					}
					return false;
				}

				bool TryDecodeUtf8(bool alt = false)
				{
					auto offset = alt ? 1 : 0;
					if (inputBytes.Count() <= offset) return false;
					auto first = inputBytes[offset];
					vint length = 0;
					char32_t code = 0;
					if (first < 0x80)
					{
						length = 1;
						code = first;
					}
					else if (first >= 0xC2 && first <= 0xDF)
					{
						length = 2;
						code = first & 0x1F;
					}
					else if (first >= 0xE0 && first <= 0xEF)
					{
						length = 3;
						code = first & 0x0F;
					}
					else if (first >= 0xF0 && first <= 0xF4)
					{
						length = 4;
						code = first & 0x07;
					}
					else
					{
						inputBytes.RemoveRange(0, offset + 1);
						QueueChar(L'\uFFFD', alt);
						return true;
					}
					if (inputBytes.Count() < offset + length) return false;
					for (vint i = 1; i < length; i++)
					{
						auto next = inputBytes[offset + i];
						if ((next & 0xC0) != 0x80)
						{
							inputBytes.RemoveRange(0, offset + 1);
							QueueChar(L'\uFFFD', alt);
							return true;
						}
						code = (code << 6) | (next & 0x3F);
					}
					auto minimum = length == 1 ? 0 : length == 2 ? 0x80 : length == 3 ? 0x800 : 0x10000;
					if (code < minimum || code > 0x10FFFF || (code >= 0xD800 && code <= 0xDFFF))
					{
						inputBytes.RemoveRange(0, offset + length);
						QueueChar(L'\uFFFD', alt);
						return true;
					}
					inputBytes.RemoveRange(0, offset + length);
					escapeDeadline = 0;
					QueueChar((wchar_t)code, alt);
					return true;
				}

				bool ParseInput()
				{
					if (inputBytes.Count() == 0) return false;
					if (inputBytes[0] == 0x1B)
					{
						if (TryParseMouse()) return true;
						if (TryConsumeSpecialSequence()) return true;
						if (inputBytes.Count() > 1 && inputBytes[1] != '[')
						{
							return TryDecodeUtf8(true);
						}
						if (escapeDeadline == 0) escapeDeadline = GetMonotonicTime() + 30;
						if (GetMonotonicTime() >= escapeDeadline)
						{
							inputBytes.RemoveAt(0);
							escapeDeadline = 0;
							QueueChar((wchar_t)0x1B);
							return true;
						}
						return false;
					}
					return TryDecodeUtf8();
				}

				void AppendColor(std::string& output, TuiColor foreground, TuiColor background, TuiColorMode colorMode)
				{
					output += "\x1B[";
					if (colorMode == TuiColorMode::TrueColor)
					{
						output += "38;2;";
						AppendNumber(output, foreground.r);
						output += ";";
						AppendNumber(output, foreground.g);
						output += ";";
						AppendNumber(output, foreground.b);
						output += ";48;2;";
						AppendNumber(output, background.r);
						output += ";";
						AppendNumber(output, background.g);
						output += ";";
						AppendNumber(output, background.b);
					}
					else if (colorMode == TuiColorMode::Color256)
					{
						output += "38;5;";
						AppendNumber(output, QuantizeColor(foreground, colorMode));
						output += ";48;5;";
						AppendNumber(output, QuantizeColor(background, colorMode));
					}
					else
					{
						auto foregroundIndex = QuantizeColor(foreground, colorMode);
						auto backgroundIndex = QuantizeColor(background, colorMode);
						AppendNumber(output, foregroundIndex < 8 ? 30 + foregroundIndex : 90 + foregroundIndex - 8);
						output += ";";
						AppendNumber(output, backgroundIndex < 8 ? 40 + backgroundIndex : 100 + backgroundIndex - 8);
					}
					output += "m";
				}

			public:
				TuiColorMode Start(const TuiStartOptions& options) override
				{
					CHECK_ERROR(!started, L"vl::console::TUI POSIX backend is already active.");
					CHECK_ERROR(isatty(STDIN_FILENO) && isatty(STDOUT_FILENO), L"vl::console::TUI requires interactive terminal descriptors.");
					EnsureResizePipe();
					DrainResizePipe();
					CHECK_ERROR(tcgetattr(STDIN_FILENO, &savedTermios) == 0, L"vl::console::TUI failed to query terminal attributes.");
					started = true;

					try
					{
						auto raw = savedTermios;
						cfmakeraw(&raw);
						raw.c_cc[VMIN] = 0;
						raw.c_cc[VTIME] = 0;
						CHECK_ERROR(tcsetattr(STDIN_FILENO, TCSANOW, &raw) == 0, L"vl::console::TUI failed to activate raw terminal input.");
						termiosChanged = true;

						sigset_t blocked;
						sigemptyset(&blocked);
						sigaddset(&blocked, SIGWINCH);
						CHECK_ERROR(pthread_sigmask(SIG_BLOCK, &blocked, &savedMask) == 0, L"vl::console::TUI failed to block SIGWINCH during handler installation.");
						struct sigaction action = {};
						action.sa_handler = ResizeSignalHandler;
						sigemptyset(&action.sa_mask);
						auto signalResult = sigaction(SIGWINCH, &action, &savedAction);
						signalInstalled = signalResult == 0;
						auto maskResult = pthread_sigmask(SIG_SETMASK, &savedMask, nullptr);
						CHECK_ERROR(signalResult == 0, L"vl::console::TUI failed to install its SIGWINCH handler.");
						CHECK_ERROR(maskResult == 0, L"vl::console::TUI failed to restore the owner thread signal mask.");

						const char sequence[] = "\x1B[?1049h\x1B[?25l\x1B[?1003h\x1B[?1006h";
						WriteAll(STDOUT_FILENO, sequence, sizeof(sequence) - 1);
						if (options.colorMode != TuiColorMode::Auto) return options.colorMode;
						auto colorTerm = getenv("COLORTERM");
						if (colorTerm && (strstr(colorTerm, "truecolor") || strstr(colorTerm, "24bit"))) return TuiColorMode::TrueColor;
						auto term = getenv("TERM");
						if (term && strstr(term, "256color")) return TuiColorMode::Color256;
						return TuiColorMode::Color16;
					}
					catch (...)
					{
						Stop();
						throw;
					}
				}

				void Stop() override
				{
					if (!started) return;
					const char sequence[] = "\x1B[?1006l\x1B[?1003l\x1B[0m\x1B[?25h\x1B[?1049l";
					auto ignored = write(STDOUT_FILENO, sequence, sizeof(sequence) - 1);
					(void)ignored;
					if (termiosChanged) tcsetattr(STDIN_FILENO, TCSANOW, &savedTermios);
					if (signalInstalled)
					{
						sigset_t blocked;
						sigemptyset(&blocked);
						sigaddset(&blocked, SIGWINCH);
						pthread_sigmask(SIG_BLOCK, &blocked, nullptr);
						sigaction(SIGWINCH, &savedAction, nullptr);
						pthread_sigmask(SIG_SETMASK, &savedMask, nullptr);
					}
					inputBytes.Clear();
					pendingEvents.Clear();
					escapeDeadline = 0;
					left = middle = right = false;
					termiosChanged = false;
					signalInstalled = false;
					started = false;
				}

				bool TryGetConsoleSize(vint& width, vint& height) override
				{
					winsize size = {};
					if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &size) != 0) return false;
					width = size.ws_col;
					height = size.ws_row;
					return width > 0 && height > 0;
				}

				vuint64_t GetMonotonicTime() override
				{
					timespec time = {};
					clock_gettime(CLOCK_MONOTONIC, &time);
					return (vuint64_t)time.tv_sec * 1000 + time.tv_nsec / 1000000;
				}

				bool ReadEvent(vint milliseconds, unittest::TuiBackendEvent& event) override
				{
					if (pendingEvents.Count() == 0) ParseInput();
					if (pendingEvents.Count() == 0)
					{
						auto wait = milliseconds;
						if (escapeDeadline != 0)
						{
							auto now = GetMonotonicTime();
							auto remaining = escapeDeadline <= now ? 0 : (vint)(escapeDeadline - now);
							if (wait < 0 || remaining < wait) wait = remaining;
						}
						pollfd descriptors[] =
						{
							{ STDIN_FILENO, POLLIN, 0 },
							{ resizePipe[0], POLLIN, 0 },
						};
						vint result = 0;
						do
						{
							result = poll(descriptors, sizeof(descriptors) / sizeof(*descriptors), wait);
						} while (result == -1 && errno == EINTR);
						CHECK_ERROR(result >= 0, L"vl::console::TUI POSIX backend failed while waiting for terminal input.");
						if (descriptors[1].revents & POLLIN)
						{
							DrainResizePipe();
							QueueResize();
						}
						if (descriptors[0].revents & POLLIN)
						{
							vuint8_t bytes[256];
							auto count = read(STDIN_FILENO, bytes, sizeof(bytes));
							if (count > 0)
							{
								for (vint i = 0; i < count; i++) inputBytes.Add(bytes[i]);
							}
						}
						if (pendingEvents.Count() == 0) ParseInput();
					}
					if (pendingEvents.Count() == 0) return false;
					event = pendingEvents[0];
					pendingEvents.RemoveAt(0);
					return true;
				}

				void Render(const TuiPixel* buffer, vint width, vint height, TuiColorMode colorMode) override
				{
					std::string output;
					TuiColor lastForeground;
					TuiColor lastBackground;
					bool hasLastColor = false;
					for (vint y = 0; y < height; y++)
					{
						output += "\x1B[";
						AppendNumber(output, y + 1);
						output += ";1H";
						for (vint x = 0; x < width; x++)
						{
							auto& pixel = buffer[y * width + x];
							if (pixel.glyph == TuiPixelGlyph::WideCharContinuation) continue;
							if (!hasLastColor || pixel.foregroundColor != lastForeground || pixel.backgroundColor != lastBackground)
							{
								AppendColor(output, pixel.foregroundColor, pixel.backgroundColor, colorMode);
								lastForeground = pixel.foregroundColor;
								lastBackground = pixel.backgroundColor;
								hasLastColor = true;
							}
							auto code = pixel.GetChar32();
							AppendUtf8(output, code == 0 ? U' ' : code);
						}
					}
					output += "\x1B[0m";
					WriteAll(STDOUT_FILENO, output.data(), (vint)output.size());
				}
			};

			Ptr<unittest::ITuiBackend> CreateTuiBackend()
			{
				return Ptr(new PosixTuiBackend);
			}
		}
	}
}

#endif

