1 | /* |
2 | * Copyright 2018-present Facebook, Inc. |
3 | * |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | * you may not use this file except in compliance with the License. |
6 | * You may obtain a copy of the License at |
7 | * |
8 | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | * |
10 | * Unless required by applicable law or agreed to in writing, software |
11 | * distributed under the License is distributed on an "AS IS" BASIS, |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. |
15 | */ |
16 | |
17 | #pragma once |
18 | |
19 | #include <chrono> |
20 | |
21 | #include <folly/concurrency/UnboundedQueue.h> |
22 | #include <folly/executors/DrivableExecutor.h> |
23 | |
24 | namespace folly { |
25 | |
26 | /* |
27 | * A DrivableExecutor can be driven via its drive() method or its driveUntil() |
28 | * that drives until some time point. |
29 | */ |
30 | class TimedDrivableExecutor : public DrivableExecutor { |
31 | public: |
32 | ~TimedDrivableExecutor() noexcept { |
33 | // Drain on destruction so that if work is added here during the collapse |
34 | // of a future train, it will propagate. |
35 | drain(); |
36 | } |
37 | |
38 | /// Implements DrivableExecutor |
39 | void drive() noexcept override; |
40 | |
41 | // Make progress if there is work to do and return true. Otherwise return |
42 | // false. |
43 | bool try_drive() noexcept { |
44 | return try_wait() && run() > 0; |
45 | } |
46 | |
47 | // Make progress on this Executor's work. Acts as drive, except it will only |
48 | // wait for a period of timeout for work to be enqueued. If no work is |
49 | // enqueued by that point, it will return. |
50 | template <typename Rep, typename Period> |
51 | bool try_drive_for( |
52 | const std::chrono::duration<Rep, Period>& timeout) noexcept { |
53 | return try_wait_for(timeout) && run() > 0; |
54 | } |
55 | |
56 | // Make progress on this Executor's work. Acts as drive, except it will only |
57 | // wait until deadline for work to be enqueued. If no work is enqueued by |
58 | // that point, it will return. |
59 | template <typename Clock, typename Duration> |
60 | bool try_drive_until( |
61 | const std::chrono::time_point<Clock, Duration>& deadline) noexcept { |
62 | return try_wait_until(deadline) && run() > 0; |
63 | } |
64 | |
65 | void add(Func) override; |
66 | |
67 | /// Do work. Returns the number of functions that were executed (maybe 0). |
68 | /// Non-blocking, in the sense that we don't wait for work (we can't |
69 | /// control whether one of the functions blocks). |
70 | /// This is stable, it will not chase an ever-increasing tail of work. |
71 | /// This also means, there may be more work available to perform at the |
72 | /// moment that this returns. |
73 | size_t run() noexcept; |
74 | |
75 | // Do work until there is no more work to do. |
76 | // Returns the number of functions that were executed (maybe 0). |
77 | // Unlike run, this method is not stable. It will chase an infinite tail of |
78 | // work so should be used with care. |
79 | // There will be no work available to perform at the moment that this |
80 | // returns. |
81 | size_t drain() noexcept; |
82 | |
83 | /// Wait for work to do. |
84 | void wait() noexcept; |
85 | |
86 | // Return true if there is work to do, false otherwise |
87 | bool try_wait() noexcept { |
88 | return func_ || queue_.try_dequeue(func_); |
89 | } |
90 | |
91 | /// Wait for work to do or for a period of timeout, whichever is sooner. |
92 | template <typename Rep, typename Period> |
93 | bool try_wait_for( |
94 | const std::chrono::duration<Rep, Period>& timeout) noexcept { |
95 | return func_ || queue_.try_dequeue_for(func_, timeout); |
96 | } |
97 | |
98 | /// Wait for work to do or until deadline passes, whichever is sooner. |
99 | template <typename Clock, typename Duration> |
100 | bool try_wait_until( |
101 | const std::chrono::time_point<Clock, Duration>& deadline) noexcept { |
102 | return func_ || queue_.try_dequeue_until(func_, deadline); |
103 | } |
104 | |
105 | private: |
106 | UMPSCQueue<Func, true> queue_; |
107 | Func func_; |
108 | }; |
109 | |
110 | } // namespace folly |
111 | |