5import Data.Array (delete, filter, find, length, snoc, zip)
6import Data.Foldable (for_)
7import Data.Generic.Rep (class Generic)
8import Data.Generic.Rep.Show (genericShow)
9import Data.JSDate (JSDate)
10import Data.JSDate as JSDate
11import Data.Maybe (Maybe(..))
12import Data.Newtype (class Newtype, over, un)
13import Data.Time.Duration (Milliseconds(..))
14import Data.Tuple.Nested ((/\))
15import Data.UUID (UUID, genUUID)
17import Effect.Aff (Aff, Fiber, delay, error, killFiber, launchAff, launchAff_, parallel, sequential)
18import Effect.Class (class MonadEffect, liftEffect)
19import Effect.Ref (Ref)
20import Effect.Ref as Ref
25derive instance ntJobId :: Newtype JobId _
26derive instance genericJobId :: Generic JobId _
27instance showJobId :: Show JobId where
30derive newtype instance jobIdEq :: Eq JobId
32 = NewJob (JobId -> a -> Aff Unit)
42 = Pending (PendingJob a)
43 | Running (RunningJob a)
45getAddedAt ∷ ∀ a. Job a -> JSDate
47 Pending (PendingJob { addedAt }) -> addedAt
48 Running (RunningJob { addedAt }) -> addedAt
50instance eqJob :: Eq (Job a) where
51 eq j1 j2 = getId j1 == getId j2
53instance ordJob :: Ord (Job a) where
54 compare j1 j2 = compare (getAddedAt j1) (getAddedAt j2)
56getId ∷ ∀ a. Job a -> JobId
58 Pending (PendingJob { id }) -> id
59 Running (RunningJob { id }) -> id
72 , timeout ∷ Milliseconds
76 = ResourcePool (Array a)
78derive instance ntResourcePool :: Newtype (ResourcePool a) _
80 = Queue QueueParams (Ref (ResourcePool a)) (Ref (Array (Job a)))
82mkQueue ∷ ∀ m a. MonadEffect m => QueueParams -> ResourcePool a -> m (Queue a)
85 poolRef <- Ref.new pool
87 pure $ Queue params poolRef jobsRef
93derive instance eqEnqueueResult :: Eq EnqueueResult
94derive instance genericEnqueueResult :: Generic EnqueueResult _
95instance showEnqueueResult :: Show EnqueueResult where
98startJob ∷ ∀ a. Eq a => Queue a -> a -> PendingJob a -> Effect Unit
99startJob q@(Queue { timeout } _ jobsRef) resource (PendingJob { id, job, addedAt }) = do
100 startedAt <- JSDate.now
101 iso <- JSDate.toISOString startedAt
102 -- log $ "Starting Job: " <> toString (unwrap id) <> " at: " <> iso
104 remove = removeFromQueue id q
106 launchAff <<< sequential
107 $ parallel (job resource *> remove)
108 *> parallel (delay timeout *> remove)
110 running = RunningJob { id, fiber, addedAt, startedAt, resource }
112 # Ref.modify_ (map \j -> if getId j == id then Running running else j)
114jobsInQueue ∷ ∀ a. Queue a -> Effect Int
115jobsInQueue (Queue _ _ jobsRef) = do
116 jobs <- Ref.read jobsRef
119runningJobs ∷ ∀ a. Queue a -> Effect (Array (RunningJob a))
120runningJobs (Queue _ resourceRef jobsRef) = do
121 jobs <- Ref.read jobsRef
128pendingJobs ∷ ∀ a. Queue a -> Effect (Array (PendingJob a))
129pendingJobs (Queue _ resourceRef jobsRef) = do
130 jobs <- Ref.read jobsRef
131 pool <- Ref.read resourceRef
138startJobs ∷ ∀ a. Eq a => Queue a -> Effect Unit
139startJobs q@(Queue { timeout } poolRef jobsRef) = do
140 pool <- Ref.read poolRef
141 pending <- pendingJobs q
142 for_ (pending `zip` un ResourcePool pool) \(job /\ resource) -> do
143 Ref.modify_ (over ResourcePool $ delete resource) poolRef
144 startJob q resource job
146removeStaleJobs ∷ ∀ a. Eq a => Queue a -> Effect Unit
147removeStaleJobs q@(Queue { timeout } resourceRef jobsRef) = do
148 running <- runningJobs q
149 nowMillis <- JSDate.now <#> JSDate.getTime
151 $ \(RunningJob { startedAt, id }) -> do
153 runningSince = (nowMillis - JSDate.getTime startedAt) # Milliseconds
155 timedOut = runningSince > timeout
156 when timedOut $ launchAff_ (removeFromQueue id q)
158removeFromQueue ∷ ∀ a. Eq a => JobId -> Queue a -> Aff Unit
159removeFromQueue id q@(Queue _ poolRef jobsRef) = do
160 maybeJob <- Ref.read jobsRef <#> find (\x -> (getId x) == id) # liftEffect
162 jobsRef # Ref.modify_ (filter (\x -> (getId x) /= id)) # liftEffect
164 Just (Running (RunningJob { fiber })) -> killFiber (error "Removed from queue") fiber
168 Just (Running (RunningJob { resource })) -> do
169 poolRef # Ref.modify_ (over ResourcePool (_ `snoc` resource)) # liftEffect
171 startJobs q # liftEffect
173--| Add to the end of the queue. If the queue is full the result will be `false`
174enqueue ∷ ∀ a. Eq a => NewJob a -> Queue a -> Effect EnqueueResult
175enqueue (NewJob newJob) q@(Queue { maxSize, timeout } _ jobsRef) = do
176 id <- genUUID <#> JobId
177 jobs <- Ref.read jobsRef
178 if length jobs >= maxSize then
181 addedAt <- JSDate.now
183 new = Pending (PendingJob { id, addedAt, job: newJob id })
184 jobsRef # Ref.modify_ (_ `snoc` new)
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list