]> git.lizzy.rs Git - rust.git/blob - src/doc/guide-runtime.md
auto merge of #16438 : phi-gamma/rust/doc-fixes, r=brson
[rust.git] / src / doc / guide-runtime.md
1 % A Guide to the Rust Runtime
2
3 Rust includes two runtime libraries in the standard distribution, which provide
4 a unified interface to primitives such as I/O, but the language itself does not
5 require a runtime. The compiler is capable of generating code that works in all
6 environments, even kernel environments. Neither does the Rust language need a
7 runtime to provide memory safety; the type system itself is sufficient to write
8 safe code, verified statically at compile time. The runtime merely uses the
9 safety features of the language to build a number of convenient and safe
10 high-level abstractions.
11
12 That being said, code without a runtime is often very limited in what it can do.
13 As a result, Rust's standard libraries supply a set of functionality that is
14 normally considered the Rust runtime.  This guide will discuss Rust's user-space
15 runtime, how to use it, and what it can do.
16
17 # What is the runtime?
18
19 The Rust runtime can be viewed as a collection of code which enables services
20 like I/O, task spawning, TLS, etc. It's essentially an ephemeral collection of
21 objects which enable programs to perform common tasks more easily. The actual
22 implementation of the runtime itself is mostly a sparse set of opt-in primitives
23 that are all self-contained and avoid leaking their abstractions into libraries.
24
25 The current runtime is the engine behind these features (not a comprehensive
26 list):
27
28 * I/O
29 * Task spawning
30 * Message passing
31 * Task synchronization
32 * Task-local storage
33 * Logging
34 * Local heaps (GC heaps)
35 * Task unwinding
36
37 ## What is the runtime accomplishing?
38
39 The runtime is designed with a few goals in mind:
40
41 * Rust libraries should work in a number of environments without having to worry
42   about the exact details of the environment itself. Two commonly referred to
43   environments are the M:N and 1:1 environments. Since the Rust runtime was
44   first designed, it has supported M:N threading, and it has since gained 1:1
45   support as well.
46
47 * The runtime should not enforce separate "modes of compilation" in order to
48   work in multiple circumstances. It is an explicit goal that you compile a Rust
49   library once and use it forever (in all environments).
50
51 * The runtime should be fast. There should be no architectural design barrier
52   which is preventing programs from running at optimal speeds. It is not a goal
53   for the runtime to be written "as fast as can be" at every moment in time. For
54   example, no claims will be made that the current implementation of the runtime
55   is the fastest it will ever be. This goal is simply to prevent any
56   architectural roadblock from hindering performance.
57
58 * The runtime should be nearly invisible. The design of the runtime should not
59   encourage direct interaction with it, and using the runtime should be
60   essentially transparent to libraries. This does not mean it should be
61   impossible to query the runtime, but rather it should be unconventional.
62
63 # Architecture of the runtime
64
65 This section explains the current architecture of the Rust runtime. It has
66 evolved over the development of Rust through many iterations, and this is simply
67 the documentation of the current iteration.
68
69 ## A local task
70
71 The core abstraction of the Rust runtime is the task. A task represents a
72 "thread" of execution of Rust code, but it does not necessarily correspond to an
73 OS thread. Most runtime services are accessed through the local task, allowing
74 for runtime policy decisions to be made on a per-task basis.
75
76 A consequence of this decision is to require all Rust code using the standard
77 library to have a local `Task` structure available to them. This `Task` is
78 stored in the OS's thread local storage (OS TLS) to allow for efficient access
79 to it.
80
81 It has also been decided that the presence or non-presence of a local `Task` is
82 essentially the *only* assumption that the runtime can make. Almost all runtime
83 services are routed through this local structure.
84
85 This requirement of a local task is a core assumption on behalf of *all* code
86 using the standard library, hence it is defined in the standard library itself.
87
88 ## I/O
89
90 When dealing with I/O in general, there are a few flavors by which it can be
91 dealt with, and not all flavors are right for all situations. I/O is also a
92 tricky topic that is nearly impossible to get consistent across all
93 environments. As a result, a Rust task is not guaranteed to have access to I/O,
94 and it is not even guaranteed what the implementation of the I/O will be.
95
96 This conclusion implies that I/O *cannot* be defined in the standard library.
97 The standard library does, however, provide the interface to I/O that all Rust
98 tasks are able to consume.
99
100 This interface is implemented differently for various flavors of tasks, and is
101 designed with a focus around synchronous I/O calls. This architecture does not
102 fundamentally prevent other forms of I/O from being defined, but it is not done
103 at this time.
104
105 The I/O interface that the runtime must provide can be found in the
106 [std::rt::rtio](std/rt/rtio/trait.IoFactory.html) module. Note that this
107 interface is *unstable*, and likely always will be.
108
109 ## Task Spawning
110
111 A frequent operation performed by tasks is to spawn a child task to perform some
112 work. This is the means by which parallelism is enabled in Rust. This decision
113 of how to spawn a task is not a general decision, and is hence a local decision
114 to the task (not defined in the standard library).
115
116 Task spawning is interpreted as "spawning a sibling" and is enabled through the
117 high level interface in `std::task`. The child task can be configured
118 accordingly, and runtime implementations must respect these options when
119 spawning a new task.
120
121 Another local task operation is dealing with the runnable state of the task
122 itself.  This frequently comes up when the question is "how do I block a task?"
123 or "how do I wake up a task?". These decisions are inherently local to the task
124 itself, yet again implying that they are not defined in the standard library.
125
126 ## The `Runtime` trait and the `Task` structure
127
128 The full complement of runtime features is defined by the [`Runtime`
129 trait](std/rt/trait.Runtime.html) and the [`Task`
130 struct](std/rt/task/struct.Task.html). A `Task` is constant among all runtime
131 implementations, but each runtime has its own implementation of the
132 `Runtime` trait.
133
134 The local `Task` stores the runtime value inside of itself, and then ownership
135 dances ensue to invoke methods on the runtime.
136
137 # Implementations of the runtime
138
139 The Rust distribution provides two implementations of the runtime. These two
140 implementations are generally known as 1:1 threading and M:N threading.
141
142 As with many problems in computer science, there is no right answer in this
143 question of which implementation of the runtime to choose. Each implementation
144 has its benefits and each has its drawbacks. The descriptions below are meant to
145 inform programmers about what the implementation provides and what it doesn't
146 provide in order to make an informed decision about which to choose.
147
148 ## 1:1 - using `libnative`
149
150 The library `libnative` is an implementation of the runtime built upon native OS
151 threads plus libc blocking I/O calls. This is called 1:1 threading because each
152 user-space thread corresponds to exactly one kernel thread.
153
154 In this model, each Rust task corresponds to one OS thread, and each I/O object
155 essentially corresponds to a file descriptor (or the equivalent of the platform
156 you're running on).
157
158 Some benefits to using libnative are:
159
160 * Guaranteed interop with FFI bindings. If a C library you are using blocks the
161   thread to do I/O (such as a database driver), then this will not interfere
162   with other Rust tasks (because only the OS thread will be blocked).
163 * Less I/O overhead as opposed to M:N in some cases. Not all M:N I/O is
164   guaranteed to be "as fast as can be", and some things (like filesystem APIs)
165   are not truly asynchronous on all platforms, meaning that the M:N
166   implementation may incur more overhead than a 1:1 implementation.
167
168 ## M:N - using `libgreen`
169
170 The library `libgreen` implements the runtime with "green threads" on top of the
171 asynchronous I/O framework [libuv][libuv]. The M in M:N threading is the number
172 of OS threads that a process has, and the N is the number of Rust tasks. In this
173 model, N Rust tasks are multiplexed among M OS threads, and context switching is
174 implemented in user-space.
175
176 The primary concern of an M:N runtime is that a Rust task cannot block itself in
177 a syscall. If this happens, then the entire OS thread is frozen and unavailable
178 for running more Rust tasks, making this a (M-1):N runtime (and you can see how
179 this can reach 0/deadlock). By using asynchronous I/O under the hood (all I/O
180 still looks synchronous in terms of code), OS threads are never blocked until
181 the appropriate time comes.
182
183 Upon reading `libgreen`, you may notice that there is no I/O implementation
184 inside of the library, but rather just the infrastructure for maintaining a set
185 of green schedulers which switch among Rust tasks. The actual I/O implementation
186 is found in `librustuv` which are the Rust bindings to libuv. This distinction
187 is made to allow for other I/O implementations not built on libuv (but none
188 exist at this time).
189
190 Some benefits of using libgreen are:
191
192 * Fast task spawning. When using M:N threading, spawning a new task can avoid
193   executing a syscall entirely, which can lead to more efficient task spawning
194   times.
195 * Fast task switching. Because context switching is implemented in user-space,
196   all task contention operations (mutexes, channels, etc) never execute
197   syscalls, leading to much faster implementations and runtimes. An efficient
198   context switch also leads to higher throughput servers than 1:1 threading
199   because tasks can be switched out much more efficiently.
200
201 ### Pools of Schedulers
202
203 M:N threading is built upon the concept of a pool of M OS threads (which
204 libgreen refers to as schedulers), able to run N Rust tasks. This abstraction is
205 encompassed in libgreen's [`SchedPool`](green/struct.SchedPool.html) type. This type allows for
206 fine-grained control over the pool of schedulers which will be used to run Rust
207 tasks.
208
209 In addition the `SchedPool` type is the *only* way through which a new M:N task
210 can be spawned. Sibling tasks to Rust tasks themselves (created through
211 `std::task::spawn`) will be spawned into the same pool of schedulers that the
212 original task was home to. New tasks must previously have some form of handle
213 into the pool of schedulers in order to spawn a new task.
214
215 ## Which to choose?
216
217 With two implementations of the runtime available, a choice obviously needs to
218 be made to see which will be used. The compiler itself will always by-default
219 link to one of these runtimes.
220
221 Having a default decision made in the compiler is done out of necessity and
222 convenience. The compiler's decision of runtime to link to is *not* an
223 endorsement of one over the other. As always, this decision can be overridden.
224
225 For example, this program will be linked to "the default runtime". The current
226 default runtime is to use libnative.
227
228 ~~~{.rust}
229 fn main() {}
230 ~~~
231
232 ### Force booting with libgreen
233
234 In this example, the `main` function will be booted with I/O support powered by
235 libuv. This is done by linking to the `rustuv` crate and specifying the
236 `rustuv::event_loop` function as the event loop factory.
237
238 To create a pool of green tasks which have no I/O support, you may shed the
239 `rustuv` dependency and use the `green::basic::event_loop` function instead of
240 `rustuv::event_loop`. All tasks will have no I/O support, but they will still be
241 able to deschedule/reschedule (use channels, locks, etc).
242
243 ~~~{.rust}
244 extern crate green;
245 extern crate rustuv;
246
247 #[start]
248 fn start(argc: int, argv: *const *const u8) -> int {
249     green::start(argc, argv, rustuv::event_loop, main)
250 }
251
252 fn main() {}
253 ~~~
254
255 ### Force booting with libnative
256
257 This program's `main` function will always be booted with libnative, running
258 inside of an OS thread.
259
260 ~~~{.rust}
261 extern crate native;
262
263 #[start]
264 fn start(argc: int, argv: *const *const u8) -> int {
265     native::start(argc, argv, main)
266 }
267
268 fn main() {}
269 ~~~
270
271 # Finding the runtime
272
273 The actual code for the runtime is spread out among a few locations:
274
275 * [std::rt][stdrt]
276 * [libnative][libnative]
277 * [libgreen][libgreen]
278 * [librustuv][librustuv]
279
280 [libuv]: https://github.com/joyent/libuv/
281 [stdrt]: std/rt/index.html
282 [libnative]: native/index.html
283 [libgreen]: green/index.html
284 [librustuv]: rustuv/index.html