jvm-streaming 0.1 → 0.2
raw patch · 3 files changed
+30/−16 lines, 3 filesdep ~inline-javadep ~jnidep ~jvmnew-uploader
Dependency ranges changed: inline-java, jni, jvm
Files
- jvm-streaming.cabal +3/−4
- src/Language/Java/Streaming.hs +20/−12
- tests/Language/Java/StreamingSpec.hs +7/−0
jvm-streaming.cabal view
@@ -1,5 +1,5 @@ name: jvm-streaming-version: 0.1+version: 0.2 synopsis: Expose Java iterators as streams from the streaming package. description: Please see README.md. homepage: http://github.com/tweag/inline-java/tree/master/jvm-streaming#readme@@ -26,8 +26,8 @@ build-depends: base >= 4.7 && < 5, distributed-closure >= 0.3,- jni >= 0.2.1,- jvm >= 0.1,+ jni >= 0.3.0,+ jvm >= 0.2.0, inline-java >= 0.6, singletons >= 2.2, streaming >= 0.1.4@@ -48,4 +48,3 @@ jvm-streaming, streaming default-language: Haskell2010- extra-libraries: pthread
src/Language/Java/Streaming.hs view
@@ -20,11 +20,13 @@ module Language.Java.Streaming () where import Control.Distributed.Closure.TH+import qualified Data.Coerce as Coerce import Data.IORef (IORef, newIORef, readIORef, writeIORef) import Data.Int (Int64) import Data.Singletons (SomeSing(..)) import Data.Word (Word8)-import Foreign.Ptr (FunPtr, freeHaskellFunPtr, intPtrToPtr, ptrToIntPtr)+import Foreign.Ptr (FunPtr, Ptr, freeHaskellFunPtr, intPtrToPtr, ptrToIntPtr)+import Foreign.ForeignPtr.Unsafe (unsafeForeignPtrToPtr) import qualified Foreign.JNI as JNI import Foreign.JNI.Types (jnull) import GHC.Stable@@ -41,10 +43,11 @@ isPoppableStream :: IORef (Stream (Of a) IO ()) -> IO Word8 isPoppableStream ref = do- stream <- readIORef ref- Streaming.uncons stream >>= \case+ readIORef ref >>= Streaming.uncons >>= \case Nothing -> return $ fromIntegral $ fromEnum False- Just _ -> return $ fromIntegral $ fromEnum True+ Just (x, stream) -> do+ writeIORef ref (Streaming.cons x stream)+ return $ fromIntegral $ fromEnum True popStream :: Reflect a ty => IORef (Stream (Of a) IO ()) -> IO (J ty) popStream ref = do@@ -58,25 +61,25 @@ writeIORef ref stream' reflect x -type JNIFun a = JNIEnv -> JObject -> IO a+type JNIFun a = JNIEnv -> Ptr JObject -> IO a foreign import ccall "wrapper" wrapObjectFun- :: JNIFun (J ty) -> IO (FunPtr (JNIFun (J ty)))+ :: JNIFun (Ptr (J ty)) -> IO (FunPtr (JNIFun (Ptr (J ty)))) foreign import ccall "wrapper" wrapBooleanFun :: JNIFun Word8 -> IO (FunPtr (JNIFun Word8)) -- Export only to get a FunPtr. foreign export ccall "jvm_streaming_freeIterator" freeIterator- :: JNIEnv -> JObject -> Int64 -> IO ()+ :: JNIEnv -> Ptr JObject -> Int64 -> IO () foreign import ccall "&jvm_streaming_freeIterator" freeIteratorPtr- :: FunPtr (JNIEnv -> JObject -> Int64 -> IO ())+ :: FunPtr (JNIEnv -> Ptr JObject -> Int64 -> IO ()) data FunPtrTable = forall ty. FunPtrTable { hasNextPtr :: FunPtr (JNIFun Word8)- , nextPtr :: FunPtr (JNIFun (J ty))+ , nextPtr :: FunPtr (JNIFun (Ptr (J ty))) } -freeIterator :: JNIEnv -> JObject -> Int64 -> IO ()+freeIterator :: JNIEnv -> Ptr JObject -> Int64 -> IO () freeIterator _ _ ptr = do let sptr = castPtrToStablePtr $ intPtrToPtr $ fromIntegral ptr FunPtrTable{..} <- deRefStablePtr sptr@@ -91,7 +94,9 @@ newIterator stream = do ref <- newIORef stream hasNextPtr <- wrapBooleanFun $ \_ _ -> isPoppableStream ref- nextPtr <- wrapObjectFun $ \_ _ -> popStream ref+ nextPtr <- wrapObjectFun $ \_ _ ->+ -- Conversion is safe, because result is always a reflected object.+ unsafeForeignPtrToPtr <$> Coerce.coerce <$> popStream ref -- Keep FunPtr's in a table that can be referenced from the Java side, so -- that they can be freed. tblPtr :: Int64 <- fromIntegral . ptrToIntPtr . castStablePtrToPtr <$> newStablePtr FunPtrTable{..}@@ -136,7 +141,10 @@ type instance Interp (Stream (Of a) m r) = 'Iface "java.util.Iterator" instance Reify a ty => Reify (Stream (Of a) IO ()) ('Iface "java.util.Iterator") where- reify it = return $ Streaming.untilRight $ do+ reify itLocal = do+ -- We make sure the iterator remains valid while we reference it.+ it <- JNI.newGlobalRef itLocal+ return $ Streaming.untilRight $ do call it "hasNext" [] >>= \case False -> return (Right ()) True -> do
tests/Language/Java/StreamingSpec.hs view
@@ -4,6 +4,7 @@ module Language.Java.StreamingSpec where import Data.Int+import Data.IORef import Language.Java import Language.Java.Inline import Language.Java.Streaming ()@@ -34,3 +35,9 @@ iterator <- reflect (Streaming.each [1..10000] :: Stream (Of Int32) IO ()) stream <- reify iterator Streaming.toList_ stream `shouldReturn` [1..10000 :: Int32]+ it "don't redo effects" $ do+ ref <- newIORef (1 :: Int32)+ iterator <- reflect $+ Streaming.replicateM 10 $ atomicModifyIORef ref (\i -> (i + 1, i))+ stream <- reify iterator+ Streaming.toList_ stream `shouldReturn` [1..10 :: Int32]