summaryrefslogtreecommitdiff
path: root/testsuite/tests
diff options
context:
space:
mode:
Diffstat (limited to 'testsuite/tests')
-rw-r--r--testsuite/tests/rts/pause-resume/all.T20
-rw-r--r--testsuite/tests/rts/pause-resume/pause_and_use_rts_api.hs28
-rw-r--r--testsuite/tests/rts/pause-resume/pause_and_use_rts_api.stdout34
-rw-r--r--testsuite/tests/rts/pause-resume/pause_resume.c243
-rw-r--r--testsuite/tests/rts/pause-resume/pause_resume.h10
-rw-r--r--testsuite/tests/rts/pause-resume/pause_resume_via_pthread.hs34
-rw-r--r--testsuite/tests/rts/pause-resume/pause_resume_via_safe_ffi.hs38
-rw-r--r--testsuite/tests/rts/pause-resume/pause_resume_via_safe_ffi_concurrent.hs52
-rw-r--r--testsuite/tests/rts/pause-resume/pause_resume_via_safe_ffi_concurrent.stdout1
-rw-r--r--testsuite/tests/rts/pause-resume/shouldfail/all.T23
-rw-r--r--testsuite/tests/rts/pause-resume/shouldfail/rts_double_pause.hs23
-rw-r--r--testsuite/tests/rts/pause-resume/shouldfail/rts_double_pause.stderr1
-rw-r--r--testsuite/tests/rts/pause-resume/shouldfail/rts_double_pause.stdout2
-rw-r--r--testsuite/tests/rts/pause-resume/shouldfail/rts_lock_when_paused.hs23
-rw-r--r--testsuite/tests/rts/pause-resume/shouldfail/rts_lock_when_paused.stderr2
-rw-r--r--testsuite/tests/rts/pause-resume/shouldfail/rts_lock_when_paused.stdout2
-rw-r--r--testsuite/tests/rts/pause-resume/shouldfail/rts_pause_lock.c83
-rw-r--r--testsuite/tests/rts/pause-resume/shouldfail/rts_pause_lock.h5
-rw-r--r--testsuite/tests/rts/pause-resume/shouldfail/rts_pause_when_locked.hs23
-rw-r--r--testsuite/tests/rts/pause-resume/shouldfail/rts_pause_when_locked.stderr2
-rw-r--r--testsuite/tests/rts/pause-resume/shouldfail/rts_pause_when_locked.stdout2
-rw-r--r--testsuite/tests/rts/pause-resume/shouldfail/unsafe_rts_pause.hs21
-rw-r--r--testsuite/tests/rts/pause-resume/shouldfail/unsafe_rts_pause.stderr2
23 files changed, 674 insertions, 0 deletions
diff --git a/testsuite/tests/rts/pause-resume/all.T b/testsuite/tests/rts/pause-resume/all.T
new file mode 100644
index 0000000000..3099a8f12c
--- /dev/null
+++ b/testsuite/tests/rts/pause-resume/all.T
@@ -0,0 +1,20 @@
+test('pause_resume_via_safe_ffi',
+ [ only_ways(['threaded1', 'threaded2'])
+ , extra_files(['pause_resume.c','pause_resume.h'])
+ ],
+ multi_compile_and_run, ['pause_resume_via_safe_ffi', [('pause_resume.c','')], ''])
+test('pause_resume_via_pthread',
+ [ only_ways(['threaded1', 'threaded2'])
+ , extra_files(['pause_resume.c','pause_resume.h'])
+ ],
+ multi_compile_and_run, ['pause_resume_via_pthread', [('pause_resume.c','')], ''])
+test('pause_resume_via_safe_ffi_concurrent',
+ [ only_ways(['threaded1', 'threaded2'])
+ , extra_files(['pause_resume.c','pause_resume.h'])
+ ],
+ multi_compile_and_run, ['pause_resume_via_safe_ffi_concurrent', [('pause_resume.c','')], ''])
+test('pause_and_use_rts_api',
+ [ only_ways(['threaded1', 'threaded2'])
+ , extra_files(['pause_resume.c','pause_resume.h'])
+ ],
+ multi_compile_and_run, ['pause_and_use_rts_api', [('pause_resume.c','')], ''])
diff --git a/testsuite/tests/rts/pause-resume/pause_and_use_rts_api.hs b/testsuite/tests/rts/pause-resume/pause_and_use_rts_api.hs
new file mode 100644
index 0000000000..f31ac1c82c
--- /dev/null
+++ b/testsuite/tests/rts/pause-resume/pause_and_use_rts_api.hs
@@ -0,0 +1,28 @@
+{-# LANGUAGE ForeignFunctionInterface #-}
+
+import Foreign
+import System.Exit
+import System.Timeout
+
+foreign import ccall safe "pause_resume.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
diff --git a/testsuite/tests/rts/pause-resume/pause_and_use_rts_api.stdout b/testsuite/tests/rts/pause-resume/pause_and_use_rts_api.stdout
new file mode 100644
index 0000000000..6c9c98af02
--- /dev/null
+++ b/testsuite/tests/rts/pause-resume/pause_and_use_rts_api.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
diff --git a/testsuite/tests/rts/pause-resume/pause_resume.c b/testsuite/tests/rts/pause-resume/pause_resume.c
new file mode 100644
index 0000000000..213adf726c
--- /dev/null
+++ b/testsuite/tests/rts/pause-resume/pause_resume.c
@@ -0,0 +1,243 @@
+#include <assert.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include "Rts.h"
+#include "RtsAPI.h"
+
+#include "pause_resume.h"
+
+void expectNoChange(const char * msg, volatile unsigned int * count);
+void expectChange(const char * msg, volatile unsigned int * count);
+
+// Test rts_pause/rts_resume by observing a count that we expect to be
+// incremented by concurrent Haskell thread(s). We expect rts_pause to stop
+// those threads and hence stop incrementing the count.
+void pauseAndResume
+ ( bool assertNotPaused // [in] True to enable assertions before rts_pause and after rts_resume.
+ // Often disabled when calling this concurrently.
+ , volatile unsigned int * count // [in] Haskell threads should be forever incrementing this.
+ )
+{
+ // Assert the RTS is resumed.
+ if (assertNotPaused)
+ {
+ expectChange("RTS should be running", count);
+ if(rts_isPaused()) {
+ errorBelch("Expected the RTS to be resumed.");
+ exit(1);
+ }
+ }
+
+ // Pause and assert.
+ PauseToken * token = rts_pause();
+ Capability * cap = pauseTokenCapability(token);
+ if(cap == NULL) {
+ errorBelch("rts_pause() returned NULL.");
+ exit(1);
+ }
+
+ if(!rts_isPaused()) {
+ errorBelch("Expected the RTS to be paused.");
+ exit(1);
+ }
+
+ expectNoChange("RTS should be paused", count);
+
+ // Resume.
+ rts_resume(token);
+
+ // Assert the RTS is resumed.
+ if (assertNotPaused)
+ {
+ expectChange("RTS should be resumed", count);
+ if(rts_isPaused()) {
+ errorBelch("Expected the RTS to be resumed.");
+ exit(1);
+ }
+ }
+}
+
+void helloWorld()
+{
+ printf("Hello World!");
+}
+
+// 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...");
+ PauseToken * token = rts_pause();
+ Capability * cap = pauseTokenCapability(token);
+ 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
+ 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, &helloWorld ));
+ 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, (HaskellObj)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 = (HaskellObj)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(token);
+ assert(cap == cap0);
+ printf("Resumed\n");
+}
+
+void* pauseAndResumeViaThread_helper(void * count)
+{
+ pauseAndResume(false, (volatile unsigned int *)count);
+ return NULL;
+}
+
+// Call pauseAndResume via a new thread and return the thread ID.
+void pauseAndResumeViaThread
+ ( volatile unsigned int * count // [in] Haskell threads should be forever incrementing this.
+ )
+{
+ OSThreadId threadId;
+ createOSThread(&threadId, "Pause and resume thread", &pauseAndResumeViaThread_helper, (void *)count);
+}
+
+const int TIMEOUT = 1000000; // 1 second
+
+// Wait for &count to change (else exit(1) after TIMEOUT).
+void expectChange(const char * msg, volatile unsigned int * count)
+{
+ unsigned int count_0 = *count;
+ int microSecondsLeft = TIMEOUT;
+ unsigned int sleepTime = 10000;
+ while (true)
+ {
+ usleep(sleepTime);
+ microSecondsLeft -= sleepTime;
+
+ if (count_0 != *count)
+ {
+ // Change detected.
+ return;
+ }
+
+ if (microSecondsLeft < 0)
+ {
+ printf("Expected: %s\n", msg);
+ exit(1);
+ }
+ }
+}
+
+// Ensure &count does NOT change (for TIMEOUT else exit(1)).
+void expectNoChange(const char * msg, volatile unsigned int * count)
+{
+ unsigned int count_0 = *count;
+ int microSecondsLeft = TIMEOUT;
+ unsigned int sleepTime = 10000;
+ while (true)
+ {
+ usleep(sleepTime);
+ microSecondsLeft -= sleepTime;
+
+ if (count_0 != *count)
+ {
+ // Change detected.
+ printf("Expected: %s\n", msg);
+ exit(1);
+ }
+
+ if (microSecondsLeft < 0)
+ {
+ return;
+ }
+ }
+}
diff --git a/testsuite/tests/rts/pause-resume/pause_resume.h b/testsuite/tests/rts/pause-resume/pause_resume.h
new file mode 100644
index 0000000000..3c928b905d
--- /dev/null
+++ b/testsuite/tests/rts/pause-resume/pause_resume.h
@@ -0,0 +1,10 @@
+
+void pauseAndResume(bool assertNotPaused, volatile unsigned int * count);
+void pauseAndResumeViaThread(volatile unsigned int * count);
+void pauseAndUseRtsAPIAndResume
+ ( HaskellObj haskellFn
+ , HaskellObj haskellFnArgument
+ , HaskellObj obj1
+ , HaskellObj obj2
+ , HsStablePtr stablePtrIO
+ );
diff --git a/testsuite/tests/rts/pause-resume/pause_resume_via_pthread.hs b/testsuite/tests/rts/pause-resume/pause_resume_via_pthread.hs
new file mode 100644
index 0000000000..f8b59c01fb
--- /dev/null
+++ b/testsuite/tests/rts/pause-resume/pause_resume_via_pthread.hs
@@ -0,0 +1,34 @@
+{-# LANGUAGE ForeignFunctionInterface #-}
+
+import Control.Concurrent
+import Control.Concurrent.MVar
+import Control.Monad
+import Foreign.C.Types
+import Foreign.Marshal.Alloc
+import Foreign.Ptr
+import Foreign.Storable
+import GHC.Exts
+
+foreign import ccall safe "pause_resume.h pauseAndResumeViaThread"
+ safe_pauseAndResumeViaThread_c :: Ptr CUInt -> IO ()
+
+-- Simple test of rts_pause() followed by rts_resume() via a new thread created
+-- in c code.
+main :: IO ()
+main = do
+ alloca $ \countPtr -> do
+ poke countPtr 0
+
+ -- forever increment count. Changes will be observed from the c code.
+ sequence_ $ replicate 4 $ forkIO $ forever $ do
+ count <- peek countPtr
+ poke countPtr (count + 1)
+ threadDelay 10000 -- 10 milliseconds
+
+ -- Test rts_pause/rts_resume.
+ safe_pauseAndResumeViaThread_c countPtr
+
+ -- Test rts_pause/rts_resume from a unbound (worker) thread.
+ forkIO $ safe_pauseAndResumeViaThread_c countPtr
+
+ threadDelay 5000000 -- 5 seconds
diff --git a/testsuite/tests/rts/pause-resume/pause_resume_via_safe_ffi.hs b/testsuite/tests/rts/pause-resume/pause_resume_via_safe_ffi.hs
new file mode 100644
index 0000000000..4581a8be81
--- /dev/null
+++ b/testsuite/tests/rts/pause-resume/pause_resume_via_safe_ffi.hs
@@ -0,0 +1,38 @@
+{-# LANGUAGE ForeignFunctionInterface #-}
+
+import Control.Concurrent
+import Control.Concurrent.MVar
+import Control.Monad
+import Foreign.C.Types
+import Foreign.Marshal.Alloc
+import Foreign.Ptr
+import Foreign.Storable
+import GHC.Stack
+
+foreign import ccall safe "pause_resume.h pauseAndResume"
+ safe_pauseAndResume_c :: CBool -> Ptr CUInt -> IO ()
+
+-- Simple test of rts_pause() followed by rts_resume()
+main :: IO ()
+main = do
+ alloca $ \countPtr -> do
+ poke countPtr 0
+
+ -- forever increment count. Changes will be observed from the c code.
+ sequence_ $ replicate 4 $ forkIO $ forever $ do
+ count <- peek countPtr
+ poke countPtr (count + 1)
+ threadDelay 10000 -- 10 milliseconds
+
+ -- Test rts_pause/rts_resume.
+ safe_pauseAndResume_c cTrue countPtr
+
+ -- Test rts_pause/rts_resume from a unbound (worker) thread.
+ mvar <- newEmptyMVar
+ forkIO $ do
+ safe_pauseAndResume_c cTrue countPtr
+ putMVar mvar ()
+ takeMVar mvar
+
+cTrue :: CBool
+cTrue = 1
diff --git a/testsuite/tests/rts/pause-resume/pause_resume_via_safe_ffi_concurrent.hs b/testsuite/tests/rts/pause-resume/pause_resume_via_safe_ffi_concurrent.hs
new file mode 100644
index 0000000000..6cc5f8b44a
--- /dev/null
+++ b/testsuite/tests/rts/pause-resume/pause_resume_via_safe_ffi_concurrent.hs
@@ -0,0 +1,52 @@
+{-# LANGUAGE ForeignFunctionInterface #-}
+
+import Control.Concurrent
+import Control.Concurrent.MVar
+import Control.Monad
+import Foreign.C.Types
+import Foreign.Marshal.Alloc
+import Foreign.Ptr
+import Foreign.Storable
+import System.Exit
+import System.Timeout
+
+foreign import ccall safe "pause_resume.h pauseAndResume"
+ safe_pauseAndResume_c :: CBool -> Ptr CUInt -> IO ()
+
+-- Test that concurrent calls to rts_pause()/rts_resume() doesn't cause deadlock.
+main :: IO ()
+main = do
+ alloca $ \countPtr -> do
+ poke countPtr 0
+
+ -- forever increment count. Changes will be observed from the c code.
+ sequence_ $ replicate 4 $ forkIO $ forever $ do
+ count <- peek countPtr
+ poke countPtr (count + 1)
+ threadDelay 10000 -- 10 milliseconds
+
+ -- Note that each call blocks for about a second, so this will take 5
+ -- seconds to complete.
+ let n = 5
+ mvars <- sequence $ replicate n newEmptyMVar
+ forM_ mvars $ \mvar -> forkIO $ do
+ safe_pauseAndResume_c
+ -- Don't check rts_isPaused() before rts_pause nore after rts_resume
+ -- because we're doing this concurrently so that would introduce a race
+ -- condition.
+ cFalse
+ countPtr
+ putMVar mvar ()
+
+ -- Wait (at least 2n seconds to be safe) for all threads to finish.
+ result <- timeout (2 * n * 1000000) (mapM_ takeMVar mvars)
+ case result of
+ Nothing -> do
+ putStrLn "Not all rts_pause/rts_resume threads have finished. Assuming deadlocked and failing test."
+ exitFailure
+ Just () -> do
+ putStrLn "All threads finished"
+ exitSuccess
+
+cFalse :: CBool
+cFalse = 0
diff --git a/testsuite/tests/rts/pause-resume/pause_resume_via_safe_ffi_concurrent.stdout b/testsuite/tests/rts/pause-resume/pause_resume_via_safe_ffi_concurrent.stdout
new file mode 100644
index 0000000000..a265a6f39e
--- /dev/null
+++ b/testsuite/tests/rts/pause-resume/pause_resume_via_safe_ffi_concurrent.stdout
@@ -0,0 +1 @@
+All threads finished
diff --git a/testsuite/tests/rts/pause-resume/shouldfail/all.T b/testsuite/tests/rts/pause-resume/shouldfail/all.T
new file mode 100644
index 0000000000..f778f8a257
--- /dev/null
+++ b/testsuite/tests/rts/pause-resume/shouldfail/all.T
@@ -0,0 +1,23 @@
+
+test('unsafe_rts_pause',
+ [ only_ways(['threaded1', 'threaded2'])
+ , exit_code(1)
+ ], compile_and_run, [''])
+test('rts_lock_when_paused',
+ [ only_ways(['threaded1', 'threaded2'])
+ , exit_code(1)
+ , extra_files(['rts_pause_lock.c','rts_pause_lock.h'])
+ ],
+ multi_compile_and_run, ['rts_lock_when_paused', [('rts_pause_lock.c','')], ''])
+test('rts_pause_when_locked',
+ [ only_ways(['threaded1', 'threaded2'])
+ , exit_code(1)
+ , extra_files(['rts_pause_lock.c','rts_pause_lock.h'])
+ ],
+ multi_compile_and_run, ['rts_pause_when_locked', [('rts_pause_lock.c','')], ''])
+test('rts_double_pause',
+ [ only_ways(['threaded1', 'threaded2'])
+ , exit_code(1)
+ , extra_files(['rts_pause_lock.c','rts_pause_lock.h'])
+ ],
+ multi_compile_and_run, ['rts_double_pause', [('rts_pause_lock.c','')], ''])
diff --git a/testsuite/tests/rts/pause-resume/shouldfail/rts_double_pause.hs b/testsuite/tests/rts/pause-resume/shouldfail/rts_double_pause.hs
new file mode 100644
index 0000000000..1068b44437
--- /dev/null
+++ b/testsuite/tests/rts/pause-resume/shouldfail/rts_double_pause.hs
@@ -0,0 +1,23 @@
+{-# LANGUAGE ForeignFunctionInterface #-}
+
+import Control.Concurrent
+import Foreign
+import Foreign.C
+import System.Exit
+import System.Timeout
+
+foreign import ccall safe "rts_pause_lock.h assertDoneAfterOneSecond"
+ safe_assertDoneAfterOneSecond_c :: Ptr CInt -> IO ()
+
+foreign import ccall safe "rts_pause_lock.h doublePause"
+ safe_doublePause_c :: Ptr CInt -> IO ()
+
+main :: IO ()
+main = alloca $ \donePtr -> do
+ -- We don't expect a deadlock, but we want to avoid one in the case of a
+ -- failed test.
+ poke donePtr 0
+ forkIO $ safe_assertDoneAfterOneSecond_c donePtr
+
+ -- The actual test.
+ safe_doublePause_c donePtr
diff --git a/testsuite/tests/rts/pause-resume/shouldfail/rts_double_pause.stderr b/testsuite/tests/rts/pause-resume/shouldfail/rts_double_pause.stderr
new file mode 100644
index 0000000000..59a19d2fec
--- /dev/null
+++ b/testsuite/tests/rts/pause-resume/shouldfail/rts_double_pause.stderr
@@ -0,0 +1 @@
+rts_double_pause: error: rts_pause: This thread has already paused the RTS.
diff --git a/testsuite/tests/rts/pause-resume/shouldfail/rts_double_pause.stdout b/testsuite/tests/rts/pause-resume/shouldfail/rts_double_pause.stdout
new file mode 100644
index 0000000000..8c2f9a4e9d
--- /dev/null
+++ b/testsuite/tests/rts/pause-resume/shouldfail/rts_double_pause.stdout
@@ -0,0 +1,2 @@
+Pausing...Paused
+Pausing... \ No newline at end of file
diff --git a/testsuite/tests/rts/pause-resume/shouldfail/rts_lock_when_paused.hs b/testsuite/tests/rts/pause-resume/shouldfail/rts_lock_when_paused.hs
new file mode 100644
index 0000000000..7ca1107211
--- /dev/null
+++ b/testsuite/tests/rts/pause-resume/shouldfail/rts_lock_when_paused.hs
@@ -0,0 +1,23 @@
+{-# LANGUAGE ForeignFunctionInterface #-}
+
+import Control.Concurrent
+import Foreign
+import Foreign.C
+import System.Exit
+import System.Timeout
+
+foreign import ccall safe "rts_pause_lock.h assertDoneAfterOneSecond"
+ safe_assertDoneAfterOneSecond_c :: Ptr CInt -> IO ()
+
+foreign import ccall safe "rts_pause_lock.h lockThenPause"
+ safe_lockThenPause_c :: Ptr CInt -> IO ()
+
+main :: IO ()
+main = alloca $ \donePtr -> do
+ -- We don't expect a deadlock, but we want to avoid one in the case of a
+ -- failed test.
+ poke donePtr 0
+ forkIO $ safe_assertDoneAfterOneSecond_c donePtr
+
+ -- The actual test.
+ safe_lockThenPause_c donePtr
diff --git a/testsuite/tests/rts/pause-resume/shouldfail/rts_lock_when_paused.stderr b/testsuite/tests/rts/pause-resume/shouldfail/rts_lock_when_paused.stderr
new file mode 100644
index 0000000000..32af222649
--- /dev/null
+++ b/testsuite/tests/rts/pause-resume/shouldfail/rts_lock_when_paused.stderr
@@ -0,0 +1,2 @@
+rts_lock_when_paused: error: rts_pause: attempting to pause from a Task that owns a capability.
+ Have you already acquired a capability e.g. with rts_lock?
diff --git a/testsuite/tests/rts/pause-resume/shouldfail/rts_lock_when_paused.stdout b/testsuite/tests/rts/pause-resume/shouldfail/rts_lock_when_paused.stdout
new file mode 100644
index 0000000000..397b92f9fc
--- /dev/null
+++ b/testsuite/tests/rts/pause-resume/shouldfail/rts_lock_when_paused.stdout
@@ -0,0 +1,2 @@
+Locking...Locked
+Pausing...
diff --git a/testsuite/tests/rts/pause-resume/shouldfail/rts_pause_lock.c b/testsuite/tests/rts/pause-resume/shouldfail/rts_pause_lock.c
new file mode 100644
index 0000000000..60145b28a0
--- /dev/null
+++ b/testsuite/tests/rts/pause-resume/shouldfail/rts_pause_lock.c
@@ -0,0 +1,83 @@
+#include <stdio.h>
+#include <unistd.h>
+
+#include "Rts.h"
+#include "RtsAPI.h"
+
+#include "rts_pause_lock.h"
+
+// Although we expect errors rather than deadlock, we don't want a failed test
+// to be a deadlocked test. Hence we use this as a 1 second timeout mechanism.
+void assertDoneAfterOneSecond(int * done)
+{
+ sleep(1);
+ if (!*done)
+ {
+ printf("Deadlock detected.");
+ exit(1);
+ }
+}
+
+void lockThenPause (int * done) {
+ printf("Locking...");
+ Capability * lockCap = rts_lock();
+ printf("Locked\n");
+
+ printf("Pausing...");
+ PauseToken * token = rts_pause();
+ Capability * pauseCap = pauseTokenCapability(token);
+ printf("Paused\n");
+
+ printf("Resuming...");
+ rts_resume(token);
+ printf("Resumed\n");
+
+ printf("Unlocking...");
+ rts_unlock(lockCap);
+ printf("Unlocked\n");
+
+ *done = 1;
+}
+
+void pauseThenLock (int * done) {
+ printf("Pausing...");
+ PauseToken * token = rts_pause();
+ Capability * pauseCap = pauseTokenCapability(token);
+ printf("Paused\n");
+
+ printf("Locking...");
+ Capability * lockCap = rts_lock();
+ printf("Locked\n");
+
+ printf("Unlocking...");
+ rts_unlock(lockCap);
+ printf("Unlocked\n");
+
+ printf("Resuming...");
+ rts_resume(token);
+ printf("Resumed\n");
+
+ *done = 1;
+}
+
+void doublePause (int * done) {
+ printf("Pausing...");
+ PauseToken * tokenA = rts_pause();
+ Capability * pauseCapA = pauseTokenCapability(tokenA);
+ printf("Paused\n");
+
+ printf("Pausing...");
+ PauseToken * tokenB = rts_pause();
+ Capability * pauseCapB = pauseTokenCapability(tokenB);
+ printf("Paused\n");
+
+ printf("Resuming...");
+ rts_resume(tokenA);
+ printf("Resuming\n");
+
+ printf("Resuming...");
+ rts_resume(tokenB);
+ printf("Resumed\n");
+
+ *done = 1;
+}
diff --git a/testsuite/tests/rts/pause-resume/shouldfail/rts_pause_lock.h b/testsuite/tests/rts/pause-resume/shouldfail/rts_pause_lock.h
new file mode 100644
index 0000000000..fb9d920040
--- /dev/null
+++ b/testsuite/tests/rts/pause-resume/shouldfail/rts_pause_lock.h
@@ -0,0 +1,5 @@
+
+void assertDoneAfterOneSecond(int * done);
+void lockThenPause (int * done);
+void pauseThenLock (int * done);
+void doublePause (int * done);
diff --git a/testsuite/tests/rts/pause-resume/shouldfail/rts_pause_when_locked.hs b/testsuite/tests/rts/pause-resume/shouldfail/rts_pause_when_locked.hs
new file mode 100644
index 0000000000..0f1b7636bd
--- /dev/null
+++ b/testsuite/tests/rts/pause-resume/shouldfail/rts_pause_when_locked.hs
@@ -0,0 +1,23 @@
+{-# LANGUAGE ForeignFunctionInterface #-}
+
+import Control.Concurrent
+import Foreign
+import Foreign.C
+import System.Exit
+import System.Timeout
+
+foreign import ccall safe "rts_pause_lock.h assertDoneAfterOneSecond"
+ safe_assertDoneAfterOneSecond_c :: Ptr CInt -> IO ()
+
+foreign import ccall safe "rts_pause_lock.h pauseThenLock"
+ safe_pauseThenLock_c :: Ptr CInt -> IO ()
+
+main :: IO ()
+main = alloca $ \donePtr -> do
+ -- We don't expect a deadlock, but we want to avoid one in the case of a
+ -- failed test.
+ poke donePtr 0
+ forkIO $ safe_assertDoneAfterOneSecond_c donePtr
+
+ -- The actual test.
+ safe_pauseThenLock_c donePtr
diff --git a/testsuite/tests/rts/pause-resume/shouldfail/rts_pause_when_locked.stderr b/testsuite/tests/rts/pause-resume/shouldfail/rts_pause_when_locked.stderr
new file mode 100644
index 0000000000..d63f38e009
--- /dev/null
+++ b/testsuite/tests/rts/pause-resume/shouldfail/rts_pause_when_locked.stderr
@@ -0,0 +1,2 @@
+rts_pause_when_locked: error: rts_lock: The RTS is already paused by this thread.
+ There is no need to call rts_lock if you have already called rts_pause.
diff --git a/testsuite/tests/rts/pause-resume/shouldfail/rts_pause_when_locked.stdout b/testsuite/tests/rts/pause-resume/shouldfail/rts_pause_when_locked.stdout
new file mode 100644
index 0000000000..17cc37ba06
--- /dev/null
+++ b/testsuite/tests/rts/pause-resume/shouldfail/rts_pause_when_locked.stdout
@@ -0,0 +1,2 @@
+Pausing...Paused
+Locking...
diff --git a/testsuite/tests/rts/pause-resume/shouldfail/unsafe_rts_pause.hs b/testsuite/tests/rts/pause-resume/shouldfail/unsafe_rts_pause.hs
new file mode 100644
index 0000000000..d4ec1acd25
--- /dev/null
+++ b/testsuite/tests/rts/pause-resume/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 Capability
+
+foreign import ccall unsafe "RtsAPI.h rts_pause"
+ unsafe_rts_pause_c :: IO (Ptr Capability)
+
+main :: IO ()
+main = do
+ -- Making a unsafe call to rts_pause() should fail. 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."
diff --git a/testsuite/tests/rts/pause-resume/shouldfail/unsafe_rts_pause.stderr b/testsuite/tests/rts/pause-resume/shouldfail/unsafe_rts_pause.stderr
new file mode 100644
index 0000000000..208752f88d
--- /dev/null
+++ b/testsuite/tests/rts/pause-resume/shouldfail/unsafe_rts_pause.stderr
@@ -0,0 +1,2 @@
+unsafe_rts_pause: error: rts_pause: attempting to pause via an unsafe FFI call.
+ Perhaps a 'foreign import unsafe' should be 'safe'?