{"id":40889897,"url":"https://github.com/lhmouse/mcfgthread","last_synced_at":"2026-01-22T01:45:46.654Z","repository":{"id":7411178,"uuid":"56123116","full_name":"lhmouse/mcfgthread","owner":"lhmouse","description":"Cornerstone of the MOST efficient std::thread on Windows for mingw-w64","archived":false,"fork":false,"pushed_at":"2026-01-15T03:56:14.000Z","size":2796,"stargazers_count":307,"open_issues_count":2,"forks_count":32,"subscribers_count":18,"default_branch":"master","last_synced_at":"2026-01-15T09:46:10.522Z","etag":null,"topics":["gcc","mingw","mingw-w64","mutex","thread","windows"],"latest_commit_sha":null,"homepage":"https://web.libera.chat/#mcfproj","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/lhmouse.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.TXT","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2016-04-13T05:20:25.000Z","updated_at":"2026-01-15T03:56:07.000Z","dependencies_parsed_at":"2023-02-16T06:16:11.204Z","dependency_job_id":"a5a54714-3552-41a2-85e6-fe87bcb3ef50","html_url":"https://github.com/lhmouse/mcfgthread","commit_stats":{"total_commits":985,"total_committers":1,"mean_commits":985.0,"dds":0.0,"last_synced_commit":"6fc9b99920a1b69f08235ea463cd6404e4a896af"},"previous_names":[],"tags_count":75,"template":false,"template_full_name":null,"purl":"pkg:github/lhmouse/mcfgthread","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lhmouse%2Fmcfgthread","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lhmouse%2Fmcfgthread/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lhmouse%2Fmcfgthread/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lhmouse%2Fmcfgthread/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lhmouse","download_url":"https://codeload.github.com/lhmouse/mcfgthread/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lhmouse%2Fmcfgthread/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28649885,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-22T01:17:37.254Z","status":"ssl_error","status_checked_at":"2026-01-22T01:17:35.564Z","response_time":86,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["gcc","mingw","mingw-w64","mutex","thread","windows"],"created_at":"2026-01-22T01:45:46.562Z","updated_at":"2026-01-22T01:45:46.645Z","avatar_url":"https://github.com/lhmouse.png","language":"C","readme":"# The MCF Gthread Library\n\n**MCF Gthread** is a threading support library for **Windows 7** and above that\nimplements the _gthread interface set_, which is used internally both by **GCC**\nto provide synchronization of initialization of local static objects, and by\n**libstdc++** to provide C++11 threading facilities.\n\n* **cs4k**: Windows `CRITICAL_SECTION` with a spin count of `4000`\n* **srw**: Windows `SRWLOCK`\n* **boost**: `boost::mutex`\n* **mcf0i**: `_MCF_mutex` without inlining\n\n![hyperfine](hyperfine.png)\n\n\u003e [!WARNING]\n\u003e This project uses some undocumented NT system calls and is not guaranteed to\n\u003e work on some Windows versions. The author gives no warranty for this project.\n\u003e Use it at your own risk.\n\n## How to Build\n\nCompiling natively can be done in MSYS2. We take the UCRT64 shell as an example.\nOthers are similar. Clang shells are also supported.\n\n```sh\npacman -S --noconfirm mingw-w64-ucrt-x86_64-{{headers,crt,tools}-git,gcc,binutils,meson}\nmeson setup build_debug\ncd build_debug\nninja test\n```\n\nCross-compiling from Debian, Ubuntu or Linux Mint is supported. In order to run\ntests, Wine is required.\n\n```sh\nsudo apt-get install -y --no-install-recommends mingw-w64-{x86-64-dev,tools}  \\\n        {gcc,g++,binutils}-mingw-w64-x86-64 meson wine wine-binfmt\nmeson setup --cross-file cross/gcc.x86_64-w64-mingw32 build_debug\ncd build_debug\nninja test\n```\n\n\u003e [!TIP]\n\u003e In order for `__cxa_atexit()` (and the non-standard `__cxa_at_quick_exit()`) to\n\u003e conform to the Itanium C++ ABI, it is required 1) for a process to call\n\u003e `__cxa_finalize(NULL)` when exiting, and 2) for a DLL to call\n\u003e `__cxa_finalize(\u0026__dso_handle)` when it is unloaded dynamically. This requires\n\u003e [hacking the CRT](https://github.com/lhmouse/MINGW-packages/blob/0274a6e7e0da258cf5e32efe6e4427454741fa32/mingw-w64-crt-git/9003-crt-Implement-standard-conforming-termination-suppor.patch). If you don't\n\u003e have the modified CRT, you may still get standard compliance by 1) calling\n\u003e `__MCF_exit()` instead of `exit()` from your program, and 2) calling\n\u003e `__cxa_finalize(\u0026__dso_handle)` followed by `fflush(NULL)` upon receipt of\n\u003e `DLL_PROCESS_DETACH` in your `DllMain()`.\n\n## Benchmarking\n\n* **#THREADS**: number of threads\n* **#ITERATIONS**: number of iterations per thread\n* **SRWLOCK**: Windows `SRWLOCK`\n* **CRITICAL_SECTION**: Windows `CRITICAL_SECTION`\n* **WINPTHREAD**: winpthread `pthread_mutex_t`\n* **MCFGTHREAD**: mcfgthread `_MCF_mutex` without inlining\n\nThese are results of [the test program](mutex_performance.c) on an x86-64\n*Windows 10* machine with a 10-core *Intel i9 10900K* processor:\n\n| #THREADS | #ITERATIONS |       SRWLOCK | CRITICAL_SECTION |    WINPTHREAD |    MCFGTHREAD |\n|---------:|------------:|--------------:|-----------------:|--------------:|--------------:|\n|        1 |  20,000,000 |  1541.035 ms  |     1684.556 ms  |**1537.788 ms**|  1539.504 ms  |\n|        2 |  10,000,000 |  1410.687 ms  |     1916.520 ms  |  2135.853 ms  |**1377.103 ms**|\n|        4 |   5,000,000 |  2070.238 ms  |     4613.832 ms  |  2979.166 ms  |**1553.278 ms**|\n|        6 |   3,000,000 |  2500.003 ms  |     5016.650 ms  |  3159.182 ms  |**1409.130 ms**|\n|       10 |   1,500,000 |  2416.953 ms  |     6239.123 ms  |  3004.653 ms  |**1177.269 ms**|\n|       20 |     600,000 |  2266.024 ms  |     8687.350 ms  |  2559.691 ms  |**1001.314 ms**|\n|       60 |     200,000 |**2831.348 ms**|    10164.012 ms  |  3814.880 ms  |  3299.509 ms  |\n|      200 |      60,000 |**2849.850 ms**|    10544.007 ms  |  3825.518 ms  |  3579.925 ms  |\n\nAnd these are results of the same program on *Wine 6.0.3* on an x86-64\n*Ubuntu 22.04* virtual machine with a 16-core *AMD EPYC2* processor:\n\n| #THREADS | #ITERATIONS |       SRWLOCK | CRITICAL_SECTION |    WINPTHREAD |    MCFGTHREAD |\n|---------:|------------:|--------------:|-----------------:|--------------:|--------------:|\n|        1 |  10,000,000 |  2466.983 ms  |     2574.892 ms  |**2444.599 ms**|  3167.704 ms  |\n|        2 |   5,000,000 |  1940.147 ms  |   **1918.091 ms**|  2078.076 ms  |  2213.607 ms  |\n|        4 |   2,000,000 |  3717.442 ms  |     5356.369 ms  |  3859.484 ms  |**1974.007 ms**|\n|        6 |   1,000,000 |  3517.333 ms  |     4519.209 ms  |  2474.208 ms  |**1582.614 ms**|\n|       10 |     500,000 |  3105.191 ms  |     4706.027 ms  |  2388.662 ms  |**1363.926 ms**|\n|       20 |     200,000 |  2721.077 ms  |     4262.151 ms  |  1966.195 ms  |**1340.997 ms**|\n|       60 |      60,000 |  2397.048 ms  |     3807.141 ms  |  1530.147 ms  |**1511.931 ms**|\n|      200 |      20,000 |  2632.933 ms  |     4148.604 ms  |**1615.904 ms**|  1784.553 ms  |\n\nAnd these are results of the same program on an ARM *Windows 11* machine with\nan 8-core *Qualcomm Snapdragon 8cx Gen 3* processor, compiled with Clang:\n\n| #THREADS | #ITERATIONS |       SRWLOCK | CRITICAL_SECTION |    WINPTHREAD |    MCFGTHREAD |\n|---------:|------------:|--------------:|-----------------:|--------------:|--------------:|\n|        1 |  10,000,000 |  2105.027 ms  |     2164.209 ms  |  2122.998 ms  |**2033.915 ms**|\n|        2 |   5,000,000 |  1701.007 ms  |     1620.484 ms  |  1547.963 ms  |**1496.309 ms**|\n|        4 |   2,000,000 |**1395.439 ms**|     3067.075 ms  |  2583.215 ms  |  1525.453 ms  |\n|        6 |   1,000,000 |**1181.352 ms**|     4334.280 ms  |  2167.916 ms  |  1354.046 ms  |\n|       10 |     500,000 |  2738.153 ms  |     2799.624 ms  |**2687.904 ms**|  2739.022 ms  |\n|       20 |     100,000 |  3259.999 ms  |   **3220.732 ms**|  3287.581 ms  |  3291.146 ms  |\n|       60 |      30,000 |  2931.157 ms  |     2934.896 ms  |  2938.784 ms  |**2922.015 ms**|\n|      200 |      10,000 |**3197.414 ms**|     3216.323 ms  |  3221.090 ms  |  3229.249 ms  |\n\n## Implementation details\n\n### The condition variable\n\nA condition variable is implemented as an atomic counter of threads that are\ncurrently waiting on it. Initially the counter is zero, which means no thread\nis waiting.\n\nWhen a thread is about to start waiting on a condition variable, it increments\nthe counter and suspends itself using the global keyed event, passing the\naddress of the condition variable as the key. Another thread may read the\ncounter to tell how many threads that it will have to wake up (note this has to\nbe atomic), and release them from the global keyed event, also passing the\naddress of the condition variable as the key.\n\n### The primitive mutex\n\nA primitive mutex is just a condition variable with a boolean bit, which\ndesignates whether the mutex is LOCKED. A mutex is initialized to all-bit zeroes\nwhich means it is unlocked and no thread is waiting.\n\nWhen a thread wishes to lock a mutex, it checks whether the LOCKED bit is clear.\nIf so, it sets the LOCKED bit and returns, having taken ownership of the mutex.\nIf the LOCKED bit has been set by another thread, it goes to wait on the\ncondition variable. If the thread wishes to unlock this mutex, it clears the\nLOCKED bit and wakes up at most one waiting thread on the condition variable, if\nany.\n\n### The 'real' mutex\n\nIn reality, critical sections are fairly small. If a thread fails to lock a\nmutex, it might be able to do so soon, and we don't want it to give up its time\nslice as a syscall is an overkill. Therefore, it is reasonable for a thread to\nperform some spinning (busy waiting), before it actually decides to sleep.\n\nThis could however lead to severe problems in case of heavy contention. When\nthere are hundreds of thread attempting to lock the same mutex, the system\nscheduler has no idea whether they are spinning or not. As it is likely that a\nlot of threads will eventually give up spinning and make a syscall to sleep, we\nare wasting a lot of CPU time and aggravating the situation.\n\nThis issue is ultimately solved by mcfgthread by encoding a spin failure counter\nin each mutex. If a thread gives up spinning because it couldn't lock the mutex\nwithin a given number of iterations, the spin failure counter is incremented. If\na thread locks a mutex successfully while it is spinning, the spin failure\ncounter is decremented. This counter provides a heuristic way to determine how\nheavily a mutex is seized. If there have been many spin failures, newcomers will\nnot attempt to spin, but will make a syscall to sleep on the mutex directly.\n\n### The once-initialization flag\n\nA once-initialization flag contains a READY byte (this is the first one according\nto Itanium ABI) which indicates whether initialization has completed. The other\nbytes are used as a primitive mutex.\n\nA thread that sees the READY byte set to non-zero knows initialization has been\ndone, so it will return immediately. A thread that sees the READY byte set to\nzero will lock the bundled primitive mutex, and shall perform initialization\nthereafter. If initialization fails, it unlocks the primitive mutex without\nsetting the READY byte, so the next thread that locks the primitive mutex will\nperform initialization. If initialization is successful, it sets the READY byte\nand unlocks the primitive mutex, releasing all threads that are waiting on it.\n(Do you remember that a primitive mutex actually contains a condition variable?)\n\n### List of imported functions\n\n|Function                       |DLL             |Category                   |\n|:------------------------------|:---------------|:------------------------- |\n|`BaseGetNamedObjectDirectory`  |KERNEL32        |Undocumented |\n|`CreateThread`                 |KERNEL32        |[Windows API](https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createthread) |\n|`DecodePointer`                |KERNEL32, NTDLL |[Windows API](https://learn.microsoft.com/en-us/previous-versions/bb432242(v=vs.85)) |\n|`EncodePointer`                |KERNEL32, NTDLL |[Windows API](https://learn.microsoft.com/en-us/previous-versions/bb432254(v=vs.85)) |\n|`ExitThread`                   |KERNEL32        |[Windows API](https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-exitthread) |\n|`FormatMessageW`               |KERNEL32        |[Windows API](https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-formatmessagew) |\n|`GetCurrentProcessId`          |KERNEL32        |[Windows API](https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getcurrentprocessid) |\n|`GetLastError`                 |KERNEL32        |[Windows API](https://learn.microsoft.com/en-us/windows/win32/api/errhandlingapi/nf-errhandlingapi-getlasterror) |\n|`GetModuleFileNameW`           |KERNEL32        |[Windows API](https://learn.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-getmodulefilenamew) |\n|`GetModuleHandleExW`           |KERNEL32        |[Windows API](https://learn.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-getmodulehandleexw) |\n|`GetProcAddress`               |KERNEL32        |[Windows API](https://learn.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-getprocaddress) |\n|`GetProcessHeap`               |KERNEL32        |[Windows API](https://learn.microsoft.com/en-us/windows/win32/api/heapapi/nf-heapapi-getprocessheap) |\n|`GetSystemInfo`                |KERNEL32        |[Windows API](https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getsysteminfo) |\n|`GetSystemTimeAsFileTime`      |KERNEL32        |[Windows API](https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getsystemtimeasfiletime) |\n|`GetThreadPriority`            |KERNEL32        |[Windows API](https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getthreadpriority) |\n|`GetTickCount64`               |KERNEL32        |[Windows API](https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-gettickcount64) |\n|`HeapAlloc`                    |KERNEL32        |[Windows API](https://learn.microsoft.com/en-us/windows/win32/api/heapapi/nf-heapapi-heapalloc) |\n|`HeapFree`                     |KERNEL32        |[Windows API](https://learn.microsoft.com/en-us/windows/win32/api/heapapi/nf-heapapi-heapfree) |\n|`HeapReAlloc`                  |KERNEL32        |[Windows API](https://learn.microsoft.com/en-us/windows/win32/api/heapapi/nf-heapapi-heaprealloc) |\n|`HeapSetInformation`           |KERNEL32        |[Windows API](https://learn.microsoft.com/en-us/windows/win32/api/heapapi/nf-heapapi-heapsetinformation) |\n|`HeapSize`                     |KERNEL32        |[Windows API](https://learn.microsoft.com/en-us/windows/win32/api/heapapi/nf-heapapi-heapsize) |\n|`NtClose`                      |NTDLL           |[Windows Driver API](https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-zwclose) |\n|`NtCreateSection`              |NTDLL           |[Windows Driver API](https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-zwcreatesection) |\n|`NtDelayExecution`             |NTDLL           |Undocumented |\n|`NtDuplicateObject`            |NTDLL           |[Windows Driver API](https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/ntifs/nf-ntifs-zwduplicateobject) |\n|`NtMapViewOfSection`           |NTDLL           |[Windows Driver API](https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-zwmapviewofsection) |\n|`NtRaiseHardError`             |NTDLL           |Undocumented |\n|`NtReleaseKeyedEvent`          |NTDLL           |Undocumented |\n|`NtUnmapViewOfSection`         |NTDLL           |[Windows Driver API](https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-zwunmapviewofsection) |\n|`NtWaitForKeyedEvent`          |NTDLL           |Undocumented |\n|`NtWaitForSingleObject`        |NTDLL           |[Windows Driver API](https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/ntifs/nf-ntifs-zwwaitforsingleobject) |\n|`QueryPerformanceCounter`      |KERNEL32        |[Windows API](https://learn.microsoft.com/en-us/windows/win32/api/profileapi/nf-profileapi-queryperformancecounter) |\n|`QueryPerformanceFrequency`    |KERNEL32        |[Windows API](https://learn.microsoft.com/en-us/windows/win32/api/profileapi/nf-profileapi-queryperformancefrequency) |\n|`QueryUnbiasedInterruptTime`   |KERNEL32        |[Windows API](https://learn.microsoft.com/en-us/windows/win32/api/realtimeapiset/nf-realtimeapiset-queryunbiasedinterrupttime) |\n|`RaiseFailFastException`       |KERNEL32        |[Windows API](https://learn.microsoft.com/en-us/windows/win32/api/errhandlingapi/nf-errhandlingapi-raisefailfastexception) |\n|`RtlDllShutdownInProgress`     |NTDLL           |[Windows API](https://learn.microsoft.com/en-us/windows/win32/devnotes/rtldllshutdowninprogress)|\n|`RtlFillMemory`                |NTDLL           |[Windows Driver API](https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-rtlfillmemory) |\n|`RtlMoveMemory`                |NTDLL           |[Windows Driver API](https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-rtlmovememory) |\n|`RtlNtStatusToDosError`        |NTDLL           |[Windows Driver API](https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/ntifs/nf-ntifs-rtlntstatustodoserror) |\n|`RtlZeroMemory`                |NTDLL           |[Windows Driver API](https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-rtlzeromemory) |\n|`SetConsoleCtrlHandler`        |KERNEL32        |[Windows API](https://learn.microsoft.com/en-us/windows/console/setconsolectrlhandler) |\n|`SetLastError`                 |KERNEL32        |[Windows API](https://learn.microsoft.com/en-us/windows/win32/api/errhandlingapi/nf-errhandlingapi-setlasterror) |\n|`SetThreadPriority`            |KERNEL32        |[Windows API](https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-setthreadpriority) |\n|`SwitchToThread`               |KERNEL32        |[Windows API](https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-switchtothread) |\n|`TerminateProcess`             |KERNEL32        |[Windows API](https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-terminateprocess) |\n|`TlsAlloc`                     |KERNEL32        |[Windows API](https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-tlsalloc) |\n|`TlsGetValue`                  |KERNEL32        |[Windows API](https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-tlsgetvalue) |\n|`TlsGetValue2`                 |KERNEL32        |[Windows API](https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-tlsgetvalue2) |\n|`TlsSetValue`                  |KERNEL32        |[Windows API](https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-tlssetvalue) |\n|`VirtualProtect`               |KERNEL32        |[Windows API](https://learn.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualprotect) |\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flhmouse%2Fmcfgthread","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flhmouse%2Fmcfgthread","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flhmouse%2Fmcfgthread/lists"}