blob: eb56a1631ec8b168275887af50733320cc8138c5 [file] [log] [blame]
/** @file
NetTimeout
@section license License
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#pragma once
#include "tscore/List.h"
#include "tscore/ink_hrtime.h"
#include "I_EventSystem.h"
/**
NetTimeout - handle active & inactive timeout
*/
class NetTimeout
{
public:
void set_active_timeout(ink_hrtime timeout_in);
void set_inactive_timeout(ink_hrtime timeout_in);
void cancel_active_timeout();
void cancel_inactive_timeout();
void reset_active_timeout();
void reset_inactive_timeout();
bool is_active_timeout_expired(ink_hrtime now);
bool is_inactive_timeout_expired(ink_hrtime now);
ink_hrtime idle_time(ink_hrtime now);
void update_inactivity();
private:
ink_hrtime _active_timeout_in = 0;
ink_hrtime _inactive_timeout_in = 0;
ink_hrtime _next_active_timeout_at = 0;
ink_hrtime _next_inactive_timeout_at = 0;
};
/**
ActivityCop - Check activity of T in the List in every @f seconds
T have to handle VC_EVENT_ACTIVE_TIMEOUT and VC_EVENT_INACTIVITY_TIMEOUT events.
TODO: add concepts like below with C++20
```
template <class T, class List = DLL<T>>
concept Timeoutable = requires(T *t, List *list, ink_hrtime time) {
t->handleEvent();
t->head;
{list->next(t)} -> std::convertible_to<T *>;
{t->is_active_timeout_expired(time)} -> std::same_as<bool>;
{t->is_inactive_timeout_expired(time)} -> std::same_as<bool>;
};
```
*/
template <class T, class List = DLL<T>> class ActivityCop : public Continuation
{
public:
ActivityCop(){};
ActivityCop(Ptr<ProxyMutex> &m, List *l, int f);
void start();
void stop();
int check_activity(int event, Event *e);
private:
Event *_event = nullptr;
List *_list = nullptr;
int _freq = 1;
};
////
// Inline functions
//
// NetTimeout
//
inline void
NetTimeout::set_active_timeout(ink_hrtime timeout_in)
{
if (timeout_in == 0) {
return;
}
_active_timeout_in = timeout_in;
_next_active_timeout_at = Thread::get_hrtime() + timeout_in;
}
inline void
NetTimeout::set_inactive_timeout(ink_hrtime timeout_in)
{
if (timeout_in == 0) {
return;
}
_inactive_timeout_in = timeout_in;
_next_inactive_timeout_at = Thread::get_hrtime() + timeout_in;
}
inline void
NetTimeout::cancel_active_timeout()
{
_active_timeout_in = 0;
_next_active_timeout_at = 0;
}
inline void
NetTimeout::cancel_inactive_timeout()
{
_inactive_timeout_in = 0;
_next_inactive_timeout_at = 0;
}
inline void
NetTimeout::reset_active_timeout()
{
if (_active_timeout_in == 0) {
return;
}
_next_active_timeout_at = Thread::get_hrtime() + _active_timeout_in;
}
inline void
NetTimeout::reset_inactive_timeout()
{
if (_inactive_timeout_in == 0) {
return;
}
_next_inactive_timeout_at = Thread::get_hrtime() + _inactive_timeout_in;
}
inline bool
NetTimeout::is_active_timeout_expired(ink_hrtime now)
{
ink_assert(now > 0);
if (_active_timeout_in == 0) {
return false;
}
if (0 < _next_active_timeout_at && _next_active_timeout_at < now) {
Debug("activity_cop", "active timeout cont=%p now=%" PRId64 " timeout_at=%" PRId64 " timeout_in=%" PRId64, this,
ink_hrtime_to_sec(now), ink_hrtime_to_sec(_next_active_timeout_at), ink_hrtime_to_sec(_active_timeout_in));
return true;
}
return false;
}
inline bool
NetTimeout::is_inactive_timeout_expired(ink_hrtime now)
{
ink_assert(now > 0);
if (_inactive_timeout_in == 0) {
return false;
}
if (0 < _next_inactive_timeout_at && _next_inactive_timeout_at < now) {
Debug("activity_cop", "inactive timeout cont=%p now=%" PRId64 " timeout_at=%" PRId64 " timeout_in=%" PRId64, this,
ink_hrtime_to_sec(now), ink_hrtime_to_sec(_next_inactive_timeout_at), ink_hrtime_to_sec(_inactive_timeout_in));
return true;
}
return false;
}
/**
Return how log this was inactive.
*/
inline ink_hrtime
NetTimeout::idle_time(ink_hrtime now)
{
if (now < _next_inactive_timeout_at) {
return 0;
}
return ink_hrtime_to_sec((now - _next_inactive_timeout_at) + _inactive_timeout_in);
}
inline void
NetTimeout::update_inactivity()
{
if (_inactive_timeout_in == 0) {
return;
}
_next_inactive_timeout_at = Thread::get_hrtime() + _inactive_timeout_in;
}
//
// ActivityCop
//
template <class T, class List>
inline ActivityCop<T, List>::ActivityCop(Ptr<ProxyMutex> &m, List *l, int f) : Continuation(m.get()), _list(l), _freq(f)
{
SET_HANDLER((&ActivityCop<T, List>::check_activity));
}
template <class T, class List>
inline void
ActivityCop<T, List>::start()
{
_event = this_ethread()->schedule_every(this, HRTIME_SECONDS(_freq));
}
template <class T, class List>
inline void
ActivityCop<T, List>::stop()
{
_event->cancel();
}
template <class T, class List>
inline int
ActivityCop<T, List>::check_activity(int /* event */, Event *e)
{
ink_hrtime now = Thread::get_hrtime();
// Traverse list & check inactivity or activity
T *t = _list->head;
while (t) {
T *next = _list->next(t);
if (t->mutex == nullptr) {
t = next;
continue;
}
MUTEX_TRY_LOCK(lock, t->mutex, this_ethread());
if (!lock.is_locked()) {
t = next;
continue;
}
if (t->is_inactive_timeout_expired(now)) {
t->handleEvent(VC_EVENT_INACTIVITY_TIMEOUT, e);
} else if (t->is_active_timeout_expired(now)) {
t->handleEvent(VC_EVENT_ACTIVE_TIMEOUT, e);
}
t = next;
}
return EVENT_DONE;
}