replace more folly::Optional::clear by reset (3)
[hiphop-php.git] / hphp / runtime / ext / intervaltimer / ext_intervaltimer.cpp
blob36fe854145750162ed1e3f6f53f946e363a8d19d
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
6 | Copyright (c) 1997-2010 The PHP Group |
7 +----------------------------------------------------------------------+
8 | This source file is subject to version 3.01 of the PHP license, |
9 | that is bundled with this package in the file LICENSE, and is |
10 | available through the world-wide-web at the following url: |
11 | http://www.php.net/license/3_01.txt |
12 | If you did not receive a copy of the PHP license and are unable to |
13 | obtain it through the world-wide-web, please send a note to |
14 | license@php.net so we can mail you a copy immediately. |
15 +----------------------------------------------------------------------+
17 #include "hphp/runtime/ext/intervaltimer/ext_intervaltimer.h"
19 #include "hphp/runtime/base/array-init.h"
20 #include "hphp/runtime/base/builtin-functions.h"
21 #include "hphp/runtime/base/req-optional.h"
22 #include "hphp/runtime/base/surprise-flags.h"
23 #include "hphp/runtime/base/request-info.h"
24 #include "hphp/runtime/ext/asio/ext_waitable-wait-handle.h"
25 #include "hphp/runtime/vm/native-data.h"
26 #include "hphp/util/rds-local.h"
28 namespace HPHP {
29 ///////////////////////////////////////////////////////////////////////////////
31 struct TimerPool final : RequestEventHandler {
32 using TimerSet = req::fast_set<IntervalTimer*>;
33 TimerSet& timers() { return *m_timers; }
35 void requestInit() override {
36 m_timers.emplace();
39 void requestShutdown() override {
40 do {
41 for (auto it = m_timers->begin(); it != m_timers->end(); ) {
42 auto timer = *it;
43 it = m_timers->erase(it);
44 timer->~IntervalTimer();
46 } while (!m_timers->empty());
47 m_timers.reset();
50 private:
51 req::Optional<TimerSet> m_timers;
54 IMPLEMENT_STATIC_REQUEST_LOCAL(TimerPool, s_timer_pool);
56 ///////////////////////////////////////////////////////////////////////////////
58 namespace {
60 const StaticString
61 s_IOWait("IOWait"),
62 s_ResumeAwait("ResumeAwait"),
63 s_Enter("Enter"),
64 s_Exit("Exit");
66 static StaticString sample_type_string(IntervalTimer::SampleType type) {
67 switch (type) {
68 case IntervalTimer::IOWaitSample: return s_IOWait;
69 case IntervalTimer::ResumeAwaitSample: return s_ResumeAwait;
70 case IntervalTimer::EnterSample: return s_Enter;
71 case IntervalTimer::ExitSample: return s_Exit;
73 not_reached();
78 void IntervalTimer::RunCallbacks(
79 IntervalTimer::SampleType type,
80 c_WaitableWaitHandle* wh
81 ) {
82 clearSurpriseFlag(IntervalTimerFlag);
84 auto const timers = s_timer_pool->timers(); // makes a copy!
85 for (auto timer : timers) {
86 if (!s_timer_pool->timers().count(timer)) {
87 // This timer has been removed from the pool by one of the callbacks.
88 continue;
90 int count = 0;
92 std::lock_guard<std::mutex> lock(timer->m_signalMutex);
93 count = timer->m_count;
94 timer->m_count = 0;
95 if (count == 0) {
96 continue;
99 try {
100 auto args = make_vec_array(sample_type_string(type), count, Object{wh});
101 vm_call_user_func(timer->m_callback, args);
102 } catch (Object& ex) {
103 raise_error("Uncaught exception escaping IntervalTimer: %s",
104 throwable_to_string(ex.get()).data());
109 IntervalTimer::~IntervalTimer() {
110 stop();
113 void IntervalTimer::init(double interval,
114 double initial,
115 const Variant& callback,
116 RequestInjectionData* data) {
117 m_interval = interval;
118 m_initial = initial;
119 m_callback = callback;
120 m_data = data;
123 void IntervalTimer::start() {
124 if (!m_data) return;
125 if (m_thread.joinable()) return;
126 m_done = false;
127 m_thread = std::thread([](IntervalTimer* t) { t->run(); }, this);
128 s_timer_pool->timers().insert(this);
131 void IntervalTimer::stop() {
132 if (!m_data) return;
133 if (!m_thread.joinable()) return;
135 std::lock_guard<std::mutex> lock(m_mutex);
136 m_done = true;
138 m_cv.notify_one();
139 m_thread.join();
140 s_timer_pool->timers().erase(this);
143 void IntervalTimer::run() {
144 auto waitTime = m_initial;
145 do {
146 std::unique_lock<std::mutex> lock(m_mutex);
147 auto status = m_cv.wait_for(lock,
148 #ifdef MSVC_NO_STD_CHRONO_DURATION_DOUBLE_ADD
149 std::chrono::duration<__int64>((__int64)waitTime),
150 #else
151 std::chrono::duration<double>(waitTime),
152 #endif
153 [this]{ return m_done; });
154 if (status) break;
156 std::lock_guard<std::mutex> l(m_signalMutex);
157 m_data->setFlag(IntervalTimerFlag);
158 ++m_count;
160 waitTime = m_interval;
161 } while (waitTime != 0.0);
164 ///////////////////////////////////////////////////////////////////////////////
166 Class* IntervalTimer::c_Class = nullptr;
167 const StaticString IntervalTimer::c_ClassName("IntervalTimer");
169 void HHVM_METHOD(IntervalTimer, __construct,
170 double interval,
171 double initial,
172 const Variant& callback) {
173 auto data = Native::data<IntervalTimer>(this_);
174 data->init(interval, initial, callback,
175 &RequestInfo::s_requestInfo->m_reqInjectionData);
178 void HHVM_METHOD(IntervalTimer, start) {
179 auto data = Native::data<IntervalTimer>(this_);
180 data->start();
183 void HHVM_METHOD(IntervalTimer, stop) {
184 auto data = Native::data<IntervalTimer>(this_);
185 data->stop();
188 ///////////////////////////////////////////////////////////////////////////////
190 static struct IntervalTimerExtension final : Extension {
191 IntervalTimerExtension() : Extension("intervaltimer") {}
193 void moduleInit() override {
194 HHVM_ME(IntervalTimer, __construct);
195 HHVM_ME(IntervalTimer, start);
196 HHVM_ME(IntervalTimer, stop);
197 Native::registerNativeDataInfo<IntervalTimer>(
198 IntervalTimer::c_ClassName.get(),
199 Native::NDIFlags::NO_SWEEP);
201 loadSystemlib("intervaltimer");
203 } s_intervaltimer_extension;
205 ///////////////////////////////////////////////////////////////////////////////