packages feed

mpi-hs 0.1.0.1 → 0.3.0.0

raw patch · 9 files changed

+1556/−979 lines, 9 filesdep +bytestringdep +storedep −tastydep −tasty-hspecdep −tasty-hunitdep ~basenew-component:exe:examplePVP ok

version bump matches the API change (PVP)

Dependencies added: bytestring, store

Dependencies removed: tasty, tasty-hspec, tasty-hunit, unix

Dependency ranges changed: base

API changes (from Hackage documentation)

- Control.Distributed.MPI: class Pointer p
- Control.Distributed.MPI: datatype :: HasDatatype a => Datatype
- Control.Distributed.MPI: instance Control.Distributed.MPI.Pointer GHC.ForeignPtr.ForeignPtr
- Control.Distributed.MPI: instance Control.Distributed.MPI.Pointer GHC.Ptr.Ptr
- Control.Distributed.MPI: instance Control.Distributed.MPI.Pointer GHC.Stable.StablePtr
- Control.Distributed.MPI: withPtr :: (Pointer p, Storable a) => p a -> (Ptr a -> IO b) -> IO b
- Control.Distributed.MPI.Simple: finalize :: IO ()
- Control.Distributed.MPI.Simple: init :: IO ()
- Control.Distributed.MPI.Simple: initThread :: ThreadSupport -> IO ()
+ Control.Distributed.MPI: class Buffer buf where {
+ Control.Distributed.MPI: getDatatype :: HasDatatype a => Datatype
+ Control.Distributed.MPI: instance (Foreign.Storable.Storable a, Control.Distributed.MPI.HasDatatype a, GHC.Real.Integral i) => Control.Distributed.MPI.Buffer (GHC.ForeignPtr.ForeignPtr a, i)
+ Control.Distributed.MPI: instance (Foreign.Storable.Storable a, Control.Distributed.MPI.HasDatatype a, GHC.Real.Integral i) => Control.Distributed.MPI.Buffer (GHC.Ptr.Ptr a, i)
+ Control.Distributed.MPI: instance (Foreign.Storable.Storable a, Control.Distributed.MPI.HasDatatype a, GHC.Real.Integral i) => Control.Distributed.MPI.Buffer (GHC.Stable.StablePtr a, i)
+ Control.Distributed.MPI: instance Control.Distributed.MPI.Buffer Data.ByteString.Internal.ByteString
+ Control.Distributed.MPI: requestGetStatus :: Request -> IO (Maybe Status)
+ Control.Distributed.MPI: requestGetStatus_ :: Request -> IO Bool
+ Control.Distributed.MPI: type family Elem buf;
+ Control.Distributed.MPI: withPtrLenType :: Buffer buf => buf -> (Ptr (Elem buf) -> Count -> Datatype -> IO a) -> IO a
+ Control.Distributed.MPI: }
+ Control.Distributed.MPI.Simple: Comm :: (ForeignPtr (Comm)) -> Comm
+ Control.Distributed.MPI.Simple: Count :: CInt -> Count
+ Control.Distributed.MPI.Simple: Rank :: CInt -> Rank
+ Control.Distributed.MPI.Simple: Status :: !Rank -> !Tag -> Status
+ Control.Distributed.MPI.Simple: Tag :: CInt -> Tag
+ Control.Distributed.MPI.Simple: [msgRank] :: Status -> !Rank
+ Control.Distributed.MPI.Simple: [msgTag] :: Status -> !Tag
+ Control.Distributed.MPI.Simple: abort :: (Comm) -> (Int) -> IO ()
+ Control.Distributed.MPI.Simple: anySource :: (Rank)
+ Control.Distributed.MPI.Simple: anyTag :: (Tag)
+ Control.Distributed.MPI.Simple: barrier :: (Comm) -> IO ()
+ Control.Distributed.MPI.Simple: bcastRecv :: Store a => Rank -> Comm -> IO a
+ Control.Distributed.MPI.Simple: bcastSend :: Store a => a -> Rank -> Comm -> IO ()
+ Control.Distributed.MPI.Simple: commRank :: (Comm) -> IO ((Rank))
+ Control.Distributed.MPI.Simple: commSelf :: (Comm)
+ Control.Distributed.MPI.Simple: commSize :: (Comm) -> IO ((Rank))
+ Control.Distributed.MPI.Simple: commWorld :: (Comm)
+ Control.Distributed.MPI.Simple: data Request a
+ Control.Distributed.MPI.Simple: data Status
+ Control.Distributed.MPI.Simple: fromCount :: Integral i => Count -> i
+ Control.Distributed.MPI.Simple: fromTag :: Enum e => Tag -> e
+ Control.Distributed.MPI.Simple: ibarrier :: Comm -> IO (Request ())
+ Control.Distributed.MPI.Simple: instance GHC.Classes.Eq Control.Distributed.MPI.Simple.Status
+ Control.Distributed.MPI.Simple: instance GHC.Classes.Ord Control.Distributed.MPI.Simple.Status
+ Control.Distributed.MPI.Simple: instance GHC.Read.Read Control.Distributed.MPI.Simple.Status
+ Control.Distributed.MPI.Simple: instance GHC.Show.Show Control.Distributed.MPI.Simple.Status
+ Control.Distributed.MPI.Simple: irecv :: Store a => Rank -> Tag -> Comm -> IO (Request a)
+ Control.Distributed.MPI.Simple: isend :: Store a => a -> Rank -> Tag -> Comm -> IO (Request ())
+ Control.Distributed.MPI.Simple: mainMPI :: IO () -> IO ()
+ Control.Distributed.MPI.Simple: newtype Comm
+ Control.Distributed.MPI.Simple: newtype Count
+ Control.Distributed.MPI.Simple: newtype Rank
+ Control.Distributed.MPI.Simple: newtype Tag
+ Control.Distributed.MPI.Simple: recv :: Store a => Rank -> Tag -> Comm -> IO (Status, a)
+ Control.Distributed.MPI.Simple: recv_ :: Store a => Rank -> Tag -> Comm -> IO a
+ Control.Distributed.MPI.Simple: rootRank :: Rank
+ Control.Distributed.MPI.Simple: send :: Store a => a -> Rank -> Tag -> Comm -> IO ()
+ Control.Distributed.MPI.Simple: sendrecv :: (Store a, Store b) => a -> Rank -> Tag -> Rank -> Tag -> Comm -> IO (Status, b)
+ Control.Distributed.MPI.Simple: sendrecv_ :: (Store a, Store b) => a -> Rank -> Tag -> Rank -> Tag -> Comm -> IO b
+ Control.Distributed.MPI.Simple: test :: Request a -> IO (Maybe (Status, a))
+ Control.Distributed.MPI.Simple: test_ :: Request a -> IO (Maybe a)
+ Control.Distributed.MPI.Simple: toCount :: Integral i => i -> Count
+ Control.Distributed.MPI.Simple: toTag :: Enum e => e -> Tag
+ Control.Distributed.MPI.Simple: unitTag :: Tag
+ Control.Distributed.MPI.Simple: wait :: Request a -> IO (Status, a)
+ Control.Distributed.MPI.Simple: wait_ :: Request a -> IO a
- Control.Distributed.MPI: allgather :: forall a b p q. (Pointer p, Pointer q, Storable a, HasDatatype a, Storable b, HasDatatype b) => p a -> Count -> q b -> Count -> Comm -> IO ()
+ Control.Distributed.MPI: allgather :: (Buffer sb, Buffer rb) => sb -> rb -> Comm -> IO ()
- Control.Distributed.MPI: allreduce :: forall a p q. (Pointer p, Pointer q, Storable a, HasDatatype a) => p a -> q a -> Count -> Op -> Comm -> IO ()
+ Control.Distributed.MPI: allreduce :: (Buffer sb, Buffer rb) => sb -> rb -> Op -> Comm -> IO ()
- Control.Distributed.MPI: alltoall :: forall a b p q. (Pointer p, Pointer q, Storable a, HasDatatype a, Storable b, HasDatatype b) => p a -> Count -> q b -> Count -> Comm -> IO ()
+ Control.Distributed.MPI: alltoall :: (Buffer sb, Buffer rb) => sb -> rb -> Comm -> IO ()
- Control.Distributed.MPI: bcast :: forall a p. (Pointer p, Storable a, HasDatatype a) => p a -> Count -> Rank -> Comm -> IO ()
+ Control.Distributed.MPI: bcast :: Buffer b => b -> Rank -> Comm -> IO ()
- Control.Distributed.MPI: exscan :: forall a p q. (Pointer p, Pointer q, Storable a, HasDatatype a) => p a -> q a -> Count -> Op -> Comm -> IO ()
+ Control.Distributed.MPI: exscan :: (Buffer sb, Buffer rb) => sb -> rb -> Op -> Comm -> IO ()
- Control.Distributed.MPI: gather :: forall a b p q. (Pointer p, Pointer q, Storable a, HasDatatype a, Storable b, HasDatatype b) => p a -> Count -> q b -> Count -> Rank -> Comm -> IO ()
+ Control.Distributed.MPI: gather :: (Buffer sb, Buffer rb) => sb -> rb -> Rank -> Comm -> IO ()
- Control.Distributed.MPI: getCount :: (Status) -> (Datatype) -> IO ((Int))
+ Control.Distributed.MPI: getCount :: (Status) -> (Datatype) -> IO ((Count))
- Control.Distributed.MPI: iallgather :: forall a b p q. (Pointer p, Pointer q, Storable a, HasDatatype a, Storable b, HasDatatype b) => p a -> Count -> q b -> Count -> Comm -> IO Request
+ Control.Distributed.MPI: iallgather :: (Buffer sb, Buffer rb) => sb -> rb -> Comm -> IO Request
- Control.Distributed.MPI: iallreduce :: forall a p q. (Pointer p, Pointer q, Storable a, HasDatatype a) => p a -> q a -> Count -> Op -> Comm -> IO Request
+ Control.Distributed.MPI: iallreduce :: (Buffer sb, Buffer rb) => sb -> rb -> Op -> Comm -> IO Request
- Control.Distributed.MPI: ialltoall :: forall a b p q. (Pointer p, Pointer q, Storable a, HasDatatype a, Storable b, HasDatatype b) => p a -> Count -> q b -> Count -> Comm -> IO Request
+ Control.Distributed.MPI: ialltoall :: (Buffer sb, Buffer rb) => sb -> rb -> Comm -> IO Request
- Control.Distributed.MPI: ibcast :: forall a p. (Pointer p, Storable a, HasDatatype a) => p a -> Count -> Rank -> Comm -> IO Request
+ Control.Distributed.MPI: ibcast :: Buffer b => b -> Rank -> Comm -> IO Request
- Control.Distributed.MPI: iexscan :: forall a p q. (Pointer p, Pointer q, Storable a, HasDatatype a) => p a -> q a -> Count -> Op -> Comm -> IO Request
+ Control.Distributed.MPI: iexscan :: (Buffer sb, Buffer rb) => sb -> rb -> Op -> Comm -> IO Request
- Control.Distributed.MPI: igather :: forall a b p q. (Pointer p, Pointer q, Storable a, HasDatatype a, Storable b, HasDatatype b) => p a -> Count -> q b -> Count -> Rank -> Comm -> IO Request
+ Control.Distributed.MPI: igather :: (Buffer rb, Buffer sb) => sb -> rb -> Rank -> Comm -> IO Request
- Control.Distributed.MPI: irecv :: forall a p. (Pointer p, Storable a, HasDatatype a) => p a -> Count -> Rank -> Tag -> Comm -> IO Request
+ Control.Distributed.MPI: irecv :: Buffer rb => rb -> Rank -> Tag -> Comm -> IO Request
- Control.Distributed.MPI: ireduce :: forall a p q. (Pointer p, Pointer q, Storable a, HasDatatype a) => p a -> q a -> Count -> Op -> Rank -> Comm -> IO Request
+ Control.Distributed.MPI: ireduce :: (Buffer sb, Buffer rb) => sb -> rb -> Op -> Rank -> Comm -> IO Request
- Control.Distributed.MPI: iscan :: forall a p q. (Pointer p, Pointer q, Storable a, HasDatatype a) => p a -> q a -> Count -> Op -> Comm -> IO Request
+ Control.Distributed.MPI: iscan :: (Buffer sb, Buffer rb) => sb -> rb -> Op -> Comm -> IO Request
- Control.Distributed.MPI: iscatter :: forall a b p q. (Pointer p, Pointer q, Storable a, HasDatatype a, Storable b, HasDatatype b) => p a -> Count -> q b -> Count -> Rank -> Comm -> IO Request
+ Control.Distributed.MPI: iscatter :: (Buffer sb, Buffer rb) => sb -> rb -> Rank -> Comm -> IO Request
- Control.Distributed.MPI: isend :: forall a p. (Pointer p, Storable a, HasDatatype a) => p a -> Count -> Rank -> Tag -> Comm -> IO Request
+ Control.Distributed.MPI: isend :: Buffer sb => sb -> Rank -> Tag -> Comm -> IO Request
- Control.Distributed.MPI: recv :: forall a p. (Pointer p, Storable a, HasDatatype a) => p a -> Count -> Rank -> Tag -> Comm -> IO Status
+ Control.Distributed.MPI: recv :: Buffer rb => rb -> Rank -> Tag -> Comm -> IO Status
- Control.Distributed.MPI: recv_ :: forall a p. (Pointer p, Storable a, HasDatatype a) => p a -> Count -> Rank -> Tag -> Comm -> IO ()
+ Control.Distributed.MPI: recv_ :: Buffer rb => rb -> Rank -> Tag -> Comm -> IO ()
- Control.Distributed.MPI: reduce :: forall a p q. (Pointer p, Pointer q, Storable a, HasDatatype a) => p a -> q a -> Count -> Op -> Rank -> Comm -> IO ()
+ Control.Distributed.MPI: reduce :: (Buffer sb, Buffer rb) => sb -> rb -> Op -> Rank -> Comm -> IO ()
- Control.Distributed.MPI: scan :: forall a p q. (Pointer p, Pointer q, Storable a, HasDatatype a) => p a -> q a -> Count -> Op -> Comm -> IO ()
+ Control.Distributed.MPI: scan :: (Buffer sb, Buffer rb) => sb -> rb -> Op -> Comm -> IO ()
- Control.Distributed.MPI: scatter :: forall a b p q. (Pointer p, Pointer q, Storable a, HasDatatype a, Storable b, HasDatatype b) => p a -> Count -> q b -> Count -> Rank -> Comm -> IO ()
+ Control.Distributed.MPI: scatter :: (Buffer sb, Buffer rb) => sb -> rb -> Rank -> Comm -> IO ()
- Control.Distributed.MPI: send :: forall a p. (Pointer p, Storable a, HasDatatype a) => p a -> Count -> Rank -> Tag -> Comm -> IO ()
+ Control.Distributed.MPI: send :: Buffer sb => sb -> Rank -> Tag -> Comm -> IO ()
- Control.Distributed.MPI: sendrecv :: forall a b p q. (Pointer p, Pointer q, Storable a, HasDatatype a, Storable b, HasDatatype b) => p a -> Count -> Rank -> Tag -> q b -> Count -> Rank -> Tag -> Comm -> IO Status
+ Control.Distributed.MPI: sendrecv :: (Buffer sb, Buffer rb) => sb -> Rank -> Tag -> rb -> Rank -> Tag -> Comm -> IO Status
- Control.Distributed.MPI: sendrecv_ :: forall a b p q. (Pointer p, Pointer q, Storable a, HasDatatype a, Storable b, HasDatatype b) => p a -> Count -> Rank -> Tag -> q a -> Count -> Rank -> Tag -> Comm -> IO ()
+ Control.Distributed.MPI: sendrecv_ :: (Buffer sb, Buffer rb) => sb -> Rank -> Tag -> rb -> Rank -> Tag -> Comm -> IO ()

Files

