packages feed

atomic-primops 0.2.2.1 → 0.3

raw patch · 11 files changed

+334/−94 lines, 11 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

+ Data.Atomics.Counter.IORef: incrCounter :: Int -> AtomicCounter -> IO Int
+ Data.Atomics.Counter.Reference: incrCounter :: Int -> AtomicCounter -> IO Int
- Data.Atomics.Counter.Foreign: incrCounter :: AtomicCounter -> IO Int
+ Data.Atomics.Counter.Foreign: incrCounter :: Int -> AtomicCounter -> IO Int
- Data.Atomics.Counter.Foreign: newCounter :: IO AtomicCounter
+ Data.Atomics.Counter.Foreign: newCounter :: Int -> IO AtomicCounter
- Data.Atomics.Counter.IORef: newCounter :: IO AtomicCounter
+ Data.Atomics.Counter.IORef: newCounter :: Int -> IO AtomicCounter
- Data.Atomics.Counter.Reference: newCounter :: IO AtomicCounter
+ Data.Atomics.Counter.Reference: newCounter :: Int -> IO AtomicCounter

Files

DEVLOG.md view
@@ -151,6 +151,7 @@   [2013.04.20] {Debugging}+------------------------  I fixed a major bug in the primop.  I also verified that GC is what is causing all_hammer_one to observe fewer successes than ideal.@@ -197,4 +198,99 @@     cabal-dev install "-v --enable-library-profiling --ghc-options=-prof" -s cabal-dev/ghc-7.6.2_prof --disable-documentation --with-ghc=ghc-7.6.2 --program-suffix=_ghc-7.6.2 .      How did that cause it to compile and yet run with an internal failure?++++[2013.07.18] {Timing atomic Counter ops}+================================================================================++The foreign/unboxed counter is the fasted for single thread repeated incr:++  Laptop, Macbook Retina  (4 physical core w/ hyperthreading):+  +    $ ./dist/build/test-atomic-primops/test-atomic-primops -j 1 +RTS -N4 -RTS -t single+  +    Timing readIORef/writeIORef on one thread+    SELFTIMED: 0.137 sec+    Final value: 10000000+    RAW_single_thread_repeat_incr: [OK]    ++    Timing CAS increments on one thread without retries+    SELFTIMED: 0.220 sec+    Final value: 10000000+    CAS_single_thread_repeat_incr: [OK]+    +    SELFTIMED: 0.510 sec+    CounterReference_single_thread_repeat_incr: [OK]+    +    SELFTIMED: 0.187 sec+    CounterIORef_single_thread_repeat_incr: [OK]+    +    SELFTIMED: 0.119 sec+    CounterForeign_single_thread_repeat_incr: [OK]++  Desktop, veronica westmere 3.1 ghz, 4 core no-HT:++    SELFTIMED: 0.137 sec+    Final value: 10000000+    Timing CAS increments on one thread without retries+    RAW_single_thread_repeat_incr: [OK]+    +    SELFTIMED: 0.228 sec+    Final value: 10000000+    CAS_single_thread_repeat_incr: [OK]+    +    SELFTIMED: 0.638 sec+    CounterReference_single_thread_repeat_incr: [OK]+    +    SELFTIMED: 0.242 sec+    CounterIORef_single_thread_repeat_incr: [OK]+    +    SELFTIMED: 0.135 sec+    CounterForeign_single_thread_repeat_incr: [OK]++The numbers for boolean-flipping (nots) are different than adds:++    Timing readIORef/writeIORef on one thread+    SELFTIMED: 0.097 sec+    Timing CAS boolean flips on one thread without retries+    RAW_single_thread_repeat_flip: [OK]+    +    SELFTIMED: 0.136 sec+    Final value: True+    Timing readIORef/writeIORef on one thread+    CAS_single_thread_repeat_flip: [OK]+++--------------------------------------------------++With max contention on four threads (all threads hammering counter),+the atomicModifyIORef version falls apart.  E.g. for 1M total+increments here are the numbers:++  Laptop, Macbook Retina  (4 physical core w/ hyperthreading):+    SELFTIMED: 45.768 sec+    CounterReference_concurrent_repeat_incr: [OK]+    +    SELFTIMED: 0.348 sec+    CounterIORef_concurrent_repeat_incr: [OK]++    SELFTIMED: 0.060 sec+    CounterForeign_concurrent_repeat_incr: [OK]++  Desktop, veronica westmere 3.1 ghz, 4 core no-HT:++    SELFTIMED: 14.260 sec+    CounterReference_concurrent_repeat_incr: [OK]+    +    SELFTIMED: 0.426 sec+    CounterIORef_concurrent_repeat_incr: [OK]+    +    SELFTIMED: 0.066 sec+    CounterForeign_concurrent_repeat_incr: [OK]+    +MASSIVE difference between the foreign fetchAndAdd version and the+atomicModifyIORef.  But the difference is MUCH worse on my laptop.+The laptop has hyperthreading, but in the benchmark we only use four+unpinned threads, not eight.... 
Data/Atomics/Counter.hs view
@@ -2,7 +2,9 @@  module Data.Atomics.Counter        (-         module Data.Atomics.Counter.IORef+--         module Data.Atomics.Counter.IORef+         module Data.Atomics.Counter.Foreign        ) where -import Data.Atomics.Counter.IORef+--import Data.Atomics.Counter.IORef+import Data.Atomics.Counter.Foreign
Data/Atomics/Counter/Foreign.hs view
@@ -1,7 +1,8 @@ {-# LANGUAGE BangPatterns #-} --- | This implementation stores an unboxed counter and uses FFI--- operations to modify its contents.+-- | This implementation stores an unboxed counter and uses FFI operations to modify+-- its contents.  It has the advantage that it can use true fetch-and-add operations.+-- It has the disadvantage of extra overhead due to FFI calls.  module Data.Atomics.Counter.Foreign    where@@ -15,23 +16,32 @@  type CTicket = Int --- | Create a new counter initialized to zero.-newCounter :: IO AtomicCounter-newCounter = do x <- mallocForeignPtr-                writeCounter x 0-                return x+-- | Create a new counter initialized to the given value.+newCounter :: Int -> IO AtomicCounter+newCounter n = do x <- mallocForeignPtr+                  writeCounter x n+                  -- Do we need a write barrier here?+                  return x --- | Try repeatedly until we successfully increment the counter.--- Returns the original value before the increment.-incrCounter :: AtomicCounter -> IO Int-incrCounter r = withForeignPtr r$ \r' -> fetchAndAdd r' 1 +-- | Increment the counter by a given amount.+--   Returns the original value before the increment.+--                 +--   Note that UNLIKE with boxed implementations of counters, where increment is+--   based on CAS, this increment is /O(1)/.  Fetch-and-add does not require a retry+--   loop like CAS.+incrCounter :: Int -> AtomicCounter -> IO Int+incrCounter bump r = withForeignPtr r$ \r' -> fetchAndAdd r' bump +-- | Just like the "Data.Atomics" CAS interface, this routine returns an opaque+-- ticket that can be used in CAS operations. readCounterForCAS :: AtomicCounter -> IO CTicket readCounterForCAS = readCounter +-- | Opaque tickets cannot be constructed, but they can be destructed into values. peekCTicket :: CTicket -> Int peekCTicket x = x +-- | Equivalent to `readCounterForCAS` followed by `peekCTicket`. readCounter :: AtomicCounter -> IO Int readCounter r = withForeignPtr r peek  @@ -39,6 +49,7 @@ writeCounter :: AtomicCounter -> Int -> IO () writeCounter r !new = withForeignPtr r $ \r' -> poke r' new +-- | Compare and swap for the counter ADT. casCounter :: AtomicCounter -> CTicket -> Int -> IO (Bool, CTicket) casCounter r !tick !new = withForeignPtr r $ \r' -> do    b <- compareAndSwap r' tick new
Data/Atomics/Counter/IORef.hs view
@@ -1,7 +1,8 @@ {-# LANGUAGE BangPatterns #-} --- | This version uses a boxed IORef representation, but it can be--- somewhat cheaper because it uses raw CAS rather than full atomicModifyIORef.+-- | This version uses a boxed IORef representation, but it can be somewhat cheaper+-- than the Refence version because it uses raw CAS rather than full+-- atomicModifyIORef.  module Data.Atomics.Counter.IORef        where@@ -16,25 +17,47 @@  type CTicket = Ticket Int --- | Create a new counter initialized to zero.-newCounter :: IO AtomicCounter-newCounter = fmap AtomicCounter $ newIORef 0+{-# INLINE newCounter #-}+-- | Create a new counter initialized to the given value.+newCounter :: Int -> IO AtomicCounter+newCounter n = fmap AtomicCounter $ newIORef n --- | Try repeatedly until we successfully increment the counter.--- incrCounter =+{-# INLINE incrCounter #-}+-- | Try repeatedly until we successfully increment the counter by a given amount.+-- Returns the original value of the counter (pre-increment).+incrCounter :: Int -> AtomicCounter -> IO Int+-- <DUPLICATED CODE FROM Reference.hs>+incrCounter bump cntr =+    loop =<< readCounterForCAS cntr+  where+    loop tick = do+      (b,tick') <- casCounter cntr tick (peekCTicket tick + bump)+      if b then return (peekCTicket tick')+           else loop tick'+-- </DUPLICATED CODE FROM Reference.hs> +{-# INLINE readCounterForCAS #-}+-- | Just like the "Data.Atomics" CAS interface, this routine returns an opaque+-- ticket that can be used in CAS operations. readCounterForCAS :: AtomicCounter -> IO CTicket readCounterForCAS (AtomicCounter r) = readForCAS r +{-# INLINE peekCTicket #-}+-- | Opaque tickets cannot be constructed, but they can be destructed into values. peekCTicket :: CTicket -> Int peekCTicket = peekTicket  +{-# INLINE readCounter #-}+-- | Equivalent to `readCounterForCAS` followed by `peekCTicket`. readCounter :: AtomicCounter -> IO Int readCounter (AtomicCounter r) = readIORef r +{-# INLINE writeCounter #-} -- | Make a non-atomic write to the counter.  No memory-barrier. writeCounter :: AtomicCounter -> Int -> IO () writeCounter (AtomicCounter r) !new = writeIORef r new +{-# INLINE casCounter #-}+-- | Compare and swap for the counter ADT. casCounter :: AtomicCounter -> CTicket -> Int -> IO (Bool, CTicket) casCounter (AtomicCounter r) tick !new = casIORef r tick new
Data/Atomics/Counter/Reference.hs view
@@ -1,7 +1,8 @@ {-# LANGUAGE BangPatterns #-}  -- | This reference version is implemented with atomicModifyIORef and can be a useful--- fallback if one of the other implementations needs to be debugged.+-- fallback if one of the other implementations needs to be debugged for a given+-- architecture. module Data.Atomics.Counter.Reference        where @@ -16,26 +17,46 @@  type CTicket = Int --- | Create a new counter initialized to zero.-newCounter :: IO AtomicCounter-newCounter = fmap AtomicCounter $ newIORef 0---- | Try repeatedly until we successfully increment the counter.--- incrCounter =+{-# INLINE newCounter #-}+-- | Create a new counter initialized to the given value.+newCounter :: Int -> IO AtomicCounter+newCounter !n = fmap AtomicCounter $ newIORef n +{-# INLINE incrCounter #-}+-- | Try repeatedly until we successfully increment the counter by a given amount.+-- Returns the original value of the counter (pre-increment).+incrCounter :: Int -> AtomicCounter -> IO Int+incrCounter !bump !cntr =+    loop =<< readCounterForCAS cntr+  where+    loop tick = do+      (b,tick') <- casCounter cntr tick (peekCTicket tick + bump)+      if b then return (peekCTicket tick')+           else loop tick'+                +{-# INLINE readCounterForCAS #-}+-- | Just like the "Data.Atomics" CAS interface, this routine returns an opaque+-- ticket that can be used in CAS operations. readCounterForCAS :: AtomicCounter -> IO CTicket readCounterForCAS = readCounter +{-# INLINE peekCTicket #-}+-- | Opaque tickets cannot be constructed, but they can be destructed into values. peekCTicket :: CTicket -> Int-peekCTicket x = x+peekCTicket !x = x +{-# INLINE readCounter #-}+-- | Equivalent to `readCounterForCAS` followed by `peekCTicket`. readCounter :: AtomicCounter -> IO Int readCounter (AtomicCounter r) = readIORef r +{-# INLINE writeCounter #-} -- | Make a non-atomic write to the counter.  No memory-barrier. writeCounter :: AtomicCounter -> Int -> IO () writeCounter (AtomicCounter r) !new = writeIORef r new +{-# INLINE casCounter #-}+-- | Compare and swap for the counter ADT. casCounter :: AtomicCounter -> CTicket -> Int -> IO (Bool, CTicket) casCounter (AtomicCounter r) oldT !new = 
atomic-primops.cabal view
@@ -1,5 +1,5 @@ Name:                atomic-primops-Version:             0.2.2.1+Version:             0.3 License:             BSD3 License-file:        LICENSE Author:              Ryan Newton@@ -19,15 +19,12 @@ -- 0.2 -- Critical bugfix and add Counter. -- 0.2.2 -- Add more counters -- 0.2.2.1 -- Minor, add warning.+-- 0.3 -- Major internal change.  Duplicate GHC RTS barriers and support non -threaded.  Synopsis: A safe approach to CAS and other atomic ops in Haskell.  Description: -  WARNING: Your program MUST be built with "ghc -threaded" if you depend on this library.-           The library depends on threaded Haskell RTS, which is only conditionally linked.-           If you don't do this you will see an error at link time about "cas" being missing.-  .    After GHC 7.4 a new `casMutVar#` primop became available, but it's   difficult to use safely, because pointer equality is a highly   unstable property in Haskell.  This library provides a safer method@@ -36,9 +33,15 @@   Also, this library uses the "foreign primop" capability of GHC to   add access to other variants that may be of   interest, specifically, compare and swap inside an array.+ .+ Changes in 0.3:+ .+ * Major internal change.  Duplicate the barrier code from the GHC RTS and thus +   enable support for executables that are NOT built with '-threaded'. + Extra-Source-Files:  DEVLOG.md,-                     testing/runTest.hs, testing/Test.hs, testing/test-atomic-primops.cabal+                     testing/Test.hs, testing/test-atomic-primops.cabal --                    Makefile, Test.hs, README.md  Flag debug@@ -65,7 +68,7 @@    Include-Dirs:     cbits   C-Sources:        cbits/primops.cmm-  CC-Options:       -Wall+  CC-Options:       -Wall    ghc-prof-options: -DGHC_PROFILING_ON   -- if( cabal-version < 1.17 ) {   --   ghc-prof-options: ERROR_DO_NOT_BUILD_THIS_WITH_PROFILING_YET__SEE_CABAL_ISSUE_1284@@ -73,6 +76,10 @@    if flag(debug)     cpp-options: -DDEBUG_ATOMICS++  -- Duplicate RTS functionality: +  C-Sources:    cbits/RtsDup.c+  -- -- [2013.04.08] This isn't working presently: -- -- I'm having problems with building it along with the library; see DEVLOG.
+ cbits/RtsDup.c view
@@ -0,0 +1,92 @@+#define THREADED_RTS+#undef  KEEP_INLINES++// Stg.h will pull in SMP.h:+// #include "Stg.h"+// Also having some problems with Regs.h.+// This is probably too big a chunk to suck in.++//--------------------------------------------------------------------------------+// #define EXTERN_INLINE inline+#define EXTERN_INLINE ++#include "MachDeps.h"+#include "stg/Types.h"++// Force the GHC RTS code to provide the desired symbols:+// #define IN_STGCRUN 1++// If I pull this in I get duplicated symbols:+// #include "stg/SMP.h"+//--------------------------------------------------------------------------------++/*+ * We need to tell both the compiler AND the CPU about the barriers.+ * It's no good preventing the CPU from reordering the operations if+ * the compiler has already done so - hence the "memory" restriction+ * on each of the barriers below.+ */+EXTERN_INLINE void+DUP_write_barrier(void) {+#if i386_HOST_ARCH || x86_64_HOST_ARCH+    __asm__ __volatile__ ("" : : : "memory");+#elif powerpc_HOST_ARCH+    __asm__ __volatile__ ("lwsync" : : : "memory");+#elif sparc_HOST_ARCH+    /* Sparc in TSO mode does not require store/store barriers. */+    __asm__ __volatile__ ("" : : : "memory");+#elif arm_HOST_ARCH && defined(arm_HOST_ARCH_PRE_ARMv7)+    __asm__ __volatile__ ("" : : : "memory");+#elif arm_HOST_ARCH && !defined(arm_HOST_ARCH_PRE_ARMv7)+    __asm__ __volatile__ ("dmb  st" : : : "memory");+#elif !defined(WITHSMP)+    return;+#else+#error memory barriers unimplemented on this architecture+#endif+}++EXTERN_INLINE void+DUP_store_load_barrier(void) {+#if i386_HOST_ARCH+    __asm__ __volatile__ ("lock; addl $0,0(%%esp)" : : : "memory");+#elif x86_64_HOST_ARCH+    __asm__ __volatile__ ("lock; addq $0,0(%%rsp)" : : : "memory");+#elif powerpc_HOST_ARCH+    __asm__ __volatile__ ("sync" : : : "memory");+#elif sparc_HOST_ARCH+    __asm__ __volatile__ ("membar #StoreLoad" : : : "memory");+#elif arm_HOST_ARCH && !defined(arm_HOST_ARCH_PRE_ARMv7)+    __asm__ __volatile__ ("dmb" : : : "memory");+#elif !defined(WITHSMP)+    return;+#else+#error memory barriers unimplemented on this architecture+#endif+}++EXTERN_INLINE void+DUP_load_load_barrier(void) {+#if i386_HOST_ARCH+    __asm__ __volatile__ ("" : : : "memory");+#elif x86_64_HOST_ARCH+    __asm__ __volatile__ ("" : : : "memory");+#elif powerpc_HOST_ARCH+    __asm__ __volatile__ ("lwsync" : : : "memory");+#elif sparc_HOST_ARCH+    /* Sparc in TSO mode does not require load/load barriers. */+    __asm__ __volatile__ ("" : : : "memory");+#elif arm_HOST_ARCH && !defined(arm_HOST_ARCH_PRE_ARMv7)+    __asm__ __volatile__ ("dmb" : : : "memory");+#elif !defined(WITHSMP)+    return;+#else+#error memory barriers unimplemented on this architecture+#endif+}++// Load a pointer from a memory location that might be being modified+// concurrently.  This prevents the compiler from optimising away+// multiple loads of the memory location, as it might otherwise do in+// a busy wait loop for example.+// #define VOLATILE_LOAD(p) (*((StgVolatilePtr)(p)))
cbits/primops.cmm view
@@ -1,16 +1,18 @@  #include "Cmm.h" -// Duplicate some of the RTS here (CAS instruction).-// #include "RtsDup.h"+#warning "Duplicating functionality from the GHC RTS..."+#define WHICH_CAS       cas+#define WHICH_SLBARRIER DUP_store_load_barrier+#define WHICH_LLBARRIER DUP_load_load_barrier+#define WHICH_WBARRIER  DUP_write_barrier -// #include "Cmm.h"-// Problems if we try this:-// #include "stg/SMP.h"-// #include "Rts.h"+/* #define WHICH_CAS       cas */+/* #define WHICH_SLBARRIER store_load_barrier */+/* #define WHICH_LLBARRIER load_load_barrier */+/* #define WHICH_WBARRIER  write_barrier */ -// Defined in SMP.h: -// EXTERN_INLINE StgWord cas(StgVolatilePtr p, StgWord o, StgWord n);+// ================================================================================  add1Op /* Int# -> Int# */@@ -31,7 +33,7 @@     new = R4;      p = arr + SIZEOF_StgMutArrPtrs + WDS(ind);-    (h) = foreign "C" cas(p, old, new) [];+    (h) = foreign "C" WHICH_CAS(p, old, new) [];          if (h != old) {         // Failure, return what was there instead of 'old':@@ -61,7 +63,7 @@     // The "cas" function from the C runtime abstracts over     // platform/architecture differences.  It returns the old value,     // which, if equal to "old", means success.-    (h) = foreign "C" cas(mv + SIZEOF_StgHeader + OFFSET_StgMutVar_var,+    (h) = foreign "C" WHICH_CAS(mv + SIZEOF_StgHeader + OFFSET_StgMutVar_var,                           old, new) [];     if (h != old) {         // Failure:@@ -95,16 +97,16 @@ // handles the complexity of architecture-portability.  We just need // to expose it here. stg_store_load_barrier {-    foreign "C" store_load_barrier();+    foreign "C" WHICH_SLBARRIER();     RET_N(0); }  stg_load_load_barrier {-    foreign "C" load_load_barrier();+    foreign "C" WHICH_LLBARRIER();     RET_N(0); }  stg_write_barrier {-    foreign "C" write_barrier();+    foreign "C" WHICH_WBARRIER();     RET_N(0); }
testing/Test.hs view
@@ -1,5 +1,4 @@ {-# LANGUAGE MagicHash, UnboxedTuples, BangPatterns, ScopedTypeVariables, NamedFieldPuns, CPP #-}--- {-# LANGUAGE TemplateHaskell #-}  -- | This test has three different modes which can be toggled via the -- C preprocessor.  Any subset of the three may be activated.@@ -12,16 +11,11 @@ import Data.IORef (modifyIORef') import Data.Int import Data.Time.Clock--- import System.Mem.StableName--- import GHC.IO (unsafePerformIO) import Text.Printf--- import qualified GHC.Prim     as P--- import GHC.ST import GHC.STRef import GHC.IORef import GHC.Stats (getGCStats, GCStats(..)) import Data.Primitive.Array--- import Control.Monad import Data.Word import qualified Data.Set as S import System.Random (randomIO, randomRIO)@@ -33,8 +27,6 @@ import Test.Framework  (Test, defaultMain, testGroup) import Test.Framework.Providers.HUnit (testCase) --- import Text.Printf(fprintf)- import GHC.IO (unsafePerformIO) import System.Mem (performGC) import System.Mem.StableName (makeStableName, hashStableName)@@ -42,8 +34,6 @@ import System.IO        (stdout, stderr, hPutStrLn, hFlush) import Debug.Trace      (trace) --- import Test.Framework.TH (defaultMainGenerator)- import CommonTesting  import CounterTests (counterTests) @@ -57,10 +47,15 @@                do GCStats{numGcs} <- getGCStats                   return numGcs            | otherwise = return 0-#if 1--- main = $(defaultMainGenerator)+ main :: IO ()-main =        +main = do+       -- TEMP: Fixing this at four processors because it takes a REALLY long time at larger numbers:+       -- It does 248 test cases and takes 55s at -N16...+       -- numcap <- getNumProcessors+       let numcap = 4+       setNumCapabilities numcap+               defaultMain $           [ testCase "casTicket1"              case_casTicket1          , testCase "create_and_read"         case_create_and_read@@ -80,7 +75,7 @@          -- Test several configurations of this one:          [ testCase ("test_all_hammer_one_"++show threads++"_"++show iters ++":")                     (test_all_hammer_one threads iters (0::Int))-         | threads <- [1 .. 2*numCapabilities]+         | threads <- [1 .. 2*numcap]          , iters   <- [1, 10, 100, 1000, 10000, 100000, 500000]] ++          [ testCase ("test_hammer_many_threads_1000_10000:")                     (test_all_hammer_one 1000 10000 (0::Int)) ]  ++@@ -89,19 +84,14 @@          [ testCase ("test_random_array_comm_"++show threads++"_"++show size++"_"++show iters ++":")                     (test_random_array_comm threads size iters)          | threads <- filter (>0) $ setify $-                      [1, numCapabilities `quot` 2, numCapabilities, 2*numCapabilities]+                      [1, numcap `quot` 2, numcap, 2*numcap]          , size    <- [1, 10, 100]          , iters   <- [10000]]          ++ counterTests-         + setify :: [Int] -> [Int] setify = S.toList . S.fromList -#else-main = do-  test_all_hammer_one 1 10000 (0::Int)-  putStrLn "Test Done!"-#endif ------------------------------------------------------------------------ {-# NOINLINE mynum #-} mynum :: Int
− testing/runTest.hs
@@ -1,15 +0,0 @@--import GHC.Conc-import System.Process-import System.Exit---- Run the testing executable with different thread settings.-main = do---  p <- getNumProcessors-  putStrLn "[TestHarness] Calling compiled test-atomic-primops executable..."-  ExitSuccess <- system "./dist/build/test-atomic-primops/test-atomic-primops +RTS -N1"-  -- TEMP: Fixing this at four processors because it takes a REALLY long time at larger numbers:-  ExitSuccess <- system$"./dist/build/test-atomic-primops/test-atomic-primops +RTS -N4"-  -- It does 248 test cases and takes 55s at -N16...---  ExitSuccess <- system$"./dist/build/test-atomic-primops/test-atomic-primops +RTS -N"++show p-  return ()
testing/test-atomic-primops.cabal view
@@ -6,17 +6,28 @@ Build-type:          Simple Cabal-version:       >=1.8 -Executable test-atomic-primops+Flag opt+    Description: Enable GHC optimization.+    Default: True++Flag threaded+    Description: Enable GHC threaded RTS.+    Default: True++Test-Suite test-atomic-primops+    type:       exitcode-stdio-1.0     main-is:    Test.hs-    ghc-options: -O2 -threaded -rtsopts+    ghc-options: -rtsopts -    build-depends: base, ghc-prim, primitive, containers, random,-    -- For Testing:-                   atomic-primops,+    if flag(opt)+       ghc-options: -O2 -funbox-strict-fields+    if flag(threaded)+       ghc-options: -threaded +       +    build-depends: base, ghc-prim, primitive, containers, random, atomic-primops,+                   -- For Testing:                    time, HUnit, test-framework, test-framework-hunit---                   test-framework-th -Test-Suite test-atomic-primops-runner-    type:       exitcode-stdio-1.0-    main-is:    runTest.hs-    build-depends: base, process+Executable hello-world-atomic-primops+  main-is: hello.hs+  build-depends: base >= 4.5, atomic-primops