[Git][ghc/ghc][wip/ghc-debug] 3 commits: Fix list_threads_and_misc_roots test

David Eichmann gitlab at gitlab.haskell.org
Tue Sep 22 21:48:12 UTC 2020



David Eichmann pushed to branch wip/ghc-debug at Glasgow Haskell Compiler / GHC


Commits:
d5c177c6 by David Eichmann at 2020-09-22T13:32:12+01:00
Fix list_threads_and_misc_roots test

- - - - -
deb2ee4b by David Eichmann at 2020-09-22T14:31:22+01:00
Allow rts_pause to be called multiple times from the same thread

- - - - -
6e7134f5 by David Eichmann at 2020-09-22T22:47:58+01:00
Return a Capability from rts_pause to allow use of other RtsAPI.h functions

- - - - -


9 changed files:

- includes/RtsAPI.h
- libraries/ghc-heap/tests/list_threads_and_misc_roots_c.c
- rts/RtsAPI.c
- testsuite/tests/rts/ghc-debug/all.T
- testsuite/tests/rts/ghc-debug/ghc_debug.c
- testsuite/tests/rts/ghc-debug/ghc_debug.h
- + testsuite/tests/rts/ghc-debug/ghc_debug_03.stdout
- + testsuite/tests/rts/ghc-debug/ghc_debug_04.hs
- + testsuite/tests/rts/ghc-debug/ghc_debug_04.stdout


Changes:

=====================================
includes/RtsAPI.h
=====================================
@@ -335,11 +335,12 @@ extern void (*exitFn)(int);
 /* ----------------------------------------------------------------------------
    Locking.
 
-   You have to surround all access to the RtsAPI with these calls.
+   You have to surround all access to the RtsAPI with rts_lock and rts_unlock
+   or with rts_pause and rts_resume.
    ------------------------------------------------------------------------- */
 
-// acquires a token which may be used to create new objects and
-// evaluate them.
+// acquires a token which may be used to create new objects and evaluate them.
+// Calling rts_lock in between rts_pause/rts_resume will cause a deadlock.
 Capability *rts_lock (void);
 
 // releases the token acquired with rts_lock().
@@ -485,28 +486,34 @@ void rts_checkSchedStatus (char* site, Capability *);
 
 SchedulerStatus rts_getSchedStatus (Capability *cap);
 
-// Halt execution of all Haskell threads (OS threads may continue) by acquiring
-// all capabilities. Blocks untill pausing is completed. This is different to
-// rts_lock() because rts_pause() pauses all capabilities while rts_lock() only
-// pauses a single capability. rts_pause() and rts_resume() must be executed
-// from the same OS thread. Must not be called when the rts is already paused.
-void rts_pause (void);
+// Halt execution of all Haskell threads by acquiring all capabilities (safe FFI
+// calls may continue). rts_resume() must later be called on the same thread to
+// resume the RTS. Only one thread at a time can keep the rts paused. The
+// rts_pause function will block until the current thread is given exclusive
+// permission to pause the RTS. If the RTS was already paused by the current OS
+// thread, then rts_pause will return immediately and have no effect. Returns a
+// token which may be used to create new objects and evaluate them (like
+// rts_lock) .This is different to rts_lock() which only pauses a single
+// capability. Calling rts_pause in between rts_lock/rts_unlock will cause a
+// deadlock.
+Capability * rts_pause (void);
 
 // Counterpart of rts_pause: Continue from a pause. All capabilities are
 // released. Must be done while RTS is paused and on the same thread as
 // rts_pause().
-void rts_resume (void);
+// [in] cap: the token returned by rts_pause.
+void rts_resume (Capability * cap);
 
 // Tells the current state of the RTS regarding rts_pause() and rts_resume().
 bool rts_isPaused(void);
 
 // List all live threads. Must be done while RTS is paused and on the same
-// thread as rts_pause().
+// thread that called rts_pause().
 typedef void (*ListThreadsCb)(void *user, StgTSO *);
 void rts_listThreads(ListThreadsCb cb, void *user);
 
-// List all non-thread GC roots. Must be done while RTS is paused (see
-// rts_pause()).
+// List all non-thread GC roots. Must be done while RTS is paused  on the same
+// thread that called rts_pause().
 typedef void (*ListRootsCb)(void *user, StgClosure *);
 void rts_listMiscRoots(ListRootsCb cb, void *user);
 


=====================================
libraries/ghc-heap/tests/list_threads_and_misc_roots_c.c
=====================================
@@ -23,10 +23,10 @@ void collectMiscRootsCallback(void *user, StgClosure* closure){
 }
 
 void* listThreads_thread(void* unused){
-    RtsPaused paused = rts_pause();
+    Capability * cap = rts_pause();
     rts_listThreads(&collectTSOsCallback, NULL);
     rts_listMiscRoots(&collectMiscRootsCallback, NULL);
-    rts_resume(paused);
+    rts_resume(cap);
 
     return NULL;
 }


=====================================
rts/RtsAPI.c
=====================================
@@ -652,8 +652,15 @@ rts_unlock (Capability *cap)
 Task * rts_pausing_task = NULL;
 
 // See RtsAPI.h
-void rts_pause (void)
+Capability * rts_pause (void)
 {
+    // Return immediately if this thread already paused the RTS. If another
+    // thread has paused the RTS, then rts_pause will block until rts_resume is
+    // called (and compete with other threads calling rts_pause). The blocking
+    // behavior is implied by the use of `stopAllCapabilities`.
+    Task * task = getMyTask();
+    if (rts_pausing_task == task) return task->cap;
+
     // The current task must not own a capability. This is true when a new
     // thread is stareted, or when making a safe FFI call. If
     // `task->cap->running_task == task` then that is also ok because the
@@ -661,7 +668,6 @@ void rts_pause (void)
     // (rather than ASSERT which only happens with `-debug`) because this is a
     // user facing function and we want good error reporting. We also don't
     // expect rts_pause to be performance critical.
-    Task * task = getMyTask();
     if (task->cap && task->cap->running_task == task)
     {
         // This task owns a capability (at it can't be taken by other capabilities).
@@ -673,15 +679,6 @@ void rts_pause (void)
         stg_exit(EXIT_FAILURE);
     }
 
-    // Note that if the rts was paused by another task/thread, then we block
-    // instead of error. It's only an error if the same thread tries to pause
-    // twice in a row.
-    if (rts_pausing_task == task)
-    {
-        errorBelch("error: rts_pause: attempting to pause an already paused RTS.");
-        stg_exit(EXIT_FAILURE);
-    }
-
     // NOTE ghc-debug deadlock:
     //
     // stopAllCapabilities attempts to acquire all capabilities and will only
@@ -743,10 +740,13 @@ void rts_pause (void)
 
     // Now we own all capabilities so we own rts_pausing_task.
     rts_pausing_task = task;
+
+    return task->cap;
 }
 
-// See RtsAPI.h
-void rts_resume (void)
+// See RtsAPI.h The cap argument is here just for symmetry with rts_pause and to
+// match the pattern of rts_lock/rts_unlock.
+void rts_resume (Capability * cap STG_UNUSED)
 {
     Task * task = getMyTask(); // This thread has ownership over its Task.
 
@@ -848,14 +848,14 @@ void rts_listMiscRoots (ListRootsCb cb, void *user)
 }
 
 #else
-void rts_pause (void)
+Capability * rts_pause (void)
 {
     errorBelch("Warning: Pausing the RTS is only possible for "
                "multithreaded RTS.");
     stg_exit(EXIT_FAILURE);
 }
 
-void rts_resume (void)
+void rts_resume (Capability * cap)
 {
     errorBelch("Warning: Unpausing the RTS is only possible for "
                "multithreaded RTS.");


=====================================
testsuite/tests/rts/ghc-debug/all.T
=====================================
@@ -1,18 +1,8 @@
-test('ghc_debug_01',
-     [ extra_files(['ghc_debug.c','ghc_debug.h']),
-      ignore_stdout,
-      ignore_stderr
-     ],
+test('ghc_debug_01', [extra_files(['ghc_debug.c','ghc_debug.h'])],
      multi_compile_and_run, ['ghc_debug_01', [('ghc_debug.c','')], '-threaded '])
-test('ghc_debug_02',
-     [ extra_files(['ghc_debug.c','ghc_debug.h']),
-      ignore_stdout,
-      ignore_stderr
-     ],
+test('ghc_debug_02', [extra_files(['ghc_debug.c','ghc_debug.h'])],
      multi_compile_and_run, ['ghc_debug_02', [('ghc_debug.c','')], '-threaded '])
-test('ghc_debug_03',
-     [ extra_files(['ghc_debug.c','ghc_debug.h']),
-      ignore_stdout,
-      ignore_stderr
-     ],
+test('ghc_debug_03', [extra_files(['ghc_debug.c','ghc_debug.h'])],
      multi_compile_and_run, ['ghc_debug_03', [('ghc_debug.c','')], '-threaded '])
+test('ghc_debug_04', [extra_files(['ghc_debug.c','ghc_debug.h'])],
+     multi_compile_and_run, ['ghc_debug_04', [('ghc_debug.c','')], '-threaded '])


=====================================
testsuite/tests/rts/ghc-debug/ghc_debug.c
=====================================
@@ -1,3 +1,4 @@
+#include <assert.h>
 #include <stdio.h>
 #include <unistd.h>
 
@@ -29,7 +30,17 @@ void pauseAndResume
     }
 
     // Pause and assert.
-    rts_pause();
+    Capability * cap = rts_pause();
+    if(cap == NULL) {
+        errorBelch("rts_pause() returned NULL.");
+        exit(1);
+    }
+    Capability * cap2 = rts_pause(); // This should have no effect and return immediately.
+    if(cap != cap2) {
+        errorBelch("A second call to rts_pause() returned a different Capability.");
+        exit(1);
+    }
+
     if(!rts_isPaused()) {
         errorBelch("Expected the RTS to be paused.");
         exit(1);
@@ -38,7 +49,7 @@ void pauseAndResume
     expectNoChange("RTS should be paused", count);
 
     // Resume.
-    rts_resume();
+    rts_resume(cap);
 
     // Assert the RTS is resumed.
     if (assertNotPaused)
@@ -51,6 +62,125 @@ void pauseAndResume
     }
 }
 
+int addOne(int a)
+{
+    return a + 1;
+}
+
+// Pause tht RTS and call all RtsAPI.h functions.
+void pauseAndUseRtsAPIAndResume
+    ( HaskellObj haskellFn          // [in] A Haskell function (StablePtr (a -> a))
+    , HaskellObj haskellFnArgument  // [in] An argument to apply to haskellFn (a)
+    , HaskellObj obj1  // [in] arbitrary haskell value to evaluate of arbitrary type.
+    , HaskellObj obj2  // [in] arbitrary haskell value to evaluate of arbitrary type.
+    , HsStablePtr stablePtrIO  // [in] arbitrary haskell IO action to execute (StablePtr (IO t))
+    )
+{
+    // Pause the RTS.
+    printf("Pause the RTS...");
+    Capability * cap = rts_pause();
+    printf("Paused\n");
+
+    // Note the original capability. We assert that cap is not changed by
+    // functions that take &cap.
+    Capability *const cap0 = cap;
+
+    // Call RtsAPI.h functions
+
+    // TODO print out what funciton is running to give better debug output if one of these deadlocks
+
+    printf("getRTSStats...\n");
+    RTSStats s;
+    getRTSStats (&s);
+    printf("getRTSStatsEnabled...\n");
+    getRTSStatsEnabled();
+    printf("getAllocations...\n");
+    getAllocations();
+    printf("rts_getSchedStatus...\n");
+    rts_getSchedStatus(cap);
+    printf("rts_getChar, rts_mkChar...\n");
+    rts_getChar     (rts_mkChar       ( cap, 0 ));
+    printf("rts_getInt, rts_mkInt...\n");
+    rts_getInt      (rts_mkInt        ( cap, 0 ));
+    printf("rts_getInt8, rts_mkInt8...\n");
+    rts_getInt8     (rts_mkInt8       ( cap, 0 ));
+    printf("rts_getInt16, rts_mkInt16...\n");
+    rts_getInt16    (rts_mkInt16      ( cap, 0 ));
+    printf("rts_getInt32, rts_mkInt32...\n");
+    rts_getInt32    (rts_mkInt32      ( cap, 0 ));
+    printf("rts_getInt64, rts_mkInt64...\n");
+    rts_getInt64    (rts_mkInt64      ( cap, 0 ));
+    printf("rts_getWord, rts_mkWord...\n");
+    rts_getWord     (rts_mkWord       ( cap, 0 ));
+    printf("rts_getWord8, rts_mkWord8...\n");
+    rts_getWord8    (rts_mkWord8      ( cap, 0 ));
+    printf("rts_getWord16, rts_mkWord16...\n");
+    rts_getWord16   (rts_mkWord16     ( cap, 0 ));
+    printf("rts_getWord32, rts_mkWord32...\n");
+    rts_getWord32   (rts_mkWord32     ( cap, 0 ));
+    printf("rts_getWord64, rts_mkWord64...\n");
+    rts_getWord64   (rts_mkWord64     ( cap, 0 ));
+    printf("rts_getPtr, rts_mkPtr...\n");
+    int x = 0;
+    rts_getPtr      (rts_mkPtr        ( cap, &x));
+    printf("rts_getFunPtr, rts_mkFunPtr...\n");
+    rts_getFunPtr   (rts_mkFunPtr     ( cap, &addOne ));
+    printf("rts_getFloat, rts_mkFloat...\n");
+    rts_getFloat    (rts_mkFloat      ( cap, 0.0 ));
+    printf("rts_getDouble, rts_mkDouble...\n");
+    rts_getDouble   (rts_mkDouble     ( cap, 0.0 ));
+    printf("rts_getStablePtr, rts_mkStablePtr...\n");
+    rts_getStablePtr (rts_mkStablePtr ( cap, &x ));
+    printf("rts_getBool, rts_mkBool...\n");
+    rts_getBool     (rts_mkBool       ( cap, 0 ));
+    printf("rts_mkString...\n");
+    rts_mkString     ( cap, "Hello ghc-debug!" );
+    printf("rts_apply...\n");
+    rts_apply        ( cap, deRefStablePtr(haskellFn), haskellFnArgument );
+
+    printf("rts_eval...\n");
+    HaskellObj ret;
+    rts_eval(&cap, obj1, &ret);
+    assert(cap == cap0);
+
+    printf("rts_eval_...\n");
+    rts_eval_ (&cap, obj2, 50, &ret);
+    assert(cap == cap0);
+
+    printf("rts_evalIO...\n");
+    HaskellObj io = deRefStablePtr(stablePtrIO);
+    rts_evalIO (&cap, io, &ret);
+    assert(cap == cap0);
+
+    printf("rts_evalStableIOMain...\n");
+    HsStablePtr retStablePtr;
+    rts_evalStableIOMain (&cap, stablePtrIO, &retStablePtr);
+    assert(cap == cap0);
+
+    printf("rts_evalStableIO...\n");
+    rts_evalStableIO (&cap, stablePtrIO, &retStablePtr);
+    assert(cap == cap0);
+
+    printf("rts_evalLazyIO...\n");
+    rts_evalLazyIO (&cap, io, &ret);
+    assert(cap == cap0);
+
+    printf("rts_evalLazyIO_...\n");
+    rts_evalLazyIO_ (&cap,  io, 50, &ret);
+    assert(cap == cap0);
+
+    printf("rts_setInCallCapability...\n");
+    rts_setInCallCapability (0, 1);
+    printf("rts_pinThreadToNumaNode...\n");
+    rts_pinThreadToNumaNode (0);
+
+    // Resume the RTS.
+    printf("Resume the RTS...");
+    rts_resume(cap);
+    assert(cap == cap0);
+    printf("Resumed\n");
+}
+
 void* pauseAndResumeViaThread_helper(volatile unsigned int * count)
 {
     pauseAndResume(false, count);


=====================================
testsuite/tests/rts/ghc-debug/ghc_debug.h
=====================================
@@ -1,3 +1,10 @@
 
 void pauseAndResume(bool assertNotPaused, volatile unsigned int * count);
 unsigned long pauseAndResumeViaThread(volatile unsigned int * count);
+void pauseAndUseRtsAPIAndResume
+    ( HaskellObj haskellFn
+    , HaskellObj haskellFnArgument
+    , HaskellObj obj1
+    , HaskellObj obj2
+    , HsStablePtr stablePtrIO
+    );
\ No newline at end of file


=====================================
testsuite/tests/rts/ghc-debug/ghc_debug_03.stdout
=====================================
@@ -0,0 +1 @@
+All threads finished


=====================================
testsuite/tests/rts/ghc-debug/ghc_debug_04.hs
=====================================
@@ -0,0 +1,28 @@
+{-# LANGUAGE ForeignFunctionInterface #-}
+
+import Foreign
+import System.Exit
+import System.Timeout
+
+foreign import ccall safe "ghc_debug.h pauseAndUseRtsAPIAndResume"
+    pauseAndUseRtsAPIAndResume
+        :: (StablePtr (Int -> Int))
+        -> Int
+        -> Int
+        -> Int
+        -> (StablePtr (IO Int))
+        -> IO ()
+
+main :: IO ()
+main = do
+    addOne <- newStablePtr ((+1) :: Int -> Int)
+    ioOne <- newStablePtr (return 1 :: IO Int)
+    successMay <- timeout 5000000 $ pauseAndUseRtsAPIAndResume
+        addOne
+        1
+        2
+        3
+        ioOne
+    case successMay of
+        Nothing -> exitFailure
+        Just () -> exitSuccess


=====================================
testsuite/tests/rts/ghc-debug/ghc_debug_04.stdout
=====================================
@@ -0,0 +1,34 @@
+Pause the RTS...Paused
+getRTSStats...
+getRTSStatsEnabled...
+getAllocations...
+rts_getSchedStatus...
+rts_getChar, rts_mkChar...
+rts_getInt, rts_mkInt...
+rts_getInt8, rts_mkInt8...
+rts_getInt16, rts_mkInt16...
+rts_getInt32, rts_mkInt32...
+rts_getInt64, rts_mkInt64...
+rts_getWord, rts_mkWord...
+rts_getWord8, rts_mkWord8...
+rts_getWord16, rts_mkWord16...
+rts_getWord32, rts_mkWord32...
+rts_getWord64, rts_mkWord64...
+rts_getPtr, rts_mkPtr...
+rts_getFunPtr, rts_mkFunPtr...
+rts_getFloat, rts_mkFloat...
+rts_getDouble, rts_mkDouble...
+rts_getStablePtr, rts_mkStablePtr...
+rts_getBool, rts_mkBool...
+rts_mkString...
+rts_apply...
+rts_eval...
+rts_eval_...
+rts_evalIO...
+rts_evalStableIOMain...
+rts_evalStableIO...
+rts_evalLazyIO...
+rts_evalLazyIO_...
+rts_setInCallCapability...
+rts_pinThreadToNumaNode...
+Resume the RTS...Resumed



View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/1762210f8b17de124dd2a20594eca75f97f4db9b...6e7134f59827bf197f310d24d33df74d6de6c0d9

-- 
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/1762210f8b17de124dd2a20594eca75f97f4db9b...6e7134f59827bf197f310d24d33df74d6de6c0d9
You're receiving this email because of your account on gitlab.haskell.org.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.haskell.org/pipermail/ghc-commits/attachments/20200922/590f3d6c/attachment-0001.html>


More information about the ghc-commits mailing list