README.md view
@@ -2,13 +2,17 @@  [MPI](https://www.mpi-forum.org) bindings for Haskell -[![CircleCI](https://circleci.com/gh/eschnett/mpi-hs.svg?style=svg)](https://circleci.com/gh/eschnett/mpi-hs)+* [<img alt="Github" src="share/GitHub_Logo.png" height="25"+  align="middle">](https://github.com/eschnett/mpi-hs)+* [[Hackage]](http://hackage.haskell.org/package/mpi-hs) Haskell+  package and documentation+* [![CircleCI](https://circleci.com/gh/eschnett/mpi-hs.svg?style=svg)](https://circleci.com/gh/eschnett/mpi-hs)    ## Overview -MPI (the _Message Passing Interface_) is widely used standard for+MPI (the Message Passing Interface) is widely used standard for distributed-memory programming on HPC (High Performance Computing) systems. MPI allows exchanging data (_messages_) between programs running in parallel. There are several high-quality open source MPI@@ -61,3 +65,83 @@      putStrLn $ "This is process " ++ show rank ++ " of " ++ show size      MPI.finalize ```++++## Installing++`mpi-hs` requires an external MPI library to be available on the+system. How to install such a library is beyond the scope of these+instructions.++<!---+(It is important that the MPI library's include files, libraries, and+executables are installed consistently. A common source of problems is+that there are several MPI implementations available on a system, and+that the default include file `mpi.h`, the library `libmpi.a`, and/or+the executable `mpirun` are provided by different implementations.+This will lead to various problems, often segfaults, since neither the+operating system nor these libraries provide any protection against+such a mismatch.)+-->++In many cases, the MPI library will be installed in `/usr/include`,+`/usr/lib`, and `/usr/bin`, respectively. In this case, no further+configuration is necessary, and `mpi-hs` will build out of the box+with `stack build`.++On Ubuntu, one MPI package is `openmpi-dev`. It installs into+`/usr/lib/openmpi/include`, `/usr/lib/openmpi/lib`, and `/usr/bin/`.+You need to ensure that these settings are present in `stack.yaml`:++```yaml+extra-include-dirs:+  - /usr/lib/openmpi/include+extra-lib-dirs:+  - /usr/lib/openmpi/lib+```++On MacOS, one MPI package is the [MacPorts](https://www.macports.org)+package `openmpi`. It installs into `/opt/local/include/openmpi-mp`,+`/opt/local/lib/openmpi-mp`, and `/opt/local/bin`. You need to ensure+that these settings are present in `stack.yaml`:++```yaml+extra-include-dirs:+  - /opt/local/include/openmpi-mp+extra-lib-dirs:+  - /opt/local/lib/openmpi-mp+```++Both these settings are there by default.++### Testing the MPI installation++To test your MPI installation independently of using Haskell, copy the+example MPI C code into a file `mpi-example.c`, and run these commands:++```sh+cc -I/usr/lib/openmpi/include -c mpi-example.c+cc -o mpi-example mpi-example.o -L/usr/lib/openmpi/lib -lmpi+mpirun -np 3 ./mpi-example+```++All three commands must complete without error, and the last command+must output something like++```+This is process 0 of 3+This is process 1 of 3+This is process 2 of 3+```++where the order in which the lines are printed can be random. (The+output might even be jumbled, i.e. the characters of the three lines+might be mixed up.)++If these commands do not work, then this needs to be corrected before+`mpi-hs` can work. If additional compiler options or libraries are+needed, then these need to be added to the `stack.yaml` configuration+file (for include and library paths; see `extra-include-dirs` and+`extra-lib-dirs` there) or the `package.yaml` configuration file (for+additional libraries; see `extra-libraries` there).
lib/Control/Distributed/MPI.chs view
@@ -1,6 +1,7 @@ {-# LANGUAGE AllowAmbiguousTypes #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE ForeignFunctionInterface #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE MultiWayIf #-}@@ -8,6 +9,7 @@ {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE StandaloneDeriving #-} {-# LANGUAGE TypeApplications #-}+{-# LANGUAGE TypeFamilies #-} {-# OPTIONS_GHC -Wno-type-defaults #-}  #include <mpi.h>@@ -81,10 +83,13 @@ --   functions could be exposed as well when needed.)  module Control.Distributed.MPI-  ( -- * Types, and associated functions constants+  ( -- * Types, and associated functions and constants +    -- ** Communication buffers+    Buffer(..)+     -- ** Communicators-    Comm(..)+  , Comm(..)   , ComparisonResult(..)   , commCompare   , commRank@@ -101,7 +106,6 @@      -- ** Datatypes   , Datatype(..)-  , Pointer(..)   -- TODO: use a module for this namespace   , datatypeNull   , datatypeByte@@ -200,6 +204,8 @@   , iprobe_   , irecv   , isend+  , requestGetStatus+  , requestGetStatus_   , test   , test_ @@ -235,7 +241,10 @@ import Prelude hiding (fromEnum, fst, init, toEnum) import qualified Prelude -import Control.Monad (liftM)+import Control.Exception+import Control.Monad+import qualified Data.ByteString as B+import qualified Data.ByteString.Unsafe as B import Data.Coerce import Data.IORef import Data.Ix@@ -304,22 +313,36 @@   --- | A generic pointer-like type that supports converting to a 'Ptr'.--- This class describes the buffers used to send and receive messages.-class Pointer p where-  withPtr :: Storable a => p a -> (Ptr a -> IO b) -> IO b+-- | A generic pointer-like type that supports converting to a 'Ptr',+-- and which knows the type and number of its elements. This class+-- describes the MPI buffers used to send and receive messages.+class Buffer buf where+  type Elem buf+  withPtrLenType :: buf -> (Ptr (Elem buf) -> Count -> Datatype -> IO a) -> IO a -instance Pointer Ptr where-  withPtr p f = f p+instance (Storable a, HasDatatype a, Integral i) => Buffer (Ptr a, i) where+  type Elem (Ptr a, i) = a+  withPtrLenType (ptr, len) f = f ptr (toCount len) (getDatatype @a) -instance Pointer ForeignPtr where-  withPtr = withForeignPtr+instance (Storable a, HasDatatype a, Integral i) => Buffer (ForeignPtr a, i)+    where+  type Elem (ForeignPtr a, i) = a+  withPtrLenType (fptr, len) f =+    withForeignPtr fptr $ \ptr -> f ptr (toCount len) (getDatatype @a) -instance Pointer StablePtr where-  withPtr p f = f (castPtr (castStablePtrToPtr p))+instance (Storable a, HasDatatype a, Integral i) => Buffer (StablePtr a, i)+    where+  type Elem (StablePtr a, i) = a+  withPtrLenType (ptr, len) f =+    f (castPtr (castStablePtrToPtr ptr)) (toCount len) (getDatatype @a) +instance Buffer B.ByteString where+  type Elem B.ByteString = CChar+  withPtrLenType bs f =+    B.unsafeUseAsCStringLen bs $ \(ptr, len) -> f ptr (toCount len) datatypeByte  + -- | An MPI communicator, wrapping @MPI_Comm@. A communicator defines -- an independent communication channel between a group of processes. -- Communicators need to be explicitly created and freed by the MPI@@ -594,18 +617,18 @@ -- | A type class mapping Haskell types to MPI datatypes. This is used -- to automatically determine the MPI datatype for communication -- buffers.-class HasDatatype a where datatype :: Datatype-instance HasDatatype CChar where datatype = datatypeChar-instance HasDatatype CDouble where datatype = datatypeDouble-instance HasDatatype CFloat where datatype = datatypeFloat-instance HasDatatype CInt where datatype = datatypeInt-instance HasDatatype CLLong where datatype = datatypeLongLongInt-instance HasDatatype CLong where datatype = datatypeLong-instance HasDatatype CShort where datatype = datatypeShort-instance HasDatatype CUChar where datatype = datatypeUnsignedChar-instance HasDatatype CUInt where datatype = datatypeUnsigned-instance HasDatatype CULong where datatype = datatypeUnsignedLong-instance HasDatatype CUShort where datatype = datatypeUnsignedShort+class HasDatatype a where getDatatype :: Datatype+instance HasDatatype CChar where getDatatype = datatypeChar+instance HasDatatype CDouble where getDatatype = datatypeDouble+instance HasDatatype CFloat where getDatatype = datatypeFloat+instance HasDatatype CInt where getDatatype = datatypeInt+instance HasDatatype CLLong where getDatatype = datatypeLongLongInt+instance HasDatatype CLong where getDatatype = datatypeLong+instance HasDatatype CShort where getDatatype = datatypeShort+instance HasDatatype CUChar where getDatatype = datatypeUnsignedChar+instance HasDatatype CUInt where getDatatype = datatypeUnsigned+instance HasDatatype CULong where getDatatype = datatypeUnsignedLong+instance HasDatatype CUShort where getDatatype = datatypeUnsignedShort  -- instance Coercible Int CChar => HasDatatype Int where --   datatype = datatype @CChar@@ -732,13 +755,13 @@ {#fun pure mpihs_get_sum as opSum {+} -> `Op'#}  instance HasDatatype a => HasDatatype (Monoid.Product a) where-  datatype = datatype @a+  getDatatype = getDatatype @a instance HasDatatype a => HasDatatype (Monoid.Sum a) where-  datatype = datatype @a+  getDatatype = getDatatype @a instance HasDatatype a => HasDatatype (Semigroup.Max a) where-  datatype = datatype @a+  getDatatype = getDatatype @a instance HasDatatype a => HasDatatype (Semigroup.Min a) where-  datatype = datatype @a+  getDatatype = getDatatype @a  -- class (Monoid a, HasDatatype a) => HasOp a where op :: Op -- instance (Num a, HasDatatype a) => HasOp (Monoid.Product a) where@@ -807,22 +830,16 @@ -- | Gather data from all processes and broadcast the result -- (collective, -- @[MPI_Allgather](https://www.open-mpi.org/doc/current/man3/MPI_Allgather.3.php)@).--- The MPI datatypes are determined automatically from the buffer--- pointer types.-allgather :: forall a b p q.-             ( Pointer p, Pointer q-             , Storable a, HasDatatype a, Storable b, HasDatatype b)-          => p a                -- ^ Source buffer-          -> Count              -- ^ Number of source elements-          -> q b                -- ^ Destination buffer-          -> Count              -- ^ Number of destination elements+allgather :: (Buffer sb, Buffer rb)+          => sb                 -- ^ Source buffer+          -> rb                 -- ^ Destination buffer           -> Comm               -- ^ Communicator           -> IO ()-allgather sendbuf sendcount recvbuf recvcount comm =-  withPtr sendbuf $ \sendbuf' ->-  withPtr recvbuf $ \recvbuf' ->-  allgatherTyped (castPtr sendbuf') sendcount (datatype @a)-                 (castPtr recvbuf') recvcount (datatype @b)+allgather sendbuf recvbuf comm =+  withPtrLenType sendbuf $ \sendptr sendcount senddatatype ->+  withPtrLenType recvbuf $ \recvptr recvcount recvdatatype ->+  allgatherTyped (castPtr sendptr) sendcount senddatatype+                 (castPtr recvptr) recvcount recvdatatype                  comm  {#fun Allreduce as allreduceTyped@@ -839,18 +856,17 @@ -- @[MPI_Allreduce](https://www.open-mpi.org/doc/current/man3/MPI_Allreduce.3.php)@). -- The MPI datatype is determined automatically from the buffer -- pointer types.-allreduce :: forall a p q.-             ( Pointer p, Pointer q, Storable a, HasDatatype a)-          => p a                -- ^ Source buffer-          -> q a                -- ^ Destination buffer-          -> Count              -- ^ Number of elements+allreduce :: (Buffer sb, Buffer rb)+          => sb                 -- ^ Source buffer+          -> rb                 -- ^ Destination buffer           -> Op                 -- ^ Reduction operation           -> Comm               -- ^ Communicator           -> IO ()-allreduce sendbuf recvbuf count op comm =-  withPtr sendbuf $ \sendbuf' ->-  withPtr recvbuf $ \recvbuf' ->-  allreduceTyped (castPtr sendbuf') (castPtr recvbuf') count (datatype @a) op+allreduce sendbuf recvbuf op comm =+  withPtrLenType sendbuf $ \sendptr sendcount senddatatype ->+  withPtrLenType recvbuf $ \recvptr recvcount recvdatatype ->+  assert (sendcount == recvcount && senddatatype == recvdatatype) $+  allreduceTyped (castPtr sendptr) (castPtr recvptr) sendcount senddatatype op                  comm  {#fun Alltoall as alltoallTyped@@ -867,20 +883,16 @@ -- @[MPI_Alltoall](https://www.open-mpi.org/doc/current/man3/MPI_Alltoall.php)@). -- The MPI datatypes are determined automatically from the buffer -- pointer types.-alltoall :: forall a b p q.-            ( Pointer p, Pointer q-            , Storable a, HasDatatype a, Storable b, HasDatatype b)-         => p a                 -- ^ Source buffer-         -> Count               -- ^ Number of source elements-         -> q b                 -- ^ Destination buffer-         -> Count               -- ^ Number of destination elements+alltoall :: (Buffer sb, Buffer rb)+         => sb                  -- ^ Source buffer+         -> rb                  -- ^ Destination buffer          -> Comm                -- ^ Communicator          -> IO ()-alltoall sendbuf sendcount recvbuf recvcount comm =-  withPtr sendbuf $ \sendbuf' ->-  withPtr recvbuf $ \recvbuf' ->-  alltoallTyped (castPtr sendbuf') sendcount (datatype @a)-                (castPtr recvbuf') recvcount (datatype @b)+alltoall sendbuf recvbuf comm =+  withPtrLenType sendbuf $ \sendptr sendcount senddatatype ->+  withPtrLenType recvbuf $ \recvptr recvcount recvdatatype ->+  alltoallTyped (castPtr sendptr) sendcount senddatatype+                (castPtr recvptr) recvcount recvdatatype                 comm  -- | Barrier (collective,@@ -901,16 +913,15 @@ -- @[MPI_Bcast](https://www.open-mpi.org/doc/current/man3/MPI_Bcast.3.php)@). -- The MPI datatype is determined automatically from the buffer -- pointer type.-bcast :: forall a p. (Pointer p, Storable a, HasDatatype a)-      => p a -- ^ Buffer pointer (read on the root process, written on-             -- all other processes)-      -> Count                  -- ^ Number of elements+bcast :: Buffer b+      => b -- ^ Buffer (read on the root process, written on all other+           -- processes)       -> Rank                   -- ^ Root rank (sending process)       -> Comm                   -- ^ Communicator       -> IO ()-bcast buf count root comm =-  withPtr buf $ \buf' ->-  bcastTyped (castPtr buf') count (datatype @a) root comm+bcast buf root comm =+  withPtrLenType buf $ \ptr count datatype ->+  bcastTyped (castPtr ptr) count datatype root comm  -- | Compare two communicators -- (@[MPI_Comm_compare](https://www.open-mpi.org/doc/current/man3/MPI_Comm_compare.3.php)@).@@ -954,18 +965,17 @@ -- -- The MPI datatype is determined automatically from the buffer -- pointer type.-exscan :: forall a p q.-          ( Pointer p, Pointer q, Storable a, HasDatatype a)-       => p a                   -- ^ Source buffer-       -> q a                   -- ^ Destination buffer-       -> Count                 -- ^ Number of elements+exscan :: (Buffer sb, Buffer rb)+       => sb                    -- ^ Source buffer+       -> rb                    -- ^ Destination buffer        -> Op                    -- ^ Reduction operation        -> Comm                  -- ^ Communicator        -> IO ()-exscan sendbuf recvbuf count op comm =-  withPtr sendbuf $ \sendbuf' ->-  withPtr recvbuf $ \recvbuf' ->-  exscanTyped (castPtr sendbuf') (castPtr recvbuf') count (datatype @a) op comm+exscan sendbuf recvbuf op comm =+  withPtrLenType sendbuf $ \sendptr sendcount senddatatype ->+  withPtrLenType recvbuf $ \recvptr recvcount recvdatatype ->+  assert (sendcount == recvcount && senddatatype == recvdatatype) $+  exscanTyped (castPtr sendptr) (castPtr recvptr) sendcount senddatatype op comm  -- | Finalize (shut down) the MPI library (collective, @[MPI_Finalize](https://www.open-mpi.org/doc/current/man3/MPI_Finalize.3.php)@). {#fun Finalize as ^ {} -> `()' return*-#}@@ -989,22 +999,17 @@ -- @[MPI_Gather](https://www.open-mpi.org/doc/current/man3/MPI_Gather.3.php)@). -- The MPI datatypes are determined automatically from the buffer -- pointer types.-gather :: forall a b p q.-          ( Pointer p, Pointer q-          , Storable a, HasDatatype a, Storable b, HasDatatype b)-       => p a                   -- ^ Source buffer-       -> Count                 -- ^ Number of source elements-       -> q b  -- ^ Destination buffer (only used on the root process)-       -> Count -- ^ Number of destination elements (only used on the-                -- root process)+gather :: (Buffer sb, Buffer rb)+       => sb                    -- ^ Source buffer+       -> rb   -- ^ Destination buffer (only used on the root process)        -> Rank                  -- ^ Root rank        -> Comm                  -- ^ Communicator        -> IO ()-gather sendbuf sendcount recvbuf recvcount root comm =-  withPtr sendbuf $ \sendbuf' ->-  withPtr recvbuf $ \recvbuf' ->-  gatherTyped (castPtr sendbuf') sendcount (datatype @a)-              (castPtr recvbuf') recvcount (datatype @b)+gather sendbuf recvbuf root comm =+  withPtrLenType sendbuf $ \sendptr sendcount senddatatype ->+  withPtrLenType recvbuf $ \recvptr recvcount recvdatatype ->+  gatherTyped (castPtr sendptr) sendcount senddatatype+              (castPtr recvptr) recvcount recvdatatype               root comm  -- | Get the size of a message, in terms of objects of type 'Datatype'@@ -1014,7 +1019,7 @@ {#fun unsafe Get_count as ^     { withStatus* `Status'      -- ^ Message status     , withDatatype* %`Datatype' -- ^ MPI datatype-    , alloca- `Int' peekInt*+    , alloca- `Count' peekCoerce*     } -> `()' return*-#}  -- | Get the number of elements in message, in terms of sub-object of@@ -1096,20 +1101,16 @@ -- The request must be freed by calling 'test', 'wait', or similar. -- The MPI datatypes are determined automatically from the buffer -- pointer types.-iallgather :: forall a b p q.-              ( Pointer p, Pointer q-              , Storable a, HasDatatype a, Storable b, HasDatatype b)-           => p a               -- ^ Source buffer-           -> Count             -- ^ Number of source elements-           -> q b               -- ^ Destination buffer-           -> Count             -- ^ Number of destination elements+iallgather :: (Buffer sb, Buffer rb)+           => sb                -- ^ Source buffer+           -> rb                -- ^ Destination buffer            -> Comm              -- ^ Communicator            -> IO Request        -- ^ Communication request-iallgather sendbuf sendcount recvbuf recvcount comm =-  withPtr sendbuf $ \sendbuf' ->-  withPtr recvbuf $ \recvbuf' ->-  iallgatherTyped (castPtr sendbuf') sendcount (datatype @a)-                  (castPtr recvbuf') recvcount (datatype @b)+iallgather sendbuf recvbuf comm =+  withPtrLenType sendbuf $ \sendptr sendcount senddatatype ->+  withPtrLenType recvbuf $ \recvptr recvcount recvdatatype ->+  iallgatherTyped (castPtr sendptr) sendcount senddatatype+                  (castPtr recvptr) recvcount recvdatatype                   comm  {#fun Iallreduce as iallreduceTyped@@ -1129,18 +1130,17 @@ -- The request must be freed by calling 'test', 'wait', or similar. -- The MPI datatype is determined automatically from the buffer -- pointer types.-iallreduce :: forall a p q.-              ( Pointer p, Pointer q, Storable a, HasDatatype a)-           => p a               -- ^ Source buffer-           -> q a               -- ^ Destination buffer-           -> Count             -- ^ Number of elements+iallreduce :: (Buffer sb, Buffer rb)+           => sb                -- ^ Source buffer+           -> rb                -- ^ Destination buffer            -> Op                -- ^ Reduction operation            -> Comm              -- ^ Communicator            -> IO Request        -- ^ Communication request-iallreduce sendbuf recvbuf count op comm =-  withPtr sendbuf $ \sendbuf' ->-  withPtr recvbuf $ \recvbuf' ->-  iallreduceTyped (castPtr sendbuf') (castPtr recvbuf') count (datatype @a) op+iallreduce sendbuf recvbuf op comm =+  withPtrLenType sendbuf $ \sendptr sendcount senddatatype ->+  withPtrLenType recvbuf $ \recvptr recvcount recvdatatype ->+  assert (sendcount == recvcount && senddatatype == recvdatatype) $+  iallreduceTyped (castPtr sendptr) (castPtr recvptr) sendcount senddatatype op                   comm  {#fun Ialltoall as ialltoallTyped@@ -1161,20 +1161,16 @@ -- The request must be freed by calling 'test', 'wait', or similar. -- The MPI datatypes are determined automatically from the buffer -- pointer types.-ialltoall :: forall a b p q.-             ( Pointer p, Pointer q-             , Storable a, HasDatatype a, Storable b, HasDatatype b)-          => p a                -- ^ Source buffer-          -> Count              -- ^ Number of source elements-          -> q b                -- ^ Destination buffer-          -> Count              -- ^ Number of destination elements+ialltoall :: (Buffer sb, Buffer rb)+          => sb                 -- ^ Source buffer+          -> rb                 -- ^ Destination buffer           -> Comm               -- ^ Communicator           -> IO Request         -- ^ Communication request-ialltoall sendbuf sendcount recvbuf recvcount comm =-  withPtr sendbuf $ \sendbuf' ->-  withPtr recvbuf $ \recvbuf' ->-  ialltoallTyped (castPtr sendbuf') sendcount (datatype @a)-                 (castPtr recvbuf') recvcount (datatype @b)+ialltoall sendbuf recvbuf comm =+  withPtrLenType sendbuf $ \sendptr sendcount senddatatype ->+  withPtrLenType recvbuf $ \recvptr recvcount recvdatatype ->+  ialltoallTyped (castPtr sendptr) sendcount senddatatype+                 (castPtr recvptr) recvcount recvdatatype                  comm  -- | Start a barrier, and return a handle to the communication request@@ -1202,16 +1198,15 @@ -- The request must be freed by calling 'test', 'wait', or similar. -- The MPI datatype is determined automatically from the buffer -- pointer type.-ibcast :: forall a p. (Pointer p, Storable a, HasDatatype a)-       => p a -- ^ Buffer pointer (read on the root process, written on-              -- all other processes)-       -> Count                 -- ^ Number of elements+ibcast :: Buffer b+       => b      -- ^ Buffer (read on the root process, written on all+                 -- other processes)        -> Rank                  -- ^ Root rank (sending process)        -> Comm                  -- ^ Communicator        -> IO Request            -- ^ Communication request-ibcast buf count root comm =-  withPtr buf $ \buf' ->-  ibcastTyped (castPtr buf') count (datatype @a) root comm+ibcast buf root comm =+  withPtrLenType buf $ \ptr count datatype->+  ibcastTyped (castPtr ptr) count datatype root comm  {#fun Iexscan as iexscanTyped     { id `Ptr ()'@@ -1236,18 +1231,18 @@ -- The request must be freed by calling 'test', 'wait', or similar. -- The MPI datatype is determined automatically from the buffer -- pointer type.-iexscan :: forall a p q.-           ( Pointer p, Pointer q, Storable a, HasDatatype a)-        => p a                  -- ^ Source buffer-        -> q a                  -- ^ Destination buffer-        -> Count                -- ^ Number of elements+iexscan :: (Buffer sb, Buffer rb)+        => sb                   -- ^ Source buffer+        -> rb                   -- ^ Destination buffer         -> Op                   -- ^ Reduction operation         -> Comm                 -- ^ Communicator         -> IO Request           -- ^ Communication request-iexscan sendbuf recvbuf count op comm =-  withPtr sendbuf $ \sendbuf' ->-  withPtr recvbuf $ \recvbuf' ->-  iexscanTyped (castPtr sendbuf') (castPtr recvbuf') count (datatype @a) op comm+iexscan sendbuf recvbuf op comm =+  withPtrLenType sendbuf $ \sendptr sendcount senddatatype ->+  withPtrLenType recvbuf $ \recvptr recvcount recvdatatype ->+  assert (sendcount == recvcount && senddatatype == recvdatatype) $+  iexscanTyped (castPtr sendptr) (castPtr recvptr) sendcount senddatatype op+               comm  {#fun Igather as igatherTyped     { id `Ptr ()'@@ -1268,23 +1263,17 @@ -- The request must be freed by calling 'test', 'wait', or similar. -- The MPI datatypes are determined automatically from the buffer -- pointer types.-igather :: forall a b p q.-           ( Pointer p, Pointer q-           , Storable a, HasDatatype a, Storable b, HasDatatype b)-        => p a                  -- ^ Source buffer-        -> Count                -- ^ Number of source elements-        -> q b                  -- ^ Destination buffer (relevant only-                                -- on the root process)-        -> Count                -- ^ Number of destination elements-                                -- (relevant only on the root process)+igather :: (Buffer rb, Buffer sb)+        => sb                   -- ^ Source buffer+        -> rb -- ^ Destination buffer (relevant only on the root process)         -> Rank                 -- ^ Root rank         -> Comm                 -- ^ Communicator         -> IO Request           -- ^ Communication request-igather sendbuf sendcount recvbuf recvcount root comm =-  withPtr sendbuf $ \sendbuf' ->-  withPtr recvbuf $ \recvbuf' ->-  igatherTyped (castPtr sendbuf') sendcount (datatype @a)-               (castPtr recvbuf') recvcount (datatype @b)+igather sendbuf recvbuf root comm =+  withPtrLenType sendbuf $ \sendptr sendcount senddatatype ->+  withPtrLenType recvbuf $ \recvptr recvcount recvdatatype ->+  igatherTyped (castPtr sendptr) sendcount senddatatype+               (castPtr recvptr) recvcount recvdatatype                root comm  -- | Return whether the MPI library has been initialized@@ -1373,16 +1362,15 @@ -- The request must be freed by calling 'test', 'wait', or similar. -- The MPI datatype is determined automatically from the buffer -- pointer type.-irecv :: forall a p. (Pointer p, Storable a, HasDatatype a)-      => p a                    -- ^ Receive buffer-      -> Count                  -- ^ Number of elements to receive+irecv :: Buffer rb+      => rb                     -- ^ Receive buffer       -> Rank                   -- ^ Source rank (may be 'anySource')       -> Tag                    -- ^ Message tag (may be 'anyTag')       -> Comm                   -- ^ Communicator       -> IO Request             -- ^ Communication request-irecv recvbuf recvcount recvrank recvtag comm =-  withPtr recvbuf $ \recvbuf' ->-  irecvTyped (castPtr recvbuf') recvcount (datatype @a) recvrank recvtag comm+irecv recvbuf recvrank recvtag comm =+  withPtrLenType recvbuf $ \recvptr recvcount recvdatatype ->+  irecvTyped (castPtr recvptr) recvcount recvdatatype recvrank recvtag comm  {#fun Ireduce as ireduceTyped     { id `Ptr ()'@@ -1401,20 +1389,19 @@ -- The result is only available on the root process. The request must -- be freed by calling 'test', 'wait', or similar. The MPI datatypes -- are determined automatically from the buffer pointer types.-ireduce :: forall a p q.-           ( Pointer p, Pointer q, Storable a, HasDatatype a)-        => p a                  -- ^ Source buffer-        -> q a                  -- ^ Destination buffer-        -> Count                -- ^ Number of elements+ireduce :: (Buffer sb, Buffer rb)+        => sb                   -- ^ Source buffer+        -> rb                   -- ^ Destination buffer         -> Op                   -- ^ Reduction operation         -> Rank                 -- ^ Root rank         -> Comm                 -- ^ Communicator         -> IO Request           -- ^ Communication request-ireduce sendbuf recvbuf count op rank comm =-  withPtr sendbuf $ \sendbuf' ->-  withPtr recvbuf $ \recvbuf' ->-  ireduceTyped (castPtr sendbuf') (castPtr recvbuf') count (datatype @a) op rank-               comm+ireduce sendbuf recvbuf op rank comm =+  withPtrLenType sendbuf $ \sendptr sendcount senddatatype ->+  withPtrLenType recvbuf $ \recvptr recvcount recvdatatype ->+  assert (sendcount == recvcount && senddatatype == recvdatatype) $+  ireduceTyped (castPtr sendptr) (castPtr recvptr) sendcount senddatatype op+               rank comm  {#fun Iscan as iscanTyped     { id `Ptr ()'@@ -1434,18 +1421,17 @@ -- from rank @0@ to rank @r@ (inclusive). The request must be freed by -- calling 'test', 'wait', or similar. The MPI datatype is determined -- automatically from the buffer pointer type.-iscan :: forall a p q.-         ( Pointer p, Pointer q, Storable a, HasDatatype a)-      => p a                    -- ^ Source buffer-      -> q a                    -- ^ Destination buffer-      -> Count                  -- ^ Number of elements+iscan :: (Buffer sb, Buffer rb)+      => sb                     -- ^ Source buffer+      -> rb                     -- ^ Destination buffer       -> Op                     -- ^ Reduction operation       -> Comm                   -- ^ Communicator       -> IO Request             -- ^ Communication request-iscan sendbuf recvbuf count op comm =-  withPtr sendbuf $ \sendbuf' ->-  withPtr recvbuf $ \recvbuf' ->-  iscanTyped (castPtr sendbuf') (castPtr recvbuf') count (datatype @a) op comm+iscan sendbuf recvbuf op comm =+  withPtrLenType sendbuf $ \sendptr sendcount senddatatype ->+  withPtrLenType recvbuf $ \recvptr recvcount recvdatatype ->+  assert (sendcount == recvcount && senddatatype == recvdatatype) $+  iscanTyped (castPtr sendptr) (castPtr recvptr) sendcount senddatatype op comm  {#fun Iscatter as iscatterTyped     { id `Ptr ()'@@ -1466,21 +1452,17 @@ -- The request must be freed by calling 'test', 'wait', or similar. -- The MPI datatypes are determined automatically from the buffer -- pointer types.-iscatter :: forall a b p q.-            ( Pointer p, Pointer q-            , Storable a, HasDatatype a, Storable b, HasDatatype b)-         => p a     -- ^ Source buffer (only used on the root process)-         -> Count -- ^ Number of source elements (only used on the root process)-         -> q b                 -- ^ Destination buffer-         -> Count               -- ^ Number of destination elements+iscatter :: (Buffer sb, Buffer rb)+         => sb      -- ^ Source buffer (only used on the root process)+         -> rb                  -- ^ Destination buffer          -> Rank                -- ^ Root rank          -> Comm                -- ^ Communicator          -> IO Request          -- ^ Communication request-iscatter sendbuf sendcount recvbuf recvcount root comm =-  withPtr sendbuf $ \sendbuf' ->-  withPtr recvbuf $ \recvbuf' ->-  iscatterTyped (castPtr sendbuf') sendcount (datatype @a)-                (castPtr recvbuf') recvcount (datatype @b)+iscatter sendbuf recvbuf root comm =+  withPtrLenType sendbuf $ \sendptr sendcount senddatatype ->+  withPtrLenType recvbuf $ \recvptr recvcount recvdatatype ->+  iscatterTyped (castPtr sendptr) sendcount senddatatype+                (castPtr recvptr) recvcount recvdatatype                 root comm  {#fun Isend as isendTyped@@ -1499,16 +1481,15 @@ -- The request must be freed by calling 'test', 'wait', or similar. -- The MPI datatype is determined automatically from the buffer -- pointer type.-isend :: forall a p. (Pointer p, Storable a, HasDatatype a)-      => p a                    -- ^ Send buffer-      -> Count                  -- ^ Number of elements to send+isend :: Buffer sb+      => sb                     -- ^ Send buffer       -> Rank                   -- ^ Destination rank       -> Tag                    -- ^ Message tag       -> Comm                   -- ^ Communicator       -> IO Request             -- ^ Communication request-isend sendbuf sendcount sendrank sendtag comm =-  withPtr sendbuf $ \sendbuf' ->-  isendTyped (castPtr sendbuf') sendcount (datatype @a) sendrank sendtag comm+isend sendbuf sendrank sendtag comm =+  withPtrLenType sendbuf $ \sendptr sendcount senddatatype ->+  isendTyped (castPtr sendptr) sendcount senddatatype sendrank sendtag comm  -- | Probe (wait) for an incoming message -- (@[MPI_Probe](https://www.open-mpi.org/doc/current/man3/MPI_Probe.3.php)@).@@ -1545,16 +1526,15 @@ -- (@[MPI_Recv](https://www.open-mpi.org/doc/current/man3/MPI_Recv.3.php)@). -- The MPI datatypeis determined automatically from the buffer -- pointer type.-recv :: forall a p. (Pointer p, Storable a, HasDatatype a)-     => p a                     -- ^ Receive buffer-     -> Count                   -- ^ Number of elements to receive+recv :: Buffer rb+     => rb                      -- ^ Receive buffer      -> Rank                    -- ^ Source rank (may be 'anySource')      -> Tag                     -- ^ Message tag (may be 'anyTag')      -> Comm                    -- ^ Communicator      -> IO Status               -- ^ Message status-recv recvbuf recvcount recvrank recvtag comm =-  withPtr recvbuf $ \recvbuf' ->-  recvTyped (castPtr recvbuf') recvcount (datatype @a) recvrank recvtag comm+recv recvbuf recvrank recvtag comm =+  withPtrLenType recvbuf $ \recvptr recvcount recvdatatype ->+  recvTyped (castPtr recvptr) recvcount recvdatatype recvrank recvtag comm  {#fun Recv as recvTyped_     { id `Ptr ()'@@ -1571,16 +1551,15 @@ -- The MPI datatype is determined automatically from the buffer -- pointer type. This function does not return a status, which might -- be more efficient if the status is not needed.-recv_ :: forall a p. (Pointer p, Storable a, HasDatatype a)-      => p a                    -- ^ Receive buffer-      -> Count                  -- ^ Number of elements to receive+recv_ :: Buffer rb+      => rb                     -- ^ Receive buffer       -> Rank                   -- ^ Source rank (may be 'anySource')       -> Tag                    -- ^ Message tag (may be 'anyTag')       -> Comm                   -- ^ Communicator       -> IO ()-recv_ recvbuf recvcount recvrank recvtag comm =-  withPtr recvbuf $ \recvbuf' ->-  recvTyped_ (castPtr recvbuf') recvcount (datatype @a) recvrank recvtag comm+recv_ recvbuf recvrank recvtag comm =+  withPtrLenType recvbuf $ \recvptr recvcount recvdatatype ->+  recvTyped_ (castPtr recvptr) recvcount recvdatatype recvrank recvtag comm  {#fun Reduce as reduceTyped     { id `Ptr ()'@@ -1596,21 +1575,61 @@ -- @[MPI_Reduce](https://www.open-mpi.org/doc/current/man3/MPI_Reduce.3.php)@). -- The result is only available on the root process. The MPI datatypes -- are determined automatically from the buffer pointer types.-reduce :: forall a p q.-          ( Pointer p, Pointer q, Storable a, HasDatatype a)-       => p a                   -- ^ Source buffer-       -> q a                   -- ^ Destination buffer-       -> Count                 -- ^ Number of elements+reduce :: (Buffer sb, Buffer rb)+       => sb                    -- ^ Source buffer+       -> rb                    -- ^ Destination buffer        -> Op                    -- ^ Reduction operation        -> Rank                  -- ^ Root rank        -> Comm                  -- ^ Communicator        -> IO ()-reduce sendbuf recvbuf count op rank comm =-  withPtr sendbuf $ \sendbuf' ->-  withPtr recvbuf $ \recvbuf' ->-  reduceTyped (castPtr sendbuf') (castPtr recvbuf') count (datatype @a) op rank+reduce sendbuf recvbuf op rank comm =+  withPtrLenType sendbuf $ \sendptr sendcount senddatatype ->+  withPtrLenType recvbuf $ \recvptr recvcount recvdatatype ->+  assert (sendcount == recvcount && senddatatype == recvdatatype) $+  reduceTyped (castPtr sendptr) (castPtr recvptr) sendcount senddatatype op rank               comm +requestGetStatusBool :: Request -> IO (Bool, Status)+requestGetStatusBool req =+  withRequest req $ \req' ->+  alloca $ \flag ->+  do st <- Status <$> mallocForeignPtrBytes {#sizeof MPI_Status#}+     withStatus st $ \st' ->+       do _ <- {#call Request_get_status as requestGetStatusBool_#}+               (castPtr req') flag st'+          b <- peekBool flag+          return (b, st)++-- | Check whether a communication has completed without freeing the+-- communication request+-- (@[MPI_Request_get_status](https://www.open-mpi.org/doc/current/man3/MPI_Request_get_status.3.php)@).+requestGetStatus :: Request     -- ^ Communication request+                 -> IO (Maybe Status) -- ^ 'Just' 'Status' if the+                                      -- request has completed, else+                                      -- 'Nothing'+requestGetStatus req = bool2maybe <$> requestGetStatusBool req++-- {#fun Request_get_status as requestGetStatus_+--     { withRequest* `Request'+--     , alloca- `Bool' peekBool*+--     , withStatusIgnore- `Status'+--     } -> `()' return*-#}++-- | Check whether a communication has completed without freeing the+-- communication request+-- (@[MPI_Request_get_status](https://www.open-mpi.org/doc/current/man3/MPI_Request_get_status.3.php)@).+-- This function does not return a status, which might be more+-- efficient if the status is not needed.+requestGetStatus_ :: Request    -- ^ Communication request+                  -> IO Bool    -- ^ Whether the request had completed+requestGetStatus_ req =+  withRequest req $ \req' ->+  alloca $ \flag ->+  withStatusIgnore $ \st ->+  do _ <- {#call MPI_Request_get_status as requestGetStatus__#}+          (castPtr req') flag st+     peekBool flag+ {#fun Scan as scanTyped     { id `Ptr ()'     , id `Ptr ()'@@ -1626,18 +1645,17 @@ --  Each process with rank @r@ receives the result of reducing data --  from rank @0@ to rank @r@ (inclusive). The MPI datatype is --  determined automatically from the buffer pointer type.-scan :: forall a p q.-        ( Pointer p, Pointer q, Storable a, HasDatatype a)-     => p a                     -- ^ Source buffer-     -> q a                     -- ^ Destination buffer-     -> Count                   -- ^ Number of elements+scan :: (Buffer sb, Buffer rb)+     => sb                      -- ^ Source buffer+     -> rb                      -- ^ Destination buffer      -> Op                      -- ^ Reduction operation      -> Comm                    -- ^ Communicator      -> IO ()-scan sendbuf recvbuf count op comm =-  withPtr sendbuf $ \sendbuf' ->-  withPtr recvbuf $ \recvbuf' ->-  scanTyped (castPtr sendbuf') (castPtr recvbuf') count (datatype @a) op comm+scan sendbuf recvbuf op comm =+  withPtrLenType sendbuf $ \sendptr sendcount senddatatype ->+  withPtrLenType recvbuf $ \recvptr recvcount recvdatatype ->+  assert (sendcount == recvcount && senddatatype == recvdatatype) $+  scanTyped (castPtr sendptr) (castPtr recvptr) sendcount senddatatype op comm  {#fun Scatter as scatterTyped     { id `Ptr ()'@@ -1654,21 +1672,17 @@ -- @[MPI_Scatter](https://www.open-mpi.org/doc/current/man3/MPI_Scatter.3.php)@). -- The MPI datatypes are determined automatically from the buffer -- pointer types.-scatter :: forall a b p q.-           ( Pointer p, Pointer q-           , Storable a, HasDatatype a, Storable b, HasDatatype b)-        => p a      -- ^ Source buffer (only used on the root process)-        -> Count -- ^ Number of source elements (only used on the root process)-        -> q b                  -- ^ Destination buffer-        -> Count                -- ^ Number of destination elements+scatter :: (Buffer sb, Buffer rb)+        => sb        -- ^ Source buffer (only used on the root process)+        -> rb                   -- ^ Destination buffer         -> Rank                 -- ^ Root rank         -> Comm                 -- ^ Communicator         -> IO ()-scatter sendbuf sendcount recvbuf recvcount root comm =-  withPtr sendbuf $ \sendbuf' ->-  withPtr recvbuf $ \recvbuf' ->-  scatterTyped (castPtr sendbuf') sendcount (datatype @a)-               (castPtr recvbuf') recvcount (datatype @b)+scatter sendbuf recvbuf root comm =+  withPtrLenType sendbuf $ \sendptr sendcount senddatatype ->+  withPtrLenType recvbuf $ \recvptr recvcount recvdatatype ->+  scatterTyped (castPtr sendptr) sendcount senddatatype+               (castPtr recvptr) recvcount recvdatatype                root comm  {#fun Send as sendTyped@@ -1684,16 +1698,15 @@ -- (@[MPI_Send](https://www.open-mpi.org/doc/current/man3/MPI_Send.3.php)@). -- The MPI datatype is determined automatically from the buffer -- pointer type.-send :: forall a p. (Pointer p, Storable a, HasDatatype a)-     => p a                     -- ^ Send buffer-     -> Count                   -- ^ Number of elements to send+send :: Buffer sb+     => sb                      -- ^ Send buffer      -> Rank                    -- ^ Destination rank      -> Tag                     -- ^ Message tag      -> Comm                    -- ^ Communicator      -> IO ()-send sendbuf sendcount sendrank sendtag comm =-  withPtr sendbuf $ \sendbuf' ->-  sendTyped (castPtr sendbuf') sendcount (datatype @a) sendrank sendtag comm+send sendbuf sendrank sendtag comm =+  withPtrLenType sendbuf $ \sendptr sendcount senddatatype ->+  sendTyped (castPtr sendptr) sendcount senddatatype sendrank sendtag comm  {#fun Sendrecv as sendrecvTyped     { id `Ptr ()'@@ -1714,26 +1727,22 @@ -- (@[MPI_Sendrecv](https://www.open-mpi.org/doc/current/man3/MPI_Sendrecv.3.php)@). -- The MPI datatypes are determined automatically from the buffer -- pointer types.-sendrecv :: forall a b p q.-            ( Pointer p, Pointer q-            , Storable a, HasDatatype a, Storable b, HasDatatype b)-         => p a                 -- ^ Send buffer-         -> Count               -- ^ Number of elements to send+sendrecv :: (Buffer sb, Buffer rb)+         => sb                  -- ^ Send buffer          -> Rank                -- ^ Destination rank          -> Tag                 -- ^ Sent message tag-         -> q b                 -- ^ Receive buffer-         -> Count               -- ^ Number of elements to receive+         -> rb                  -- ^ Receive buffer          -> Rank                -- ^ Source rank (may be 'anySource')          -> Tag                 -- ^ Received message tag (may be 'anyTag')          -> Comm                -- ^ Communicator          -> IO Status           -- ^ Status for received message-sendrecv sendbuf sendcount sendrank sendtag-         recvbuf recvcount recvrank recvtag+sendrecv sendbuf sendrank sendtag+         recvbuf recvrank recvtag          comm =-  withPtr sendbuf $ \sendbuf' ->-  withPtr recvbuf $ \recvbuf' ->-  sendrecvTyped (castPtr sendbuf') sendcount (datatype @a) sendrank sendtag-                (castPtr recvbuf') recvcount (datatype @b) recvrank recvtag+  withPtrLenType sendbuf $ \sendptr sendcount senddatatype ->+  withPtrLenType recvbuf $ \recvptr recvcount recvdatatype ->+  sendrecvTyped (castPtr sendptr) sendcount senddatatype sendrank sendtag+                (castPtr recvptr) recvcount recvdatatype recvrank recvtag                 comm  {#fun Sendrecv as sendrecvTyped_@@ -1756,26 +1765,22 @@ -- The MPI datatypes are determined automatically from the buffer -- pointer types. This function does not return a status, which might -- be more efficient if the status is not needed.-sendrecv_ :: forall a b p q.-             ( Pointer p, Pointer q-             , Storable a, HasDatatype a, Storable b, HasDatatype b)-          => p a                -- ^ Send buffer-          -> Count              -- ^ Number of elements to send+sendrecv_ :: (Buffer sb, Buffer rb)+          => sb                 -- ^ Send buffer           -> Rank               -- ^ Destination rank           -> Tag                -- ^ Sent message tag-          -> q a                -- ^ Receive buffer-          -> Count              -- ^ Number of elements to receive+          -> rb                 -- ^ Receive buffer           -> Rank               -- ^ Source rank (may be 'anySource')           -> Tag                -- ^ Received message tag (may be 'anyTag')           -> Comm               -- ^ Communicator           -> IO ()-sendrecv_ sendbuf sendcount sendrank sendtag-          recvbuf recvcount recvrank recvtag+sendrecv_ sendbuf sendrank sendtag+          recvbuf recvrank recvtag           comm =-  withPtr sendbuf $ \sendbuf' ->-  withPtr recvbuf $ \recvbuf' ->-  sendrecvTyped_ (castPtr sendbuf') sendcount (datatype @a) sendrank sendtag-                 (castPtr recvbuf') recvcount (datatype @b) recvrank recvtag+  withPtrLenType sendbuf $ \sendptr sendcount senddatatype ->+  withPtrLenType recvbuf $ \recvptr recvcount recvdatatype ->+  sendrecvTyped_ (castPtr sendptr) sendcount senddatatype sendrank sendtag+                 (castPtr recvptr) recvcount recvdatatype recvrank recvtag                  comm  testBool :: Request -> IO (Bool, Status)
lib/Control/Distributed/MPI/Simple.hs view
@@ -1,69 +1,348 @@+{-# LANGUAGE TypeApplications #-}++-- | Module: Control.Distributed.MPI.Simple+-- Description: Simplified MPI bindings with automatic serialization+-- Copyright: (C) 2018 Erik Schnetter+-- License: Apache-2.0+-- Maintainer: Erik Schnetter <schnetter@gmail.com>+-- Stability: experimental+-- Portability: Requires an externally installed MPI library+ module Control.Distributed.MPI.Simple-  ( MPIException(..)-  , finalize-  , init-  , initThread+  ( -- * Types, and associated functions constants+    MPIException(..)++    -- ** Communicators+  , Comm(..)+  , commSelf+  , commWorld++    -- ** Message sizes+  , Count(..)+  , fromCount+  , toCount++    -- ** Process ranks+  , Rank(..)+  , anySource+  , commRank+  , commSize+  , rootRank++    -- ** Message status+  , Status(..)++    -- ** Message tags+  , Tag(..)+  , anyTag+  , fromTag+  , toTag+  , unitTag++  , Request++    -- * Functions++    -- ** Initialization and shutdown+  , abort+  , mainMPI++    -- ** Point-to-point (blocking)+  , recv+  , recv_+  , send+  , sendrecv+  , sendrecv_++    -- ** Point-to-point (non-blocking)+  , irecv+  , isend+  , test+  , test_+  , wait+  , wait_++    -- ** Collective (blocking)+  , barrier+  , bcastRecv+  , bcastSend++    -- ** Collective (non-blocking)+  , ibarrier   ) where  import Prelude hiding (init)  import Control.Concurrent import Control.Exception+import Control.Monad+import Control.Monad.Loops+import qualified Data.ByteString as B+import qualified Data.ByteString.Unsafe as B+import Data.Store hiding (peek, poke) import Data.Typeable-import System.IO.Unsafe+import Foreign+import Foreign.C.Types  import qualified Control.Distributed.MPI as MPI----didInit :: MVar Bool-didInit = unsafePerformIO newEmptyMVar+import Control.Distributed.MPI+  ( Comm(..)+  , commSelf+  , commWorld+  , Count(..)+  , fromCount+  , toCount+  , Rank(..)+  , anySource+  , commRank+  , commSize+  , rootRank+  , Tag(..)+  , anyTag+  , fromTag+  , toTag+  , unitTag+  , abort+  , barrier+  )   +-- | Exception type indicating an error in a call to MPI newtype MPIException = MPIException String   deriving (Eq, Ord, Read, Show, Typeable) instance Exception MPIException +mpiAssert :: Bool -> String -> IO ()+mpiAssert cond msg =+  do when (not cond) $ throw (MPIException msg)+     return ()  -finalize :: IO ()-finalize =-  do e <- isEmptyMVar didInit-     if e-       then throw (MPIException "Control flow error")-       else return ()-     did <- takeMVar didInit-     if did-       then MPI.finalize-       else return () -init :: IO ()-init =-  do e <- isEmptyMVar didInit-     if not e-       then throw (MPIException "Control flow error")-       else return ()-     i <- MPI.initialized-     if not i-       then do MPI.init-               putMVar didInit True-       else putMVar didInit False+data DidInit = DidInit | DidNotInit -initThread :: MPI.ThreadSupport -> IO ()-initThread threadSupport =-  do e <- isEmptyMVar didInit-     if not e-       then throw (MPIException "Control flow error")-       else return ()-     i <- MPI.initialized-     if not i-       then do ts <- MPI.initThread threadSupport-               if ts < threadSupport-                 then throw $ MPIException-                      ("Insufficient thread support: caller required " ++-                        show threadSupport ++ ", MPI library provided only " ++-                        show ts)-                 else return ()-               putMVar didInit True-       else putMVar didInit False+initMPI :: IO DidInit+initMPI =+  do isInit <- MPI.initialized+     if isInit+       then return DidNotInit+       else do ts <- MPI.initThread MPI.ThreadMultiple+               mpiAssert (ts >= MPI.ThreadMultiple)+                 ("MPI.init: Insufficient thread support: requiring " +++                  show MPI.ThreadMultiple +++                  ", but MPI library provided only " ++ show ts)+               return DidInit++finalizeMPI :: DidInit -> IO ()+finalizeMPI DidInit =+  do isFinalized <- MPI.finalized+     if isFinalized+       then return ()+       else do MPI.finalize+finalizeMPI DidNotInit = return ()++-- | Convenience function to initialize and finalize MPI. This+-- initializes MPI with 'ThreadMultiple' thread support.+mainMPI :: IO () -- ^ action to run with MPI, typically the whole program+        -> IO ()+mainMPI action = bracket initMPI finalizeMPI (\_ -> action)++++-- | A communication request, usually created by a non-blocking+-- communication function.+newtype Request a = Request (MVar (Status, a))++-- | The status of a finished communication, indicating rank and tag+-- of the other communication end point.+data Status = Status { msgRank :: !Rank+                     , msgTag :: !Tag+                     }+  deriving (Eq, Ord, Read, Show)++++-- | Receive an object.+recv :: Store a+     => Rank                    -- ^ Source rank+     -> Tag                     -- ^ Source tag+     -> Comm                    -- ^ Communicator+     -> IO (Status, a)          -- ^ Message status and received object+recv recvrank recvtag comm =+  do status <- untilJust $+       do yield+          MPI.iprobe recvrank recvtag comm+     source <- MPI.getSource status+     tag <- MPI.getTag status+     count <- MPI.getCount status MPI.datatypeByte+     let len = MPI.fromCount count+     ptr <- mallocBytes len+     buffer <- B.unsafePackMallocCStringLen (ptr, len)+     req <- MPI.irecv buffer source tag comm+     whileM_ (not <$> MPI.test_ req) yield+     return (Status source tag, decodeEx buffer)++-- | Receive an object without returning a status.+recv_ :: Store a+      => Rank                   -- ^ Source rank+      -> Tag                    -- ^ Source tag+      -> Comm                   -- ^ Communicator+      -> IO a                   -- ^ Received object+recv_ recvrank recvtag comm =+  snd <$> recv recvrank recvtag comm++-- | Send an object.+send :: Store a+     => a                     -- ^ Object to send+     -> Rank                  -- ^ Destination rank+     -> Tag                   -- ^ Message tag+     -> Comm                  -- ^ Communicator+     -> IO ()+send sendobj sendrank sendtag comm =+  do let sendbuf = encode sendobj+     MPI.send sendbuf sendrank sendtag comm++-- | Send and receive objects simultaneously.+sendrecv :: (Store a, Store b)+         => a                   -- ^ Object to send+         -> Rank                -- ^ Destination rank+         -> Tag                 -- ^ Send message tag+         -> Rank                -- ^ Source rank+         -> Tag                 -- ^ Receive message tag+         -> Comm                -- ^ Communicator+         -> IO (Status, b)      -- ^ Message status and received object+sendrecv sendobj sendrank sendtag recvrank recvtag comm =+  do sendreq <- isend sendobj sendrank sendtag comm+     recvreq <- irecv recvrank recvtag comm+     wait_ sendreq+     wait recvreq++-- | Send and receive objects simultaneously, without returning a+-- status for the received message.+sendrecv_ :: (Store a, Store b)+          => a                  -- ^ Object to send+          -> Rank               -- ^ Destination rank+          -> Tag                -- ^ Send message tag+          -> Rank               -- ^ Source rank+          -> Tag                -- ^ Receive message tag+          -> Comm               -- ^ Communicator+          -> IO b               -- ^ Received object+sendrecv_ sendobj sendrank sendtag recvrank recvtag comm =+  snd <$> sendrecv sendobj sendrank sendtag recvrank recvtag comm++-- | Begin to receive an object. Call `test` or `wait` to finish the+-- communication, and to obtain the received object.+irecv :: Store a+      => Rank                   -- ^ Source rank+      -> Tag                    -- ^ Source tag+      -> Comm                   -- ^ Communicator+      -> IO (Request a)         -- ^ Communication request+irecv recvrank recvtag comm =+  do result <- newEmptyMVar+     _ <- forkIO $+       do status <- untilJust $+            do yield+               MPI.iprobe recvrank recvtag comm+          source <- MPI.getSource status+          tag <- MPI.getTag status+          count <- MPI.getCount status MPI.datatypeByte+          let len = MPI.fromCount count+          ptr <- mallocBytes len+          buffer <- B.unsafePackMallocCStringLen (ptr, len)+          req <- MPI.irecv buffer source tag comm+          whileM_ (not <$> MPI.test_ req) yield+          putMVar result (Status source tag, decodeEx buffer)+     return (Request result)++-- | Begin to send an object. Call 'test' or 'wait' to finish the+-- communication.+isend :: Store a+      => a                     -- ^ Object to send+      -> Rank                  -- ^ Destination rank+      -> Tag                   -- ^ Message tag+      -> Comm                  -- ^ Communicator+      -> IO (Request ())       -- ^ Communication request+isend sendobj sendrank sendtag comm =+  do let sendbuf = encode sendobj+     req <- MPI.isend sendbuf sendrank sendtag comm+     result <- newEmptyMVar+     _ <- forkIO $ B.unsafeUseAsCString sendbuf $ \_ ->+       do whileM_ (not <$> MPI.test_ req) yield+          putMVar result (Status sendrank sendtag, ())+     return (Request result)++-- | Check whether a communication has finished, and return the+-- communication result if so.+test :: Request a               -- ^ Communication request+     -> IO (Maybe (Status, a))  -- ^ 'Just' communication result, if+                                -- communication has finished, else 'Nothing'+test (Request result) = tryTakeMVar result++-- | Check whether a communication has finished, and return the+-- communication result if so, without returning a message status.+test_ :: Request a       -- ^ Communication request+      -> IO (Maybe a) -- ^ 'Just' communication result, if+                      -- communication has finished, else 'Nothing'+test_ req = fmap snd <$> test req++-- | Wait for a communication to finish and return the communication+-- result.+wait :: Request a               -- ^ Communication request+     -> IO (Status, a)          -- ^ Message status and communication result+wait (Request result) = takeMVar result++-- | Wait for a communication to finish and return the communication+-- result, without returning a message status.+wait_ :: Request a              -- ^ Communication request+      -> IO a                   -- ^ Communication result+wait_ req = snd <$> wait req++++-- | Broadcast a message from one process (the "root") to all other+-- processes in the communicator. Call this function on all non-root+-- processes. Call 'bcastSend' instead on the root process.+bcastRecv :: Store a+          => Rank+          -> Comm+          -> IO a+bcastRecv root comm =+  do rank <- MPI.commRank comm+     mpiAssert (rank /= root) "bcastRecv: expected rank /= root"+     buf <- mallocForeignPtr @CLong+     MPI.bcast (buf, 1::Int) root comm+     len <- withForeignPtr buf peek+     ptr <- mallocBytes (fromIntegral len)+     recvbuf <- B.unsafePackMallocCStringLen (ptr, fromIntegral len)+     MPI.bcast recvbuf root comm               +     return (decodeEx recvbuf)++-- | Broadcast a message from one process (the "root") to all other+-- processes in the communicator. Call this function on the root+-- process. Call 'bcastRecv' instead on all non-root processes.+bcastSend :: Store a+          => a+          -> Rank+          -> Comm+          -> IO ()+bcastSend sendobj root comm =+  do rank <- MPI.commRank comm+     mpiAssert (rank == root) "bcastSend: expected rank == root"+     let sendbuf = encode sendobj+     buf <- mallocForeignPtr @CLong+     withForeignPtr buf $ \ptr -> poke ptr (fromIntegral (B.length sendbuf))+     MPI.bcast (buf, 1::Int) root comm+     MPI.bcast sendbuf root comm++-- | Begin a barrier. Call 'test' or 'wait' to finish the+-- communication.+ibarrier :: Comm+         -> IO (Request ())+ibarrier comm =+  do result <- newEmptyMVar+     req <- MPI.ibarrier comm+     _ <- forkIO $+       do whileM_ (not <$> MPI.test_ req) yield+          putMVar result (Status MPI.anySource MPI.anyTag, ())+     return (Request result)
mpi-hs.cabal view
@@ -2,10 +2,10 @@ -- -- see: https://github.com/sol/hpack ----- hash: a7b4d3931ec548e76111da431f03030741d23d3e21f765d151c2455add19efe5+-- hash: 56b0d5d26cff8ef0c8754f17c3ab3709be1da2be1a0567c8d8a5b779b4b44e01  name:           mpi-hs-version:        0.1.0.1+version:        0.3.0.0 synopsis:       MPI bindings for Haskell description:    MPI (the [Message Passing Interface](https://www.mpi-forum.org)) is                 widely used standard for distributed-memory programming on HPC (High@@ -28,11 +28,17 @@                 stored to a pointer are in Haskell regular return values). A                 high-level API simplifies exchanging arbitrary values that can be                 serialized.+                .+                Note that the automated builds on+                [Hackage](http://hackage.haskell.org) will currently always fail+                since no MPI library is present there. However, builds on+                [Stackage](https://www.stackage.org) should succeed -- if not, there+                is an error in this package. category:       Distributed Computing homepage:       https://github.com/eschnett/mpi-hs#readme bug-reports:    https://github.com/eschnett/mpi-hs/issues-author:         Erik Schnetter-maintainer:     Erik Schnetter+author:         Erik Schnetter <schnetter@gmail.com>+maintainer:     Erik Schnetter <schnetter@gmail.com> license:        Apache-2.0 license-file:   LICENSE build-type:     Simple@@ -65,12 +71,15 @@   extra-libraries:       mpi   build-depends:-      base >=4.11 && <4.12+      base >=4 && <5+    , bytestring+    , monad-loops+    , store   build-tools:       c2hs   default-language: Haskell2010 -executable mpi-hs+executable example   main-is: Main.hs   other-modules:       Paths_mpi_hs@@ -82,22 +91,32 @@     , mpi-hs   default-language: Haskell2010 -test-suite mpi-hs-test-suite+test-suite mpi-simple-tests   type: exitcode-stdio-1.0   main-is: Main.hs   other-modules:       Paths_mpi_hs   hs-source-dirs:-      test+      test/simple   ghc-options: -Wall -rtsopts -threaded -with-rtsopts=-N   build-depends:       base     , monad-loops     , mpi-hs-    , tasty-    , tasty-hspec-    , tasty-hunit-    , unix+  default-language: Haskell2010++test-suite mpi-tests+  type: exitcode-stdio-1.0+  main-is: Main.hs+  other-modules:+      Paths_mpi_hs+  hs-source-dirs:+      test/mpi+  ghc-options: -Wall -rtsopts -threaded -with-rtsopts=-N+  build-depends:+      base+    , monad-loops+    , mpi-hs   default-language: Haskell2010  benchmark mpi-hs-benchmarks
package.yaml view
@@ -1,9 +1,9 @@ name: mpi-hs-version: '0.1.0.1'+version: '0.3.0.0' github: "eschnett/mpi-hs" license: Apache-2.0-author: "Erik Schnetter"-maintainer: "Erik Schnetter"+author: "Erik Schnetter <schnetter@gmail.com>"+maintainer: "Erik Schnetter <schnetter@gmail.com>" category: Distributed Computing synopsis: MPI bindings for Haskell description: |@@ -29,6 +29,12 @@   high-level API simplifies exchanging arbitrary values that can be   serialized. +  Note that the automated builds on+  [Hackage](http://hackage.haskell.org) will currently always fail+  since no MPI library is present there. However, builds on+  [Stackage](https://www.stackage.org) should succeed -- if not, there+  is an error in this package.+ extra-source-files:   - LICENSE   - README.md@@ -42,11 +48,13 @@  library:   dependencies:-    - base >=4.11 && <4.12+    - base >=4 && <5            # tested with 4.11 and 4.12+    - bytestring+    - monad-loops+    - store   build-tools:     - c2hs-  source-dirs:-    - lib+  source-dirs: lib   c-sources:     - c/src/mpihs.c   include-dirs:@@ -55,7 +63,7 @@     - mpi  executables:-  mpi-hs:+  example:     source-dirs: src     main: Main.hs     dependencies:@@ -80,17 +88,32 @@       - -with-rtsopts=-N  tests:-  mpi-hs-test-suite:-    source-dirs: test+  mpi-tests:+    source-dirs: test/mpi     main: Main.hs     dependencies:       - base       - monad-loops       - mpi-hs-      - tasty-      - tasty-hunit-      - tasty-hspec-      - unix+      # - tasty+      # - tasty-hunit+      # - tasty-hspec+      # - unix+    ghc-options:+      - -rtsopts+      - -threaded+      - -with-rtsopts=-N+  mpi-simple-tests:+    source-dirs: test/simple+    main: Main.hs+    dependencies:+      - base+      - monad-loops+      - mpi-hs+      # - tasty+      # - tasty-hunit+      # - tasty-hspec+      # - unix     ghc-options:       - -rtsopts       - -threaded
stack.yaml view
@@ -1,8 +1,11 @@+# Resolver to choose a 'specific' stackage snapshot or a compiler version. resolver: lts-12.13 +# User packages to be built. packages:   - . +# Extra directories used by stack for building extra-include-dirs:   - /opt/local/include/openmpi-mp   - /usr/lib/openmpi/include
− test/Main.hs
@@ -1,629 +0,0 @@-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TypeApplications #-}--import Control.Concurrent-import Control.Exception-import Control.Monad-import Control.Monad.Loops-import Data.IORef-import Foreign-import Foreign.C.Types-import System.Exit-import System.IO--- import Test.Tasty--- import Test.Tasty.HUnit--import qualified Control.Distributed.MPI as MPI--------------------------------------------------------------------------------------infix 1 @?-(@?) :: Bool -> String -> IO ()-x @? msg = if not x then die msg else return ()--infix 1 @?=-(@?=) :: Eq a => a -> a -> IO ()-x @?= y = x == y @? "test failed"----type TestTree = IO ()--testCase :: String -> IO () -> TestTree-testCase name test =-  do rank <- MPI.commRank MPI.commWorld-     if rank == 0-       then do putStrLn $ "  " ++ name ++ "..."-               hFlush stdout-       else return ()-     MPI.barrier MPI.commWorld-     test-     MPI.barrier MPI.commWorld----testGroup :: String -> [TestTree] -> TestTree-testGroup name cases =-  do rank <- MPI.commRank MPI.commWorld-     if rank == 0-       then do putStrLn $ name ++ ":"-               hFlush stdout-       else return ()-     sequence_ cases----defaultMain :: TestTree -> IO ()-defaultMain tree =-  do rank <- MPI.commRank MPI.commWorld-     size <- MPI.commSize MPI.commWorld-     if rank == 0-       then do putStrLn $ "MPI Tests: running on " ++ show size ++ " processes"-               hFlush stdout-       else return ()-     tree----------------------------------------------------------------------------------------main :: IO ()-main = bracket-  (do _ <- MPI.initThread MPI.ThreadMultiple-      return ())-  (\_ -> MPI.finalize)-  (\_ -> defaultMain tests)--tests :: TestTree-tests = testGroup "MPI"-  [ initialized-  , rankSize-  , pointToPoint-  , pointToPointNonBlocking-  , collective-  , collectiveNonBlocking-  , reductions-  , dynamic-  ]----initialized :: TestTree-initialized = testGroup "initialized"-  [ testCase "initialized" $-      do isInit <- MPI.initialized-         isInit @?= True-  , testCase "finalized" $-      do isFini <- MPI.finalized-         isFini @?= False-  ]----rankSize :: TestTree-rankSize = testGroup "rank and size"-  [ testCase "commSelf" $-    do rank <- MPI.commRank MPI.commSelf-       size <- MPI.commSize MPI.commSelf-       rank == 0 && size == 1 @? ""-  , testCase "commWorld" $-    do rank <- MPI.commRank MPI.commWorld-       size <- MPI.commSize MPI.commWorld-       rank >= 0 && rank < size @? ""-  ]----pointToPoint :: TestTree-pointToPoint = testGroup "point-to-point"-  [ testCase "send and recv" $-    do rank <- MPI.commRank MPI.commWorld--       let msg = 42-       buf <- mallocForeignPtr @CInt-       withForeignPtr buf $ \ptr -> poke ptr msg--       MPI.send buf 1 rank MPI.unitTag MPI.commWorld--       buf' <- mallocForeignPtr @CInt-       st <- MPI.recv buf' 1 rank MPI.unitTag MPI.commWorld-       msg' <- withForeignPtr buf' peek--       source <- MPI.getSource st-       tag <- MPI.getTag st-       count <- MPI.getCount st MPI.datatypeInt-       (msg' == msg && source == rank && tag == MPI.unitTag && count == 1) @? ""-  , testCase "sendrecv" $-    do rank <- MPI.commRank MPI.commWorld-       size <- MPI.commSize MPI.commWorld--       let msg = 42 + MPI.fromRank rank-       buf <- mallocForeignPtr @CInt-       withForeignPtr buf $ \ptr -> poke ptr msg--       buf' <- mallocForeignPtr @CInt--       st <- MPI.sendrecv-             buf 1 ((rank + 1) `mod` size) MPI.unitTag-             buf' 1 ((rank - 1) `mod` size) MPI.unitTag-             MPI.commWorld--       msg' <- withForeignPtr buf' peek--       source <- MPI.getSource st-       tag <- MPI.getTag st-       count <- MPI.getCount st MPI.datatypeInt-       (msg' == 42 + MPI.fromRank ((rank - 1) `mod` size) &&-        source == (rank - 1) `mod` size &&-        tag == MPI.unitTag &&-        count == 1) @? ""-  ]----pointToPointNonBlocking :: TestTree-pointToPointNonBlocking = testGroup "point-to-point non-blocking"-  [ testCase "send and recv" $-    do rank <- MPI.commRank MPI.commWorld--       let msg = 42-       buf <- mallocForeignPtr @CInt-       withForeignPtr buf $ \ptr -> poke ptr msg--       req <- MPI.isend buf 1 rank MPI.unitTag MPI.commWorld--       buf' <- mallocForeignPtr @CInt-       req' <- MPI.irecv buf' 1 rank MPI.unitTag MPI.commWorld--       MPI.wait_ req-       st <- MPI.wait req'--       touchForeignPtr buf-       msg' <- withForeignPtr buf' peek--       source <- MPI.getSource st-       tag <- MPI.getTag st-       count <- MPI.getCount st MPI.datatypeInt-       (msg' == msg && source == rank && tag == MPI.unitTag && count == 1) @? ""-  ]----collective :: TestTree-collective = testGroup "collective"-  [ testCase "allgather" $-    do rank <- MPI.commRank MPI.commWorld-       size <- MPI.commSize MPI.commWorld-       let rk = MPI.fromRank rank-       let sz = MPI.fromRank size-       let msg = 42 + rk-       buf <- mallocForeignPtr @CInt-       withForeignPtr buf $ \ptr -> poke ptr msg-       buf' <- mallocForeignPtrArray @CInt sz-       MPI.allgather buf 1 buf' 1 MPI.commWorld-       msgs' <- withForeignPtr buf' (peekArray sz)-       msgs' == [42 .. 42 + fromIntegral (sz-1)] @? ""-  , testCase "allreduce" $-    do rank <- MPI.commRank MPI.commWorld-       size <- MPI.commSize MPI.commWorld-       let rk = MPI.fromRank rank-       let sz = MPI.fromRank size-       let msg = 42 + rk-       buf <- mallocForeignPtr @CInt-       withForeignPtr buf $ \ptr -> poke ptr msg-       buf' <- mallocForeignPtr @CInt-       MPI.allreduce buf buf' 1 MPI.opSum MPI.commWorld-       msg' <- withForeignPtr buf' peek-       msg' == sum [42 .. 42 + (sz-1)] @? ""-  , testCase "alltoall" $-    do rank <- MPI.commRank MPI.commWorld-       size <- MPI.commSize MPI.commWorld-       let rk = MPI.fromRank rank-       let sz = MPI.fromRank size-       let msgs = fromIntegral <$> [42 + sz * rk + i | i <- [0 .. sz-1]]-       buf <- mallocForeignPtrArray @CInt sz-       withForeignPtr buf $ \ptr -> pokeArray ptr msgs-       buf' <- mallocForeignPtrArray @CInt sz-       MPI.alltoall buf 1 buf' 1 MPI.commWorld-       msgs' <- withForeignPtr buf' (peekArray sz)-       msgs' == (fromIntegral <$> [42 + sz * i + rk | i <- [0 .. sz-1]]) @? ""-  , testCase "barrier" $-    MPI.barrier MPI.commWorld-  , testCase "bcast" $-    do rank <- MPI.commRank MPI.commWorld-       let rk = MPI.fromRank rank-       let msg = 42 + rk-       buf <- mallocForeignPtr @CInt-       withForeignPtr buf $ \ptr -> poke ptr msg-       MPI.bcast buf 1 MPI.rootRank MPI.commWorld-       msg' <- withForeignPtr buf peek-       msg' == 42 @? ""-  , testCase "exscan" $-    do rank <- MPI.commRank MPI.commWorld-       let rk = MPI.fromRank rank-       let msg = 42 + rk-       buf <- mallocForeignPtr @CInt-       withForeignPtr buf $ \ptr -> poke ptr msg-       buf' <- mallocForeignPtr @CInt-       MPI.exscan buf buf' 1 MPI.opSum MPI.commWorld-       msg' <- withForeignPtr buf' (if rank == 0 then \_ -> return 0 else peek)-       msg' == sum [42 .. 42 + rk-1] @? ""-  , testCase "gather" $-    do rank <- MPI.commRank MPI.commWorld-       size <- MPI.commSize MPI.commWorld-       let rk = MPI.fromRank rank-       let sz = MPI.fromRank size-       let isroot = rank == MPI.rootRank-       let msg = 42 + rk-       buf <- mallocForeignPtr @CInt-       withForeignPtr buf $ \ptr -> poke ptr msg-       buf' <- mallocForeignPtrArray @CInt (if isroot then sz else 0)-       MPI.gather buf 1 buf' 1 MPI.rootRank MPI.commWorld-       msgs' <- withForeignPtr buf' $ peekArray (if isroot then sz else 0)-       (if isroot then msgs' == [42 .. 42 + fromIntegral sz-1] else True) @? ""-  , testCase "reduce" $-    do rank <- MPI.commRank MPI.commWorld-       size <- MPI.commSize MPI.commWorld-       let rk = MPI.fromRank rank-       let sz = MPI.fromRank size-       let isroot = rank == MPI.rootRank-       let msg = 42 + rk-       buf <- mallocForeignPtr @CInt-       withForeignPtr buf $ \ptr -> poke ptr msg-       buf' <- mallocForeignPtr @CInt-       MPI.reduce buf buf' 1 MPI.opSum MPI.rootRank MPI.commWorld-       msg' <- withForeignPtr buf' $ if isroot then peek else \_ -> return 0-       (if isroot then msg' == sum [42 .. 42 + sz-1] else True) @? ""-  , testCase "scan" $-    do rank <- MPI.commRank MPI.commWorld-       let rk = MPI.fromRank rank-       let msg = 42 + rk-       buf <- mallocForeignPtr @CInt-       withForeignPtr buf $ \ptr -> poke ptr msg-       buf' <- mallocForeignPtr @CInt-       MPI.scan buf buf' 1 MPI.opSum MPI.commWorld-       msg' <- withForeignPtr buf' peek-       msg' == sum [42 .. 42 + rk] @? ""-  , testCase "scatter" $-    do rank <- MPI.commRank MPI.commWorld-       size <- MPI.commSize MPI.commWorld-       let rk = MPI.fromRank rank-       let sz = MPI.fromRank size-       let isroot = rank == MPI.rootRank-       let msgs =-             if isroot then [42 + fromIntegral i | i <- [0 .. sz-1]] else []-       buf <- mallocForeignPtrArray @CInt (if isroot then sz else 0)-       withForeignPtr buf $-         \ptr -> if isroot then pokeArray ptr msgs else return ()-       buf' <- mallocForeignPtr @CInt-       MPI.scatter buf 1 buf' 1 MPI.rootRank MPI.commWorld-       msg' <- withForeignPtr buf' peek-       msg' == 42 + rk @? ""-  ]----reductions :: TestTree-reductions = testGroup "reduction operations"-  [ testCase "max" $-    do rank <- MPI.commRank MPI.commWorld-       size <- MPI.commSize MPI.commWorld-       let rk = MPI.fromRank rank-       let sz = MPI.fromRank size-       let isroot = rank == MPI.rootRank-       let msg = 42 + rk-       buf <- mallocForeignPtr @CInt-       withForeignPtr buf $ \ptr -> poke ptr msg-       buf' <- mallocForeignPtr @CInt-       MPI.reduce buf buf' 1 MPI.opMax MPI.rootRank MPI.commWorld-       msg' <- withForeignPtr buf' $ if isroot then peek else \_ -> return 0-       (if isroot then msg' == maximum [42 .. 42 + sz-1] else True) @? ""-  , testCase "min" $-    do rank <- MPI.commRank MPI.commWorld-       size <- MPI.commSize MPI.commWorld-       let rk = MPI.fromRank rank-       let sz = MPI.fromRank size-       let isroot = rank == MPI.rootRank-       let msg = 42 + rk-       buf <- mallocForeignPtr @CInt-       withForeignPtr buf $ \ptr -> poke ptr msg-       buf' <- mallocForeignPtr @CInt-       MPI.reduce buf buf' 1 MPI.opMin MPI.rootRank MPI.commWorld-       msg' <- withForeignPtr buf' $ if isroot then peek else \_ -> return 0-       (if isroot then msg' == minimum [42 .. 42 + sz-1] else True) @? ""-  , testCase "sum" $-    do rank <- MPI.commRank MPI.commWorld-       size <- MPI.commSize MPI.commWorld-       let rk = MPI.fromRank rank-       let sz = MPI.fromRank size-       let isroot = rank == MPI.rootRank-       let msg = 42 + rk-       buf <- mallocForeignPtr @CInt-       withForeignPtr buf $ \ptr -> poke ptr msg-       buf' <- mallocForeignPtr @CInt-       MPI.reduce buf buf' 1 MPI.opSum MPI.rootRank MPI.commWorld-       msg' <- withForeignPtr buf' $ if isroot then peek else \_ -> return 0-       (if isroot then msg' == sum [42 .. 42 + sz-1] else True) @? ""-  ]----collectiveNonBlocking :: TestTree-collectiveNonBlocking = testGroup "collective non-blocking"-  [ testCase "iallgather" $-    do rank <- MPI.commRank MPI.commWorld-       size <- MPI.commSize MPI.commWorld-       let rk = MPI.fromRank rank-       let sz = MPI.fromRank size-       let msg = 42 + rk-       buf <- mallocForeignPtr @CInt-       withForeignPtr buf $ \ptr -> poke ptr msg-       buf' <- mallocForeignPtrArray @CInt sz-       req <- MPI.iallgather buf 1 buf' 1 MPI.commWorld-       MPI.wait_ req-       touchForeignPtr buf-       msgs' <- withForeignPtr buf' (peekArray sz)-       msgs' == [42 .. 42 + fromIntegral (sz-1)] @? ""-  , testCase "iallreduce" $-    do rank <- MPI.commRank MPI.commWorld-       size <- MPI.commSize MPI.commWorld-       let rk = MPI.fromRank rank-       let sz = MPI.fromRank size-       let msg = 42 + rk-       buf <- mallocForeignPtr @CInt-       withForeignPtr buf $ \ptr -> poke ptr msg-       buf' <- mallocForeignPtr @CInt-       req <- MPI.iallreduce buf buf' 1 MPI.opSum MPI.commWorld-       MPI.wait_ req-       touchForeignPtr buf-       msg' <- withForeignPtr buf' peek-       msg' == sum [42 .. 42 + (sz-1)] @? ""-  , testCase "ialltoall" $-    do rank <- MPI.commRank MPI.commWorld-       size <- MPI.commSize MPI.commWorld-       let rk = MPI.fromRank rank-       let sz = MPI.fromRank size-       let msgs = fromIntegral <$> [42 + sz * rk + i | i <- [0 .. sz-1]]-       buf <- mallocForeignPtrArray @CInt sz-       withForeignPtr buf $ \ptr -> pokeArray ptr msgs-       buf' <- mallocForeignPtrArray @CInt sz-       req <- MPI.ialltoall buf 1 buf' 1 MPI.commWorld-       MPI.wait_ req-       touchForeignPtr buf-       msgs' <- withForeignPtr buf' (peekArray sz)-       msgs' == (fromIntegral <$> [42 + sz * i + rk | i <- [0 .. sz-1]]) @? ""-  , testCase "ibarrier" $-    do req <- MPI.ibarrier MPI.commWorld-       MPI.wait_ req-  , testCase "ibcast" $-    do rank <- MPI.commRank MPI.commWorld-       let rk = MPI.fromRank rank-       let msg = 42 + rk-       buf <- mallocForeignPtr @CInt-       withForeignPtr buf $ \ptr -> poke ptr msg-       req <- MPI.ibcast buf 1 MPI.rootRank MPI.commWorld-       MPI.wait_ req-       touchForeignPtr buf-       msg' <- withForeignPtr buf peek-       msg' == 42 @? ""-  , testCase "iexscan" $-    do rank <- MPI.commRank MPI.commWorld-       let rk = MPI.fromRank rank-       let msg = 42 + rk-       buf <- mallocForeignPtr @CInt-       withForeignPtr buf $ \ptr -> poke ptr msg-       buf' <- mallocForeignPtr @CInt-       req <- MPI.iexscan buf buf' 1 MPI.opSum MPI.commWorld-       MPI.wait_ req-       touchForeignPtr buf-       msg' <- withForeignPtr buf' (if rank == 0 then \_ -> return 0 else peek)-       msg' == sum [42 .. 42 + rk-1] @? ""-  , testCase "igather" $-    do rank <- MPI.commRank MPI.commWorld-       size <- MPI.commSize MPI.commWorld-       let rk = MPI.fromRank rank-       let sz = MPI.fromRank size-       let isroot = rank == MPI.rootRank-       let msg = 42 + rk-       buf <- mallocForeignPtr @CInt-       withForeignPtr buf $ \ptr -> poke ptr msg-       buf' <- mallocForeignPtrArray @CInt (if isroot then sz else 0)-       req <- MPI.igather buf 1 buf' 1 MPI.rootRank MPI.commWorld-       MPI.wait_ req-       touchForeignPtr buf-       msgs' <- withForeignPtr buf' $ peekArray (if isroot then sz else 0)-       (if isroot then msgs' == [42 .. 42 + fromIntegral sz-1] else True) @? ""-  , testCase "ireduce" $-    do rank <- MPI.commRank MPI.commWorld-       size <- MPI.commSize MPI.commWorld-       let rk = MPI.fromRank rank-       let sz = MPI.fromRank size-       let isroot = rank == MPI.rootRank-       let msg = 42 + rk-       buf <- mallocForeignPtr @CInt-       withForeignPtr buf $ \ptr -> poke ptr msg-       buf' <- mallocForeignPtr @CInt-       req <- MPI.ireduce buf buf' 1 MPI.opSum MPI.rootRank MPI.commWorld-       MPI.wait_ req-       touchForeignPtr buf-       msg' <- withForeignPtr buf' $ if isroot then peek else \_ -> return 0-       (if isroot then msg' == sum [42 .. 42 + sz-1] else True) @? ""-  , testCase "iscan" $-    do rank <- MPI.commRank MPI.commWorld-       let rk = MPI.fromRank rank-       let msg = 42 + rk-       buf <- mallocForeignPtr @CInt-       withForeignPtr buf $ \ptr -> poke ptr msg-       buf' <- mallocForeignPtr @CInt-       req <- MPI.iscan buf buf' 1 MPI.opSum MPI.commWorld-       MPI.wait_ req-       touchForeignPtr buf-       msg' <- withForeignPtr buf' peek-       msg' == sum [42 .. 42 + rk] @? ""-  , testCase "iscatter" $-    do rank <- MPI.commRank MPI.commWorld-       size <- MPI.commSize MPI.commWorld-       let rk = MPI.fromRank rank-       let sz = MPI.fromRank size-       let isroot = rank == MPI.rootRank-       let msgs = [42 + fromIntegral i | i <- [0 .. sz-1]]-       buf <- mallocForeignPtrArray @CInt (if isroot then sz else 0)-       withForeignPtr buf $ \ptr -> pokeArray ptr msgs-       buf' <- mallocForeignPtr @CInt-       req <- MPI.iscatter buf 1 buf' 1 MPI.rootRank MPI.commWorld-       MPI.wait_ req-       touchForeignPtr buf-       msg' <- withForeignPtr buf' peek-       msg' == 42 + rk @? ""-  ]----dynamic :: TestTree-dynamic = testGroup "dynamic"-  [ testCase "sequential" $-    do rank <- MPI.commRank MPI.commWorld-       size <- MPI.commSize MPI.commWorld--       breq <- newIORef Nothing-       let signalDone =-             do r <- MPI.ibarrier MPI.commWorld-                writeIORef breq (Just r)-       let checkDone =-             do mreq <- readIORef breq-                case mreq of-                  Nothing -> return False-                  Just req -> MPI.test_ req--       sendreqs <- newIORef []-       let sendMsg dst =-             when (dst < size) $-             do buf <- mallocForeignPtr @CInt-                withForeignPtr buf $ \ptr -> poke ptr 42-                r <- MPI.isend buf 1 dst MPI.unitTag MPI.commWorld-                modifyIORef' sendreqs ((buf, r) :)-       let drainSendQueue =-             do srs <- readIORef sendreqs-                srs' <- filterM (\(_, r) -> not <$> MPI.test_ r) srs-                writeIORef sendreqs srs'-       let checkForMsg = MPI.iprobe MPI.anySource MPI.unitTag MPI.commWorld-       let recvMsg st =-             do src <- MPI.getSource st-                buf <- mallocForeignPtr @CInt-                MPI.recv_ buf 1 src MPI.unitTag MPI.commWorld--       -- each rank sends to the next-       when (rank == 0) $-         do sendMsg (rank + 1)-            signalDone--       untilM_-         (do drainSendQueue-             mst <- checkForMsg-             case mst of-               Nothing -> return ()-               Just st -> do recvMsg st-                             sendMsg (rank + 1)-                             signalDone-         )-         checkDone-  , testCase "tree" $-    do rank <- MPI.commRank MPI.commWorld-       size <- MPI.commSize MPI.commWorld--       breq <- newIORef Nothing-       let signalDone =-             do r <- MPI.ibarrier MPI.commWorld-                writeIORef breq (Just r)-       let checkDone =-             do mreq <- readIORef breq-                case mreq of-                  Nothing -> return False-                  Just req -> MPI.test_ req--       sendreqs <- newIORef []-       let sendMsg dst =-             when (dst < size) $-             do buf <- mallocForeignPtr @CInt-                withForeignPtr buf $ \ptr -> poke ptr 42-                r <- MPI.isend buf 1 dst MPI.unitTag MPI.commWorld-                modifyIORef' sendreqs ((buf, r) :)-       let drainSendQueue =-             do srs <- readIORef sendreqs-                srs' <- filterM (\(_, r) -> not <$> MPI.test_ r) srs-                writeIORef sendreqs srs'-       let checkForMsg = MPI.iprobe MPI.anySource MPI.unitTag MPI.commWorld-       let recvMsg st =-             do src <- MPI.getSource st-                buf <- mallocForeignPtr @CInt-                MPI.recv_ buf 1 src MPI.unitTag MPI.commWorld--       -- rank r sends to 2*r+1 and 2*r+2-       when (rank == 0) $-         do sendMsg (2 * rank + 1)-            sendMsg (2 * rank + 2)-            signalDone--       untilM_-         (do drainSendQueue-             mst <- checkForMsg-             case mst of-               Nothing -> return ()-               Just st -> do recvMsg st-                             sendMsg (2 * rank + 1)-                             sendMsg (2 * rank + 2)-                             signalDone-         )-         checkDone-  , testCase "multi-threaded" $-    do mts <- MPI.threadSupport-       let Just ts = mts-       when (ts >= MPI.ThreadMultiple) $-         do rank <- MPI.commRank MPI.commWorld-            size <- MPI.commSize MPI.commWorld-     -            breq <- newEmptyMVar-            let signalDone =-                  do _ <- forkIO $-                       do req <- MPI.ibarrier MPI.commWorld-                          whileM_ (not <$> MPI.test_ req) yield-                          putMVar breq ()-                     return ()-            let checkDone = not <$> isEmptyMVar breq-     -            let sendMsg dst =-                  when (dst < size) $-                  do _ <- forkIO $-                       do buf <- mallocForeignPtr @CInt-                          withForeignPtr buf $ \ptr -> poke ptr 42-                          req <- MPI.isend buf 1 dst MPI.unitTag MPI.commWorld-                          whileM_ (not <$> MPI.test_ req) yield-                     return ()-            let checkForMsg = MPI.iprobe MPI.anySource MPI.unitTag MPI.commWorld-            let recvMsg st =-                  do src <- MPI.getSource st-                     buf <- mallocForeignPtr @CInt-                     MPI.recv_ buf 1 src MPI.unitTag MPI.commWorld-     -            -- rank r sends to 2*r+1 and 2*r+2-            when (rank == 0) $-              do sendMsg (2 * rank + 1)-                 sendMsg (2 * rank + 2)-                 signalDone-     -            untilM_-              (do mst <- checkForMsg-                  case mst of-                    Nothing -> return ()-                    Just st -> do recvMsg st-                                  sendMsg (2 * rank + 1)-                                  sendMsg (2 * rank + 2)-                                  signalDone-                  yield-              )-              checkDone-  ]
+ test/mpi/Main.hs view
@@ -0,0 +1,638 @@+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications #-}++import Control.Concurrent+import Control.Exception+import Control.Monad+import Control.Monad.Loops+import Data.IORef+import Foreign+import Foreign.C.Types+import System.Exit+import System.IO+-- import Test.Tasty+-- import Test.Tasty.HUnit++import qualified Control.Distributed.MPI as MPI++++--------------------------------------------------------------------------------++infix 1 @?+(@?) :: Bool -> String -> IO ()+x @? msg = if not x then die msg else return ()++infix 1 @?=+(@?=) :: Eq a => a -> a -> IO ()+x @?= y = x == y @? "test failed"++++type TestTree = IO ()++testCase :: String -> IO () -> TestTree+testCase name test =+  do rank <- MPI.commRank MPI.commWorld+     if rank == 0+       then do putStrLn $ "  " ++ name ++ "..."+               hFlush stdout+       else return ()+     MPI.barrier MPI.commWorld+     test+     MPI.barrier MPI.commWorld++++testGroup :: String -> [TestTree] -> TestTree+testGroup name cases =+  do rank <- MPI.commRank MPI.commWorld+     if rank == 0+       then do putStrLn $ name ++ ":"+               hFlush stdout+       else return ()+     sequence_ cases++++defaultMain :: TestTree -> IO ()+defaultMain tree =+  do rank <- MPI.commRank MPI.commWorld+     size <- MPI.commSize MPI.commWorld+     if rank == 0+       then do putStrLn $ "MPI Tests: running on " ++ show size ++ " processes"+               hFlush stdout+       else return ()+     tree++++--------------------------------------------------------------------------------++++main :: IO ()+main = bracket+  (do _ <- MPI.initThread MPI.ThreadMultiple+      return ())+  (\_ -> MPI.finalize)+  (\_ -> defaultMain tests)++tests :: TestTree+tests = testGroup "MPI"+  [ initialized+  , rankSize+  , pointToPoint+  , pointToPointNonBlocking+  , collective+  , collectiveNonBlocking+  , reductions+  , dynamic+  ]++++initialized :: TestTree+initialized = testGroup "initialized"+  [ testCase "initialized" $+      do isInit <- MPI.initialized+         isInit @?= True+  , testCase "finalized" $+      do isFini <- MPI.finalized+         isFini @?= False+  ]++++rankSize :: TestTree+rankSize = testGroup "rank and size"+  [ testCase "commSelf" $+    do rank <- MPI.commRank MPI.commSelf+       size <- MPI.commSize MPI.commSelf+       rank == 0 && size == 1 @? ""+  , testCase "commWorld" $+    do rank <- MPI.commRank MPI.commWorld+       size <- MPI.commSize MPI.commWorld+       rank >= 0 && rank < size @? ""+  ]++++pointToPoint :: TestTree+pointToPoint = testGroup "point-to-point"+  [ testCase "send and recv" $+    do rank <- MPI.commRank MPI.commWorld++       let msg = 42+       buf <- mallocForeignPtr @CInt+       withForeignPtr buf $ \ptr -> poke ptr msg++       MPI.send (buf, 1::Int) rank MPI.unitTag MPI.commWorld++       buf' <- mallocForeignPtr @CInt+       st <- MPI.recv (buf', 1::Int) rank MPI.unitTag MPI.commWorld+       msg' <- withForeignPtr buf' peek++       source <- MPI.getSource st+       tag <- MPI.getTag st+       count <- MPI.getCount st MPI.datatypeInt+       (msg' == msg && source == rank && tag == MPI.unitTag && count == 1) @? ""+  , testCase "sendrecv" $+    do rank <- MPI.commRank MPI.commWorld+       size <- MPI.commSize MPI.commWorld++       let msg = 42 + MPI.fromRank rank+       buf <- mallocForeignPtr @CInt+       withForeignPtr buf $ \ptr -> poke ptr msg++       buf' <- mallocForeignPtr @CInt++       st <- MPI.sendrecv+             (buf, 1::Int) ((rank + 1) `mod` size) MPI.unitTag+             (buf', 1::Int) ((rank - 1) `mod` size) MPI.unitTag+             MPI.commWorld++       msg' <- withForeignPtr buf' peek++       source <- MPI.getSource st+       tag <- MPI.getTag st+       count <- MPI.getCount st MPI.datatypeInt+       (msg' == 42 + MPI.fromRank ((rank - 1) `mod` size) &&+        source == (rank - 1) `mod` size &&+        tag == MPI.unitTag &&+        count == 1) @? ""+  ]++++pointToPointNonBlocking :: TestTree+pointToPointNonBlocking = testGroup "point-to-point non-blocking"+  [ testCase "send and recv" $+    do rank <- MPI.commRank MPI.commWorld++       let msg = 42+       buf <- mallocForeignPtr @CInt+       withForeignPtr buf $ \ptr -> poke ptr msg++       req <- MPI.isend (buf, 1::Int) rank MPI.unitTag MPI.commWorld++       buf' <- mallocForeignPtr @CInt+       req' <- MPI.irecv (buf', 1::Int) rank MPI.unitTag MPI.commWorld++       MPI.wait_ req+       st <- MPI.wait req'++       touchForeignPtr buf+       msg' <- withForeignPtr buf' peek++       source <- MPI.getSource st+       tag <- MPI.getTag st+       count <- MPI.getCount st MPI.datatypeInt+       (msg' == msg && source == rank && tag == MPI.unitTag && count == 1) @? ""+  ]++++collective :: TestTree+collective = testGroup "collective"+  [ testCase "allgather" $+    do rank <- MPI.commRank MPI.commWorld+       size <- MPI.commSize MPI.commWorld+       let rk = MPI.fromRank rank+       let sz = MPI.fromRank size+       let msg = 42 + rk+       buf <- mallocForeignPtr @CInt+       withForeignPtr buf $ \ptr -> poke ptr msg+       buf' <- mallocForeignPtrArray @CInt sz+       MPI.allgather (buf, 1::Int) (buf', 1::Int) MPI.commWorld+       msgs' <- withForeignPtr buf' (peekArray sz)+       msgs' == [42 .. 42 + fromIntegral (sz-1)] @? ""+  , testCase "allreduce" $+    do rank <- MPI.commRank MPI.commWorld+       size <- MPI.commSize MPI.commWorld+       let rk = MPI.fromRank rank+       let sz = MPI.fromRank size+       let msg = 42 + rk+       buf <- mallocForeignPtr @CInt+       withForeignPtr buf $ \ptr -> poke ptr msg+       buf' <- mallocForeignPtr @CInt+       MPI.allreduce (buf, 1::Int) (buf', 1::Int) MPI.opSum MPI.commWorld+       msg' <- withForeignPtr buf' peek+       msg' == sum [42 .. 42 + (sz-1)] @? ""+  , testCase "alltoall" $+    do rank <- MPI.commRank MPI.commWorld+       size <- MPI.commSize MPI.commWorld+       let rk = MPI.fromRank rank+       let sz = MPI.fromRank size+       let msgs = fromIntegral <$> [42 + sz * rk + i | i <- [0 .. sz-1]]+       buf <- mallocForeignPtrArray @CInt sz+       withForeignPtr buf $ \ptr -> pokeArray ptr msgs+       buf' <- mallocForeignPtrArray @CInt sz+       MPI.alltoall (buf, 1::Int) (buf', 1::Int) MPI.commWorld+       msgs' <- withForeignPtr buf' (peekArray sz)+       msgs' == (fromIntegral <$> [42 + sz * i + rk | i <- [0 .. sz-1]]) @? ""+  , testCase "barrier" $+    MPI.barrier MPI.commWorld+  , testCase "bcast" $+    do rank <- MPI.commRank MPI.commWorld+       let rk = MPI.fromRank rank+       let msg = 42 + rk+       buf <- mallocForeignPtr @CInt+       withForeignPtr buf $ \ptr -> poke ptr msg+       MPI.bcast (buf, 1::Int) MPI.rootRank MPI.commWorld+       msg' <- withForeignPtr buf peek+       msg' == 42 @? ""+  , testCase "exscan" $+    do rank <- MPI.commRank MPI.commWorld+       let rk = MPI.fromRank rank+       let msg = 42 + rk+       buf <- mallocForeignPtr @CInt+       withForeignPtr buf $ \ptr -> poke ptr msg+       buf' <- mallocForeignPtr @CInt+       MPI.exscan (buf, 1::Int) (buf', 1::Int) MPI.opSum MPI.commWorld+       msg' <- withForeignPtr buf' (if rank == 0 then \_ -> return 0 else peek)+       msg' == sum [42 .. 42 + rk-1] @? ""+  , testCase "gather" $+    do rank <- MPI.commRank MPI.commWorld+       size <- MPI.commSize MPI.commWorld+       let rk = MPI.fromRank rank+       let sz = MPI.fromRank size+       let isroot = rank == MPI.rootRank+       let msg = 42 + rk+       buf <- mallocForeignPtr @CInt+       withForeignPtr buf $ \ptr -> poke ptr msg+       buf' <- mallocForeignPtrArray @CInt (if isroot then sz else 0)+       MPI.gather (buf, 1::Int) (buf', 1::Int) MPI.rootRank MPI.commWorld+       msgs' <- withForeignPtr buf' $ peekArray (if isroot then sz else 0)+       (if isroot then msgs' == [42 .. 42 + fromIntegral sz-1] else True) @? ""+  , testCase "reduce" $+    do rank <- MPI.commRank MPI.commWorld+       size <- MPI.commSize MPI.commWorld+       let rk = MPI.fromRank rank+       let sz = MPI.fromRank size+       let isroot = rank == MPI.rootRank+       let msg = 42 + rk+       buf <- mallocForeignPtr @CInt+       withForeignPtr buf $ \ptr -> poke ptr msg+       buf' <- mallocForeignPtr @CInt+       MPI.reduce (buf, 1::Int) (buf', 1::Int) MPI.opSum MPI.rootRank+         MPI.commWorld+       msg' <- withForeignPtr buf' $ if isroot then peek else \_ -> return 0+       (if isroot then msg' == sum [42 .. 42 + sz-1] else True) @? ""+  , testCase "scan" $+    do rank <- MPI.commRank MPI.commWorld+       let rk = MPI.fromRank rank+       let msg = 42 + rk+       buf <- mallocForeignPtr @CInt+       withForeignPtr buf $ \ptr -> poke ptr msg+       buf' <- mallocForeignPtr @CInt+       MPI.scan (buf, 1::Int) (buf', 1::Int) MPI.opSum MPI.commWorld+       msg' <- withForeignPtr buf' peek+       msg' == sum [42 .. 42 + rk] @? ""+  , testCase "scatter" $+    do rank <- MPI.commRank MPI.commWorld+       size <- MPI.commSize MPI.commWorld+       let rk = MPI.fromRank rank+       let sz = MPI.fromRank size+       let isroot = rank == MPI.rootRank+       let msgs =+             if isroot then [42 + fromIntegral i | i <- [0 .. sz-1]] else []+       buf <- mallocForeignPtrArray @CInt (if isroot then sz else 0)+       withForeignPtr buf $+         \ptr -> if isroot then pokeArray ptr msgs else return ()+       buf' <- mallocForeignPtr @CInt+       MPI.scatter (buf, 1::Int) (buf', 1::Int) MPI.rootRank MPI.commWorld+       msg' <- withForeignPtr buf' peek+       msg' == 42 + rk @? ""+  ]++++reductions :: TestTree+reductions = testGroup "reduction operations"+  [ testCase "max" $+    do rank <- MPI.commRank MPI.commWorld+       size <- MPI.commSize MPI.commWorld+       let rk = MPI.fromRank rank+       let sz = MPI.fromRank size+       let isroot = rank == MPI.rootRank+       let msg = 42 + rk+       buf <- mallocForeignPtr @CInt+       withForeignPtr buf $ \ptr -> poke ptr msg+       buf' <- mallocForeignPtr @CInt+       MPI.reduce (buf, 1::Int) (buf', 1::Int) MPI.opMax MPI.rootRank+         MPI.commWorld+       msg' <- withForeignPtr buf' $ if isroot then peek else \_ -> return 0+       (if isroot then msg' == maximum [42 .. 42 + sz-1] else True) @? ""+  , testCase "min" $+    do rank <- MPI.commRank MPI.commWorld+       size <- MPI.commSize MPI.commWorld+       let rk = MPI.fromRank rank+       let sz = MPI.fromRank size+       let isroot = rank == MPI.rootRank+       let msg = 42 + rk+       buf <- mallocForeignPtr @CInt+       withForeignPtr buf $ \ptr -> poke ptr msg+       buf' <- mallocForeignPtr @CInt+       MPI.reduce (buf, 1::Int) (buf', 1::Int) MPI.opMin MPI.rootRank+         MPI.commWorld+       msg' <- withForeignPtr buf' $ if isroot then peek else \_ -> return 0+       (if isroot then msg' == minimum [42 .. 42 + sz-1] else True) @? ""+  , testCase "sum" $+    do rank <- MPI.commRank MPI.commWorld+       size <- MPI.commSize MPI.commWorld+       let rk = MPI.fromRank rank+       let sz = MPI.fromRank size+       let isroot = rank == MPI.rootRank+       let msg = 42 + rk+       buf <- mallocForeignPtr @CInt+       withForeignPtr buf $ \ptr -> poke ptr msg+       buf' <- mallocForeignPtr @CInt+       MPI.reduce (buf, 1::Int) (buf', 1::Int) MPI.opSum MPI.rootRank+         MPI.commWorld+       msg' <- withForeignPtr buf' $ if isroot then peek else \_ -> return 0+       (if isroot then msg' == sum [42 .. 42 + sz-1] else True) @? ""+  ]++++collectiveNonBlocking :: TestTree+collectiveNonBlocking = testGroup "collective non-blocking"+  [ testCase "iallgather" $+    do rank <- MPI.commRank MPI.commWorld+       size <- MPI.commSize MPI.commWorld+       let rk = MPI.fromRank rank+       let sz = MPI.fromRank size+       let msg = 42 + rk+       buf <- mallocForeignPtr @CInt+       withForeignPtr buf $ \ptr -> poke ptr msg+       buf' <- mallocForeignPtrArray @CInt sz+       req <- MPI.iallgather (buf, 1::Int) (buf', 1::Int) MPI.commWorld+       MPI.wait_ req+       touchForeignPtr buf+       msgs' <- withForeignPtr buf' (peekArray sz)+       msgs' == [42 .. 42 + fromIntegral (sz-1)] @? ""+  , testCase "iallreduce" $+    do rank <- MPI.commRank MPI.commWorld+       size <- MPI.commSize MPI.commWorld+       let rk = MPI.fromRank rank+       let sz = MPI.fromRank size+       let msg = 42 + rk+       buf <- mallocForeignPtr @CInt+       withForeignPtr buf $ \ptr -> poke ptr msg+       buf' <- mallocForeignPtr @CInt+       req <- MPI.iallreduce (buf, 1::Int) (buf', 1::Int) MPI.opSum+              MPI.commWorld+       MPI.wait_ req+       touchForeignPtr buf+       msg' <- withForeignPtr buf' peek+       msg' == sum [42 .. 42 + (sz-1)] @? ""+  , testCase "ialltoall" $+    do rank <- MPI.commRank MPI.commWorld+       size <- MPI.commSize MPI.commWorld+       let rk = MPI.fromRank rank+       let sz = MPI.fromRank size+       let msgs = fromIntegral <$> [42 + sz * rk + i | i <- [0 .. sz-1]]+       buf <- mallocForeignPtrArray @CInt sz+       withForeignPtr buf $ \ptr -> pokeArray ptr msgs+       buf' <- mallocForeignPtrArray @CInt sz+       req <- MPI.ialltoall (buf, 1::Int) (buf', 1::Int) MPI.commWorld+       MPI.wait_ req+       touchForeignPtr buf+       msgs' <- withForeignPtr buf' (peekArray sz)+       msgs' == (fromIntegral <$> [42 + sz * i + rk | i <- [0 .. sz-1]]) @? ""+  , testCase "ibarrier" $+    do req <- MPI.ibarrier MPI.commWorld+       MPI.wait_ req+  , testCase "ibcast" $+    do rank <- MPI.commRank MPI.commWorld+       let rk = MPI.fromRank rank+       let msg = 42 + rk+       buf <- mallocForeignPtr @CInt+       withForeignPtr buf $ \ptr -> poke ptr msg+       req <- MPI.ibcast (buf, 1::Int) MPI.rootRank MPI.commWorld+       MPI.wait_ req+       touchForeignPtr buf+       msg' <- withForeignPtr buf peek+       msg' == 42 @? ""+  , testCase "iexscan" $+    do rank <- MPI.commRank MPI.commWorld+       let rk = MPI.fromRank rank+       let msg = 42 + rk+       buf <- mallocForeignPtr @CInt+       withForeignPtr buf $ \ptr -> poke ptr msg+       buf' <- mallocForeignPtr @CInt+       req <- MPI.iexscan (buf, 1::Int) (buf', 1::Int) MPI.opSum MPI.commWorld+       MPI.wait_ req+       touchForeignPtr buf+       msg' <- withForeignPtr buf' (if rank == 0 then \_ -> return 0 else peek)+       msg' == sum [42 .. 42 + rk-1] @? ""+  , testCase "igather" $+    do rank <- MPI.commRank MPI.commWorld+       size <- MPI.commSize MPI.commWorld+       let rk = MPI.fromRank rank+       let sz = MPI.fromRank size+       let isroot = rank == MPI.rootRank+       let msg = 42 + rk+       buf <- mallocForeignPtr @CInt+       withForeignPtr buf $ \ptr -> poke ptr msg+       buf' <- mallocForeignPtrArray @CInt (if isroot then sz else 0)+       req <- MPI.igather (buf, 1::Int) (buf', 1::Int) MPI.rootRank+              MPI.commWorld+       MPI.wait_ req+       touchForeignPtr buf+       msgs' <- withForeignPtr buf' $ peekArray (if isroot then sz else 0)+       (if isroot then msgs' == [42 .. 42 + fromIntegral sz-1] else True) @? ""+  , testCase "ireduce" $+    do rank <- MPI.commRank MPI.commWorld+       size <- MPI.commSize MPI.commWorld+       let rk = MPI.fromRank rank+       let sz = MPI.fromRank size+       let isroot = rank == MPI.rootRank+       let msg = 42 + rk+       buf <- mallocForeignPtr @CInt+       withForeignPtr buf $ \ptr -> poke ptr msg+       buf' <- mallocForeignPtr @CInt+       req <- MPI.ireduce (buf, 1::Int) (buf', 1::Int) MPI.opSum MPI.rootRank+              MPI.commWorld+       MPI.wait_ req+       touchForeignPtr buf+       msg' <- withForeignPtr buf' $ if isroot then peek else \_ -> return 0+       (if isroot then msg' == sum [42 .. 42 + sz-1] else True) @? ""+  , testCase "iscan" $+    do rank <- MPI.commRank MPI.commWorld+       let rk = MPI.fromRank rank+       let msg = 42 + rk+       buf <- mallocForeignPtr @CInt+       withForeignPtr buf $ \ptr -> poke ptr msg+       buf' <- mallocForeignPtr @CInt+       req <- MPI.iscan (buf, 1::Int) (buf', 1::Int) MPI.opSum MPI.commWorld+       MPI.wait_ req+       touchForeignPtr buf+       msg' <- withForeignPtr buf' peek+       msg' == sum [42 .. 42 + rk] @? ""+  , testCase "iscatter" $+    do rank <- MPI.commRank MPI.commWorld+       size <- MPI.commSize MPI.commWorld+       let rk = MPI.fromRank rank+       let sz = MPI.fromRank size+       let isroot = rank == MPI.rootRank+       let msgs = [42 + fromIntegral i | i <- [0 .. sz-1]]+       buf <- mallocForeignPtrArray @CInt (if isroot then sz else 0)+       withForeignPtr buf $ \ptr -> pokeArray ptr msgs+       buf' <- mallocForeignPtr @CInt+       req <- MPI.iscatter (buf, 1::Int) (buf', 1::Int) MPI.rootRank+              MPI.commWorld+       MPI.wait_ req+       touchForeignPtr buf+       msg' <- withForeignPtr buf' peek+       msg' == 42 + rk @? ""+  ]++++dynamic :: TestTree+dynamic = testGroup "dynamic"+  [ testCase "sequential" $+    do rank <- MPI.commRank MPI.commWorld+       size <- MPI.commSize MPI.commWorld++       breq <- newIORef Nothing+       let signalDone =+             do r <- MPI.ibarrier MPI.commWorld+                writeIORef breq (Just r)+       let checkDone =+             do mreq <- readIORef breq+                case mreq of+                  Nothing -> return False+                  Just req -> MPI.test_ req++       sendreqs <- newIORef []+       let sendMsg dst =+             when (dst < size) $+             do buf <- mallocForeignPtr @CInt+                withForeignPtr buf $ \ptr -> poke ptr 42+                r <- MPI.isend (buf, 1::Int) dst MPI.unitTag MPI.commWorld+                modifyIORef' sendreqs ((buf, r) :)+       let drainSendQueue =+             do srs <- readIORef sendreqs+                srs' <- filterM (\(_, r) -> not <$> MPI.test_ r) srs+                writeIORef sendreqs srs'+       let checkForMsg = MPI.iprobe MPI.anySource MPI.unitTag MPI.commWorld+       let recvMsg st =+             do src <- MPI.getSource st+                buf <- mallocForeignPtr @CInt+                MPI.recv_ (buf, 1::Int) src MPI.unitTag MPI.commWorld++       -- each rank sends to the next+       when (rank == 0) $+         do sendMsg (rank + 1)+            signalDone++       untilM_+         (do drainSendQueue+             mst <- checkForMsg+             case mst of+               Nothing -> return ()+               Just st -> do recvMsg st+                             sendMsg (rank + 1)+                             signalDone+         )+         checkDone+  , testCase "tree" $+    do rank <- MPI.commRank MPI.commWorld+       size <- MPI.commSize MPI.commWorld++       breq <- newIORef Nothing+       let signalDone =+             do r <- MPI.ibarrier MPI.commWorld+                writeIORef breq (Just r)+       let checkDone =+             do mreq <- readIORef breq+                case mreq of+                  Nothing -> return False+                  Just req -> MPI.test_ req++       sendreqs <- newIORef []+       let sendMsg dst =+             when (dst < size) $+             do buf <- mallocForeignPtr @CInt+                withForeignPtr buf $ \ptr -> poke ptr 42+                r <- MPI.isend (buf, 1::Int) dst MPI.unitTag MPI.commWorld+                modifyIORef' sendreqs ((buf, r) :)+       let drainSendQueue =+             do srs <- readIORef sendreqs+                srs' <- filterM (\(_, r) -> not <$> MPI.test_ r) srs+                writeIORef sendreqs srs'+       let checkForMsg = MPI.iprobe MPI.anySource MPI.unitTag MPI.commWorld+       let recvMsg st =+             do src <- MPI.getSource st+                buf <- mallocForeignPtr @CInt+                MPI.recv_ (buf, 1::Int) src MPI.unitTag MPI.commWorld++       -- rank r sends to 2*r+1 and 2*r+2+       when (rank == 0) $+         do sendMsg (2 * rank + 1)+            sendMsg (2 * rank + 2)+            signalDone++       untilM_+         (do drainSendQueue+             mst <- checkForMsg+             case mst of+               Nothing -> return ()+               Just st -> do recvMsg st+                             sendMsg (2 * rank + 1)+                             sendMsg (2 * rank + 2)+                             signalDone+         )+         checkDone+  , testCase "multi-threaded" $+    do mts <- MPI.threadSupport+       let Just ts = mts+       when (ts >= MPI.ThreadMultiple) $+         do rank <- MPI.commRank MPI.commWorld+            size <- MPI.commSize MPI.commWorld+     +            breq <- newEmptyMVar+            let signalDone =+                  do _ <- forkIO $+                       do req <- MPI.ibarrier MPI.commWorld+                          whileM_ (not <$> MPI.test_ req) yield+                          putMVar breq ()+                     return ()+            let checkDone = not <$> isEmptyMVar breq+     +            let sendMsg dst =+                  when (dst < size) $+                  do _ <- forkIO $+                       do buf <- mallocForeignPtr @CInt+                          withForeignPtr buf $ \ptr -> poke ptr 42+                          req <- MPI.isend (buf, 1::Int) dst MPI.unitTag+                                 MPI.commWorld+                          whileM_ (not <$> MPI.test_ req) yield+                     return ()+            let checkForMsg = MPI.iprobe MPI.anySource MPI.unitTag MPI.commWorld+            let recvMsg st =+                  do src <- MPI.getSource st+                     buf <- mallocForeignPtr @CInt+                     MPI.recv_ (buf, 1::Int) src MPI.unitTag MPI.commWorld+     +            -- rank r sends to 2*r+1 and 2*r+2+            when (rank == 0) $+              do sendMsg (2 * rank + 1)+                 sendMsg (2 * rank + 2)+                 signalDone+     +            untilM_+              (do mst <- checkForMsg+                  case mst of+                    Nothing -> return ()+                    Just st -> do recvMsg st+                                  sendMsg (2 * rank + 1)+                                  sendMsg (2 * rank + 2)+                                  signalDone+                  yield+              )+              checkDone+  ]
+ test/simple/Main.hs view
@@ -0,0 +1,155 @@+{-# LANGUAGE ScopedTypeVariables #-}++import System.IO+import System.Exit++import qualified Control.Distributed.MPI.Simple as MPI++default (Int)++++--------------------------------------------------------------------------------++infix 1 @?+(@?) :: Bool -> String -> IO ()+x @? msg = if not x then die msg else return ()++infix 1 @?=+(@?=) :: Eq a => a -> a -> IO ()+x @?= y = x == y @? "test failed"++++type TestTree = IO ()++testCase :: String -> IO () -> TestTree+testCase name test =+  do rank <- MPI.commRank MPI.commWorld+     if rank == 0+       then do putStrLn $ "  " ++ name ++ "..."+               hFlush stdout+       else return ()+     MPI.barrier MPI.commWorld+     test+     MPI.barrier MPI.commWorld++++testGroup :: String -> [TestTree] -> TestTree+testGroup name cases =+  do rank <- MPI.commRank MPI.commWorld+     if rank == 0+       then do putStrLn $ name ++ ":"+               hFlush stdout+       else return ()+     sequence_ cases++++defaultMain :: TestTree -> IO ()+defaultMain tree =+  do rank <- MPI.commRank MPI.commWorld+     size <- MPI.commSize MPI.commWorld+     if rank == 0+       then do putStrLn $ "MPI Tests: running on " ++ show size ++ " processes"+               hFlush stdout+       else return ()+     tree++++--------------------------------------------------------------------------------++++main :: IO ()+main = MPI.mainMPI $ defaultMain tests++tests :: TestTree+tests = testGroup "MPI"+  [ rankSize+  , pointToPoint+  , pointToPointNonBlocking+  , collective+  , collectiveNonBlocking+  ]++++rankSize :: TestTree+rankSize = testGroup "rank and size"+  [ testCase "commSelf" $+    do rank <- MPI.commRank MPI.commSelf+       size <- MPI.commSize MPI.commSelf+       rank == 0 && size == 1 @? ""+  , testCase "commWorld" $+    do rank <- MPI.commRank MPI.commWorld+       size <- MPI.commSize MPI.commWorld+       rank >= 0 && rank < size @? ""+  ]++++pointToPoint :: TestTree+pointToPoint = testGroup "point-to-point"+  [ testCase "sendrecv" $+    do rank <- MPI.commRank MPI.commWorld+       size <- MPI.commSize MPI.commWorld+       let sendmsg :: String = "Hello, World!"+       let sendrank = (rank + 1) `mod` size+       let recvrank = (rank - 1) `mod` size+       (status, recvmsg :: String) <-+         MPI.sendrecv sendmsg sendrank MPI.unitTag recvrank MPI.unitTag+         MPI.commWorld+       (recvmsg == sendmsg &&+        MPI.msgRank status == recvrank &&+        MPI.msgTag status == MPI.unitTag) @? ""+  ]++++pointToPointNonBlocking :: TestTree+pointToPointNonBlocking = testGroup "point-to-point non-blocking"+  [ testCase "send and recv" $+    do rank <- MPI.commRank MPI.commWorld+       size <- MPI.commSize MPI.commWorld+       let sendmsg :: String = "Hello, World!"+       let sendrank = (rank + 1) `mod` size+       sendreq <- MPI.isend sendmsg sendrank MPI.unitTag MPI.commWorld+       let recvrank = (rank - 1) `mod` size+       recvreq <- MPI.irecv recvrank MPI.unitTag MPI.commWorld+       (sendstatus, ()) <- MPI.wait sendreq+       (recvstatus, recvmsg :: String) <- MPI.wait recvreq+       (recvmsg == sendmsg &&+        MPI.msgRank sendstatus == sendrank &&+        MPI.msgTag sendstatus == MPI.unitTag &&+        MPI.msgRank recvstatus == recvrank &&+        MPI.msgTag recvstatus == MPI.unitTag) @? ""+  ]++++collective :: TestTree+collective = testGroup "collective"+  [ testCase "barrier" $+    do MPI.barrier MPI.commWorld+  , testCase "bcast" $+    do rank <- MPI.commRank MPI.commWorld+       let sendmsg :: String = "Hello, World!"+       recvmsg :: String <-+         if rank == MPI.rootRank+         then do MPI.bcastSend sendmsg MPI.rootRank MPI.commWorld+                 return sendmsg+         else do MPI.bcastRecv MPI.rootRank MPI.commWorld+       recvmsg == sendmsg @? ""+  ]++++collectiveNonBlocking :: TestTree+collectiveNonBlocking = testGroup "collective non-blocking"+  [ testCase "barrier" $+    do req <- MPI.ibarrier MPI.commWorld+       MPI.wait_ req+  ]