You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficserver.apache.org by ma...@apache.org on 2020/03/13 07:10:50 UTC

[trafficserver] branch master updated: Cleanup: check activity of Http2Stream by ActivityCop

This is an automated email from the ASF dual-hosted git repository.

masaori pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficserver.git


The following commit(s) were added to refs/heads/master by this push:
     new c05e751  Cleanup: check activity of Http2Stream by ActivityCop
c05e751 is described below

commit c05e7510665258dcac6076a1cba5dcb713bfb2fa
Author: Masaori Koshiba <ma...@apache.org>
AuthorDate: Mon Mar 9 11:01:09 2020 +0900

    Cleanup: check activity of Http2Stream by ActivityCop
---
 iocore/net/NetTimeout.h            | 263 +++++++++++++++++++++++++++++++++++++
 proxy/http2/Http2ConnectionState.h |  10 ++
 proxy/http2/Http2Stream.cc         |  71 +++-------
 proxy/http2/Http2Stream.h          |  17 +--
 4 files changed, 300 insertions(+), 61 deletions(-)

diff --git a/iocore/net/NetTimeout.h b/iocore/net/NetTimeout.h
new file mode 100644
index 0000000..dcc584e
--- /dev/null
+++ b/iocore/net/NetTimeout.h
@@ -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)
+{
+  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;
+}
diff --git a/proxy/http2/Http2ConnectionState.h b/proxy/http2/Http2ConnectionState.h
index 8a83fa7..7c81710 100644
--- a/proxy/http2/Http2ConnectionState.h
+++ b/proxy/http2/Http2ConnectionState.h
@@ -24,6 +24,9 @@
 #pragma once
 
 #include <atomic>
+
+#include "NetTimeout.h"
+
 #include "HTTP2.h"
 #include "HPACK.h"
 #include "Http2Stream.h"
@@ -122,6 +125,7 @@ public:
   HpackHandle *local_hpack_handle  = nullptr;
   HpackHandle *remote_hpack_handle = nullptr;
   DependencyTree *dependency_tree  = nullptr;
+  ActivityCop<Http2Stream> _cop;
 
   // Settings.
   Http2ConnectionSettings server_settings;
@@ -135,6 +139,9 @@ public:
     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
@@ -145,6 +152,9 @@ public:
       return;
     }
     in_destroy = true;
