When a Haskell function exported with foreign export is called from outside Haskell a new Task may be allocated for it.
There will be one Task allocated for each OS thread calling into Haskell (this happens in rts_lock()). This Task is, by design, never deallocated. This works fine with a small number of threads, but if the external caller generates a lot of threads that die the Haskell RTS will just get more and more Task objects that are never freed. The Task also contains a condition variable and a mutex, which incurs a HANDLE leak on Windows.
I don't see an elegant fix to this, but the attached code remembers the OS thread for "incall" Task, and every so often (every 100 Tasks created) if will scan the Tasks looking for ones where the OS thread is dead, and remove these.
The fix only works on Windows, I'm afraid.
Trac metadata
Trac field
Value
Version
7.6.3
Type
Bug
TypeOfFailure
OtherFailure
Priority
normal
Resolution
Unresolved
Component
Runtime System
Test case
Differential revisions
BlockedBy
Related
Blocking
CC
Operating system
Architecture
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information
Child items
0
Show closed items
No child items are currently assigned. Use child items to break down this issue into smaller parts.
When destroying a thread, you need to know if it was ever used for GHC or incurr some overhead. As long as the dealloc is cheap and doesn't require you to have done the alloc, that's OK.
You need to know when the thread goes away. If you are using a thread pool library, that's often very tricky. In our particular case it's feasible.
If a thread dies unexpectedly the memory will be leaked. That's hopefully not a big issue in practice.
So an explicit dealloc is OK for us, with the caveat that is must work successfully on a thread that was never used by GHC.
Thinking about it a bit more, it ought to be possible to clean these up automatically. The tricky bit is knowing when to do so - too eager and we penalize threads that are doing frequent in-calls, too lazy and we have the memory leak again. Perhaps just sweeping the table during a major GC would be good enough. Better might be to only free Tasks that have survived one major GC cycle without being used, but that's a bit more work.
Sorry, me again. On one of my development machines, I get
Compile failed (status 256) errors were:[1 of 1] Compiling T8124 ( T8124.hs, T8124.o )Linking T8124 ...Warning: -rtsopts and -with-rtsopts have no effect with -no-hs-main. Call hs_init_ghc() from your main() function to set these options./usr/bin/ld: T8124_c.o: undefined reference to symbol 'pthread_create@@GLIBC_2.2.5'/lib/x86_64-linux-gnu/libpthread.so.0: error adding symbols: DSO missing from command linecollect2: error: ld returned 1 exit status*** unexpected failure for T8124(normal)