You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@trafficserver.apache.org by Igor Galić <i....@brainsware.org> on 2013/10/24 10:49:05 UTC

Re: git commit: TS-2296: improve ConfigProcessor reference counting


----- Original Message -----
> Updated Branches:
>   refs/heads/master f760dac42 -> 6da424faa
> 
> 
> TS-2296: improve ConfigProcessor reference counting
> 
> Use the standard RefCountObj reference counting of ConfigInfo
> objects. Add regression tests so that we can understand how this
> is supposed to work better.
> 
> 
> Project: http://git-wip-us.apache.org/repos/asf/trafficserver/repo
> Commit: http://git-wip-us.apache.org/repos/asf/trafficserver/commit/6da424fa
> Tree: http://git-wip-us.apache.org/repos/asf/trafficserver/tree/6da424fa
> Diff: http://git-wip-us.apache.org/repos/asf/trafficserver/diff/6da424fa
> 
> Branch: refs/heads/master
> Commit: 6da424faa886e12b658dbe426aad9d19512065c6
> Parents: f760dac
> Author: James Peach <jp...@apache.org>
> Authored: Mon Oct 7 16:08:36 2013 -0700
> Committer: James Peach <jp...@apache.org>
> Committed: Wed Oct 23 09:20:20 2013 -0700
> 
> ----------------------------------------------------------------------
>  CHANGES             |   2 +
>  lib/ts/Ptr.h        |   2 +
>  mgmt/ProxyConfig.cc | 115 ++++++++++++++++++++++++++++++++++++++++++-----
>  mgmt/ProxyConfig.h  |  18 ++++----
>  4 files changed, 115 insertions(+), 22 deletions(-)
> ----------------------------------------------------------------------
> 
> 
> http://git-wip-us.apache.org/repos/asf/trafficserver/blob/6da424fa/CHANGES
> ----------------------------------------------------------------------
> diff --git a/CHANGES b/CHANGES
> index c66e53e..84b8e1b 100644
> --- a/CHANGES
> +++ b/CHANGES
> @@ -2,6 +2,8 @@
>  Changes with Apache Traffic Server 4.1.0
>  
>  
> +  *) [TS-2296] improve ConfigProcessor reference counting
> +
>    *) [ TS-2295] update statvfs usage
>  
>    *) [TS-2139] Fix PURGE twice to remove object in cache if
>    enable-interim-cache
> 
> http://git-wip-us.apache.org/repos/asf/trafficserver/blob/6da424fa/lib/ts/Ptr.h
> ----------------------------------------------------------------------
> diff --git a/lib/ts/Ptr.h b/lib/ts/Ptr.h
> index c14759a..7cea36c 100644
> --- a/lib/ts/Ptr.h
> +++ b/lib/ts/Ptr.h
> @@ -272,6 +272,7 @@ public:
>    volatile int m_refcount;
>  };
>  
> +// Increment the reference count, returning the new count.
>  inline int
>  RefCountObj::refcount_inc()
>  {
> @@ -280,6 +281,7 @@ RefCountObj::refcount_inc()
>  
>  #define REF_COUNT_OBJ_REFCOUNT_INC(_x) (_x)->refcount_inc()
>  
> +// Decrement the reference count, returning the new count.
>  inline int
>  RefCountObj::refcount_dec()
>  {
> 
> http://git-wip-us.apache.org/repos/asf/trafficserver/blob/6da424fa/mgmt/ProxyConfig.cc
> ----------------------------------------------------------------------
> diff --git a/mgmt/ProxyConfig.cc b/mgmt/ProxyConfig.cc
> index e236897..2c6f7be 100644
> --- a/mgmt/ProxyConfig.cc
> +++ b/mgmt/ProxyConfig.cc
> @@ -24,6 +24,7 @@
>  #include "libts.h"
>  #include "ProxyConfig.h"
>  #include "P_EventSystem.h"
> +#include "ts/TestBox.h"
>  
>  ConfigProcessor configProcessor;
>  
> @@ -124,7 +125,7 @@ ConfigProcessor::ConfigProcessor()
>  }
>  
>  unsigned int
> -ConfigProcessor::set(unsigned int id, ConfigInfo * info)
> +ConfigProcessor::set(unsigned int id, ConfigInfo * info, unsigned
> timeout_secs)
>  {
>    ConfigInfo *old_info;
>    int idx;
> @@ -135,7 +136,13 @@ ConfigProcessor::set(unsigned int id, ConfigInfo * info)
>      ink_assert(id <= MAX_CONFIGS);
>    }
>  
> -  info->m_refcount = 1;
> +  // Don't be an idiot and use a zero timeout ...
> +  ink_assert(timeout_secs > 0);
> +
> +  // New objects *must* start with a zero refcount. The config
> +  // processor holds it's own refcount. We should be the only
> +  // refcount holder at this point.
> +  ink_release_assert(info->refcount_inc() == 1);
>  
>    if (id > MAX_CONFIGS) {
>      // invalid index
> @@ -146,11 +153,14 @@ ConfigProcessor::set(unsigned int id, ConfigInfo *
> info)
>    idx = id - 1;
>  
>    do {
> -    old_info = (ConfigInfo *) infos[idx];
> -  } while (!ink_atomic_cas( & infos[idx], old_info, info));
> +    old_info = infos[idx];
> +  } while (!ink_atomic_cas(&infos[idx], old_info, info));
>  
>    if (old_info) {
> -    eventProcessor.schedule_in(NEW(new ConfigInfoReleaser(id, old_info)),

ConfigInfoReleaser... This feels like Java :\
(even more so than ConfigProcessor ;)

> HRTIME_SECONDS(60));
> +    // The ConfigInfoReleaser now takes our refcount, but
> +    // someother thread might also have one ...
> +    ink_assert(old_info->refcount() > 0);
> +    eventProcessor.schedule_in(NEW(new ConfigInfoReleaser(id, old_info)),
> HRTIME_SECONDS(timeout_secs));
>    }
>  
>    return id;
> @@ -171,18 +181,17 @@ ConfigProcessor::get(unsigned int id)
>    }
>  
>    idx = id - 1;
> -  info = (ConfigInfo *) infos[idx];
> -  if (ink_atomic_increment((int *) &info->m_refcount, 1) < 0) {
> -    ink_assert(!"not reached");
> -  }
> +  info = infos[idx];
>  
> +  // Hand out a refcount to the caller. We should still have out
> +  // own refcount, so it should be at least 2.
> +  ink_release_assert(info->refcount_inc() > 1);
>    return info;
>  }
>  
>  void
>  ConfigProcessor::release(unsigned int id, ConfigInfo * info)
>  {
> -  int val;
>    int idx;
>  
>    ink_assert(id != 0);
> @@ -194,9 +203,91 @@ ConfigProcessor::release(unsigned int id, ConfigInfo *
> info)
>    }
>  
>    idx = id - 1;
> -  val = ink_atomic_increment((int *) &info->m_refcount, -1);
>  
> -  if ((infos[idx] != info) && (val == 1)) {
> +  if (info->refcount_dec() == 0) {
> +    // When we release, we should already have replaced this object in the
> index.
> +    ink_release_assert(info != this->infos[idx]);
>      delete info;
>    }
>  }
> +
> +#if TS_HAS_TESTS
> +
> +enum {
> +  REGRESSION_CONFIG_FIRST   = 1,   // last config in a sequence
> +  REGRESSION_CONFIG_LAST    = 2,   // last config in a sequence
> +  REGRESSION_CONFIG_SINGLE  = 4, // single-owner config
> +};
> +
> +struct RegressionConfig : public ConfigInfo
> +{
> +
> +  RegressionConfig(RegressionTest * r, int * ps, unsigned f)
> +      : test(r), pstatus(ps), flags(f) {
> +    if (this->flags & REGRESSION_CONFIG_SINGLE) {
> +      TestBox box(this->test, this->pstatus);
> +      box.check(this->refcount() == 1, "invalid refcount %d (should be 1)",
> this->refcount());
> +    }
> +  }
> +
> +  ~RegressionConfig() {
> +    TestBox box(this->test, this->pstatus);
> +
> +    box.check(this->refcount() == 0, "invalid refcount %d (should be 0)",
> this->refcount());
> +
> +    // If we are the last config to be scheduled, pass the test.
> +    // Otherwise, verify that the test is still running ...
> +    if (REGRESSION_CONFIG_LAST & flags) {
> +      *this->pstatus = REGRESSION_TEST_PASSED;
> +    } else {
> +      box.check(*this->pstatus == REGRESSION_TEST_INPROGRESS, "intermediate
> config out of sequence");
> +    }
> +  }
> +
> +  RegressionTest * test;
> +  int *     pstatus;
> +  unsigned  flags;
> +};
> +
> +// Test that ConfigProcessor::set() correctly releases the old ConfigInfo
> after a timeout.
> +REGRESSION_TEST(ProxyConfig_Set)(RegressionTest * test, int /* atype
> ATS_UNUSED */, int * pstatus)
> +{
> +  int configid = 0;
> +  *pstatus = REGRESSION_TEST_INPROGRESS;
> +  configid = configProcessor.set(configid, new RegressionConfig(test,
> pstatus, REGRESSION_CONFIG_FIRST), 1);
> +  configid = configProcessor.set(configid, new RegressionConfig(test,
> pstatus, REGRESSION_CONFIG_FIRST), 1);
> +  configid = configProcessor.set(configid, new RegressionConfig(test,
> pstatus, REGRESSION_CONFIG_FIRST), 1);
> +  configid = configProcessor.set(configid, new RegressionConfig(test,
> pstatus, REGRESSION_CONFIG_LAST), 2);
> +
> +  // Push one more RegressionConfig to force the tagged one to
> +  // get destroyed.
> +  configid = configProcessor.set(configid, new RegressionConfig(test,
> pstatus, 0), 4);
> +
> +  return;
> +}
> +
> +// Test that ConfigProcessor::release() correctly releases the old
> ConfigInfo across an implicit
> +// release timeout.
> +REGRESSION_TEST(ProxyConfig_Release)(RegressionTest * test, int /* atype
> ATS_UNUSED */, int * pstatus)
> +{
> +  int configid = 0;
> +  RegressionConfig * config;
> +
> +  *pstatus = REGRESSION_TEST_INPROGRESS;
> +
> +  // Set an initial config, then get it back to hold a reference count.
> +  configid = configProcessor.set(configid, new RegressionConfig(test,
> pstatus, REGRESSION_CONFIG_LAST), 1);
> +  config = (RegressionConfig *)configProcessor.get(configid);
> +
> +  // Now update the config a few times.
> +  configid = configProcessor.set(configid, new RegressionConfig(test,
> pstatus, REGRESSION_CONFIG_FIRST), 1);
> +  configid = configProcessor.set(configid, new RegressionConfig(test,
> pstatus, REGRESSION_CONFIG_FIRST), 1);
> +  configid = configProcessor.set(configid, new RegressionConfig(test,
> pstatus, REGRESSION_CONFIG_FIRST), 1);
> +
> +  sleep(2);
> +
> +  // Release the reference count. Since we were keeping this alive, it
> should be the last to die.
> +  configProcessor.release(configid, config);
> +}
> +
> +#endif /* TS_HAS_TESTS */
> 
> http://git-wip-us.apache.org/repos/asf/trafficserver/blob/6da424fa/mgmt/ProxyConfig.h
> ----------------------------------------------------------------------
> diff --git a/mgmt/ProxyConfig.h b/mgmt/ProxyConfig.h
> index 8fd6aa9..bb55f1b 100644
> --- a/mgmt/ProxyConfig.h
> +++ b/mgmt/ProxyConfig.h
> @@ -54,21 +54,19 @@ pmgmt->registerMgmtCallback(_signal,_fn,_data)
>  
>  #define MAX_CONFIGS  100
>  
> -struct ConfigInfo
> -{
> -  volatile int m_refcount;
> -
> -  virtual ~ ConfigInfo()
> -  {
> -  }
> -};
> -
> +typedef RefCountObj ConfigInfo;
>  
>  class ConfigProcessor
>  {
>  public:
>    ConfigProcessor();
>  
> +  enum {
> +    // The number of seconds to wait before garbage collecting stale
> ConfigInfo objects. There's
> +    // no good reason to tune this, outside of regression tests, so don't.
> +    CONFIG_PROCESSOR_RELEASE_SECS = 60
> +  };
> +
>    template <typename ClassType, typename ConfigType>
>    struct scoped_config {
>      scoped_config() : ptr(ClassType::acquire()) {}
> @@ -82,7 +80,7 @@ public:
>      ConfigType * ptr;
>    };
>  
> -  unsigned int set(unsigned int id, ConfigInfo * info);
> +  unsigned int set(unsigned int id, ConfigInfo * info, unsigned timeout_secs
> = CONFIG_PROCESSOR_RELEASE_SECS);
>    ConfigInfo *get(unsigned int id);
>    void release(unsigned int id, ConfigInfo * data);
>  
> 
> 

-- 
Igor Galić

Tel: +43 (0) 664 886 22 883
Mail: i.galic@brainsware.org
URL: http://brainsware.org/
GPG: 6880 4155 74BD FD7C B515  2EA5 4B1D 9E08 A097 C9AE