copy.tri (3322B)
1 !import base as B 2 !import io as IO 3 !import handlers as H 4 !export cancelledCopy 5 !export copy 6 !export handledCopy 7 !export borrowedCopy 8 !export transferredCopy 9 !export joinedOnExitCopy 10 !export handledChildCopy 11 12 pump reader writer = IO.copyStream reader writer 13 14 copy source destination = IO.io <| IO.scope (owner : do IO.onOk 15 reader <- IO.openReader owner source 16 writer <- IO.openWriter owner destination 17 pump reader writer) 18 19 -- A captured service supplies the implementation, while the caller requests 20 -- policy-level work. The host scope, not handler removal, owns both resources. 21 service reader writer "run" _ = H.liftIO (pump reader writer) 22 service _ _ operation argument = H.forward "copy" operation argument 23 handledCopy source destination = IO.io <| IO.scope (owner : do IO.onOk 24 reader <- IO.openReader owner source 25 writer <- IO.openWriter owner destination 26 H.runIO (H.handle "copy" (service reader writer) (H.request "copy" "run" t))) 27 28 29 joinCopy spawned = case spawned of 30 [task ticket] = IO.require (IO.join ticket) (outcome : case outcome of 31 ["completed" value] = IO.pure value 32 ["failed" diagnostic cleanup] = IO.fail [diagnostic cleanup] 33 ["cancelled" reason cleanup] = IO.fail [reason cleanup] 34 _ = IO.fail "Invalid task outcome") 35 _ = IO.fail "Invalid spawn reply" 36 37 borrowedCopy source destination = IO.io <| IO.scope (owner : do IO.require 38 reader <- IO.openReader owner source 39 writer <- IO.openWriter owner destination 40 spawned <- IO.spawn 0 1 [] (_ : pump reader writer) 41 joinCopy spawned) 42 43 transferredPump [reader writer] = pump reader writer 44 transferredPump _ = IO.fail "Invalid transferred handles" 45 transferredCopy source destination = IO.io <| IO.scope (owner : do IO.require 46 reader <- IO.openReader owner source 47 writer <- IO.openWriter owner destination 48 spawned <- IO.spawn 0 1 [reader writer] transferredPump 49 joinCopy spawned) 50 51 -- No ticket and no explicit join: scope return still waits for complete copying. 52 joinedOnExitCopy source destination = IO.io <| IO.scope (owner : do IO.require 53 reader <- IO.openReader owner source 54 writer <- IO.openWriter owner destination 55 IO.spawn 0 0 [] (_ : pump reader writer) 56 IO.pure (B.ok t t)) 57 58 handledChildCopy source destination = IO.io <| IO.scope (owner : do IO.require 59 reader <- IO.openReader owner source 60 writer <- IO.openWriter owner destination 61 H.runIO (H.handle "copy" (service reader writer) (do H.bind 62 reply <- H.spawn 0 1 [] (_ : H.request "copy" "run" t) 63 H.liftIO (IO.require (IO.pure reply) joinCopy)))) 64 65 -- Cancellation after one acknowledged chunk is not rollback. The supervisor 66 -- observes the outcome only after the transferred handles have been released. 67 cancelPump [reader writer] = IO.require (IO.readSome reader 4096) (reply : case reply of 68 ("eof", t) = IO.cancelled "requested" 69 ("data", bytes) = IO.require (IO.writeAll writer bytes) (_ : IO.cancelled "requested") 70 _ = IO.fail "Invalid read reply") 71 cancelPump _ = IO.fail "Invalid transferred handles" 72 cancelledCopy source destination = IO.io <| IO.scope (owner : do IO.require 73 reader <- IO.openReader owner source 74 writer <- IO.openWriter owner destination 75 spawned <- IO.spawn 1 1 [reader writer] cancelPump 76 case spawned of 77 [task ticket] = IO.require (IO.join ticket) IO.pure 78 _ = IO.fail "Invalid spawn reply")