Description
Description
The PortableThreadPool
doesn't work when also using Data Plane Development Kit (DPDK).
Any attempt to run C# code that requires the ThreadPool crashes with the following trace:
Unhandled Exception: System.OutOfMemoryException: Insufficient memory to continue the execution of the program.
at System.Threading.Thread.StartCore() + 0xa5
at System.Threading.PortableThreadPool.GateThread.CreateGateThread() + 0x92
This is part of a C# library built as a Navtive .so
lib for Linux.
Reproduction Steps
Create a simple C# library that uses a Task.
public static class ThreadPoolIssue
{
[UnmanagedCallersOnly(EntryPoint = "test_task")]
public static void TestTask()
{
Console.WriteLine("Starting task");
Task.Delay(1000).ContinueWith(t =>
{
Console.WriteLine("Task completed after 1 second delay");
});
}
}
Publish the library using AOT for Linux
lin-x64.pubxml
<?xml version="1.0" encoding="utf-8"?>
<Project>
<PropertyGroup>
<Configuration>Release</Configuration>
<Platform>Any CPU</Platform>
<PublishDir>..\publish\</PublishDir>
<PublishProtocol>FileSystem</PublishProtocol>
<_TargetId>Folder</_TargetId>
<TargetFramework>net9.0</TargetFramework>
<RuntimeIdentifier>linux-x64</RuntimeIdentifier>
<SelfContained>true</SelfContained>
<PublishAot>true</PublishAot>
</PropertyGroup>
</Project>
dotnet publish -p:PublishProfile=lin-x64.pubxml
Create sample C application
testapp.h
#ifndef TESTAPP_H
#define TESTAPP_H
#include <stdint.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
void test_task();
#ifdef __cplusplus
}
#endif
#endif // TESTAPP_H
testapp.c
#include "testapp.h"
#include <unistd.h> // Include this header for sleep
int main(int argc, char* argv[])
{
test_task();
sleep(10); // Sleep for 10 seconds to allow threads to complete
return 0;
}
Compile the C application (adjust paths if needed)
gcc -g -o ./publish/testapp ./ThreadPoolIssueLib/testapp.c -L./publish -lThreadPoolIssue -ldl -lpthread -lm -Wl,-rpath='$ORIGIN'
Verify that the application run outside of DPDK (this should work):
cd ./publish
./testapp
set up DPDK:
https://learn.microsoft.com/en-us/azure/virtual-network/setup-dpdk?tabs=ubuntu
Run code inside DPDK application --> see crash
(https://core.dpdk.org/doc/quick-start/)
Even though the creation of the GateThread fails, I can see the output of the Task after one second.
Expected behavior
The thread pool should initialize correctly. The application should not crash.
Actual behavior
Crashes with the following StackTrace:
Unhandled Exception: System.OutOfMemoryException: Insufficient memory to continue the execution of the program.
at System.Threading.Thread.StartCore() + 0xa5
at System.Threading.PortableThreadPool.GateThread.CreateGateThread() + 0x92
The task does seem to run though The stdout does show "Task completed after 1 second delay" after one second.
Regression?
No response
Known Workarounds
No response
Configuration
I tested multiple combinations. All with the same behavior.
Ubuntu 18.04 and 24.04 (x64)
.NET SDK 8.0.411, 9.0.301
.NET runtimes Microsoft.NETCore.App 8.0.17, Microsoft.NETCore.App 9.0.6
DPDK 21.11.0
Other information
-
I believe the
OutOfMemoryException
is incorrect. The system has plenty of memory left. It is the GateThread that fails to start and I believe the runtime simply assumes that it's because of out of memory. See Failing to create a thread shouldn't assume that this an out of memory issue #71761 -
Creating regular threads just works, I've made 32 with success.
[UnmanagedCallersOnly(EntryPoint = "test_thread")]
public static void TestTread()
{
Console.WriteLine("Starting thread");
Thread thread = new(() =>
{
Console.WriteLine("Thread started");
for (int i = 0; i < 5; i++)
{
Console.WriteLine($"Thread running iteration {i + 1}");
Thread.Sleep(1000);
}
Console.WriteLine("Thread completed");
});
thread.Start();
}
- DPDK uses core pinning
- DPDK uses huge pages
- Any code that doesn't use the thread pool seems to run fine