]> gitweb.factorcode.org Git - factor.git/blob - libs/concurrency/concurrency.facts
more sql changes
[factor.git] / libs / concurrency / concurrency.facts
1 ! Copyright (C) 2006 Chris Double.
2 ! See http://factorcode.org/license.txt for BSD license.
3 USING: help concurrency match ;
4
5 HELP: make-mailbox
6 { $values { "mailbox" "a mailbox object" } 
7 }
8 { $description "A mailbox is an object that can be used for safe thread communication. Items can be put in the mailbox and retrieved in a FIFO order. If the mailbox is empty when a get operation is performed then the thread will block until another thread places something in the mailbox. If multiple threads are waiting on the same mailbox, only one of the waiting threads will be unblocked to process the get operation." } 
9 { $see-also mailbox-empty? mailbox-put mailbox-get mailbox-get-all while-mailbox-empty mailbox-get? } ;
10
11 HELP: mailbox-empty?
12 { $values { "mailbox" "a mailbox object" } 
13           { "bool" "a boolean value" }
14 }
15 { $description "Return true if the mailbox is empty." } 
16 { $see-also make-mailbox mailbox-put mailbox-get mailbox-get-all while-mailbox-empty mailbox-get? } ;
17
18 HELP: mailbox-put
19 { $values { "obj" "an object" } 
20           { "mailbox" "a mailbox object" } 
21 }
22 { $description "Put the object into the mailbox. Any threads that have a blocking get on the mailbox are resumed. Only one of those threads will successfully get the object, the rest will immediately block waiting for the next item in the mailbox." } 
23 { $see-also make-mailbox mailbox-empty? mailbox-get mailbox-get-all while-mailbox-empty mailbox-get? } ;
24
25 HELP: (mailbox-block-unless-pred)
26 { $values { "pred" "a quotation with stack effect " { $snippet "( X -- bool )" } } 
27           { "mailbox" "a mailbox object" } 
28           { "pred2" "same object as 'pred'" }
29           { "mailbox2" "same object as 'mailbox'" }
30 }
31 { $description "Block the thread if there are no items in the mailbox that return true when the predicate is called with the item on the stack. The predicate must have stack effect " { $snippet "( X -- bool )" } "." } 
32 { $see-also make-mailbox mailbox-empty? mailbox-put mailbox-get mailbox-get-all while-mailbox-empty mailbox-get? } ;
33
34 HELP: (mailbox-block-if-empty)
35 { $values { "mailbox" "a mailbox object" } 
36           { "mailbox2" "same object as 'mailbox'" }
37 }
38 { $description "Block the thread if the mailbox is empty." } 
39 { $see-also make-mailbox mailbox-empty? mailbox-put mailbox-get mailbox-get-all while-mailbox-empty mailbox-get? } ;
40
41 HELP: mailbox-get
42 { $values { "mailbox" "a mailbox object" } 
43           { "object" "an object" }
44 }
45 { $description "Get the first item put into the mailbox. If it is empty the thread blocks until an item is put into it. The thread then resumes, leaving the item on the stack." } 
46 { $see-also make-mailbox mailbox-empty? mailbox-put while-mailbox-empty mailbox-get-all mailbox-get? } ;
47
48 HELP: mailbox-get-all
49 { $values { "mailbox" "a mailbox object" } 
50           { "array" "an array" }
51 }
52 { $description "Blocks the thread if the mailbox is empty, otherwise removes all objects in the mailbox and returns an array containing the objects." } 
53 { $see-also make-mailbox mailbox-empty? mailbox-put while-mailbox-empty mailbox-get-all mailbox-get? } ;
54
55 HELP: while-mailbox-empty
56 { $values { "mailbox" "a mailbox object" } 
57           { "quot" "a quotation with stack effect " { $snippet "( -- )" } }
58 }
59 { $description "Repeatedly call the quotation while there are no items in the mailbox. Quotation should have stack effect " { $snippet "( -- )" } "." } 
60 { $see-also make-mailbox mailbox-empty? mailbox-put mailbox-get mailbox-get-all mailbox-get? } ;
61
62 HELP: mailbox-get?
63 { $values { "pred" "a quotation with stack effect " { $snippet "( X -- bool )" } }
64           { "mailbox" "a mailbox object" } 
65           { "obj" "an object" }
66 }
67 { $description "Get the first item in the mailbox which satisfies the predicate. 'pred' will be called repeatedly for each item in the mailbox. When 'pred' returns true that item will be returned. If nothing in the mailbox satisfies the predicate then the thread will block until something does. 'pred' must have stack effect " { $snippet "( X -- bool }" } "." } 
68 { $see-also make-mailbox mailbox-empty? mailbox-put mailbox-get mailbox-get-all while-mailbox-empty } ;
69
70 HELP: <node> 
71 { $values { "hostname" "the hostname of the node as a string" } 
72           { "port" "the integer port number of the node" } 
73           { "node" "the constructed node object" } 
74 }
75 { $description "Processes run on nodes. Each node has a hostname and a port." } 
76 { $see-also localnode } ;
77
78 HELP: localnode
79 { $values { "node" "a node object" } 
80 }
81 { $description "Return the node the process is currently running on." } 
82 { $see-also <node> } ;
83
84 HELP: <process>
85 { $values { "links" "an array of processes" } 
86           { "pid" "the process id" } 
87           { "mailbox" "a mailbox object" } 
88 }
89 { $description "Constructs a process object. A process is a lightweight thread with a mailbox that can be used to communicate with other processes. Each process has a unique process id." } 
90 { $see-also <remote-process> spawn send receive } ;
91
92 HELP: <remote-process>
93 { $values { "node" "a node object" } 
94           { "pid" "a process id" } 
95           { "remote-process" "the constructed remote-process object" } 
96 }
97 { $description "Constructs a proxy to a process running on another node. It can be used to send messages to the process it is acting as a proxy for." } 
98 { $see-also <node> <process> spawn send } ;
99
100 HELP: self
101 { $values { "process" "a process object" } 
102 }
103 { $description "Returns the currently running process object." } 
104 { $see-also <process> <remote-process> send receive receive-if } ;
105
106 HELP: send
107 { $values { "message" "an object" } 
108           { "process" "a process object" } 
109 }
110 { $description "Send the message to the process by placing it in the processes mailbox. This is an asynchronous operation and will return immediately. The receving process will act on the message the next time it retrieves that item from its mailbox (usually using the " { $link receive } " word. The message can be any Factor object. For destinations that are instances of remote-process the message must be a serializable Factor type." } 
111 { $see-also <process> <remote-process> receive receive-if } ;
112
113 HELP: receive
114 { $values { "message" "an object" } 
115 }
116 { $description "Return a message from the current processes mailbox. If the box is empty, suspend the process until another process places an item in the mailbox (usually via the " { $link send } " word." } 
117 { $see-also send receive-if } ;
118
119 HELP: receive-if
120 { $values { "pred" "a predicate with stack effect " { $snippet "( X -- bool )" } }  
121           { "message" "an object" } 
122 }
123 { $description "Return the first message from the current processes mailbox that satisfies the predicate. To satisfy the predicate, 'pred' is called  with the item on the stack and the predicate should leave a boolean indicating whether it was satisfied or not. The predicate must have stack effect " { $snippet "( X -- bool )" } ". If nothing in the mailbox satisfies the predicate then the process will block until something does." } 
124 { $see-also send receive } ;
125
126 HELP: spawn
127 { $values { "quot" "a predicate with stack effect " { $snippet "( -- )" } }  
128           { "process" "a process object" } 
129 }
130 { $description "Start a process which runs the given quotation." } 
131 { $see-also send receive receive-if self spawn-link } ;
132
133 HELP: spawn-link
134 { $values { "quot" "a predicate with stack effect " { $snippet "( -- )" } }  
135           { "process" "a process object" } 
136 }
137 { $description "Start a process which runs the given quotation. If that quotation throws an error which is not caught then the error will get propagated to the process that spawned it. This can be used to set up 'supervisor' processes that restart child processes that crash due to uncaught errors.\n" } 
138 { $see-also spawn } ;
139
140 ARTICLE: { "concurrency" "loading" } "Loading"
141 "The Factor module system can be used to load the Concurrency library:" 
142 { $code "\"libs/concurrency\" require\nUSE: concurrency" } ;
143
144 ARTICLE: { "concurrency" "processes" } "Processes"
145 "A process is basically a thread with a message queue. Other processes can place items on this queue by sending the process a message. A process can check its queue for messages, blocking if none are pending, and process them as they are queued.\n\nFactor processes are very lightweight. Each process can take as little as 900 bytes of memory. This library has been tested running hundreds of thousands of simple processes.\n\nThe messages that are sent from process to process are any Factor value. Factor tuples are ideal for this sort of thing as you can send a tuple to a process and the predicate dispatch mechanism can be used to perform actions depending on what the type of the tuple is.\n\nProcesses are usually created using " { $link spawn } ". This word takes a quotation on the stack and starts a process that will execute that quotation asynchronously. When the quotation completes the process will die. 'spawn'  leaves on the stack the process object that was started. This object can be used to send messages to the process using " { $link send }  ".\n\n'send' will return immediately after placing the message in the target processes message queue.\n\nA process can get a message from its queue using " { $link receive } ". This will get the most recent message and leave it on the stack. If there are no messages in the queue the process will 'block' until a message is available. When a process is blocked it takes no CPU time at all." 
146 { $code "[ receive print ] spawn\n\"Hello Process!\" swap send" } 
147 "This example spawns a process that first blocks, waiting to receive a message. When a message is received, the 'receive' call returns leaving it on the stack. It then prints the message and exits. 'spawn' left the process on the stack so it's available to send the 'Hello Process!' message to it. Immediately after the 'send' you should see 'Hello Process!' printed on the console.\n\nIt is also possible to selectively retrieve messages from the message queue. " { $link receive-if } " takes a predicate quotation on the stack and returns the first message in the queue that satisfies the predicate. If no items satisfy the predicate then the process is blocked until a message is received that does." 
148 { $code ": odd? ( n -- ? ) 2 mod 1 = ;\n1 self send 2 self send 3 self send\n\nreceive .\n => 1\n\n[ odd? ] receive-if .\n => 3\n\nreceive .\n => 2" } ;
149
150 ARTICLE: { "concurrency" "self" } "Self"
151 "A process can get access to its own process object using " { $link self } " so it can pass it to other processes. This allows the other processes to send messages back. A simple example of using this gets the current processes 'self' and spawns a process which sends a message to it. We then receive the message from the original process:" 
152 { $code "self [ \"Hello!\" swap send ] spawn 2drop receive .\n => \"Hello!\"" } ;
153
154 ARTICLE: { "concurrency" "servers" } "Servers"
155 "A common idiom is to create 'server' processes that act on messages that are sent to it. These follow a basic pattern of blocking until a message is received, processing that message then looping back to blocking for a message.\n\nThe following example shows a very simple server that expects an array as its message. The first item of the array should be the senders process object. If the second item is 'ping' then the server sends 'pong' back to the caller. If the second item is anything else then the server exits:" 
156 { $code ": pong-server ( -- )\n  receive {\n    { { ?from \"ping\" } [ \"pong\" ?from send pong-server ] }\n    { { ?from _ } [ \"server shutdown\" ?from send ] }\n  } match-cond ;\n\n[ pong-server ] spawn" } 
157 "Handling the deconstructing of messages and dispatching based on the message can be a bit of a chore. Especially in servers that take a number of different messages. The approach taken above is to use the 'match' library which allows easy deconstructing of messages using " { $link match-cond } "." ;
158
159 ARTICLE: { "concurrency" "synchronous-sends" } "Synchronous Sends"
160 { $link send } " sends a message asynchronously, and the sending process continues immediately. The 'pong server' example shown previously all sent messages to the server and waited for a reply back from the server. This pattern of synchronous sending is made easier with " { $link send-synchronous } ".\n\nThis word will send a message to the given process and immediately block until a reply is received for this particular message send. It leaves the reply on the stack. Note that it doesn't wait for just any reply, it waits for a reply specifically to this send.\n\nTo do this it wraps the requested message inside a tagged message format using " { $link tag-message } ":"
161 { $code "\"My Message\" tag-message .\n => { ...from... ...tag... \"My Message\" }" }
162 "The message is wrapped in array where the first item is the sending process object, the second is a unique tag, and the third is the original message. Server processes can use the 'from' to reply to the process that originally sent the message. The tag can is used in the receiving server to include the value in the reply. After the send-synchronous call the current process will block waiting for a reply that has the exact same tag. In this way you can be sure that the reply you got was for the specific message sent. Here is the pong-server recoded to use 'send-synchronous':"
163 { $code ": pong-server ( -- )\n  receive {\n    { { ?from ?tag \"ping\" } [ ?tag \"pong\" 2array ?from send pong-server ] }\n    { { ?from _ } [ ?tag \"server shutdown\" 2array ?from send ] }\n  } match-cond ;\n\n[ pong-server ] spawn \"ping\" swap send-synchronous .\n => \"pong\"" } 
164 "Notice that the code to send the reply back to the original caller wraps the reply in an array where the first item is the tag originally sent. 'send-synchronous' only returns if it receives a reply containing that specific tag." ;
165
166 ARTICLE: { "concurrency" "exceptions" } "Exceptions"
167 "A process can handle exceptions using the standard Factor exception handling mechanism. If an exception is uncaught the process will terminate. For example:" 
168 { $code "[ 1 0 / \"This will not print\" print ] spawn" } 
169 "Processes can be linked so that a parent process can receive the exception that caused the child process to terminate. In this way 'supervisor' processes can be created that are notified when child processes terminate and possibly restart them.\n\nThe easiest way to form this link is using " { $link spawn-link } ". This will create a unidirectional link, such that if an uncaught exception causes the child to terminate, the parent process can catch it:"
170 { $code "[\n  [ 1 0 / \"This will not print\" print ] spawn-link drop\n  receive\n] catch [ \"Exception caught.\" print ] when" } 
171 "Exceptions are only raised in the parent when the parent does a " { $link receive } " or " { $link receive-if } ". This is because the exception is sent from the child to the parent as a message." ;
172
173 ARTICLE: { "concurrency" "futures" } "Futures"
174 "A future is a placeholder for the result of a computation that is being calculated in a process. When the process has completed the computation the future can be queried to find out the result. If the computation has not completed when the future is queried them the process will block until the result is completed. <p>A future is created using " { $link future } ".\n\nThe quotation will be run in a spawned process, and a future object is immediately returned. This future object can be resolved using " { $link ?future } ".\n\nFutures are useful for starting calculations that take a long time to run but aren't needed until later in the process. When the process needs the value it can use '?future' to get the result or block until the result is available. For example:"
175 { $code "[ 30 fib ] future\n...do stuff...\n?future" } ;
176
177 ARTICLE: { "concurrency" "promises" } "Promises"
178 "A promise is similar to a future but it is not produced by calcuating something in the background. It represents a promise to provide a value sometime later. A process can request the value of a promise and will block if the promise is not fulfilled. Later, another process can fulfill the promise, providing a value. All threads waiting on the promise will then resume with that value on the stack. Use " { $link <promise> } " to create a promise, " { $link fulfill } " to set it to a value, and " { $link ?promise } " to retrieve the value, or block until the promise is fulfilled:"
179 { $code "<promise>\n[ ?promise \"Promise fulfilled: \" write print ] spawn drop\n[ ?promise \"Promise fulfilled: \" write print ] spawn drop\n[ ?promise \"Promise fulfilled: \" write print ] spawn drop\n\"hello\" swap fulfill\n => Promise fulfilled: hello\n    Promise fulfilled: hello\n    Promise fulfilled: hello" } ;
180
181 ARTICLE: { "concurrency" "concurrency" } "Concurrency"
182 "The concurrency library is based upon the style of concurrency used in systems like Erlang and Termite. It is built on top of the standard Factor lightweight thread system.\nA concurrency oriented program is one in which multiple processes run simultaneously in a single Factor image or across multiple running Factor instances. The processes can communicate with each other by asynchronous message sends. Although processes can share data via Factor's mutable data structures it is not recommended as the use of shared state concurrency is often a cause of problems."
183 { $subsection { "concurrency" "loading" } } 
184 { $subsection { "concurrency" "processes" } } 
185 { $subsection { "concurrency" "self" } } 
186 { $subsection { "concurrency" "servers" } } 
187 { $subsection { "concurrency" "synchronous-sends" } } 
188 { $subsection { "concurrency" "exceptions" } } 
189 { $subsection { "concurrency" "futures" } } 
190 { $subsection { "concurrency" "promises" } } ;