packages feed

context-resource (empty) → 0.1.0.0

raw patch · 8 files changed

+328/−0 lines, 8 filesdep +asyncdep +basedep +context

Dependencies added: async, base, context, context-resource, hspec

Files

+ CHANGELOG.md view
@@ -0,0 +1,5 @@+# Change log++## 0.1.0.0++Initial release
+ LICENSE.md view
@@ -0,0 +1,23 @@+[The MIT License (MIT)][]++Copyright (c) 2020 Jason Shipman++Permission is hereby granted, free of charge, to any person obtaining a copy of+this software and associated documentation files (the "Software"), to deal in+the Software without restriction, including without limitation the rights to+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies+of the Software, and to permit persons to whom the Software is furnished to do+so, subject to the following conditions:++The above copyright notice and this permission notice shall be included in all+copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+SOFTWARE.++[The MIT License (MIT)]: https://opensource.org/licenses/MIT
+ README.md view
@@ -0,0 +1,14 @@+# [context-resource][]++[![Version badge][]][version]++🚧 This README is under construction and could use some love. 🚧++`context-resource` contains a thread-safe, pool-compatible resource provider+abstraction that supports resource-sharing within nested actions.++See the Haddocks for more info on the library.++[context-resource]: https://github.com/jship/context+[Version badge]: https://img.shields.io/hackage/v/context-resource?color=brightgreen&label=version&logo=haskell+[version]: https://hackage.haskell.org/package/context-resource
+ context-resource.cabal view
@@ -0,0 +1,64 @@+cabal-version: 1.12++-- This file has been generated from package.yaml by hpack version 0.33.0.+--+-- see: https://github.com/sol/hpack+--+-- hash: 25e9584bac69d5f10c59d16e951839e19321a6ec9958b9edf594a1256b310ff9++name:           context-resource+version:        0.1.0.0+synopsis:       Thread-safe, pool-compatible resource provider+description:    A thread-safe, pool-compatible resource provider abstraction that supports+                resource-sharing within nested actions.+category:       Data+homepage:       https://github.com/jship/context#readme+bug-reports:    https://github.com/jship/context/issues+author:         Jason Shipman+maintainer:     Jason Shipman+copyright:      2020 (c) Jason Shipman+license:        MIT+license-file:   LICENSE.md+build-type:     Simple+extra-source-files:+    CHANGELOG.md+    LICENSE.md+    package.yaml+    README.md++source-repository head+  type: git+  location: https://github.com/jship/context+  subdir: context-resource++library+  exposed-modules:+      Context.Resource+  other-modules:+      Paths_context_resource+  hs-source-dirs:+      library+  ghc-options: -Wall -fwarn-tabs -Wincomplete-uni-patterns -Wredundant-constraints+  build-depends:+      base >=4.12 && <5+    , context >=0.1.0.0 && <0.2+  default-language: Haskell2010++test-suite context-resource-test-suite+  type: exitcode-stdio-1.0+  main-is: Driver.hs+  other-modules:+      Test.Context.ResourceSpec+      Paths_context_resource+  hs-source-dirs:+      test-suite+  ghc-options: -Wall -fwarn-tabs -Wincomplete-uni-patterns -Wredundant-constraints+  build-tool-depends:+      hspec-discover:hspec-discover+  build-depends:+      async+    , base+    , context+    , context-resource+    , hspec+  default-language: Haskell2010
+ library/Context/Resource.hs view
@@ -0,0 +1,96 @@+{-# LANGUAGE BlockArguments #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE NoImplicitPrelude #-}+{-# LANGUAGE RankNTypes #-}++module Context.Resource+  ( -- * Introduction+    -- $intro++    -- * Provider+    Provider+  , withProvider++    -- * Core operations+  , withResource+  , shareResource++    -- * Convenience+  , withSharedResource+  ) where++import Prelude+import qualified Context++-- | An opaque resource provider.+--+-- @since 0.1.0.0+newtype Provider res = Provider+  { store :: Context.Store (WithRes res)+  }++newtype WithRes res = WithRes (forall r. (res -> IO r) -> IO r)++-- | Given a @with@-style function to acquire a resource, supplies the caller+-- with a resource 'Provider'. This 'Provider' should ideally be long-lived and+-- threaded throughout the application to the components that need to acquire+-- resources.+--+-- @since 0.1.0.0+withProvider+  :: (forall r. (res -> IO r) -> IO r)+  -> (Provider res -> IO a)+  -> IO a+withProvider withRes f = do+  Context.withStore Context.noPropagation (Just (WithRes withRes)) \store -> do+    f Provider { store }++-- | Acquire a resource from the specified 'Provider', for the duration of the+-- specified action.+--+-- @since 0.1.0.0+withResource :: Provider res -> (res -> IO a) -> IO a+withResource Provider { store } f = do+  WithRes withRes <- Context.mine store+  withRes f++-- | Tell the specified 'Provider' to share the specified resource for the+-- duration of the specified action. All calls to 'withResource' (or+-- 'withSharedResource') within the action will return the shared resource.+--+-- @since 0.1.0.0+shareResource :: Provider res -> res -> IO a -> IO a+shareResource Provider { store } resource action = do+  Context.use store (WithRes ($ resource)) action++-- | Acquire a resource from the specified 'Provider' and share that resource+-- for the duration of the specified action. All calls to 'withResource' (or+-- 'withSharedResource') within the action will return the shared resource.+--+-- This is a convenience function combining 'withResource' and 'shareResource'.+--+-- @since 0.1.0.0+withSharedResource :: Provider res -> (res -> IO a) -> IO a+withSharedResource provider f = do+  withResource provider \resource -> do+    shareResource provider resource do+      f resource++-- $intro+--+-- This module provides a thread-safe, pool-compatible resource provider+-- abstraction that supports resource-sharing within nested actions. While it+-- was designed with resource pools in mind, the interface supports any+-- @with@-style means of acquiring a resource.+--+-- 'withResource' can be used to acquire a resource from the provider, and+-- 'shareResource' can be used to share a particular resource for the duration+-- of an action. Subsequent calls to 'shareResource' in that action are+-- idempotent. Note that if a resource-shared action spins up new threads, the+-- shared resource will /not/ be shared implicitly across thread boundaries.+--+-- While 'shareResource' offers the most control over resource-sharing,+-- 'withSharedResource' can be used as a convenience in the relatively common+-- case where a resource is acquired and then immediately shared within an+-- inner action.
+ package.yaml view
@@ -0,0 +1,44 @@+name: context-resource+version: '0.1.0.0'+github: "jship/context/context-resource"+license: MIT+license-file: LICENSE.md+copyright: 2020 (c) Jason Shipman+author: "Jason Shipman"+maintainer: "Jason Shipman"+synopsis: Thread-safe, pool-compatible resource provider+description: |+  A thread-safe, pool-compatible resource provider abstraction that supports+  resource-sharing within nested actions.+category: Data++extra-source-files:+- CHANGELOG.md+- LICENSE.md+- package.yaml+- README.md++ghc-options:+  - -Wall+  - -fwarn-tabs+  - -Wincomplete-uni-patterns+  - -Wredundant-constraints++library:+  dependencies:+  - base >=4.12 && <5+  - context >=0.1.0.0 && <0.2+  source-dirs: library++tests:+  context-resource-test-suite:+    source-dirs: test-suite+    main: Driver.hs+    build-tools:+    - hspec-discover+    dependencies:+    - async+    - base+    - context+    - context-resource+    - hspec
+ test-suite/Driver.hs view
@@ -0,0 +1,1 @@+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
+ test-suite/Test/Context/ResourceSpec.hs view
@@ -0,0 +1,81 @@+{-# LANGUAGE BlockArguments #-}+{-# LANGUAGE NoImplicitPrelude #-}++module Test.Context.ResourceSpec+  ( spec+  ) where++import Context.Resource (shareResource, withProvider, withResource, withSharedResource)+import Foreign (Ptr)+import Prelude+import Test.Hspec+import qualified Context+import qualified Control.Concurrent.Async as Async+import qualified Control.Monad as Monad+import qualified Foreign++spec :: Spec+spec = do+  describe "withResource" do+    it "concurrent test" do+      Async.forConcurrently_ [1 :: Int ..10] $ const do+        withProvider withRes \provider -> do+          withResource provider \ptr1 -> do+            withResource provider \ptr2 -> do+              ptr1 `shouldNotBe` ptr2++  describe "shareResource" do+    it "concurrent test" do+      Async.forConcurrently_ [1 :: Int ..10] $ const do+        withProvider withRes \provider -> do+          withResource provider \ptr1 -> do+            shareResource provider ptr1 do+              withResource provider \ptr2 -> do+                ptr1 `shouldBe` ptr2+                withResource provider \ptr3 -> do+                  ptr2 `shouldBe` ptr3+            withResource provider \ptr4 -> do+              ptr1 `shouldNotBe` ptr4+    it "cannot implicitly share across thread boundaries" do+      withProvider withRes \provider -> do+        withResource provider \parentThread'sPtr1 -> do+          shareResource provider parentThread'sPtr1 do+            withResource provider \parentThread'sPtr2 -> do+              parentThread'sPtr1 `shouldBe` parentThread'sPtr2+            threadDone <- Context.newEmptyMVar+            Monad.void $ Context.forkIO do+              withResource provider \childThread'sPtr1 -> do+                parentThread'sPtr1 `shouldNotBe` childThread'sPtr1+              Context.putMVar threadDone ()+            Context.takeMVar threadDone+          withResource provider \parentThread'sPtr3 -> do+            parentThread'sPtr1 `shouldNotBe` parentThread'sPtr3++  describe "withSharedResource" do+    it "concurrent test" do+      Async.forConcurrently_ [1 :: Int ..10] $ const do+        withProvider withRes \provider -> do+          withSharedResource provider \ptr1 -> do+            withResource provider \ptr2 -> do+              ptr1 `shouldBe` ptr2+              shareResource provider ptr2 do+                withResource provider \ptr3 -> do+                  ptr2 `shouldBe` ptr3+            withResource provider \ptr4 -> do+              ptr1 `shouldBe` ptr4+    it "cannot implicitly share across thread boundaries" do+      withProvider withRes \provider -> do+        withSharedResource provider \parentThread'sPtr1 -> do+          withResource provider \parentThread'sPtr2 -> do+            parentThread'sPtr1 `shouldBe` parentThread'sPtr2+          threadDone <- Context.newEmptyMVar+          Monad.void $ Context.forkIO do+            withResource provider \childThread'sPtr1 -> do+              parentThread'sPtr1 `shouldNotBe` childThread'sPtr1+            Context.putMVar threadDone ()+          Context.takeMVar threadDone+          withResource provider \parentThread'sPtr3 -> do+            parentThread'sPtr1 `shouldBe` parentThread'sPtr3++withRes :: (Ptr Int -> IO a) -> IO a+withRes = Foreign.withArray [1..3]