Commit 681aad99 authored by Simon Marlow's avatar Simon Marlow
Browse files

hs_exit()/shutdownHaskell(): wait for outstanding foreign calls to complete before returning

This is pertinent to #1177.  When shutting down a DLL, we need to be
sure that there are no OS threads that can return to the code that we
are about to unload, and previously the threaded RTS was unsafe in
this respect.

When exiting a standalone program we don't have to be quite so
paranoid: all the code will disappear at the same time as any running
threads.  Happily exiting a program happens via a different path:
shutdownHaskellAndExit().  If we're about to exit(), then there's no
need to wait for foreign calls to complete.
parent 1bd1fb93
......@@ -639,7 +639,7 @@ prodOneCapability (void)
* ------------------------------------------------------------------------- */
void
shutdownCapability (Capability *cap, Task *task)
shutdownCapability (Capability *cap, Task *task, rtsBool safe)
{
nat i;
......@@ -696,8 +696,24 @@ shutdownCapability (Capability *cap, Task *task)
yieldThread();
continue;
}
// If "safe", then busy-wait for any threads currently doing
// foreign calls. If we're about to unload this DLL, for
// example, we need to be sure that there are no OS threads
// that will try to return to code that has been unloaded.
// We can be a bit more relaxed when this is a standalone
// program that is about to terminate, and let safe=false.
if (cap->suspended_ccalling_tasks && safe) {
debugTrace(DEBUG_sched,
"thread(s) are involved in foreign calls, yielding");
cap->running_task = NULL;
RELEASE_LOCK(&cap->lock);
yieldThread();
continue;
}
debugTrace(DEBUG_sched, "capability %d is stopped.", cap->no);
freeCapability(cap);
freeCapability(cap);
RELEASE_LOCK(&cap->lock);
break;
}
......
......@@ -217,7 +217,7 @@ void prodAllCapabilities (void);
// Waits for a capability to drain of runnable threads and workers,
// and then acquires it. Used at shutdown time.
//
void shutdownCapability (Capability *cap, Task *task);
void shutdownCapability (Capability *cap, Task *task, rtsBool wait_foreign);
// Attempt to gain control of a Capability if it is free.
//
......
......@@ -355,12 +355,25 @@ hs_add_root(void (*init_root)(void))
initProfiling2();
}
/* -----------------------------------------------------------------------------
Shutting down the RTS
-------------------------------------------------------------------------- */
/* ----------------------------------------------------------------------------
* Shutting down the RTS
*
* The wait_foreign parameter means:
* True ==> wait for any threads doing foreign calls now.
* False ==> threads doing foreign calls may return in the
* future, but will immediately block on a mutex.
* (capability->lock).
*
* If this RTS is a DLL that we're about to unload, then you want
* safe=True, otherwise the thread might return to code that has been
* unloaded. If this is a standalone program that is about to exit,
* then you can get away with safe=False, which is better because we
* won't hang on exit if there is a blocked foreign call outstanding.
*
------------------------------------------------------------------------- */
void
hs_exit(void)
static void
hs_exit_(rtsBool wait_foreign)
{
if (hs_init_count <= 0) {
errorBelch("warning: too many hs_exit()s");
......@@ -386,7 +399,7 @@ hs_exit(void)
#endif
/* stop all running tasks */
exitScheduler();
exitScheduler(wait_foreign);
#if defined(GRAN)
/* end_gr_simulation prints global stats if requested -- HWL */
......@@ -497,6 +510,14 @@ hs_exit(void)
}
// The real hs_exit():
void
hs_exit(void)
{
hs_exit_(rtsTrue);
// be safe; this might be a DLL
}
// Compatibility interfaces
void
shutdownHaskell(void)
......@@ -509,7 +530,8 @@ shutdownHaskellAndExit(int n)
{
if (hs_init_count == 1) {
OnExitHook();
hs_exit();
hs_exit_(rtsFalse);
// we're about to exit(), no need to wait for foreign calls to return.
#if defined(PAR)
/* really exit (stg_exit() would call shutdownParallelSystem() again) */
exit(n);
......
......@@ -2577,7 +2577,8 @@ initScheduler(void)
}
void
exitScheduler( void )
exitScheduler( rtsBool wait_foreign )
/* see Capability.c, shutdownCapability() */
{
Task *task = NULL;
......@@ -2599,7 +2600,7 @@ exitScheduler( void )
nat i;
for (i = 0; i < n_capabilities; i++) {
shutdownCapability(&capabilities[i], task);
shutdownCapability(&capabilities[i], task, wait_foreign);
}
boundTaskExiting(task);
stopTaskManager();
......
......@@ -18,7 +18,7 @@
* Locks assumed : none
*/
void initScheduler (void);
void exitScheduler (void);
void exitScheduler (rtsBool wait_foreign);
void freeScheduler (void);
// Place a new thread on the run queue of the current Capability
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment