[Git][ghc/ghc][wip/ghc-debug] Add tests for calling ghc-debug API via safe/unsafe FFI call and via a new thread

David Eichmann gitlab at gitlab.haskell.org
Thu Sep 17 14:02:22 UTC 2020



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


Commits:
47b915a1 by David Eichmann at 2020-09-17T15:02:11+01:00
Add tests for calling ghc-debug API via safe/unsafe FFI call and via a new thread

- - - - -


6 changed files:

- testsuite/tests/rts/ghc-debug/all.T
- testsuite/tests/rts/ghc-debug/pause_and_unpause.hs → testsuite/tests/rts/ghc-debug/rts_pause_and_unpause.hs
- testsuite/tests/rts/ghc-debug/pause_and_unpause_thread.c → testsuite/tests/rts/ghc-debug/rts_pause_and_unpause_c.c
- testsuite/tests/rts/ghc-debug/pause_and_unpause_thread.h → testsuite/tests/rts/ghc-debug/rts_pause_and_unpause_c.h
- + testsuite/tests/rts/ghc-debug/shouldfail/all.T
- + testsuite/tests/rts/ghc-debug/shouldfail/unsafe_rts_pause.hs


Changes:

=====================================
testsuite/tests/rts/ghc-debug/all.T
=====================================
@@ -1,6 +1,6 @@
-test('pause_and_unpause',
-     [ extra_files(['pause_and_unpause_thread.c','pause_and_unpause_thread.h']),
+test('rts_pause_and_unpause',
+     [ extra_files(['rts_pause_and_unpause_c.c','rts_pause_and_unpause_c.h']),
       ignore_stdout,
       ignore_stderr
      ],
-     multi_compile_and_run, ['pause_and_unpause', [('pause_and_unpause_thread.c','')], '-threaded'])
+     multi_compile_and_run, ['rts_pause_and_unpause', [('rts_pause_and_unpause_c.c','')], '-threaded '])
\ No newline at end of file


=====================================
testsuite/tests/rts/ghc-debug/pause_and_unpause.hs → testsuite/tests/rts/ghc-debug/rts_pause_and_unpause.hs
=====================================
@@ -8,23 +8,30 @@ import Foreign.C.Types
 import System.Mem
 import Control.Monad
 
-foreign import ccall safe "pause_and_unpause_thread.h pauseAndUnpause"
-    pauseAndUnpause_c :: IO ()
+foreign import ccall safe "rts_pause_and_unpause_c.h pauseAndUnpause"
+    safe_pauseAndUnpause_c :: IO ()
 
-foreign import ccall safe "pause_and_unpause_thread.h getUnixTime"
+foreign import ccall unsafe "rts_pause_and_unpause_c.h pauseAndUnpause"
+    unsafe_pauseAndUnpause_c :: IO ()
+
+foreign import ccall unsafe "rts_pause_and_unpause_c.h pauseAndUnpauseViaNewThread"
+    unsafe_pauseAndUnpauseViaNewThread_c :: IO ()
+
+-- Note that these should be unsafe FFI calls. rts_pause() does not pause or
+-- wait for safe FFI calls, as they do not own a capability.
+foreign import ccall unsafe "rts_pause_and_unpause_c.h getUnixTime"
     getUnixTime_c :: IO CTime
 
-foreign import ccall safe "pause_and_unpause_thread.h getPauseBegin"
+foreign import ccall unsafe "rts_pause_and_unpause_c.h getPauseBegin"
     getPauseBegin_c :: IO CTime
 
-foreign import ccall safe "pause_and_unpause_thread.h getPauseEnd"
+foreign import ccall unsafe "rts_pause_and_unpause_c.h getPauseEnd"
     getPauseEnd_c :: IO CTime
 
 clockEachSecond :: IORef [CTime] -> IO ()
 clockEachSecond ref = forever $ do
   time <- getUnixTime_c
-  timesList <- readIORef ref
-  writeIORef ref $ time : timesList
+  modifyIORef ref $ (time:)
 
   sleepSeconds 1
 
@@ -35,12 +42,30 @@ the list that is in this 5 Seconds wide timeframe, which is defined by
 getPauseBegin_c and getPauseEnd_c. -}
 main :: IO ()
 main = do
-    ref <- newIORef []
-    forkIO $ clockEachSecond ref
+  -- Start thread that forever writes the current time to an IORef
+  ref <- newIORef []
+  forkIO $ clockEachSecond ref
 
-    sleepSeconds 3
+  -- Attempt pause and unpause in various forms
+  withPauseAndUnpause ref
+    "Pause and unpause via safe FFI call"
+    safe_pauseAndUnpause_c
+
+  withPauseAndUnpause ref
+    "Pause and unpause via unsafe FFI call"
+    unsafe_pauseAndUnpause_c
 
-    pauseAndUnpause_c
+  withPauseAndUnpause ref
+    "Pause and unpause via unsafe FFI call that creates a new OS thread"
+    unsafe_pauseAndUnpauseViaNewThread_c
+
+withPauseAndUnpause :: IORef [CTime] -> String -> IO () -> IO ()
+withPauseAndUnpause ref startMsg pauseAndUnpause = do
+    putStrLn startMsg
+
+    writeIORef ref []
+    sleepSeconds 3
+    pauseAndUnpause
 
     -- This seems to sleep for 8 - 5 Seconds. That's strange, but should be
     -- good enough for this test.
@@ -56,7 +81,7 @@ main = do
     filter (\t -> t <= pauseBegin) times `shouldNotBe` []
     filter (\t -> t >= pauseEnd) times `shouldNotBe` []
 
-    return ()
+    putStrLn "DONE"
 
 sleepSeconds :: Int -> IO ()
 sleepSeconds t = threadDelay $ oneSecondInMicroSeconds * t


=====================================
testsuite/tests/rts/ghc-debug/pause_and_unpause_thread.c → testsuite/tests/rts/ghc-debug/rts_pause_and_unpause_c.c
=====================================
@@ -1,7 +1,7 @@
 #include <pthread.h>
 #include <time.h>
 #include <unistd.h>
-#include "pause_and_unpause_thread.h"
+#include "rts_pause_and_unpause_c.h"
 #include "Rts.h"
 #include "RtsAPI.h"
 
@@ -9,8 +9,8 @@
 
 struct PauseTimestamps timestamps = {0, 0};
 
-void pauseAndUnpause(void){
-    RtsPaused r_paused = rts_pause();
+void* pauseAndUnpause_thread(void* unused){
+    RtsPaused rtsPaused = rts_pause();
 
     if(!rts_isPaused()) {
         errorBelch("Expected the RTS to be paused.");
@@ -21,7 +21,7 @@ void pauseAndUnpause(void){
     sleep(5);
     timestamps.end = time(NULL);
 
-    rts_unpause(r_paused);
+    rts_unpause(rtsPaused);
 
     if(rts_isPaused()) {
         errorBelch("Expected the RTS to be unpaused.");
@@ -31,6 +31,16 @@ void pauseAndUnpause(void){
     return NULL;
 }
 
+void pauseAndUnpause(void){
+    pauseAndUnpause_thread(NULL);
+}
+
+void pauseAndUnpauseViaNewThread(void){
+    pthread_t threadId;
+    pthread_create(&threadId, NULL, &pauseAndUnpause_thread, NULL);
+    pthread_detach(threadId);
+}
+
 time_t getPauseBegin(void) {
     return timestamps.begin;
 }


=====================================
testsuite/tests/rts/ghc-debug/pause_and_unpause_thread.h → testsuite/tests/rts/ghc-debug/rts_pause_and_unpause_c.h
=====================================


=====================================
testsuite/tests/rts/ghc-debug/shouldfail/all.T
=====================================
@@ -0,0 +1 @@
+test('unsafe_rts_pause', [ignore_stderr, exit_code(134)], compile_and_run, ['-threaded '])
\ No newline at end of file


=====================================
testsuite/tests/rts/ghc-debug/shouldfail/unsafe_rts_pause.hs
=====================================
@@ -0,0 +1,21 @@
+{-# LANGUAGE ForeignFunctionInterface #-}
+
+import Data.Word
+import Data.IORef
+import GHC.Clock
+import Control.Concurrent
+import Foreign.Ptr
+import System.Mem
+import Control.Monad
+
+data RtsPause
+
+foreign import ccall unsafe "RtsAPI.h rts_pause"
+    unsafe_rts_pause_c :: IO (Ptr RtsPause)
+
+main :: IO ()
+main = do
+  putStrLn "Making a unsafe call to rts_pause() should fail on return. We \
+           \cannot allow this haskell thread to continue if the RTS is paused."
+  _ <- unsafe_rts_pause_c
+  putStrLn "Oops! Haskell thread has continued even though RTS was paused."



View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/47b915a196db03d57ca3358a7c03e6ab97e7aac4

-- 
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/47b915a196db03d57ca3358a7c03e6ab97e7aac4
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/20200917/e6459462/attachment-0001.html>


More information about the ghc-commits mailing list