I just looked at this code and since I don't know the code I can't give you good solutions, but for others watching this thread the links might prove interesting.
My main theory is that you do have some other thread in FFI-land while you are fork()ing. The task->cond, task->lock seems to be related to this (see quoted comments below).
Also, pthread_mutex_destroy is undefined if the lock is locked, so I am guessing that the task->lock is somehow locked when it shouldn't be.
It isn't clear from your description whether this is consistently happening on Linux, or whether this only sometimes happens.
The forkProcess() code seems to hold all capabilities during fork, but that does not include FFI-land threads AFAIU.
Assuming that this happens only rarely, I am trying to understand what happens if the thread that is in FFI-land returns to the RTS (in the parent) after fork(), but before the freeTask() in the child. Based on the descriptions I read, it seems likely that this thread will try to inspect task->cap, which requires holding task->lock.
That would in turn make the pthread_mutex_destroy in the child invalid.
"""
...
When a task is migrated from sleeping on one Capability to another,
its task->cap field must be modified. When the task wakes up, it
will read the new value of task->cap to find out which Capability
it belongs to. Hence some synchronisation is required on
task->cap, and this is why we have task->lock.
If the Task is not currently owned by task->id, then the thread is
either
(a) waiting on the condition task->cond. The Task is either
(1) a bound Task, the TSO will be on a queue somewhere
(2) a worker task, on the spare_workers queue of task->cap.
...
"""
freeTask:
the comment in freeTask refers to this test:
That test calls the RTC from C which then forkIOs off actions that are outstanding when the RTS exits.
in forkProcess, child code
It look like all this code supports the notion that some other thread can be in foreign code during the fork call.
discardTasksExcept
Alexander