可可西

windows纤程(Fiber)机制

Windows 10(17763)系统上执行tjInitCompress函数时,加载dll失败,从而导致崩溃

KERNELBASE.dll pc 0x69 RaiseException (:0)[amd64:Windows NT:7BF8014B3D39BE6ADDC83DA4991008EB1]
MyGame.exe pc 0x6b517c0 _delayLoadHelper2(ImgDelayDescr const*, long long (**)()) (D:\a\_work\1\s\src\vctools\delayimp/delayhlp.cpp:312)[amd64:Windows NT:79DC3D8E2E0D4CD6B1F68294B5AACE771]
MyGame.exe pc 0x6aa6669 tjInitCompress[amd64:Windows NT:79DC3D8E2E0D4CD6B1F68294B5AACE771]
MyGame.exe pc 0x3ea56a0 FJpegImageWrapper::FJpegImageWrapper(int) (E:\UnrealEngine\Engine\Source\Runtime\ImageWrapper\Private\Formats/JpegImageWrapper.cpp:68)[amd64:Windows NT:79DC3D8E2E0D4CD6B1F68294B5AACE771]
MyGame.exe pc 0x3ea1db3 FImageWrapperModule::CreateImageWrapper(const EImageFormat) (E:\UnrealEngine\Engine\Source\Runtime\ImageWrapper\Private/ImageWrapperModule.cpp:75)[amd64:Windows NT:79DC3D8E2E0D4CD6B1F68294B5AACE771]
MyGame.exe pc 0x5223d82 UAsyncTaskDownloadImage::HandleImageRequest(TSharedPtr, TSharedPtr, bool) (E:\UnrealEngine\Engine\Source\Runtime\UMG\Private\Blueprint/AsyncTaskDownloadImage.cpp:115)[amd64:Windows NT:79DC3D8E2E0D4CD6B1F68294B5AACE771]
MyGame.exe pc 0x18805a2 TBaseUObjectMethodDelegateInstance<0,UAsyncTaskDownloadFile,void __cdecl(TSharedPtr,TSharedPtr,bool),FDefaultDelegateUserPolicy>::ExecuteIfSafe(TSharedPtr, TSharedPtr, bool) const (E:\UnrealEngine\Engine\Source\Runtime\Core\Public\Delegates/DelegateInstancesImpl.h:611)[amd64:Windows NT:79DC3D8E2E0D4CD6B1F68294B5AACE771]
MyGame.exe pc 0x4cacee4 TDelegate,TSharedPtr,bool),FDefaultDelegateUserPolicy>::ExecuteIfBound(TSharedPtr, TSharedPtr, bool) const (E:\UnrealEngine\Engine\Source\Runtime\Core\Public\Delegates/DelegateSignatureImpl.inl:599)[amd64:Windows NT:79DC3D8E2E0D4CD6B1F68294B5AACE771]
MyGame.exe pc 0x4cb297c FCurlHttpRequest::FinishedRequest() (E:\UnrealEngine\Engine\Source\Runtime\Online\HTTP\Private\Curl/CurlHttp.cpp:1274)[amd64:Windows NT:79DC3D8E2E0D4CD6B1F68294B5AACE771]
MyGame.exe pc 0x4cbd8df FHttpManager::Tick(float) (E:\UnrealEngine\Engine\Source\Runtime\Online\HTTP\Private/HttpManager.cpp:255)[amd64:Windows NT:79DC3D8E2E0D4CD6B1F68294B5AACE771]
MyGame.exe pc 0x39c00d6 FTicker::Tick(float) (E:\UnrealEngine\Engine\Source\Runtime\Core\Private\Containers/Ticker.cpp:92)[amd64:Windows NT:79DC3D8E2E0D4CD6B1F68294B5AACE771]
MyGame.exe pc 0x39a9515 static UE4Function_Private::TFunctionRefCaller<<lambda_6153fdc59763e04cd81e0809850c9c28>,bool __cdecl(float)>::Call(void*, float &) (E:\UnrealEngine\Engine\Source\Runtime\Core\Public\Templates/Function.h:539)[amd64:Windows NT:79DC3D8E2E0D4CD6B1F68294B5AACE771]
MyGame.exe pc 0x39bdd8f TBaseFunctorDelegateInstance >::Execute(float) const (E:\UnrealEngine\Engine\Source\Runtime\Core\Public\Delegates/DelegateInstancesImpl.h:831)[amd64:Windows NT:79DC3D8E2E0D4CD6B1F68294B5AACE771]
MyGame.exe pc 0x39c00d6 FTicker::Tick(float) (E:\UnrealEngine\Engine\Source\Runtime\Core\Private\Containers/Ticker.cpp:92)[amd64:Windows NT:79DC3D8E2E0D4CD6B1F68294B5AACE771]
MyGame.exe pc 0xafdf2b FEngineLoop::Tick() (E:\UnrealEngine\Engine\Source\Runtime\Launch\Private/LaunchEngineLoop.cpp:5454)[amd64:Windows NT:79DC3D8E2E0D4CD6B1F68294B5AACE771]
MyGame.exe pc 0xb0ad7c GuardedMain(wchar_t const*) (E:\UnrealEngine\Engine\Source\Runtime\Launch\Private/Launch.cpp:200)[amd64:Windows NT:79DC3D8E2E0D4CD6B1F68294B5AACE771]
MyGame.exe pc 0xb0adea GuardedMainWrapper(wchar_t const*) (E:\UnrealEngine\Engine\Source\Runtime\Launch\Private\Windows/LaunchWindows.cpp:144)[amd64:Windows NT:79DC3D8E2E0D4CD6B1F68294B5AACE771]
MyGame.exe pc 0xb164cd WinMain(HINSTANCE__ *, HINSTANCE__ *, char*, int) (E:\UnrealEngine\Engine\Source\Runtime\Launch\Private\Windows/LaunchWindows.cpp:339)[amd64:Windows NT:79DC3D8E2E0D4CD6B1F68294B5AACE771]
MyGame.exe pc 0x6b52e42 __scrt_common_main_seh() (D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup/exe_common.inl:288)[amd64:Windows NT:79DC3D8E2E0D4CD6B1F68294B5AACE771]
kernel32.dll pc 0x14 BaseThreadInitThunk (:0)[amd64:Windows NT:7C073461BC41EEA8F242AD876C2F4A9F1]
ntdll.dll pc 0x21 RtlUserThreadStart (:0)[amd64:Windows NT:2055091C8F2C5808D8DFE02C75D129591]

