VOID CALLBACK Fiber_Function(LPVOID param) { pthread_t self = (pthread_t) param; thread_or_fiber_function(param); { /* fiber_group is a main thread into which we are to call */ pthread_t group = self->fiber_group; free(self); /* pthread_np_run_in_fiber (see below) normally switches back to caller. Nullify our identity, so it knows there is nothing to switch to, and continues running instead. */ tls_impersonate(NULL); if (group) { /* Every running [pthread_create]d fiber runs in some thread that has its own pthread_self identity (that was created as thread and later converted to fiber). `group' field of running fiber always points to that other pthread. Now switch to our group ("current master fiber created as thread"), asking it to delete our (OS) fiber data with fiber_destructor. */ pthread_np_run_in_fiber(group, fiber_destructor, GetCurrentFiber()); } /* Within current pthread API we never end up here. BTW, if fibers are ever pooled, to avoid stack space reallocation etc, jumping to the beginning of Fiber_Function should be the thing to do here. */ DeleteFiber(GetCurrentFiber()); /* Exits. See Thread_Function for explanation -- why not ExitThread. */ } }
FiberPool_::coro_pull_interface* FiberPool_::getFiber(size_t size, void** sp, coro_handler h, void* param /*= NULL*/) { assert(size && size % 4096 == 0 && size <= 1024 * 1024); void* currentFiber = NULL; #if _WIN32_WINNT >= 0x0600 if (IsThreadAFiber()) { currentFiber = GetCurrentFiber(); } else { currentFiber = ConvertThreadToFiberEx(NULL, FIBER_FLAG_FLOAT_SWITCH); } #else//#elif _MSC_VER >= 0x0501 currentFiber = ConvertThreadToFiberEx(NULL, FIBER_FLAG_FLOAT_SWITCH); if (!currentFiber) { currentFiber = GetCurrentFiber(); } #endif { fiber_pool_pck& pool = s_fiberPool._fiberPool[size / 4096 - 1]; pool._mutex.lock(); if (!pool._pool.empty()) { coro_pull_interface* oldFiber = pool._pool.back(); pool._pool.pop_back(); pool._mutex.unlock(); oldFiber->_fiber._pushHandle = currentFiber; oldFiber->_fiber._currentHandler = h; oldFiber->_fiber._fiberHandle->_param = param; oldFiber->_fiber._tick = 0; *sp = oldFiber->_fiber._fiberHandle->_stackTop; SwitchToFiber(oldFiber->_fiber._fiberHandle); return oldFiber; } pool._mutex.unlock(); } s_fiberPool._stackCount++; s_fiberPool._stackTotalSize += size; coro_pull_interface* newFiber = new coro_pull_interface; #ifdef _WIN64 newFiber->_fiber._fiberHandle = (FiberStruct_*)CreateFiberEx(size, 64 * 1024, FIBER_FLAG_FLOAT_SWITCH, FiberPool_::fiberHandler, newFiber); #else newFiber->_fiber._fiberHandle = (FiberStruct_*)CreateFiberEx(size, 0, FIBER_FLAG_FLOAT_SWITCH, FiberPool_::fiberHandler, newFiber); #endif if (newFiber->_fiber._fiberHandle) { newFiber->_fiber._pushHandle = currentFiber; newFiber->_fiber._currentHandler = h; newFiber->_fiber._fiberHandle->_param = param; newFiber->_fiber._tick = 0; *sp = newFiber->_fiber._fiberHandle->_stackTop; SwitchToFiber(newFiber->_fiber._fiberHandle); return newFiber; } delete newFiber; throw std::shared_ptr<string>(new string("Fiber不足")); }
void Coro_initializeMainCoro(Coro *self) { self->isMain = 1; #ifdef USE_FIBERS // We must convert the current thread into a fiber if it hasn't already been done. if ((LPVOID) 0x1e00 == GetCurrentFiber()) // value returned when not a fiber { // Make this thread a fiber and set its data field to the main coro's address ConvertThreadToFiber(self); } // Make the main coro represent the current fiber self->fiber = GetCurrentFiber(); #endif }
std::size_t TcpConnection::write(const uint8_t* data, size_t size) { assert(dispatcher != nullptr); assert(writeContext == nullptr); if (stopped) { throw InterruptedException(); } if (size == 0) { if (shutdown(connection, SD_SEND) != 0) { throw std::runtime_error("TcpConnection::write, shutdown failed, result=" + std::to_string(WSAGetLastError())); } return 0; } WSABUF buf{static_cast<ULONG>(size), reinterpret_cast<char*>(const_cast<uint8_t*>(data))}; TcpConnectionContext context; context.hEvent = NULL; if (WSASend(connection, &buf, 1, NULL, 0, &context, NULL) != 0) { int lastError = WSAGetLastError(); if (lastError != WSA_IO_PENDING) { throw std::runtime_error("TcpConnection::write, WSASend failed, result=" + std::to_string(lastError)); } } context.context = GetCurrentFiber(); context.interrupted = false; writeContext = &context; dispatcher->dispatch(); assert(context.context == GetCurrentFiber()); assert(dispatcher != nullptr); assert(writeContext == &context); writeContext = nullptr; DWORD transferred; DWORD flags; if (WSAGetOverlappedResult(connection, &context, &transferred, FALSE, &flags) != TRUE) { int lastError = WSAGetLastError(); if (lastError != ERROR_OPERATION_ABORTED) { throw std::runtime_error("TcpConnection::write, WSASend failed, result=" + std::to_string(lastError)); } assert(context.interrupted); throw InterruptedException(); } assert(transferred == size); assert(flags == 0); return transferred; }
/* * Free function. Saves the current context in @p from * and restores the context in @p to. On windows the from * parameter is ignored. The current context is saved on the * current fiber. * Note that if the current thread is not a fiber, it will be * converted to fiber on the fly on call and unconverted before * return. This is expensive. The user should convert the * current thread to a fiber once on thread creation for better performance. * Note that we can't leave the thread unconverted on return or else we * will leak resources on thread destruction. Do the right thing by * default. */ friend void swap_context(fibers_context_impl_base& from, const fibers_context_impl_base& to, default_hint) { if(!is_fiber()) { HPX_ASSERT(from.m_ctx == 0); from.m_ctx = ConvertThreadToFiber(0); HPX_ASSERT(from.m_ctx != 0); #if HPX_HAVE_SWAP_CONTEXT_EMULATION != 0 switch_to_fiber(to.m_ctx); #else SwitchToFiber(to.m_ctx); #endif BOOL result = ConvertFiberToThread(); HPX_ASSERT(result); HPX_UNUSED(result); from.m_ctx = 0; } else { bool call_from_main = from.m_ctx == 0; if(call_from_main) from.m_ctx = GetCurrentFiber(); #if HPX_HAVE_SWAP_CONTEXT_EMULATION != 0 switch_to_fiber(to.m_ctx); #else SwitchToFiber(to.m_ctx); #endif if(call_from_main) from.m_ctx = 0; } }
void Coroutine::init() { if (!IsThreadAFiber()) pImpl->pReturnFiber = ConvertThreadToFiber(NULL); else pImpl->pReturnFiber = GetCurrentFiber(); pImpl->pFiber = CreateFiber(0, (LPFIBER_START_ROUTINE)Coroutine::Anonymous::coroutineEntry, pImpl.get()); }
/* * Return true if current thread is a fiber. */ inline bool is_fiber() { #if _WIN32_WINNT >= 0x0600 return IsThreadAFiber() ? true : false; #else fiber_ptr current = GetCurrentFiber(); return current != 0 && current != fiber_magic; #endif }
void Return() { ESS_ASSERT( GetCurrentFiber() == m_handle ); ESS_ASSERT( m_fiberToReturn != 0); LPVOID ret = m_fiberToReturn; m_fiberToReturn = 0; SwitchToFiber(ret); }
sc_cor_fiber::~sc_cor_fiber() { if( m_fiber != 0 ) { # ifdef WIN32 PVOID cur_fiber = GetCurrentFiber(); if (m_fiber != cur_fiber) # endif DeleteFiber( m_fiber ); } }
void ReentrantLock::unlock() { if (owner == GetCurrentFiber()) { if (holdCount != 0) { holdCount--; } else { SimpleLock::unlock(); } } }
static void MainThreadInit() { PVOID pData = GetCurrentFiber(); if (pData == (void*)0x1E00) // magic { LPVOID h = ConvertThreadToFiber( &MainFiberId ); ESS_ASSERT(h != 0); ESS_ASSERT( GetFiberData() == &MainFiberId ); } }
void Run() { ESS_ASSERT(m_fiberToReturn == 0); m_fiberToReturn = GetCurrentFiber(); ESS_ASSERT(m_fiberToReturn != 0); ESS_ASSERT(m_fiberToReturn != m_handle); SwitchToFiber(m_handle); }
size_t TcpConnection::read(uint8_t* data, size_t size) { assert(dispatcher != nullptr); assert(readContext == nullptr); if (stopped) { throw InterruptedException(); } WSABUF buf{static_cast<ULONG>(size), reinterpret_cast<char*>(data)}; DWORD flags = 0; TcpConnectionContext context; context.hEvent = NULL; if (WSARecv(connection, &buf, 1, NULL, &flags, &context, NULL) != 0) { int lastError = WSAGetLastError(); if (lastError != WSA_IO_PENDING) { throw std::runtime_error("TcpConnection::read, WSARecv failed, result=" + std::to_string(lastError)); } } assert(flags == 0); context.context = GetCurrentFiber(); context.interrupted = false; readContext = &context; dispatcher->dispatch(); assert(context.context == GetCurrentFiber()); assert(dispatcher != nullptr); assert(readContext == &context); readContext = nullptr; DWORD transferred; if (WSAGetOverlappedResult(connection, &context, &transferred, FALSE, &flags) != TRUE) { int lastError = WSAGetLastError(); if (lastError != ERROR_OPERATION_ABORTED) { throw std::runtime_error("TcpConnection::read, WSARecv failed, result=" + std::to_string(lastError)); } assert(context.interrupted); throw InterruptedException(); } assert(transferred <= size); assert(flags == 0); return transferred; }
void dpy_flushkeys(void) { if (GetCurrentFiber() == uifiber) { while (numqueued) { currentkey = dequeue(); SwitchToFiber(appfiber); } } }
void Dispatcher::contextProcedure() { assert(GetCurrentThreadId() == threadId); for (;;) { assert(!spawningProcedures.empty()); std::function<void()> procedure = std::move(spawningProcedures.front()); spawningProcedures.pop(); procedure(); reusableContexts.push(GetCurrentFiber()); dispatch(); } }
void ReentrantLock::lock() { // Если текущий фибер уже владеет блокировкой, // то увеличиваем счетчик, иначе ожидаем освобождения блокировки. if (owner == GetCurrentFiber()) { holdCount++; } else { SimpleLock::lock(); initNewOwner(); } }
void Dispatcher::yield() { assert(GetCurrentThreadId() == threadId); for (;;) { LARGE_INTEGER frequency; LARGE_INTEGER ticks; QueryPerformanceCounter(&ticks); QueryPerformanceFrequency(&frequency); uint64_t currentTime = ticks.QuadPart / (frequency.QuadPart / 1000); auto timerContextPair = timers.begin(); auto end = timers.end(); while (timerContextPair != end && timerContextPair->first <= currentTime) { resumingContexts.push(timerContextPair->second); timerContextPair = timers.erase(timerContextPair); } OVERLAPPED_ENTRY entries[16]; ULONG actual = 0; if (GetQueuedCompletionStatusEx(completionPort, entries, 16, &actual, 0, TRUE) == TRUE) { assert(actual > 0); for (ULONG i = 0; i < actual; ++i) { if (entries[i].lpOverlapped == reinterpret_cast<LPOVERLAPPED>(remoteSpawnOverlapped)) { EnterCriticalSection(reinterpret_cast<LPCRITICAL_SECTION>(criticalSection)); assert(remoteNotificationSent); assert(!remoteSpawningProcedures.empty()); do { spawn(std::move(remoteSpawningProcedures.front())); remoteSpawningProcedures.pop(); } while (!remoteSpawningProcedures.empty()); remoteNotificationSent = false; LeaveCriticalSection(reinterpret_cast<LPCRITICAL_SECTION>(criticalSection)); continue; } void* context = reinterpret_cast<DispatcherContext*>(entries[i].lpOverlapped)->context; resumingContexts.push(context); } } else { DWORD lastError = GetLastError(); if (lastError == WAIT_TIMEOUT) { break; } else if (lastError != WAIT_IO_COMPLETION) { throw std::runtime_error("Dispatcher::dispatch, GetQueuedCompletionStatusEx failed, result=" + std::to_string(lastError)); } } } if (!resumingContexts.empty()) { resumingContexts.push(GetCurrentFiber()); dispatch(); } }
SafeThreadToFibre (LPVOID &fiber) :m_converted(false) ,m_fiber(fiber) { fiber = GetCurrentFiber (); if ((fiber == 0) || (fiber == (LPVOID)0x1E00/*see boost*/)) { fiber = ConvertThreadToFiber (NULL); if (!fiber) throw 0; m_converted = true; } }
void coro_transfer (coro_context *prev, coro_context *next) { if (!prev->fiber) { prev->fiber = GetCurrentFiber (); if (prev->fiber == 0 || prev->fiber == (void *)0x1e00) prev->fiber = ConvertThreadToFiber (0); } SwitchToFiber (next->fiber); }
void TaskScheduler::WaitForCounter(std::shared_ptr<AtomicCounter> &counter, int value) { if (counter->load() == value) { return; } // Switch to a new Fiber m_fiberPool.wait_dequeue(tls_fiberToSwitchTo); tls_currentFiber = GetCurrentFiber(); tls_waitingCounter = counter.get(); tls_waitingValue = value; SwitchToFiber(m_counterWaitingFibers[tls_threadId]); }
Dispatcher::Dispatcher() { static_assert(sizeof(CRITICAL_SECTION) == sizeof(Dispatcher::criticalSection), "CRITICAL_SECTION size doesn't fit sizeof(Dispatcher::criticalSection)"); BOOL result = InitializeCriticalSectionAndSpinCount(reinterpret_cast<LPCRITICAL_SECTION>(criticalSection), 4000); assert(result != FALSE); std::string message; if (ConvertThreadToFiberEx(NULL, 0) == NULL) { message = "ConvertThreadToFiberEx failed, " + lastErrorMessage(); } else { completionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0); if (completionPort == NULL) { message = "CreateIoCompletionPort failed, " + lastErrorMessage(); } else { WSADATA wsaData; int wsaResult = WSAStartup(0x0202, &wsaData); if (wsaResult != 0) { message = "WSAStartup failed, " + errorMessage(wsaResult); } else { remoteNotificationSent = false; reinterpret_cast<LPOVERLAPPED>(remoteSpawnOverlapped)->hEvent = NULL; threadId = GetCurrentThreadId(); mainContext.fiber = GetCurrentFiber(); mainContext.interrupted = false; mainContext.group = &contextGroup; mainContext.groupPrev = nullptr; mainContext.groupNext = nullptr; mainContext.inExecutionQueue = false; contextGroup.firstContext = nullptr; contextGroup.lastContext = nullptr; contextGroup.firstWaiter = nullptr; contextGroup.lastWaiter = nullptr; currentContext = &mainContext; firstResumingContext = nullptr; firstReusableContext = nullptr; runningContextCount = 0; return; } BOOL result2 = CloseHandle(completionPort); assert(result2 == TRUE); } BOOL result2 = ConvertFiberToThread(); assert(result == TRUE); } DeleteCriticalSection(reinterpret_cast<LPCRITICAL_SECTION>(criticalSection)); throw std::runtime_error("Dispatcher::Dispatcher, " + message); }
void FiberManager::YieldFiber() { const auto fiber = GetCurrentFiber(); ASSERT(fiber != nullptr); const auto threadIndex = fiber->m_threadIndex; ASSERT(threadIndex >= 0 && threadIndex < int(m_threadData.size())); auto&& threadData = m_threadData[threadIndex]; #if defined (WINDOWS) SwitchToFiber(threadData.m_mainFiber); #else static_assert(false, "implement this function for this OS"); #endif }
void ScriptMainSetup() { sGameReloaded = true; sMainFib = GetCurrentFiber(); if (sScriptFib == nullptr) { // Create our own fiber for the common language runtime once sScriptFib = CreateFiber(0, reinterpret_cast<LPFIBER_START_ROUTINE>(&ScriptMainLoop), nullptr); } while (true) { // Yield execution scriptWait(0); // Switch to our own fiber and wait for it to switch back SwitchToFiber(sScriptFib); } }
void FiberManager::YieldFiberToService(FiberService* service, void* requestData) { const auto fiber = GetCurrentFiber(); ASSERT(fiber != nullptr); const auto threadIndex = fiber->m_threadIndex; ASSERT(threadIndex >= 0 && threadIndex < int(m_threadData.size())); auto&& threadData = m_threadData[threadIndex]; // we set this before pushing the service fiber so it has all the data it needs to begin ASSERT(fiber->m_serviceData == nullptr); fiber->m_serviceData = requestData; service->PushServiceFiber(fiber); #if defined (WINDOWS) SwitchToFiber(threadData.m_mainFiber); #else static_assert(false, "implement this function for this OS"); #endif }
int pthread_np_convert_self_to_fiber() { pthread_t pth = pthread_self(); if (!pth) return 1; if (!pth->fiber) { void* fiber = GetCurrentFiber(); /* Beware: undocumented (but widely used) method below to check if the thread is already converted. */ if (fiber != NULL && fiber != (void*)0x1E00) { pth->fiber = fiber; pth->own_fiber = 0; } else { pth->fiber = ConvertThreadToFiber(pth); pth->own_fiber = 1; } if (!pth->fiber) return 1; } return 0; }
/* Thread function for [pthread_create]d threads. Thread may become a fiber later, but (as stated above) it isn't supposed to be reattached to other system thread, even after it happens. */ DWORD WINAPI Thread_Function(LPVOID param) { pthread_t self = (pthread_t) param; self->teb = NtCurrentTeb(); thread_or_fiber_function(param); CloseHandle(self->handle); { void* fiber = self->fiber; free(self); if (fiber) { /* If thread was converted to fiber, deleting the fiber from itself exits the thread. There are some rumors on possible memory leaks if we just ExitThread or return here, hence the statement below. However, no memory leaks on bare ExitThread were observed yet. */ DeleteFiber(GetCurrentFiber()); } } return 0; }
int async_fibre_init_dispatcher(async_fibre *fibre) { LPVOID dispatcher; dispatcher = (LPVOID)TlsGetValue(asyncwindispatch); if (dispatcher == NULL) { fibre->fibre = ConvertThreadToFiber(NULL); if (fibre->fibre == NULL) { fibre->converted = 0; fibre->fibre = GetCurrentFiber(); if (fibre->fibre == NULL) return 0; } else { fibre->converted = 1; } if (TlsSetValue(asyncwindispatch, (LPVOID)fibre->fibre) == 0) return 0; } else { fibre->fibre = dispatcher; } return 1; }
VOID WINAPI LogMessage(PVOID pFlsValue) { TCHAR szMsg[MAX_PATH]; // Check if we are in a fiber because this function can be called // outside a fiber execution if (IsThreadAFiber()) { PVOID pFiberData = GetCurrentFiber(); PCTSTR pszFlsValue = (PCTSTR)FlsGetValue(g_dwSlot); StringCchPrintf(szMsg, _countof(szMsg), TEXT("[0x%x - %s] %s\n"), pFiberData, (pszFlsValue == NULL) ? TEXT("'Null value'") : (PCTSTR)pszFlsValue, (pFlsValue == NULL) ? TEXT("'Null value'") : (PCTSTR)pFlsValue); } else { StringCchCopy(szMsg, _countof(szMsg), TEXT("No more a fiber...\n")); } /* Sends a string to the debugger for display. */ OutputDebugString(szMsg); }
sc_cor_pkg_fiber::sc_cor_pkg_fiber( sc_simcontext* simc ) : sc_cor_pkg( simc ) { if( ++ instance_count == 1 ) { // initialize the main coroutine assert( main_cor.m_fiber == 0 ); main_cor.m_fiber = ConvertThreadToFiber( 0 ); if( !main_cor.m_fiber && GetLastError() == ERROR_ALREADY_FIBER ) { // conversion of current thread to fiber has failed, because // someone else already converted the main thread to a fiber // -> store current fiber main_cor.m_fiber = GetCurrentFiber(); } assert( main_cor.m_fiber != 0 ); # if defined(__GNUC__) && __USING_SJLJ_EXCEPTIONS__ // initialize the current coroutine assert( curr_cor == 0 ); curr_cor = &main_cor; # endif } }
void Coro_StartWithArg(CallbackBlock *block) { #ifdef USE_FIBERS MEMORY_BASIC_INFORMATION meminfo; if (block->associatedCoro->fiber != GetCurrentFiber()) abort(); // Set the start of the stack for future comparaison. According to // http://msdn.microsoft.com/en-us/library/ms686774(VS.85).aspx, // some part of the stack is reserved for running an handler if // the fiber exhaust its stack, but we have no way of retrieving // this information (SetThreadStackGuarantee() is not supported // on WindowsXP), so we have to assume that it is the default // 64kB. // Look at the descriptors of the meminfo structure, which is // conveniently located on the stack we are interested into. VirtualQuery(&meminfo, &meminfo, sizeof meminfo); block->associatedCoro->stack = (char*)meminfo.AllocationBase + 64 * 1024; #endif (block->func)(block->context); printf("Scheduler error: returned from coro start function\n"); exit(-1); }