1 /* 2 * Copyright (c) Facebook, Inc. and its affiliates. 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(); 33 ~TimedDrivableExecutor() noexcept override; 34 35 /// Implements DrivableExecutor 36 void drive() noexcept override; 37 38 // Make progress if there is work to do and return true. Otherwise return 39 // false. 40 bool try_drive() noexcept; 41 42 // Make progress on this Executor's work. Acts as drive, except it will only 43 // wait for a period of timeout for work to be enqueued. If no work is 44 // enqueued by that point, it will return. 45 template <typename Rep, typename Period> try_drive_for(const std::chrono::duration<Rep,Period> & timeout)46 bool try_drive_for( 47 const std::chrono::duration<Rep, Period>& timeout) noexcept { 48 return try_wait_for(timeout) && run() > 0; 49 } 50 51 // Make progress on this Executor's work. Acts as drive, except it will only 52 // wait until deadline for work to be enqueued. If no work is enqueued by 53 // that point, it will return. 54 template <typename Clock, typename Duration> try_drive_until(const std::chrono::time_point<Clock,Duration> & deadline)55 bool try_drive_until( 56 const std::chrono::time_point<Clock, Duration>& deadline) noexcept { 57 return try_wait_until(deadline) && run() > 0; 58 } 59 60 void add(Func) override; 61 62 /// Do work. Returns the number of functions that were executed (maybe 0). 63 /// Non-blocking, in the sense that we don't wait for work (we can't 64 /// control whether one of the functions blocks). 65 /// This is stable, it will not chase an ever-increasing tail of work. 66 /// This also means, there may be more work available to perform at the 67 /// moment that this returns. 68 size_t run() noexcept; 69 70 // Do work until there is no more work to do. 71 // Returns the number of functions that were executed (maybe 0). 72 // Unlike run, this method is not stable. It will chase an infinite tail of 73 // work so should be used with care. 74 // There will be no work available to perform at the moment that this 75 // returns. 76 size_t drain() noexcept; 77 78 /// Wait for work to do. 79 void wait() noexcept; 80 81 // Return true if there is work to do, false otherwise 82 bool try_wait() noexcept; 83 84 /// Wait for work to do or for a period of timeout, whichever is sooner. 85 template <typename Rep, typename Period> try_wait_for(const std::chrono::duration<Rep,Period> & timeout)86 bool try_wait_for( 87 const std::chrono::duration<Rep, Period>& timeout) noexcept { 88 return func_ || queue_.try_dequeue_for(func_, timeout); 89 } 90 91 /// Wait for work to do or until deadline passes, whichever is sooner. 92 template <typename Clock, typename Duration> try_wait_until(const std::chrono::time_point<Clock,Duration> & deadline)93 bool try_wait_until( 94 const std::chrono::time_point<Clock, Duration>& deadline) noexcept { 95 return func_ || queue_.try_dequeue_until(func_, deadline); 96 } 97 98 private: 99 UMPSCQueue<Func, true> queue_; 100 Func func_; 101 }; 102 103 } // namespace folly 104