[{"data":1,"prerenderedAt":1432},["ShallowReactive",2],{"page-\u002Fplatform\u002Fwin32平台的异步和系统调用":3},{"id":4,"title":5,"body":6,"description":12,"extension":1423,"meta":1424,"navigation":55,"path":1428,"seo":1429,"stem":1430,"__hash__":1431},"content\u002Fplatform\u002Fwin32平台的异步和系统调用.md","Win32平台的异步和系统调用",{"type":7,"value":8,"toc":1418},"minimark",[9,13,17,20,117,120,155,158,201,204,207,318,321,329,332,335,451,454,460,463,515,518,799,802,805,808,814,817,820,840,843,846,862,865,917,920,923,1034,1043,1088,1099,1102,1105,1111,1114,1158,1161,1308,1311,1314,1325,1339,1382,1402,1408,1411,1414],[10,11,12],"p",{},"这个笔记我想要探讨一下win32平台下的异步和平台调用方法。",[14,15,16],"h2",{"id":16},"事件对象",[10,18,19],{},"CreateEventA是一个windows win32 api提供的事件对象，在我原先的系统设计里，我是打算将AsyncPlatform当作一个跨线程的唤醒机制，我想创建一个windows平台层抽象，其中的CreateEventA创建了一个windows内核事件对象，用来让Runtime的等待线程被唤醒。",[21,22,27],"pre",{"className":23,"code":24,"language":25,"meta":26,"style":26},"language-c shiki shiki-themes github-light github-dark","struct AsyncPlatform {\n    HANDLE wake_event;\n};\n\nAsyncPlatform* async_platform_create(void) {\n    AsyncPlatform* platform = calloc(1, sizeof(AsyncPlatform));\n    if (!platform) return NULL;\n    platform->wake_event = CreateEventA(NULL, FALSE, FALSE, NULL); \u002F\u002F \n    if (!platform->wake_event) {\n        free(platform);\n        return NULL;\n    }\n    return platform;\n}\n","c","",[28,29,30,38,44,50,57,63,69,75,81,87,93,99,105,111],"code",{"__ignoreMap":26},[31,32,35],"span",{"class":33,"line":34},"line",1,[31,36,37],{},"struct AsyncPlatform {\n",[31,39,41],{"class":33,"line":40},2,[31,42,43],{},"    HANDLE wake_event;\n",[31,45,47],{"class":33,"line":46},3,[31,48,49],{},"};\n",[31,51,53],{"class":33,"line":52},4,[31,54,56],{"emptyLinePlaceholder":55},true,"\n",[31,58,60],{"class":33,"line":59},5,[31,61,62],{},"AsyncPlatform* async_platform_create(void) {\n",[31,64,66],{"class":33,"line":65},6,[31,67,68],{},"    AsyncPlatform* platform = calloc(1, sizeof(AsyncPlatform));\n",[31,70,72],{"class":33,"line":71},7,[31,73,74],{},"    if (!platform) return NULL;\n",[31,76,78],{"class":33,"line":77},8,[31,79,80],{},"    platform->wake_event = CreateEventA(NULL, FALSE, FALSE, NULL); \u002F\u002F \n",[31,82,84],{"class":33,"line":83},9,[31,85,86],{},"    if (!platform->wake_event) {\n",[31,88,90],{"class":33,"line":89},10,[31,91,92],{},"        free(platform);\n",[31,94,96],{"class":33,"line":95},11,[31,97,98],{},"        return NULL;\n",[31,100,102],{"class":33,"line":101},12,[31,103,104],{},"    }\n",[31,106,108],{"class":33,"line":107},13,[31,109,110],{},"    return platform;\n",[31,112,114],{"class":33,"line":113},14,[31,115,116],{},"}\n",[10,118,119],{},"CreateEventA的函数原型大致是：",[21,121,123],{"className":23,"code":122,"language":25,"meta":26,"style":26},"CreateEventA(\n    _In_opt_ LPSECURITY_ATTRIBUTES lpEventAttributes,\n    _In_ BOOL bManualReset,\n    _In_ BOOL bInitialState,\n    _In_opt_ LPCSTR lpName\n    );\n\n",[28,124,125,130,135,140,145,150],{"__ignoreMap":26},[31,126,127],{"class":33,"line":34},[31,128,129],{},"CreateEventA(\n",[31,131,132],{"class":33,"line":40},[31,133,134],{},"    _In_opt_ LPSECURITY_ATTRIBUTES lpEventAttributes,\n",[31,136,137],{"class":33,"line":46},[31,138,139],{},"    _In_ BOOL bManualReset,\n",[31,141,142],{"class":33,"line":52},[31,143,144],{},"    _In_ BOOL bInitialState,\n",[31,146,147],{"class":33,"line":59},[31,148,149],{},"    _In_opt_ LPCSTR lpName\n",[31,151,152],{"class":33,"line":65},[31,153,154],{},"    );\n",[10,156,157],{},"第一位是安全属性，用于决定这个event对象的安全属性和创建之后句柄是否可以被子进程继承，我们传入NULL就表示默认使用安全属性。第二位是决定手动重置还是自动重置，自动表示某个线程成功等待到了这个event之后windows就会自动把event恢复从signaled -> nosignaled，这个很适合只有一个消费线程的场景。第三个参数决定创建出来是什么状态，选用FALSE就表示创建出来就是nosignaled，这个最常用。最后的就是个event起名字，若是使用NULL就是表示这是一个匿名event，一般匿名事件对象只有在当前进程使用。",[21,159,161],{"className":23,"code":160,"language":25,"meta":26,"style":26},"void async_platform_destroy(AsyncPlatform* platform) {\n    if (!platform) return;\n    if (platform->wake_event) {\n        CloseHandle(platform->wake_event);\n        platform->wake_event = NULL;\n    }\n    free(platform);\n}\n",[28,162,163,168,173,178,183,188,192,197],{"__ignoreMap":26},[31,164,165],{"class":33,"line":34},[31,166,167],{},"void async_platform_destroy(AsyncPlatform* platform) {\n",[31,169,170],{"class":33,"line":40},[31,171,172],{},"    if (!platform) return;\n",[31,174,175],{"class":33,"line":46},[31,176,177],{},"    if (platform->wake_event) {\n",[31,179,180],{"class":33,"line":52},[31,181,182],{},"        CloseHandle(platform->wake_event);\n",[31,184,185],{"class":33,"line":59},[31,186,187],{},"        platform->wake_event = NULL;\n",[31,189,190],{"class":33,"line":65},[31,191,104],{},[31,193,194],{"class":33,"line":71},[31,195,196],{},"    free(platform);\n",[31,198,199],{"class":33,"line":77},[31,200,116],{},[10,202,203],{},"这是很典型的资源生命周期成对设计，先释放platform内部持有的资源，然后再释放platform自身。",[10,205,206],{},"我们需要把WaitForSingleObject封装一下，async_platform_wait(platform, 1000);表示最多等待1000ms，如果1000ms内的Event被唤醒，就马上返回，如果1000ms内什么都没有发生，就超时返回。",[21,208,210],{"className":23,"code":209,"language":25,"meta":26,"style":26},"int async_platform_wait(AsyncPlatform* platform, int timeout_ms) {\n    if (!platform) return -1;\n    DWORD timeout;\n    if (timeout_ms \u003C 0) {\n        timeout = INFINITE;\n    } else {\n        timeout = (DWORD)timeout_ms;\n    }\n    DWORD result = WaitForSingleObject(platform->wake_event, timeout);\n    if (result == WAIT_OBJECT_0) {\n        return 1;\n    } else if (result == WAIT_TIMEOUT) {\n        return 0;\n    } else {\n        return -1;\n    }\n}\nvoid async_platform_wakeup(AsyncPlatform* platform) {\n    if (!platform) return;\n    SetEvent(platform->wake_event);\n}\n\n",[28,211,212,217,222,227,232,237,242,247,251,256,261,266,271,276,280,286,291,296,302,307,313],{"__ignoreMap":26},[31,213,214],{"class":33,"line":34},[31,215,216],{},"int async_platform_wait(AsyncPlatform* platform, int timeout_ms) {\n",[31,218,219],{"class":33,"line":40},[31,220,221],{},"    if (!platform) return -1;\n",[31,223,224],{"class":33,"line":46},[31,225,226],{},"    DWORD timeout;\n",[31,228,229],{"class":33,"line":52},[31,230,231],{},"    if (timeout_ms \u003C 0) {\n",[31,233,234],{"class":33,"line":59},[31,235,236],{},"        timeout = INFINITE;\n",[31,238,239],{"class":33,"line":65},[31,240,241],{},"    } else {\n",[31,243,244],{"class":33,"line":71},[31,245,246],{},"        timeout = (DWORD)timeout_ms;\n",[31,248,249],{"class":33,"line":77},[31,250,104],{},[31,252,253],{"class":33,"line":83},[31,254,255],{},"    DWORD result = WaitForSingleObject(platform->wake_event, timeout);\n",[31,257,258],{"class":33,"line":89},[31,259,260],{},"    if (result == WAIT_OBJECT_0) {\n",[31,262,263],{"class":33,"line":95},[31,264,265],{},"        return 1;\n",[31,267,268],{"class":33,"line":101},[31,269,270],{},"    } else if (result == WAIT_TIMEOUT) {\n",[31,272,273],{"class":33,"line":107},[31,274,275],{},"        return 0;\n",[31,277,278],{"class":33,"line":113},[31,279,241],{},[31,281,283],{"class":33,"line":282},15,[31,284,285],{},"        return -1;\n",[31,287,289],{"class":33,"line":288},16,[31,290,104],{},[31,292,294],{"class":33,"line":293},17,[31,295,116],{},[31,297,299],{"class":33,"line":298},18,[31,300,301],{},"void async_platform_wakeup(AsyncPlatform* platform) {\n",[31,303,305],{"class":33,"line":304},19,[31,306,172],{},[31,308,310],{"class":33,"line":309},20,[31,311,312],{},"    SetEvent(platform->wake_event);\n",[31,314,316],{"class":33,"line":315},21,[31,317,116],{},[10,319,320],{},"并且timeout_ms \u003C 0的话，就设置timme为infinite，一直等待，不设置超时时间。所以具体来说就是让当前线程等待wake_event，最多等待timeout毫秒。SetEvent win32api会把一个event对象设置为signaled状态，这两个就是配对关系，上面的负责等待event，上面的负责通知event有信号了，可以唤醒了。",[21,322,327],{"className":323,"code":325,"language":326},[324],"language-text","线程 A                         线程 B\n\nasync_platform_wait()\n       │\n       ▼\nWaitForSingleObject()\n       │\n       │ 阻塞\n       │\n       │                    async_platform_wakeup()\n       │                           │\n       │                           ▼\n       │                    SetEvent(event)\n       │                           │\n       ◄───────────────────────────┘\n       │\n       ▼\n    被唤醒\n       │\n       ▼\n    return 1\n","text",[28,328,325],{"__ignoreMap":26},[10,330,331],{},"那么为了主线程可以不需要被阻塞，还可以继续运行其他同步代码，就需要制作worker线程，因为现在callback是同步执行的，因此callback如果是耗时任务，那么主线程就会被阻塞。worker的工作职责很清晰，就是wait之后取op，设置state为running，并执行callback，完成了再设置completed。",[10,333,334],{},"那么首先要思考第一个问题，加入了worker之后main thread和worker thread再pending queue上会有两个线程一起访问，这时候不能继续裸操作这些变量，需要思考线程同步了。在windows上，可以比较简单地使用CRITICAL_SECTION。",[21,336,338],{"className":23,"code":337,"language":25,"meta":26,"style":26},"static unsigned __stdcall platform_thread_entry(void* arg) {\n    AsyncPlatformWorker* worker = (AsyncPlatformWorker*)arg;\n    if (!worker) return 0;\n    worker->entry(worker->context);\n    free(worker);\n    return 0;\n}\n\nint async_platform_start_worker(AsyncPlatform* platform, void* (*entry)(void*), void* context) {\n    if (!platform || !entry) return -1;\n    if (platform->worker) return -1;\n    AsyncPlatformWorker* worker = malloc(sizeof(AsyncPlatformWorker));\n    if (!worker) return -1;\n    worker->entry = entry;\n    worker->context = context;\n    uintptr_t handler = _beginthreadex(NULL, 0, platform_thread_entry, worker, 0, NULL);\n    if (handler == 0) {\n        free(worker);\n        return -1;\n    }\n    platform->worker = (HANDLE)handler;\n    return 0;\n}\n",[28,339,340,345,350,355,360,365,370,374,378,383,388,393,398,403,408,413,418,423,428,432,436,441,446],{"__ignoreMap":26},[31,341,342],{"class":33,"line":34},[31,343,344],{},"static unsigned __stdcall platform_thread_entry(void* arg) {\n",[31,346,347],{"class":33,"line":40},[31,348,349],{},"    AsyncPlatformWorker* worker = (AsyncPlatformWorker*)arg;\n",[31,351,352],{"class":33,"line":46},[31,353,354],{},"    if (!worker) return 0;\n",[31,356,357],{"class":33,"line":52},[31,358,359],{},"    worker->entry(worker->context);\n",[31,361,362],{"class":33,"line":59},[31,363,364],{},"    free(worker);\n",[31,366,367],{"class":33,"line":65},[31,368,369],{},"    return 0;\n",[31,371,372],{"class":33,"line":71},[31,373,116],{},[31,375,376],{"class":33,"line":77},[31,377,56],{"emptyLinePlaceholder":55},[31,379,380],{"class":33,"line":83},[31,381,382],{},"int async_platform_start_worker(AsyncPlatform* platform, void* (*entry)(void*), void* context) {\n",[31,384,385],{"class":33,"line":89},[31,386,387],{},"    if (!platform || !entry) return -1;\n",[31,389,390],{"class":33,"line":95},[31,391,392],{},"    if (platform->worker) return -1;\n",[31,394,395],{"class":33,"line":101},[31,396,397],{},"    AsyncPlatformWorker* worker = malloc(sizeof(AsyncPlatformWorker));\n",[31,399,400],{"class":33,"line":107},[31,401,402],{},"    if (!worker) return -1;\n",[31,404,405],{"class":33,"line":113},[31,406,407],{},"    worker->entry = entry;\n",[31,409,410],{"class":33,"line":282},[31,411,412],{},"    worker->context = context;\n",[31,414,415],{"class":33,"line":288},[31,416,417],{},"    uintptr_t handler = _beginthreadex(NULL, 0, platform_thread_entry, worker, 0, NULL);\n",[31,419,420],{"class":33,"line":293},[31,421,422],{},"    if (handler == 0) {\n",[31,424,425],{"class":33,"line":298},[31,426,427],{},"        free(worker);\n",[31,429,430],{"class":33,"line":304},[31,431,285],{},[31,433,434],{"class":33,"line":309},[31,435,104],{},[31,437,438],{"class":33,"line":315},[31,439,440],{},"    platform->worker = (HANDLE)handler;\n",[31,442,444],{"class":33,"line":443},22,[31,445,369],{},[31,447,449],{"class":33,"line":448},23,[31,450,116],{},[10,452,453],{},"通过worker来接管异步任务，和使用lock来同步线程之后，我们就不需要run和poll两个方法了，新的架构就可以变成：",[21,455,458],{"className":456,"code":457,"language":326},[324],"submit()\n ↓\nqueue\n ↓\nwakeup\n ↓\nWorker\n ↓\ncallback\n",[28,459,457],{"__ignoreMap":26},[10,461,462],{},"如果想要扩展为多个works支持的话就可以通过在runtime添加count和limit来实现，也是十分简单。我设想，如果为了实用性考虑，一般是会忽略掉runtime的创建的，因此随考虑设置全局count数量和全局的runtime，这样在第一次创建operation的适合顺便创建就可以将runtime的职责隐藏起来，可能更加符合开发者的习惯。并且我们可以使用atexit方法，给程序注册一个正常退出要执行的方法，这样退出就可以正常销毁资源了。",[21,464,466],{"className":23,"code":465,"language":25,"meta":26,"style":26},"static void async_runtime_cleanup(void)\n{\n    if (async_runtime_global)\n    {\n        async_runtime_destroy(async_runtime_global);\n        async_runtime_global = NULL;\n    }\n}\n\natexit(async_runtime_cleanup);\n",[28,467,468,473,478,483,488,493,498,502,506,510],{"__ignoreMap":26},[31,469,470],{"class":33,"line":34},[31,471,472],{},"static void async_runtime_cleanup(void)\n",[31,474,475],{"class":33,"line":40},[31,476,477],{},"{\n",[31,479,480],{"class":33,"line":46},[31,481,482],{},"    if (async_runtime_global)\n",[31,484,485],{"class":33,"line":52},[31,486,487],{},"    {\n",[31,489,490],{"class":33,"line":59},[31,491,492],{},"        async_runtime_destroy(async_runtime_global);\n",[31,494,495],{"class":33,"line":65},[31,496,497],{},"        async_runtime_global = NULL;\n",[31,499,500],{"class":33,"line":71},[31,501,104],{},[31,503,504],{"class":33,"line":77},[31,505,116],{},[31,507,508],{"class":33,"line":83},[31,509,56],{"emptyLinePlaceholder":55},[31,511,512],{"class":33,"line":89},[31,513,514],{},"atexit(async_runtime_cleanup);\n",[10,516,517],{},"之后，我们再去头文件编写一些宏，就可以方便地使用了：",[21,519,521],{"className":23,"code":520,"language":25,"meta":26,"style":26},"#define ASYNC_WORKER_LIMIT(worker_limit) \\\n    limit_async_worker_count(worker_limit)\n\n#define ASYNC_CREATE(callback, context) \\\n    async_operation_create(callback, context)\n\n#define ASYNC_SUBMIT(operation) \\\n    async_operation_submit(operation)\n\n#define ASYNC_CANCEL(operation) \\\n    async_operation_cancel(operation)\n\n#define ASYNC_AWAIT(operation) \\\n    async_operation_await(operation)\n\n#define ASYNC_RESULT(operation) \\\n    async_operation_result(operation)\n    \n#include \u003Cstdio.h>\n#include \u003Cwindows.h>\n#include \"runtime.h\"\n\nstatic void task1(AsyncOperation* operation, void* context)\n{\n    (void)operation;\n    (void)context;\n    printf(\"task start\\n\");\n    Sleep(3000);\n    printf(\"task done\\n\");\n}\nstatic void task2(AsyncOperation* operation, void* context)\n{\n    (void)operation;\n    (void)context;\n    printf(\"task start\\n\");\n    Sleep(3000);\n    printf(\"task done\\n\");\n}\n\nint main(void)\n{\n    AsyncOperation* operation1 = ASYNC_CREATE(task1, NULL);\n    AsyncOperation* operation2 = ASYNC_CREATE(task2, NULL);\n    \u002F\u002F ASYNC_SUBMIT(operation);\n    \u002F\u002F AsyncResult result = ASYNC_AWAIT(operation);\n    \u002F\u002F printf(\"state = %d\\n\", result.state);\n    \u002F\u002F return 0;\n    printf(\"main continues\\n\");\n    Sleep(4000);\n    printf(\"state = %d\\n\", async_operation_state(operation1));\n    printf(\"state = %d\\n\", async_operation_state(operation2));\n    return 0;\n}   \n\n",[28,522,523,528,533,537,542,547,551,556,561,565,570,575,579,584,589,593,598,603,608,613,618,623,627,632,637,643,649,655,661,667,672,678,683,688,693,698,703,708,713,718,724,729,735,741,747,753,759,765,771,777,783,789,794],{"__ignoreMap":26},[31,524,525],{"class":33,"line":34},[31,526,527],{},"#define ASYNC_WORKER_LIMIT(worker_limit) \\\n",[31,529,530],{"class":33,"line":40},[31,531,532],{},"    limit_async_worker_count(worker_limit)\n",[31,534,535],{"class":33,"line":46},[31,536,56],{"emptyLinePlaceholder":55},[31,538,539],{"class":33,"line":52},[31,540,541],{},"#define ASYNC_CREATE(callback, context) \\\n",[31,543,544],{"class":33,"line":59},[31,545,546],{},"    async_operation_create(callback, context)\n",[31,548,549],{"class":33,"line":65},[31,550,56],{"emptyLinePlaceholder":55},[31,552,553],{"class":33,"line":71},[31,554,555],{},"#define ASYNC_SUBMIT(operation) \\\n",[31,557,558],{"class":33,"line":77},[31,559,560],{},"    async_operation_submit(operation)\n",[31,562,563],{"class":33,"line":83},[31,564,56],{"emptyLinePlaceholder":55},[31,566,567],{"class":33,"line":89},[31,568,569],{},"#define ASYNC_CANCEL(operation) \\\n",[31,571,572],{"class":33,"line":95},[31,573,574],{},"    async_operation_cancel(operation)\n",[31,576,577],{"class":33,"line":101},[31,578,56],{"emptyLinePlaceholder":55},[31,580,581],{"class":33,"line":107},[31,582,583],{},"#define ASYNC_AWAIT(operation) \\\n",[31,585,586],{"class":33,"line":113},[31,587,588],{},"    async_operation_await(operation)\n",[31,590,591],{"class":33,"line":282},[31,592,56],{"emptyLinePlaceholder":55},[31,594,595],{"class":33,"line":288},[31,596,597],{},"#define ASYNC_RESULT(operation) \\\n",[31,599,600],{"class":33,"line":293},[31,601,602],{},"    async_operation_result(operation)\n",[31,604,605],{"class":33,"line":298},[31,606,607],{},"    \n",[31,609,610],{"class":33,"line":304},[31,611,612],{},"#include \u003Cstdio.h>\n",[31,614,615],{"class":33,"line":309},[31,616,617],{},"#include \u003Cwindows.h>\n",[31,619,620],{"class":33,"line":315},[31,621,622],{},"#include \"runtime.h\"\n",[31,624,625],{"class":33,"line":443},[31,626,56],{"emptyLinePlaceholder":55},[31,628,629],{"class":33,"line":448},[31,630,631],{},"static void task1(AsyncOperation* operation, void* context)\n",[31,633,635],{"class":33,"line":634},24,[31,636,477],{},[31,638,640],{"class":33,"line":639},25,[31,641,642],{},"    (void)operation;\n",[31,644,646],{"class":33,"line":645},26,[31,647,648],{},"    (void)context;\n",[31,650,652],{"class":33,"line":651},27,[31,653,654],{},"    printf(\"task start\\n\");\n",[31,656,658],{"class":33,"line":657},28,[31,659,660],{},"    Sleep(3000);\n",[31,662,664],{"class":33,"line":663},29,[31,665,666],{},"    printf(\"task done\\n\");\n",[31,668,670],{"class":33,"line":669},30,[31,671,116],{},[31,673,675],{"class":33,"line":674},31,[31,676,677],{},"static void task2(AsyncOperation* operation, void* context)\n",[31,679,681],{"class":33,"line":680},32,[31,682,477],{},[31,684,686],{"class":33,"line":685},33,[31,687,642],{},[31,689,691],{"class":33,"line":690},34,[31,692,648],{},[31,694,696],{"class":33,"line":695},35,[31,697,654],{},[31,699,701],{"class":33,"line":700},36,[31,702,660],{},[31,704,706],{"class":33,"line":705},37,[31,707,666],{},[31,709,711],{"class":33,"line":710},38,[31,712,116],{},[31,714,716],{"class":33,"line":715},39,[31,717,56],{"emptyLinePlaceholder":55},[31,719,721],{"class":33,"line":720},40,[31,722,723],{},"int main(void)\n",[31,725,727],{"class":33,"line":726},41,[31,728,477],{},[31,730,732],{"class":33,"line":731},42,[31,733,734],{},"    AsyncOperation* operation1 = ASYNC_CREATE(task1, NULL);\n",[31,736,738],{"class":33,"line":737},43,[31,739,740],{},"    AsyncOperation* operation2 = ASYNC_CREATE(task2, NULL);\n",[31,742,744],{"class":33,"line":743},44,[31,745,746],{},"    \u002F\u002F ASYNC_SUBMIT(operation);\n",[31,748,750],{"class":33,"line":749},45,[31,751,752],{},"    \u002F\u002F AsyncResult result = ASYNC_AWAIT(operation);\n",[31,754,756],{"class":33,"line":755},46,[31,757,758],{},"    \u002F\u002F printf(\"state = %d\\n\", result.state);\n",[31,760,762],{"class":33,"line":761},47,[31,763,764],{},"    \u002F\u002F return 0;\n",[31,766,768],{"class":33,"line":767},48,[31,769,770],{},"    printf(\"main continues\\n\");\n",[31,772,774],{"class":33,"line":773},49,[31,775,776],{},"    Sleep(4000);\n",[31,778,780],{"class":33,"line":779},50,[31,781,782],{},"    printf(\"state = %d\\n\", async_operation_state(operation1));\n",[31,784,786],{"class":33,"line":785},51,[31,787,788],{},"    printf(\"state = %d\\n\", async_operation_state(operation2));\n",[31,790,792],{"class":33,"line":791},52,[31,793,369],{},[31,795,797],{"class":33,"line":796},53,[31,798,116],{},[10,800,801],{},"我这里不想让runtime来决定主线程的延续，所以开发里还是要开发者自行去判断程序退出的时机。",[14,803,804],{"id":804},"父子异步",[10,806,807],{},"父子异步的价值在于让运行时替你管理关系，如果一个逻辑任务是一颗任务树，比如加载一个页面，其中可能会面临这load_page，这样的话会可能包含了好几个fetch，请求不同资源，这样我们可以这样表达：",[21,809,812],{"className":810,"code":811,"language":326},[324],"load_page(page_id)                    ← root\n   ├── fetch_header(page_id)          ← child A\n   ├── fetch_body(page_id)            ← child B\n   ├── fetch_comments(page_id)        ← child C\n   └── fetch_sidebar(page_id)         ← child D\n",[28,813,811],{"__ignoreMap":26},[10,815,816],{},"用户点了取消或者切换到别的页面，那么有父子的好处就出来了，一次调用，整棵树直接自动取消。因为子任务可能是运行时动态派生的，事先不知道有几个，有多深，用列表手动管理会漏，会乱，父子链让派生这个动作本身自带归属。",[10,818,819],{},"因此我们在AsyncOperation的基础上新增几个字段：",[21,821,823],{"className":23,"code":822,"language":25,"meta":26,"style":26},"    AsyncOperation* parent;\n    AsyncOperation* first_child;\n    AsyncOperation* sibling_next;\n",[28,824,825,830,835],{"__ignoreMap":26},[31,826,827],{"class":33,"line":34},[31,828,829],{},"    AsyncOperation* parent;\n",[31,831,832],{"class":33,"line":40},[31,833,834],{},"    AsyncOperation* first_child;\n",[31,836,837],{"class":33,"line":46},[31,838,839],{},"    AsyncOperation* sibling_next;\n",[10,841,842],{},"来表达父亲、第一个孩子、下一个兄弟。",[10,844,845],{},"我们需要实现一个把operation从它父节点的first_child兄弟链里摘掉，并且清空它的parent\u002Fsibling_next。必须在子 operation 即将\"不再被父的取消链需要\"时调用。核心原则：",[847,848,849],"blockquote",{},[10,850,851,854],{},[31,852,853],{},"tip",[855,856,857,858,861],"strong",{},"只要一个子 operation 已经从父的取消链中\"用完\"，就要摘链。否则父的 ",[28,859,860],{},"first_child"," 链里会留着已释放\u002F已结束的节点，造成悬挂指针或误取消。",[10,863,864],{},"在runtime_worker里，回调执行完，state定稿之后，需要执行一次：",[21,866,868],{"className":23,"code":867,"language":25,"meta":26,"style":26},"operation->state = ASYNC_OPERATION_RUNNING;\nif (operation->callback)\n    operation->callback(operation, operation->context);\n\nif (operation->state == ASYNC_OPERATION_RUNNING) {\n    operation->state = ASYNC_OPERATION_COMPLETED;\n    operation->error = ASYNC_ERROR_NONE;\n}\n\nasync_operation_unlink_from_parent(operation);   \u002F\u002F \u003C\u003C\u003C 这里\n",[28,869,870,875,880,885,889,894,899,904,908,912],{"__ignoreMap":26},[31,871,872],{"class":33,"line":34},[31,873,874],{},"operation->state = ASYNC_OPERATION_RUNNING;\n",[31,876,877],{"class":33,"line":40},[31,878,879],{},"if (operation->callback)\n",[31,881,882],{"class":33,"line":46},[31,883,884],{},"    operation->callback(operation, operation->context);\n",[31,886,887],{"class":33,"line":52},[31,888,56],{"emptyLinePlaceholder":55},[31,890,891],{"class":33,"line":59},[31,892,893],{},"if (operation->state == ASYNC_OPERATION_RUNNING) {\n",[31,895,896],{"class":33,"line":65},[31,897,898],{},"    operation->state = ASYNC_OPERATION_COMPLETED;\n",[31,900,901],{"class":33,"line":71},[31,902,903],{},"    operation->error = ASYNC_ERROR_NONE;\n",[31,905,906],{"class":33,"line":77},[31,907,116],{},[31,909,910],{"class":33,"line":83},[31,911,56],{"emptyLinePlaceholder":55},[31,913,914],{"class":33,"line":89},[31,915,916],{},"async_operation_unlink_from_parent(operation);   \u002F\u002F \u003C\u003C\u003C 这里\n",[10,918,919],{},"这个子已经跑完了，父以后cancel时不需要管他，如果不去掉，父亲cancel的时候还会遍历到，这样链会越来越长，且这个op之后被destory释放，父链里就留下悬挂指针，下次cancel直接崩溃。",[10,921,922],{},"实现大致如下：",[21,924,926],{"className":23,"code":925,"language":25,"meta":26,"style":26},"void async_operation_unlink_from_parent(AsyncOperation* operation) {\n    if (!operation|| !operation->parent) return;\n    AsyncOperation* parent = operation->parent;\n    AsyncRuntime* runtime = operation->runtime;\n    if (!runtime) {\n        operation->parent = NULL;\n        return;\n    }\n\n    async_platform_lock(runtime->platform);\n    AsyncOperation** current = &parent->first_child;\n    while(*current) {\n        if (*current == operation) {\n            *current = operation->sibling_next;\n            break;\n        }\n        current = &(*current)->sibling_next;\n    }\n    operation->parent = NULL;\n    operation->sibling_next = NULL;\n    async_platform_unlock(runtime->platform);  \n}\n",[28,927,928,933,938,943,948,953,958,963,967,971,976,981,986,991,996,1001,1006,1011,1015,1020,1025,1030],{"__ignoreMap":26},[31,929,930],{"class":33,"line":34},[31,931,932],{},"void async_operation_unlink_from_parent(AsyncOperation* operation) {\n",[31,934,935],{"class":33,"line":40},[31,936,937],{},"    if (!operation|| !operation->parent) return;\n",[31,939,940],{"class":33,"line":46},[31,941,942],{},"    AsyncOperation* parent = operation->parent;\n",[31,944,945],{"class":33,"line":52},[31,946,947],{},"    AsyncRuntime* runtime = operation->runtime;\n",[31,949,950],{"class":33,"line":59},[31,951,952],{},"    if (!runtime) {\n",[31,954,955],{"class":33,"line":65},[31,956,957],{},"        operation->parent = NULL;\n",[31,959,960],{"class":33,"line":71},[31,961,962],{},"        return;\n",[31,964,965],{"class":33,"line":77},[31,966,104],{},[31,968,969],{"class":33,"line":83},[31,970,56],{"emptyLinePlaceholder":55},[31,972,973],{"class":33,"line":89},[31,974,975],{},"    async_platform_lock(runtime->platform);\n",[31,977,978],{"class":33,"line":95},[31,979,980],{},"    AsyncOperation** current = &parent->first_child;\n",[31,982,983],{"class":33,"line":101},[31,984,985],{},"    while(*current) {\n",[31,987,988],{"class":33,"line":107},[31,989,990],{},"        if (*current == operation) {\n",[31,992,993],{"class":33,"line":113},[31,994,995],{},"            *current = operation->sibling_next;\n",[31,997,998],{"class":33,"line":282},[31,999,1000],{},"            break;\n",[31,1002,1003],{"class":33,"line":288},[31,1004,1005],{},"        }\n",[31,1007,1008],{"class":33,"line":293},[31,1009,1010],{},"        current = &(*current)->sibling_next;\n",[31,1012,1013],{"class":33,"line":298},[31,1014,104],{},[31,1016,1017],{"class":33,"line":304},[31,1018,1019],{},"    operation->parent = NULL;\n",[31,1021,1022],{"class":33,"line":309},[31,1023,1024],{},"    operation->sibling_next = NULL;\n",[31,1026,1027],{"class":33,"line":315},[31,1028,1029],{},"    async_platform_unlock(runtime->platform);  \n",[31,1031,1032],{"class":33,"line":443},[31,1033,116],{},[10,1035,1036,1037,1042],{},"这里我还想说一下EnterCriticalSection 和  LeaveCriticalSection的原理是什么，我们目前为止到现在锁的实现都是依靠这些，这个是windows上最常用的用户态互斥原语。回想计算机系统的知识，同一进程内互斥，不能跨进程。同一线程可重入（递归锁）：同一线程多次enter不会死锁，但必须对应次数的leave。这个底层是 ",[855,1038,1039],{},[28,1040,1041],{},"RTL_CRITICAL_SECTION","  结构体，不是内核对象。",[21,1044,1046],{"className":23,"code":1045,"language":25,"meta":26,"style":26},"typedef struct _RTL_CRITICAL_SECTION {\n    PRTL_CRITICAL_SECTION_DEBUG DebugInfo;  \u002F\u002F 调试用\n    LONG LockCount;        \u002F\u002F 锁计数（负值表示被占用，绝对值-1 = 等待线程数）\n    LONG RecursionCount;   \u002F\u002F 重入次数\n    HANDLE OwningThread;   \u002F\u002F 当前持有锁的线程 ID\n    HANDLE LockSemaphore;  \u002F\u002F 一个事件对象，用于阻塞等待（懒创建）\n    ULONG_PTR SpinCount;   \u002F\u002F 自旋次数\n} RTL_CRITICAL_SECTION;\n",[28,1047,1048,1053,1058,1063,1068,1073,1078,1083],{"__ignoreMap":26},[31,1049,1050],{"class":33,"line":34},[31,1051,1052],{},"typedef struct _RTL_CRITICAL_SECTION {\n",[31,1054,1055],{"class":33,"line":40},[31,1056,1057],{},"    PRTL_CRITICAL_SECTION_DEBUG DebugInfo;  \u002F\u002F 调试用\n",[31,1059,1060],{"class":33,"line":46},[31,1061,1062],{},"    LONG LockCount;        \u002F\u002F 锁计数（负值表示被占用，绝对值-1 = 等待线程数）\n",[31,1064,1065],{"class":33,"line":52},[31,1066,1067],{},"    LONG RecursionCount;   \u002F\u002F 重入次数\n",[31,1069,1070],{"class":33,"line":59},[31,1071,1072],{},"    HANDLE OwningThread;   \u002F\u002F 当前持有锁的线程 ID\n",[31,1074,1075],{"class":33,"line":65},[31,1076,1077],{},"    HANDLE LockSemaphore;  \u002F\u002F 一个事件对象，用于阻塞等待（懒创建）\n",[31,1079,1080],{"class":33,"line":71},[31,1081,1082],{},"    ULONG_PTR SpinCount;   \u002F\u002F 自旋次数\n",[31,1084,1085],{"class":33,"line":77},[31,1086,1087],{},"} RTL_CRITICAL_SECTION;\n",[10,1089,1090,1091,1094,1095,1098],{},"LockCount = -1就表示无人持有，-2表示有人持有且有1个线程在等。大多数情况下",[28,1092,1093],{},"EnterCriticalSection"," 走",[855,1096,1097],{},"纯用户态","路径，他会先尝试用 InterlockedIncrement\u002FCompareExchange 把 LockCount 从 -1 改成 0，成功了的话就设置 OwningThread = 当前线程，RecursionCount = 1，然后直接返回，完全不进入内核。",[10,1100,1101],{},"这个没有系统调用，没有上下文切换，没有内核对象，比mutex快得多，这个也是原因，因为mutex每次WaitForSingleObject都要陷入内核。",[10,1103,1104],{},"当锁被其他线程持有的时候，EnterCriticalSection的时候会自选SpinCount次，默认是0.自选失败的话就会懒创建LockSemaphore，把 LockCount 减到更负，记录等待者数量，然后WaitForSingleObject(LockSemaphore, INFINITE) → 线程睡眠，直到唤醒之后重新尝试获取锁。",[10,1106,1107,1110],{},[28,1108,1109],{},"LeaveCriticalSection"," 的时候，RecursionCount--，如果还 > 0，直接返回（重入没退完），清空OwningThread，把 LockCount 加 1（释放），如果 LockCount 表明还有等待者（\u003C -1） → SetEvent(LockSemaphore) 唤醒一个等待线程。",[10,1112,1113],{},"重入的实现：",[21,1115,1117],{"className":23,"code":1116,"language":25,"meta":26,"style":26},"EnterCriticalSection:\n  1. 检查 OwningThread == 当前线程？\n  2. 是 → RecursionCount++，直接返回，不阻塞;\n\nEnterCriticalSection(&cs);\nEnterCriticalSection(&cs);   \u002F\u002F 不会死锁\nLeaveCriticalSection(&cs);   \u002F\u002F RecursionCount 2→1\nLeaveCriticalSection(&cs);   \u002F\u002F RecursionCount 1→0，真正释放\n",[28,1118,1119,1124,1129,1134,1138,1143,1148,1153],{"__ignoreMap":26},[31,1120,1121],{"class":33,"line":34},[31,1122,1123],{},"EnterCriticalSection:\n",[31,1125,1126],{"class":33,"line":40},[31,1127,1128],{},"  1. 检查 OwningThread == 当前线程？\n",[31,1130,1131],{"class":33,"line":46},[31,1132,1133],{},"  2. 是 → RecursionCount++，直接返回，不阻塞;\n",[31,1135,1136],{"class":33,"line":52},[31,1137,56],{"emptyLinePlaceholder":55},[31,1139,1140],{"class":33,"line":59},[31,1141,1142],{},"EnterCriticalSection(&cs);\n",[31,1144,1145],{"class":33,"line":65},[31,1146,1147],{},"EnterCriticalSection(&cs);   \u002F\u002F 不会死锁\n",[31,1149,1150],{"class":33,"line":71},[31,1151,1152],{},"LeaveCriticalSection(&cs);   \u002F\u002F RecursionCount 2→1\n",[31,1154,1155],{"class":33,"line":77},[31,1156,1157],{},"LeaveCriticalSection(&cs);   \u002F\u002F RecursionCount 1→0，真正释放\n",[10,1159,1160],{},"ok讲完了，我们回到刚才的话题，既然这样，那我们的cancel方法也要升级为递归类型的，这样才可以匹配我们的任务：",[21,1162,1164],{"className":23,"code":1163,"language":25,"meta":26,"style":26},"static void async_operation_cancel_locked(AsyncOperation* op)\n{\n    if (!op) return;\n\n    \u002F* 已取消的子树无需重复遍历 *\u002F\n    if (op->state == ASYNC_OPERATION_CANCELLED) return;\n\n    \u002F* 只有 PENDING 能直接置为 CANCELLED；\n     * RUNNING 的 op 只能靠 token 让回调自愿退出，这里改不了 *\u002F\n    if (op->state == ASYNC_OPERATION_PENDING) {\n        op->state = ASYNC_OPERATION_CANCELLED;\n        op->error = ASYNC_ERROR_CANCELLED;\n    }\n\n    for (AsyncOperation* c = op->first_child; c; c = c->sibling_next)\n        async_operation_cancel_locked(c);\n}\n\nvoid async_operation_cancel(AsyncOperation* operation)\n{\n    if (!operation) return;\n    AsyncRuntime* runtime = operation->runtime;\n    if (!runtime) return;\n\n    async_platform_lock(runtime->platform);\n    async_operation_cancel_locked(operation);\n    async_platform_unlock(runtime->platform);\n\n    \u002F* 唤醒 worker，让它们丢弃 pending 队列里已取消的 op *\u002F\n    async_platform_wakeup(runtime->platform);\n}\n",[28,1165,1166,1171,1175,1180,1184,1189,1194,1198,1203,1208,1213,1218,1223,1227,1231,1236,1241,1245,1249,1254,1258,1263,1267,1272,1276,1280,1285,1290,1294,1299,1304],{"__ignoreMap":26},[31,1167,1168],{"class":33,"line":34},[31,1169,1170],{},"static void async_operation_cancel_locked(AsyncOperation* op)\n",[31,1172,1173],{"class":33,"line":40},[31,1174,477],{},[31,1176,1177],{"class":33,"line":46},[31,1178,1179],{},"    if (!op) return;\n",[31,1181,1182],{"class":33,"line":52},[31,1183,56],{"emptyLinePlaceholder":55},[31,1185,1186],{"class":33,"line":59},[31,1187,1188],{},"    \u002F* 已取消的子树无需重复遍历 *\u002F\n",[31,1190,1191],{"class":33,"line":65},[31,1192,1193],{},"    if (op->state == ASYNC_OPERATION_CANCELLED) return;\n",[31,1195,1196],{"class":33,"line":71},[31,1197,56],{"emptyLinePlaceholder":55},[31,1199,1200],{"class":33,"line":77},[31,1201,1202],{},"    \u002F* 只有 PENDING 能直接置为 CANCELLED；\n",[31,1204,1205],{"class":33,"line":83},[31,1206,1207],{},"     * RUNNING 的 op 只能靠 token 让回调自愿退出，这里改不了 *\u002F\n",[31,1209,1210],{"class":33,"line":89},[31,1211,1212],{},"    if (op->state == ASYNC_OPERATION_PENDING) {\n",[31,1214,1215],{"class":33,"line":95},[31,1216,1217],{},"        op->state = ASYNC_OPERATION_CANCELLED;\n",[31,1219,1220],{"class":33,"line":101},[31,1221,1222],{},"        op->error = ASYNC_ERROR_CANCELLED;\n",[31,1224,1225],{"class":33,"line":107},[31,1226,104],{},[31,1228,1229],{"class":33,"line":113},[31,1230,56],{"emptyLinePlaceholder":55},[31,1232,1233],{"class":33,"line":282},[31,1234,1235],{},"    for (AsyncOperation* c = op->first_child; c; c = c->sibling_next)\n",[31,1237,1238],{"class":33,"line":288},[31,1239,1240],{},"        async_operation_cancel_locked(c);\n",[31,1242,1243],{"class":33,"line":293},[31,1244,116],{},[31,1246,1247],{"class":33,"line":298},[31,1248,56],{"emptyLinePlaceholder":55},[31,1250,1251],{"class":33,"line":304},[31,1252,1253],{},"void async_operation_cancel(AsyncOperation* operation)\n",[31,1255,1256],{"class":33,"line":309},[31,1257,477],{},[31,1259,1260],{"class":33,"line":315},[31,1261,1262],{},"    if (!operation) return;\n",[31,1264,1265],{"class":33,"line":443},[31,1266,947],{},[31,1268,1269],{"class":33,"line":448},[31,1270,1271],{},"    if (!runtime) return;\n",[31,1273,1274],{"class":33,"line":634},[31,1275,56],{"emptyLinePlaceholder":55},[31,1277,1278],{"class":33,"line":639},[31,1279,975],{},[31,1281,1282],{"class":33,"line":645},[31,1283,1284],{},"    async_operation_cancel_locked(operation);\n",[31,1286,1287],{"class":33,"line":651},[31,1288,1289],{},"    async_platform_unlock(runtime->platform);\n",[31,1291,1292],{"class":33,"line":657},[31,1293,56],{"emptyLinePlaceholder":55},[31,1295,1296],{"class":33,"line":663},[31,1297,1298],{},"    \u002F* 唤醒 worker，让它们丢弃 pending 队列里已取消的 op *\u002F\n",[31,1300,1301],{"class":33,"line":669},[31,1302,1303],{},"    async_platform_wakeup(runtime->platform);\n",[31,1305,1306],{"class":33,"line":674},[31,1307,116],{},[10,1309,1310],{},"这样我们便实现了。",[14,1312,1313],{"id":1313},"cancellation",[10,1315,1316,1317,1324],{},"我想要实现可以强制停止的异步实现，但是我们的windows平台的实现实际上是通过_beginthreadex实现的多线程，",[855,1318,1319,1320,1323],{},"用 ",[28,1321,1322],{},"TerminateThread"," 强制杀线程会破坏 C 运行时的状态","。这是 Windows 上所有强杀线程方案的坑。",[10,1326,1327,1328,1331,1332,1338],{},"所以，",[28,1329,1330],{},"_beginthreadex"," 创建的线程，",[855,1333,1334,1335,1337],{},"绝对不能用 ",[28,1336,1322],{}," 杀","。TerminateThread会立即把线程标记为终止并不等待它运行到安全点，也不立即执行__finally块，SEH的__excpt、C++析构，或是栈展开。不释放任何锁、内存、句柄。不调用CRT的per-thread清理，线程的栈不回收，可能造成泄露整块栈内存。",[10,1340,1341,1342,1344,1345,1348,1349,1352,1353,1355,1356,1359,1360,1363,1364,1363,1367,1370,1371,1374,1375,1363,1378,1381],{},"一般，",[28,1343,1330],{}," 为每个线程分配一个 ",[28,1346,1347],{},"_tiddata"," 结构（线程本地存储、errno、strtok 状态、随机数种子等）。正常退出时 ",[28,1350,1351],{},"_endthreadex"," 会释放它。但TerminatThread不会调用，所以",[28,1354,1347],{}," ",[855,1357,1358],{},"永久泄漏","。如果该线程正在用 CRT 函数（",[28,1361,1362],{},"malloc","、",[28,1365,1366],{},"printf",[28,1368,1369],{},"strtok","…），这些函数内部可能持有",[855,1372,1373],{},"全局锁","（如 ",[28,1376,1377],{},"_malloc_lock",[28,1379,1380],{},"_io_lock","），锁永远不释放",[10,1383,1384,1385,1387,1388,1391,1392,1395,1396,1398,1399,1401],{},"CRT 的 ",[28,1386,1362],{}," \u002F ",[28,1389,1390],{},"free"," 内部有",[855,1393,1394],{},"全局堆锁","（或 per-heap 锁）。如果线程在 ",[28,1397,1362],{}," 中途被 ",[28,1400,1322],{}," 杀掉：",[21,1403,1406],{"className":1404,"code":1405,"language":326},[324],"线程 A: 进入 malloc → 拿堆锁 → 正在改空闲链表 → 被 TerminateThread 杀掉\n                  ↑ 堆锁永远不释放，空闲链表处于半改状态\n线程 B: 调 malloc → 拿不到锁 → 永远阻塞\n       或拿锁后 → 看到半损坏的空闲链表 → 崩溃 \u002F 内存损坏\n",[28,1407,1405],{"__ignoreMap":26},[10,1409,1410],{},"目前我们的打断机制只有pedding能够取消，running打断不了，所以还需要支持一下running运行过程中的打断，要支持RUNNING取消，我们需要去加token。",[10,1412,1413],{},"继续更新...",[1415,1416,1417],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":26,"searchDepth":40,"depth":40,"links":1419},[1420,1421,1422],{"id":16,"depth":40,"text":16},{"id":804,"depth":40,"text":804},{"id":1313,"depth":40,"text":1313},"md",{"date":1425,"tags":1426},"2026-07-10",[1427],"win32","\u002Fplatform\u002Fwin32平台的异步和系统调用",{"description":12},"platform\u002Fwin32平台的异步和系统调用","3HZ5nhCxU8Z-P7V8jwhiPmgKZo4ccr5rjywBbsgABvM",1791394559760]