Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
263 changes: 263 additions & 0 deletions iocore/net/NetTimeout.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,263 @@
/** @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 : 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)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this check done by NetTimeout itself?

The interaction between ActivityCop and NetTimeout looks weird.
AC: Hey, it's March 1st. Is your visa expired?
NT: Yes, it is.
AC: Your visa is expired.
NT: (I know, I told you.)

I'd do
AC: Hey, when is your visa expire date?
NT: It's March 1st.
AC: Your visa is expired.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The main motivation is hiding private members as much as possible. Maybe I did too much.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I revisit this but it looks like what AC needs to know is timeout is expired or not.
We see a similar code & relations for checking a connection is closed or not.

Copy link
Copy Markdown
Member

@maskit maskit Mar 11, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just asking expiration time is enough to know whether a stream is timed out. I don't see any needs to make two function calls if NetTimeout already know it's timed out. I'd move the check logic to AC and make NetTimeout to a simple data structure (with accessors).

{
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);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What information in e is needed?

} else if (t->is_active_timeout_expired(now)) {
t->handleEvent(VC_EVENT_ACTIVE_TIMEOUT, e);
}

t = next;
}

return EVENT_DONE;
}
10 changes: 10 additions & 0 deletions proxy/http2/Http2ConnectionState.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@
#pragma once

#include <atomic>

#include "NetTimeout.h"

#include "HTTP2.h"
#include "HPACK.h"
#include "Http2Stream.h"
Expand Down Expand Up @@ -122,6 +125,7 @@ class Http2ConnectionState : public Continuation
HpackHandle *local_hpack_handle = nullptr;
HpackHandle *remote_hpack_handle = nullptr;
DependencyTree *dependency_tree = nullptr;
ActivityCop<Http2Stream> _cop;

// Settings.
Http2ConnectionSettings server_settings;
Expand All @@ -135,6 +139,9 @@ class Http2ConnectionState : public Continuation
if (Http2::stream_priority_enabled) {
dependency_tree = new DependencyTree(Http2::max_concurrent_streams_in);
}

_cop = ActivityCop<Http2Stream>(this->mutex, &stream_list, 1);
_cop.start();
}

void
Expand All @@ -145,6 +152,9 @@ class Http2ConnectionState : public Continuation
return;
}
in_destroy = true;

_cop.stop();

if (shutdown_cont_event) {
shutdown_cont_event->cancel();
shutdown_cont_event = nullptr;
Expand Down
Loading