Permalink
Browse files

Improve documentation and examples for 0.1.0.1. Dodge confusion about…

… room synchronization.
  • Loading branch information...
Christopher Lane Hinson
Christopher Lane Hinson committed Apr 29, 2009
1 parent b9b60b7 commit 2021eceeba0d927c051324c0927b45bb64c5b384
Showing with 33 additions and 50 deletions.
  1. +12 −12 Tests.hs
  2. +21 −38 priority-sync.cabal
View
@@ -140,26 +140,26 @@ stress config prioIO =
atomically $ flip unless retry . (== 0) =<< activity pool
withMVar counter $ putStrLn . show
-_example1 :: IO ()
-_example1 =
- do (pool :: TaskPool () ()) <- simpleTaskPool
- forkIO $ claim Acquire pool $ putStrLn "Hello world!"
- forkIO $ claim Acquire pool $ putStrLn "Goodbye world!"
+example :: IO ()
+example =
+ do let expensiveTask = threadDelay 1000000
+ pool <- simpleTaskPool
+ forkIO $ claim Acquire (schedule pool 1) $ putStrLn "Task 1 started . . ." >> expensiveTask >> putStrLn "Task 1 completed."
+ forkIO $ claim Acquire (schedule pool 3) $ putStrLn "Task 3 started . . ." >> expensiveTask >> putStrLn "Task 3 completed."
+ forkIO $ claim Acquire (schedule pool 2) $ putStrLn "Task 2 started . . ." >> expensiveTask >> putStrLn "Task 2 completed."
+ threadDelay 100000 -- contrive to wait for all tasks to become enqueued
+ putStrLn "Starting pool: "
startQueue pool
-
-_example2 :: IO ()
-_example2 =
- do prio_pool <- simpleTaskPool
- forkIO $ claim Acquire (schedule prio_pool 1) $ putStrLn "Hello world!"
- forkIO $ claim Acquire (schedule prio_pool 2) $ putStrLn "Goodbye world!"
- startQueue prio_pool
+ threadDelay 4000000 -- contrive to wait for all tasks to become dequeued
main :: IO ()
main =
do args <- liftM (\args -> if Prelude.null args then ["help"] else args) getArgs
let shouldRun s@('s':'t':'r':'e':'s':'s':_) = s `elem` args
+ shouldRun "example" = "example" `elem` args
shouldRun s = s `elem` args || "all" `elem` args
when (shouldRun "help") $ putStrLn "tests: all, testRoom, testMaxThreads, testQueue, testTaskPool, stressInt, stressIntFair, stressInt2, stressUnit, stressUnitFILO, stressUnitFair"
+ when (shouldRun "example") $ example
when (shouldRun "testRoom") testRoom
when (shouldRun "testMaxThreads") testMaxThreads
when (shouldRun "testQueue") testQueue
View
@@ -1,60 +1,43 @@
name: priority-sync
-version: 0.1.0.0
+version: 0.1.0.1
license: BSD3
license-file: LICENSE
author: Christopher Lane Hinson
maintainer: Christopher Lane Hinson <lane@downstairspeople.org>
stability: Unstable
category: Concurrency
-synopsis: Task prioritization.
-description: Implements cooperative task prioritization with room synchronization.
- .
- In the simplest usage, for an unprioritized FILO queue, only three operations are needed: 'simpleTaskPool', 'claim', and 'startQueue'.
+synopsis: Cooperative task prioritization.
+description: In a simple use case, we want to run some expensive tasks in prioritized order, so that only one task is running on each
+ CPU (or hardware thread) at any time. For this simple case, four operations are needed: 'simpleTaskPool',
+ 'schedule', 'claim', and 'startQueue'.
.
@
- (pool :: TaskPool () ()) <- simpleTaskPool
- forkIO $ claim Acquire pool $ putStrLn "Hello world!"
- forkIO $ claim Acquire pool $ putStrLn "Goodbye world!"
+ let expensiveTask = threadDelay 1000000
+ pool <- simpleTaskPool
+ forkIO $ claim Acquire (schedule pool 1) $ putStrLn \"Task 1 started . . .\" >> expensiveTask >> putStrLn \"Task 1 completed.\"
+ forkIO $ claim Acquire (schedule pool 3) $ putStrLn \"Task 3 started . . .\" >> expensiveTask >> putStrLn \"Task 3 completed.\"
+ forkIO $ claim Acquire (schedule pool 2) $ putStrLn \"Task 2 started . . .\" >> expensiveTask >> putStrLn \"Task 2 completed.\"
+ threadDelay 100000 -- contrive to wait for all tasks to become enqueued
+ putStrLn \"Starting pool: \"
startQueue pool
+ threadDelay 4000000 -- contrive to wait for all tasks to become dequeued
@
.
- For a simple prioritized queue, the 'schedule' operation introduces the priority. Prioritization is always least-first.
+ A 'TaskPool' combines 'Room's and 'Queue's in an efficient easy-to-use-interface.
.
- @
- prio_pool <- simpleTaskPool
- forkIO $ claim Acquire (schedule prio_pool 1) $ putStrLn "Hello world!"
- forkIO $ claim Acquire (schedule prio_pool 2) $ putStrLn "Goodbye world!"
- startQueue prio_pool
- @
+ 'Room's provide fully reentrant synchronization to any number of threads based on arbitrary resource constraints.
+ For example, the 'Room' from a 'simpleTaskPool' is constrained by 'GHC.numCapabilities'.
.
- Note that if you run these programs with @+RTS -N2@ or greater, the 'claim' operations may be processed in any order, since 'simpleTaskQueue' detects
- the number of capabilities and schedules tasks on each.
+ 'Queue's provide task prioritization. A 'Queue' systematically examines (to a configurable depth) all waiting threads with their
+ priorities and resource constraints and wakes the most eagerly prioritized thread whose constraints can be satisfied.
.
'TaskPool's are not thread pools. The concept is similar to IO Completion Ports. There are no worker threads. If a number of threads are waiting,
- the thread that is most likely to be processed next is woken and temporarily serves as a working thread. 'TaskPool's are backed by carefully
- written STM (software transactional memory) transactions.
- .
- A salient feature is that, because any thread can participate, a 'TaskPool' supports both bound threads and threads created with 'forkOnIO'.
+ the thread that is most likely to be processed next is woken and temporarily serves as a working thread.
.
- For applications that have complex resource constraints, it is possible to create a 'Room' to model each constraint. 'Room's are fully reentrant,
- and an arbitrary number of threads can 'claim' a 'Room' according to arbitrary rules, or 'RoomConstraint's. In the simple usage above,
- a single room represents the number of capabilities available to the GHC runtime.
+ 'Room's, 'Queue's, and 'TaskPool's are backed by carefully written STM (software transactional memory) transactions.
.
- Whenever a thread attempts to acquire a 'Room', a 'Claim' is generated that represents that attempt. The 'Claim' can be approved immediately,
- or it can be approved at the whim of another thread that has access to that 'Claim'. This means that 'Room's can be constructed in such
- a way that 'Claim's are approved only when a third party thread sees that the resource constraint modeled by that 'Room' has been satisfied.
- .
- The rules for generating and approving 'Claim's are described by a 'RoomContext'. By default, 'Claim's are approved immediately if their
- associated 'RoomConstraint's have been satisfied, but when a 'TaskPool' is introduced approval is deferred for prioritization.
- .
- 'Room' constraints are completely advisory: any task may claim any 'Room' without restriction at any time by using the 'UnconstrainedRoomContext'.
- .
- 'Queue's are used to prioritize tasks. Even if you have no need for prioritization, a 'Queue' ensures that only one thread is woken up
- when a 'Room' becomes available. A 'Queue' systematically examines to a configurable depth all waiting threads with their priorities
- and constraints and wakes the most eagerly prioritized thread whose constraints can be satisfied.
- .
- A 'TaskPool' combines 'Room's and 'Queue's in an efficient, easy-to-use interface.
+ A salient feature is that, because any thread can participate, a 'TaskPool' supports both bound threads and threads created with 'forkOnIO'.
.
The git repository is available at <http://www.downstairspeople.org/git/priority-sync.git>.

0 comments on commit 2021ece

Please sign in to comment.