Line data Source code
1 : /* 2 : * Copyright (c) 2017 Juniper Networks, Inc. All rights reserved. 3 : */ 4 : #include <boost/bind/bind.hpp> 5 : #include "logging.h" 6 : #include "io/event_manager.h" 7 : #include "timer_impl.h" 8 : #include "time_util.h" 9 : #include "task.h" 10 : #include "task_monitor.h" 11 : #include "task_tbbkeepawake.h" 12 : 13 : using namespace boost::placeholders; 14 : 15 : #define kPollIntervalMultiplier 2 16 : #define kInactivityMultiplier 50 17 : 18 11 : TaskMonitor::TaskMonitor(TaskScheduler *scheduler, 19 : uint64_t tbb_keepawake_time_msec, 20 : uint64_t inactivity_time_msec, 21 11 : uint64_t poll_interval_msec) : 22 11 : scheduler_(scheduler), cancelled_(false), timer_impl_(nullptr), 23 11 : inactivity_time_usec_(inactivity_time_msec * 1000), 24 11 : poll_interval_msec_(poll_interval_msec), 25 11 : tbb_keepawake_time_msec_(tbb_keepawake_time_msec), 26 11 : last_activity_(ClockMonotonicUsec()), 27 11 : last_enqueue_count_(0), last_done_count_(0), poll_count_(0) { 28 11 : } 29 : 30 11 : TaskMonitor::~TaskMonitor() { 31 11 : } 32 : 33 8 : void TaskMonitor::UpdateTimers() { 34 : // Ensure polling interval for monitor is atleast 2*keep-awake-time 35 : // It ensures when monitor is invoked, there is some job enqueued 36 : // and executed 37 8 : if ((tbb_keepawake_time_msec_ * kPollIntervalMultiplier) > poll_interval_msec_) { 38 6 : poll_interval_msec_ = 39 6 : kPollIntervalMultiplier * tbb_keepawake_time_msec_; 40 : } 41 : 42 : // Ensure monitor timeout is atleast 50 * poll-interval 43 8 : if ((poll_interval_msec_ * kInactivityMultiplier * 1000) > 44 8 : inactivity_time_usec_) { 45 7 : inactivity_time_usec_ = 46 7 : poll_interval_msec_ * kInactivityMultiplier * 1000; 47 : } 48 8 : return; 49 : } 50 : 51 3 : void TaskMonitor::Start(EventManager *evm) { 52 3 : if (inactivity_time_usec_ == 0 || poll_interval_msec_ == 0) 53 0 : return; 54 : 55 3 : UpdateTimers(); 56 3 : timer_impl_.reset(new TimerImpl(*evm->io_service())); 57 3 : Restart(); 58 3 : return; 59 : } 60 : 61 3 : void TaskMonitor::Terminate() { 62 3 : cancelled_ = true; 63 3 : } 64 : 65 3 : void TaskMonitor::Restart() { 66 3 : boost::system::error_code ec; 67 3 : timer_impl_->expires_from_now(poll_interval_msec_, ec); 68 3 : if (ec) { 69 0 : assert(0); 70 : } 71 3 : timer_impl_->async_wait(boost::bind(&TaskMonitor::Run, this, 72 : boost::asio::placeholders::error)); 73 3 : } 74 : 75 22 : bool TaskMonitor::Monitor(uint64_t t, uint64_t enqueue_count, 76 : uint64_t done_count) { 77 : // New tasks were spawned by TBB. Treat as activity seen 78 22 : if (done_count != last_done_count_) { 79 9 : last_done_count_ = done_count; 80 9 : last_enqueue_count_ = enqueue_count; 81 9 : last_activity_ = t; 82 9 : return true; 83 : } 84 : 85 : // No change in done_count_. Now validate enqueue_count. 86 : // Note: We cannot match enqueue_count_ and done_count_. Both these numbers 87 : // are updated by multiple threads and are not atomic numbers. As a result 88 : // they can potentially be out-of-sync 89 : // 90 : // If no new tasks are enqueued, then assume there is no more tasks 91 : // to run. Treat it similar to seeing activity 92 13 : if (enqueue_count == last_enqueue_count_) { 93 7 : last_done_count_ = done_count; 94 7 : last_enqueue_count_ = enqueue_count; 95 7 : last_activity_ = t; 96 7 : return true; 97 : } 98 : 99 : // Enqueues are happening, check if inactivity exceeds configured time 100 6 : return ((t - last_activity_) <= inactivity_time_usec_); 101 : } 102 : 103 0 : void TaskMonitor::Run(const boost::system::error_code &ec) { 104 0 : poll_count_++; 105 : 106 : // ASIO API aborted. Just restart ASIO timer again 107 0 : if (ec && ec.value() == boost::asio::error::operation_aborted) { 108 0 : Restart(); 109 0 : return; 110 : } 111 : 112 0 : if (cancelled_) 113 0 : return; 114 : 115 0 : if (Monitor(ClockMonotonicUsec(), scheduler_->enqueue_count(), 116 0 : scheduler_->done_count())) { 117 0 : Restart(); 118 0 : return; 119 : } 120 0 : LOG(ERROR, "!!!! ERROR !!!! Task Monitor failed"); 121 0 : assert(0); 122 : }