具体代码如下:

 

具体原因是tjInitCompress函数中会调用FlsAlloc进行fls分配,而fls分配次数超过了系统限制的128个,从而导致崩溃

00 0000003a`bbfff0b8 00007fff`1b72e7a5     KERNELBASE!FlsAlloc
01 0000003a`bbfff0c0 00007fff`1b72b1db     turbojpeg!Java_org_libjpegturbo_turbojpeg_TJDecompressor_destroy+0x19fb5
02 0000003a`bbfff0f0 00007fff`1b72b36b     turbojpeg!Java_org_libjpegturbo_turbojpeg_TJDecompressor_destroy+0x169eb
03 0000003a`bbfff120 00007fff`abb18b7f     turbojpeg!Java_org_libjpegturbo_turbojpeg_TJDecompressor_destroy+0x16b7b
04 0000003a`bbfff160 00007fff`abb5d51d     ntdll!LdrpCallInitRoutine+0x6b
05 0000003a`bbfff1d0 00007fff`abb5d2ce     ntdll!LdrpInitializeNode+0x1c9
06 0000003a`bbfff320 00007fff`abb1db0d     ntdll!LdrpInitializeGraphRecurse+0x42
07 0000003a`bbfff360 00007fff`abb18e20     ntdll!LdrpPrepareModuleForExecution+0xc5
08 0000003a`bbfff3a0 00007fff`abb090ac     ntdll!LdrpLoadDllInternal+0x20c
09 0000003a`bbfff440 00007fff`abb1a73a     ntdll!LdrpLoadDll+0xb0
0a 0000003a`bbfff600 00007fff`a923b412     ntdll!LdrLoadDll+0xfa
0b 0000003a`bbfff6f0 00007fff`a92374b1     KERNELBASE!LoadLibraryExW+0x172
0c 0000003a`bbfff760 00007fff`2243c42f     KERNELBASE!LoadLibraryExA+0x31
0d 0000003a`bbfff7a0 00007ff6`8090de0e     EOSOVH_Win64_Shipping+0x3c42f
0e 0000003a`bbfff7e0 00007ff6`80822b61     UAGame!__delayLoadHelper2+0x14a [D:\a\_work\1\s\src\vctools\delayimp\delayhlp.cpp @ 285] 
0f 0000003a`bbfff880 00007ff6`7a72da10     UAGame!_tailMerge_turbojpeg_dll+0x3f
10 0000003a`bbfff8f0 00007ff6`7a7291c3     UAGame!FJpegImageWrapper::FJpegImageWrapper+0x50 [G:\CAEngine\Engine\Source\Runtime\ImageWrapper\Private\Formats\JpegImageWrapper.cpp @ 68] 
11 0000003a`bbfff920 00007ff6`78fe9aa5     UAGame!FImageWrapperModule::CreateImageWrapper+0xc3 [G:\CAEngine\Engine\Source\Runtime\ImageWrapper\Private\ImageWrapperModule.cpp @ 75] 
12 0000003a`bbfff950 00007ff6`78fa675e     UAGame!UImageLoader::LoadImageWrapperFromDisk+0x185 [G:\CAGame\Source\UACommon\Private\ImageLoader.cpp @ 154] 
13 (Inline Function) --------`--------     UAGame!UImageLoader::LoadImageWrapperFromDiskInThreadPool::__l2::<lambda_e43cdf58c4a4f1b2f6587ee8f005a51d>::operator()+0x5 [G:\CAGame\Source\UACommon\Private\ImageLoader.cpp @ 128] 
14 (Inline Function) --------`--------     UAGame!Invoke+0x5 [G:\CAEngine\Engine\Source\Runtime\Core\Public\Templates\Invoke.h @ 51] 
15 0000003a`bbfffa00 00007ff6`78f9c277     UAGame!UE4Function_Private::TFunctionRefCaller<<lambda_e43cdf58c4a4f1b2f6587ee8f005a51d>,TSharedPtr<IImageWrapper,0> __cdecl(void)>::Call+0xe [G:\CAEngine\Engine\Source\Runtime\Core\Public\Templates\Function.h @ 539] 
16 (Inline Function) --------`--------     UAGame!UE4Function_Private::TFunctionRefBase<UE4Function_Private::TFunctionStorage<1>,TSharedPtr<IImageWrapper,0> __cdecl(void)>::operator()+0x27 [G:\CAEngine\Engine\Source\Runtime\Core\Public\Templates\Function.h @ 676] 
17 0000003a`bbfffa30 00007ff6`78fb23a6     UAGame!SetPromise<TSharedPtr<IImageWrapper,0>,TUniqueFunction<TSharedPtr<IImageWrapper,0> __cdecl(void)> &>+0x37 [G:\CAEngine\Engine\Source\Runtime\Core\Public\Async\Async.h @ 50] 
18 0000003a`bbfffa80 00007ff6`79b1a058     UAGame!TAsyncQueuedWork<TSharedPtr<IImageWrapper,0> >::DoThreadedWork+0x16 [G:\CAEngine\Engine\Source\Runtime\Core\Public\Async\Async.h @ 224] 
19 0000003a`bbfffab0 00007ff6`79fcdbdb     UAGame!FQueuedThread::Run+0xc18 [G:\CAEngine\Engine\Source\Runtime\Core\Private\HAL\ThreadingBase.cpp @ 1020] 
1a 0000003a`bbfffd20 00007ff6`79fc664a     UAGame!FRunnableThreadWin::Run+0x4b [G:\CAEngine\Engine\Source\Runtime\Core\Private\Windows\WindowsRunnableThread.cpp @ 90] 
1b 0000003a`bbfffd50 00007fff`aa3b257d     UAGame!FRunnableThreadWin::GuardedRun+0x1aa [G:\CAEngine\Engine\Source\Runtime\Core\Private\Windows\WindowsRunnableThread.cpp @ 27] 
1c 0000003a`bbfffd90 00007fff`abb4af28     KERNEL32!BaseThreadInitThunk+0x1d
1d 0000003a`bbfffdc0 00000000`00000000     ntdll!RtlUserThreadStart+0x28

 

纤程(Fiber)

Fiber是Windows提供的用户级线程的,类似于协程(Coroutine)的概念。

Fiber在单线程中非常有用,多个Fiber运行在同一个线程(Thread)中,当前Thread执行那个Fiber需要开发者主动控制,Fiber调度模块只负责提供堆栈,环境、寄存器等上下文的保存,不负责分配时间片等。

我们可以用ConvertThreadToFiber把一个Thread 转换成一个Fiber ,也可以调用CreateFiber创建一个纤程,但并不切换过去运行。

被创建出来的 Fiber 会有一个上下文的地址被返回,用于以后的切换操作。切换Fiber需要用SwitchToFiber函数,这是唯一用于 Fiber 释放操作权的途径。SwitchToFiber必须显式的指定切换的目标,所以Fiber调度的工作需要开发者写代码来实现。

GetCurrentFiber GetFiberData 这两个函数都很有用,一个用来取到运行环境,一个用来取得创建参数,这两个函数都是用inline函数的形式提供在 .h 文件中的。

注1:每个Fiber都有自己的Callstack,大小缺省为1M,在Visual Studio中,可以看到当前进程中每个线程的Callstack,但没法看到每个Fiber的Callstack,这会给调试带来困难

注2:一个线程可以包含一个或多个纤程。操作系统随时可能夺走纤程所在线程的运行。当线程被调度时,当前被选择的纤程得以运行,而其他纤程无法运行

         因为同一个线程中,每次只能有一个纤程正在运行,除非调用SwitchToFiber才能切换到另一个纤程去执行。

         与切换Thread不同,SwitchToFiber会立即切换到另一个纤程去执行(如果该线程的CPU时间还有剩余的话),而切换Thread要等CPU来调度另一个线程。

 

#

线程(Thread)

纤程(Fiber)

实现方式

是个内核对象

在用户模式中实现的一种轻量级的线程,是比线程更小的调度单位。

调度方式

由Microsoft定义的算法来调度,操作系统对线程了如指掌。内核对线程的调度是抢占式的。

由开发者自己调用SwitchToFiber来调度,内核对纤程一无所知。线程一次只能执行一个纤程代码,纤程间的调度不是抢占式的。

 

FLS(Fiber Local Storage,纤程局部存储)

FLS是类似于TLS(Thread Local Storage,线程局部存储)的数据结构,每个Fiber都会有自己FLS数据副本,互不干扰。

(1) 使用FLS的步骤如下:

 ①调用FlsAlloc分配FLS索引

 ②调用FlsSetValue将Fiber的值写入该索引FLS

 ③调用FlsGetValue取得Fiber存储的FLS值

 ④调用FlsFree释放FLS索引

(2) FlsAlloc中可以指定一个回调函数:VOID WINAPI FlsCallback(PVOID lpFlsData);

     回调函数会在纤程被销毁、调度纤程的线程退出或FlsFree时被调用,这主要便纤程有机会删除自己在FLS中存储的值。

(3) 获取当前纤程对象(上下文环境):PVOID GetCurrentFiber();

(4) 获得创建纤程时的pvParam数据:GetFiberData

(5) 判断是否正在某个纤程中运行:IsThreadFiber()

 

Windows长期以来给这个FLS Slot(槽位)设定了一个限制,一个进程最多有128个Slot。

从Windows 10 (build 18312) 版本开始,微软将这个限制大幅度提升到了4096个!

 

示例

#include <windows.h>
#include <iostream>
#include <ctime>

// 回调函数,当纤程退出时调用
void CALLBACK FlsCallback(PVOID lpFlsData)
{
    std::cout << "FlsCallback called with data: " << lpFlsData << std::endl;
    // 释放纤程上FLS中分配的内存
    // 为NewFiber时,lpFlsData中存放的是一个int类型,数值为42的内容
    // 为MainFiber时,lpFlsData中存放的是一个int类型,数值为56的内容
    if (lpFlsData)
    {
        delete static_cast<int*>(lpFlsData);
    }
}

struct FiberParam
{
    DWORD flsIndex;
    void* mainFiber;
    time_t createTime;
};

void WINAPI NewFiberFunction(void* lpParameter)
{
    // 获取 FLS 索引
    FiberParam parameter = *static_cast<FiberParam*>(lpParameter);

    // 分配一些数据并存储在NewFiber的FLS中
    int* pNewFiberData = new int(42);
    FlsSetValue(parameter.flsIndex, pNewFiberData); // 当前代码在NewFiber中执行,此时会将数据存放到NewFiber的FLS中

    // 获取并打印存储在NewFiber的FLS中的数据
    int* pNewFiberRetrievedData = static_cast<int*>(FlsGetValue(parameter.flsIndex));
    if (pNewFiberRetrievedData)
    {
        std::cout << "NewFiber: FLS data = " << *pNewFiberRetrievedData << std::endl;
    }

    // 切换回主纤程
    SwitchToFiber(parameter.mainFiber);
}



int main()
{
    // 分配一个fls索引并设置回调函数
    DWORD flsIndex = FlsAlloc(FlsCallback);
    if (flsIndex == FLS_OUT_OF_INDEXES)
    {
        std::cerr << "FlsAlloc failed" << std::endl;
        return 1;
    }

    // 将主线程转换为纤程
    void* mainFiber = ConvertThreadToFiber(nullptr);
    if (!mainFiber)
    {
        std::cerr << "ConvertThreadToFiber failed" << std::endl;
        return 1;
    }

    // 创建一个新的纤程
    FiberParam param;
    param.flsIndex = flsIndex;
    param.mainFiber = mainFiber;
    param.createTime = time(NULL);
    void* newFiber = CreateFiber(0, NewFiberFunction, &param);
    if (!newFiber)
    {
        std::cerr << "CreateFiber failed" << std::endl;
        return 1;
    }

    // 分配一些数据并存储在主线程Fiber的FLS中
    int* pMainFiberData = new int(56);
    FlsSetValue(flsIndex, pMainFiberData);  // 当前代码在主线程Fiber中执行,此时会将数据存放到主线程Fiber的FLS中   注:即使前面不执行CovertThreadToFiber也会存在一个主线程Fiber

    // 获取并打印存储在MainFiber的FLS中的数据
    int* pMainFiberRetrievedData = static_cast<int*>(FlsGetValue(flsIndex));
    if (pMainFiberRetrievedData)
    {
        std::cout << "MainFiber: FLS data = " << *pMainFiberRetrievedData << std::endl;
    }

    // 切换到新纤程
    SwitchToFiber(newFiber);

    // 删除纤程  会触发FlsCallback回调函数
    DeleteFiber(newFiber);

    // 不能在当前纤程逻辑中删除当前纤程
    //DeleteFiber(mainFiber);

    // 释放FLS索引  此时所有存放数据在该FLS索引上且未删除Fiber,会逐一触发FlsCallback回调函数
    FlsFree(flsIndex);

    return 0;
}

 

参考

Announcing Windows 10 Insider Preview Build 18312

微软说下一次的 Windows 10 19H1 更新会让你的 DAW 跑更多插件

第12章 纤程(Fiber)

纤程(云风)

posted on 2024-10-22 00:28  可可西  阅读(46)  评论(0编辑  收藏  举报

导航