+
+    _cop.stop();
+
     if (shutdown_cont_event) {
       shutdown_cont_event->cancel();
       shutdown_cont_event = nullptr;
diff --git a/proxy/http2/Http2Stream.cc b/proxy/http2/Http2Stream.cc
index 83722b8..7863cb2 100644
--- a/proxy/http2/Http2Stream.cc
+++ b/proxy/http2/Http2Stream.cc
@@ -21,8 +21,9 @@
   limitations under the License.
  */
 
-#include "HTTP2.h"
 #include "Http2Stream.h"
+
+#include "HTTP2.h"
 #include "Http2ClientSession.h"
 #include "../http/HttpSM.h"
 
@@ -81,14 +82,6 @@ Http2Stream::main_event_handler(int event, void *edata)
     return 0;
   } else if (e == cross_thread_event) {
     cross_thread_event = nullptr;
-  } else if (e == active_event) {
-    event        = VC_EVENT_ACTIVE_TIMEOUT;
-    active_event = nullptr;
-  } else if (e == inactive_event) {
-    if (inactive_timeout_at && inactive_timeout_at < Thread::get_hrtime()) {
-      event = VC_EVENT_INACTIVITY_TIMEOUT;
-      clear_inactive_timer();
-    }
   } else if (e == read_event) {
     read_event = nullptr;
   } else if (e == write_event) {
@@ -108,7 +101,7 @@ Http2Stream::main_event_handler(int event, void *edata)
     break;
   case VC_EVENT_WRITE_READY:
   case VC_EVENT_WRITE_COMPLETE:
-    inactive_timeout_at = Thread::get_hrtime() + inactive_timeout;
+    _timeout.update_inactivity();
     if (e->cookie == &write_vio) {
       if (write_vio.mutex && write_vio.cont && this->_sm) {
         this->signal_write_event(event);
@@ -119,7 +112,7 @@ Http2Stream::main_event_handler(int event, void *edata)
     break;
   case VC_EVENT_READ_COMPLETE:
   case VC_EVENT_READ_READY:
-    inactive_timeout_at = Thread::get_hrtime() + inactive_timeout;
+    _timeout.update_inactivity();
     if (e->cookie == &read_vio) {
       if (read_vio.mutex && read_vio.cont && this->_sm) {
         signal_read_event(event);
@@ -363,7 +356,6 @@ Http2Stream::do_io_close(int /* flags */)
       h2_proxy_ssn->connection_state.send_data_frames(this);
     }
 
-    clear_timers();
     clear_io_events();
 
     // Wait until transaction_done is called from HttpSM to signal that the TXN_CLOSE hook has been executed
@@ -429,7 +421,6 @@ Http2Stream::initiating_close()
     // TXN_CLOSE has been sent
     // _proxy_ssn = NULL;
 
-    clear_timers();
     clear_io_events();
 
     // This should result in do_io_close or release being called.  That will schedule the final
@@ -514,7 +505,7 @@ Http2Stream::update_read_request(int64_t read_len, bool call_update, bool check_
   int64_t read_avail = this->read_vio.buffer.writer()->max_read_avail();
   if (read_avail > 0 || send_event == VC_EVENT_READ_COMPLETE) {
     if (call_update) { // Safe to call vio handler directly
-      inactive_timeout_at = Thread::get_hrtime() + inactive_timeout;
+      _timeout.update_inactivity();
       if (read_vio.cont && this->_sm) {
         read_vio.cont->handleEvent(send_event, &read_vio);
       }
@@ -639,7 +630,7 @@ Http2Stream::signal_read_event(int event)
 
   MUTEX_TRY_LOCK(lock, read_vio.cont->mutex, this_ethread());
   if (lock.is_locked()) {
-    inactive_timeout_at = Thread::get_hrtime() + inactive_timeout;
+    _timeout.update_inactivity();
     this->read_vio.cont->handleEvent(event, &this->read_vio);
   } else {
     if (this->_read_vio_event) {
@@ -658,7 +649,7 @@ Http2Stream::signal_write_event(int event)
 
   MUTEX_TRY_LOCK(lock, write_vio.cont->mutex, this_ethread());
   if (lock.is_locked()) {
-    inactive_timeout_at = Thread::get_hrtime() + inactive_timeout;
+    _timeout.update_inactivity();
     this->write_vio.cont->handleEvent(event, &this->write_vio);
   } else {
     if (this->_write_vio_event) {
@@ -704,7 +695,7 @@ void
 Http2Stream::send_response_body(bool call_update)
 {
   Http2ClientSession *h2_proxy_ssn = static_cast<Http2ClientSession *>(this->_proxy_ssn);
-  inactive_timeout_at              = Thread::get_hrtime() + inactive_timeout;
+  _timeout.update_inactivity();
 
   if (Http2::stream_priority_enabled) {
     SCOPED_MUTEX_LOCK(lock, h2_proxy_ssn->connection_state.mutex, this_ethread());
@@ -805,7 +796,6 @@ Http2Stream::destroy()
   if (header_blocks) {
     ats_free(header_blocks);
   }
-  clear_timers();
   clear_io_events();
   http_parser_clear(&http_parser);
 
@@ -822,56 +812,37 @@ Http2Stream::response_get_data_reader() const
 void
 Http2Stream::set_active_timeout(ink_hrtime timeout_in)
 {
-  active_timeout = timeout_in;
-  clear_active_timer();
-  if (active_timeout > 0) {
-    active_event = this_ethread()->schedule_in(this, active_timeout);
-  }
+  _timeout.set_active_timeout(timeout_in);
 }
 
 void
 Http2Stream::set_inactivity_timeout(ink_hrtime timeout_in)
 {
-  inactive_timeout = timeout_in;
-  if (inactive_timeout > 0) {
-    inactive_timeout_at = Thread::get_hrtime() + inactive_timeout;
-    if (!inactive_event) {
-      inactive_event = this_ethread()->schedule_every(this, HRTIME_SECONDS(1));
-    }
-  } else {
-    clear_inactive_timer();
-  }
+  _timeout.set_inactive_timeout(timeout_in);
 }
 
 void
-Http2Stream::cancel_inactivity_timeout()
+Http2Stream::cancel_active_timeout()
 {
-  set_inactivity_timeout(0);
+  _timeout.cancel_active_timeout();
 }
+
 void
-Http2Stream::clear_inactive_timer()
+Http2Stream::cancel_inactivity_timeout()
 {
-  inactive_timeout_at = 0;
-  if (inactive_event) {
-    inactive_event->cancel();
-    inactive_event = nullptr;
-  }
+  _timeout.cancel_inactive_timeout();
 }
 
-void
-Http2Stream::clear_active_timer()
+bool
+Http2Stream::is_active_timeout_expired(ink_hrtime now)
 {
-  if (active_event) {
-    active_event->cancel();
-    active_event = nullptr;
-  }
+  return _timeout.is_active_timeout_expired(now);
 }
 
-void
-Http2Stream::clear_timers()
+bool
+Http2Stream::is_inactive_timeout_expired(ink_hrtime now)
 {
-  clear_inactive_timer();
-  clear_active_timer();
+  return _timeout.is_inactive_timeout_expired(now);
 }
 
 void
diff --git a/proxy/http2/Http2Stream.h b/proxy/http2/Http2Stream.h
index df560a1..1b184c1 100644
--- a/proxy/http2/Http2Stream.h
+++ b/proxy/http2/Http2Stream.h
@@ -23,6 +23,8 @@
 
 #pragma once
 
+#include "NetTimeout.h"
+
 #include "HTTP2.h"
 #include "ProxyTransaction.h"
 #include "Http2DebugNames.h"
@@ -94,7 +96,10 @@ public:
   // Accessors
   void set_active_timeout(ink_hrtime timeout_in) override;
   void set_inactivity_timeout(ink_hrtime timeout_in) override;
+  void cancel_active_timeout();
   void cancel_inactivity_timeout() override;
+  bool is_active_timeout_expired(ink_hrtime now);
+  bool is_inactive_timeout_expired(ink_hrtime now);
 
   bool allow_half_open() const override;
   bool is_first_transaction() const override;
@@ -104,9 +109,6 @@ public:
   int get_transaction_priority_weight() const override;
   int get_transaction_priority_dependence() const override;
 
-  void clear_inactive_timer();
-  void clear_active_timer();
-  void clear_timers();
   void clear_io_events();
 
   bool is_client_state_writeable() const;
@@ -158,6 +160,7 @@ private:
    */
   bool _switch_thread_if_not_on_right_thread(int event, void *edata);
 
+  NetTimeout _timeout{};
   HTTPParser http_parser;
   EThread *_thread = nullptr;
   Http2StreamId _id;
@@ -210,14 +213,6 @@ private:
   Event *cross_thread_event      = nullptr;
   Event *buffer_full_write_event = nullptr;
 
-  // Support stream-specific timeouts
-  ink_hrtime active_timeout = 0;
-  Event *active_event       = nullptr;
-
-  ink_hrtime inactive_timeout    = 0;
-  ink_hrtime inactive_timeout_at = 0;
-  Event *inactive_event          = nullptr;
-
   Event *read_event       = nullptr;
   Event *write_event      = nullptr;
   Event *_read_vio_event  = nullptr;