You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Summary:
# Context
I was really annoyed w/ the variable formatting of Markdown files. I decided to apply formatting to all the markdown files to make things consistent.
# This diff
Formats all the markdown files to be consistent. The next diff will enable an option in linttool to enforce formatting in all Markdown files under `eden/fs/**/*`
Reviewed By: zertosh
Differential Revision: D59930918
fbshipit-source-id: 20964f531fbe6be919e8cc391caf148d5c107ae1
Copy file name to clipboardExpand all lines: eden/fs/docs/Futures.md
+59-25Lines changed: 59 additions & 25 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,42 +1,73 @@
1
1
# Futures and Asynchronous Code
2
2
3
-
This document assumes some working knowledge of folly::Future and folly::SemiFuture. Please read the [Future overview](https://github.com/facebook/folly/blob/master/folly/docs/Futures.md) first.
3
+
This document assumes some working knowledge of folly::Future and
EdenFS is largely concurrent and asynchronous. The traditional way to write this kind of code would be explicit state machines with requests and callbacks. It's easy to forget to call a callback or call one twice under rarely-executed paths like error handling.
10
+
EdenFS is largely concurrent and asynchronous. The traditional way to write this
11
+
kind of code would be explicit state machines with requests and callbacks. It's
12
+
easy to forget to call a callback or call one twice under rarely-executed paths
13
+
like error handling.
8
14
9
-
To make asynchronous code easier to reason about, Folly provides `folly::Future` and `folly::Promise`. Each Future and Promise form a pair, where `folly::Future` holds the eventual value and Promise is how the value is published. Readers can either block on the result (offering their thread to any callbacks that may run) or schedule a callback to be run when the value is available. `folly::Promise` is fulfilled on the writing side.
15
+
To make asynchronous code easier to reason about, Folly provides `folly::Future`
16
+
and `folly::Promise`. Each Future and Promise form a pair, where `folly::Future`
17
+
holds the eventual value and Promise is how the value is published. Readers can
18
+
either block on the result (offering their thread to any callbacks that may run)
19
+
or schedule a callback to be run when the value is available. `folly::Promise`
20
+
is fulfilled on the writing side.
10
21
11
22
## Why SemiFuture?
12
23
13
-
The biggest problem with Future is that callbacks may run either on the thread calling `Future::then` or on the thread calling `Promise::set`. Callbacks have to be written carefully, and if they acquire locks, any site that calls `Future::then` or `Promise::set` must not hold those locks.
24
+
The biggest problem with Future is that callbacks may run either on the thread
25
+
calling `Future::then` or on the thread calling `Promise::set`. Callbacks have
26
+
to be written carefully, and if they acquire locks, any site that calls
27
+
`Future::then` or `Promise::set` must not hold those locks.
14
28
15
-
`folly::SemiFuture` is a reaction to these problems. It's a Future without a `SemiFuture::then` method. Assuming no use of unsafe APIs (including any `InlineExecutor`), callbacks will never run on the thread that calls `Promise::set`. Any system with an internal thread pool that cannot tolerate arbitrary callbacks running on its threads should use `SemiFuture`.
29
+
`folly::SemiFuture` is a reaction to these problems. It's a Future without a
30
+
`SemiFuture::then` method. Assuming no use of unsafe APIs (including any
31
+
`InlineExecutor`), callbacks will never run on the thread that calls
32
+
`Promise::set`. Any system with an internal thread pool that cannot tolerate
33
+
arbitrary callbacks running on its threads should use `SemiFuture`.
16
34
17
35
## Why ImmediateFuture?
18
36
19
-
`folly::Future` and `folly::SemiFuture` introduce significant overhead. A `Future`/`Promise` pair hold a heap-allocated, atomic refcounted `FutureCore`. In EdenFS, it's common to make an asynchronous call that hits cache and can answer immediately. Heap allocating the result is comparatively expensive. We introduced `facebook::eden::ImmediateFuture` for those cases. ImmediateFuture either stores the result value inline or holds a SemiFuture.
37
+
`folly::Future` and `folly::SemiFuture` introduce significant overhead. A
38
+
`Future`/`Promise` pair hold a heap-allocated, atomic refcounted `FutureCore`.
39
+
In EdenFS, it's common to make an asynchronous call that hits cache and can
40
+
answer immediately. Heap allocating the result is comparatively expensive. We
41
+
introduced `facebook::eden::ImmediateFuture` for those cases. ImmediateFuture
42
+
either stores the result value inline or holds a SemiFuture.
|Callbacks run as early as the result is available | yes | no | no|
52
+
|Callbacks may run on the fulfiller's thread | yes | no | no|
53
+
|Callbacks may run immediately or asynchronously | yes | no | yes|
54
+
|sizeof, cost of move() | void\*| void\*| Depends on sizeof(T) with minimum of 40 bytes as of Oct 2021|
32
55
33
-
`folly::Future` should be used when it's important the callback runs as early as possible. For example, measuring the duration of internal operations.
56
+
`folly::Future` should be used when it's important the callback runs as early as
57
+
possible. For example, measuring the duration of internal operations.
34
58
35
-
SemiFuture or ImmediateFuture should be used when it's important that chained callbacks never run on internal thread pools.
59
+
SemiFuture or ImmediateFuture should be used when it's important that chained
60
+
callbacks never run on internal thread pools.
36
61
37
-
ImmediateFuture should be used when the value is small and avoiding an allocation is important for performance. Large structs can use unique_ptr or shared_ptr.
62
+
ImmediateFuture should be used when the value is small and avoiding an
63
+
allocation is important for performance. Large structs can use unique_ptr or
64
+
shared_ptr.
38
65
39
-
It's important to note that, when a callback and its closures hold reference counts or are larger than the result value, it can be worth using Future, because the callbacks are collapsed into a value as early as possible. SemiFuture, even if the SemiFuture is held by an ImmediateFuture, will not collapse any chained callbacks until the SemiFuture is attached to an executor.
66
+
It's important to note that, when a callback and its closures hold reference
67
+
counts or are larger than the result value, it can be worth using Future,
68
+
because the callbacks are collapsed into a value as early as possible.
69
+
SemiFuture, even if the SemiFuture is held by an ImmediateFuture, will not
70
+
collapse any chained callbacks until the SemiFuture is attached to an executor.
40
71
41
72
## Safetyness and caveats
42
73
@@ -73,14 +104,16 @@ As a general rule of thumb, any use of `folly::InlineLikeExecutor` is widely
73
104
unsafe and should never be used. This is primarily due to forcing `Promise::set`
74
105
to execute the `folly::Future` callbacks in the context of the fulfiller' thread
75
106
76
-
For instance, if we re-use the previous example, but where the `threadPool` is an
77
-
`InlineLikeExecutor` the `setValue` will also execute both continuation before
78
-
returning.
107
+
For instance, if we re-use the previous example, but where the `threadPool` is
108
+
an `InlineLikeExecutor` the `setValue` will also execute both continuation
109
+
before returning.
79
110
80
111
This has been known to cause deadlocks in the past. This includes:
81
-
-`folly::SemiFuture::toUnsafeFuture` and any `Unsafe` methods as these are merely wrappers on `.via(&InlineExecutor::instance())`,
0 commit comments