Repository: trafficserver Updated Branches: refs/heads/master 2b5a16426 -> 9a80e8757
TS-2943: Add scoped resource class. Project: http://git-wip-us.apache.org/repos/asf/trafficserver/repo Commit: http://git-wip-us.apache.org/repos/asf/trafficserver/commit/9a80e875 Tree: http://git-wip-us.apache.org/repos/asf/trafficserver/tree/9a80e875 Diff: http://git-wip-us.apache.org/repos/asf/trafficserver/diff/9a80e875 Branch: refs/heads/master Commit: 9a80e875770a33044ac0cd80662680a5688c3952 Parents: 2b5a164 Author: Alan M. Carroll <[email protected]> Authored: Tue Jul 22 13:13:49 2014 -0500 Committer: Alan M. Carroll <[email protected]> Committed: Tue Jul 22 13:13:49 2014 -0500 ---------------------------------------------------------------------- lib/ts/ink_memory.h | 291 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 291 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/trafficserver/blob/9a80e875/lib/ts/ink_memory.h ---------------------------------------------------------------------- diff --git a/lib/ts/ink_memory.h b/lib/ts/ink_memory.h index 908685c..e0b3291 100644 --- a/lib/ts/ink_memory.h +++ b/lib/ts/ink_memory.h @@ -176,6 +176,12 @@ ink_zero(T& t) { The memory is also freed when the object is destructed. This makes handling temporary memory in a function more robust. + @DEPRECATED + + Use @c ats_scoped_str for @c xptr<char> + Use @c ats_scoped_mem<T> for @c xptr<T> (uses @c ats_malloc / @c ats_free ) + Use @c ats_scoped_obj<T> for pointers to other types using @c new and @c delete. + @internal A poor substitute for a real shared pointer copy on write class but one step at a time. It's better than doing this by hand every time. @@ -275,6 +281,291 @@ path_join (xptr<char> const& lhs, xptr<char> const& rhs) return x; } +/** Scoped resources. + + An instance of this class is used to hold a contingent resource. When this object goes out of scope + the resource is destroyed. If the resource needs to be kept valid it can be released from this container. + The standard usage pattern is + - Allocate resource. + - Perform various other checks or resource allocations which, if they fail, require this resource to be destroyed. + - Release the resource. + + This serves as a base implementation, actual use is usually through specialized subclasses. + + @see ats_scoped_fd + @see ats_scoped_mem + @see ats_scoped_obj + + For example, if you open a file descriptor and have to do other checks which result in having to call + @c close in each @c if clause. + + @code + int fd = open(...); + if (X) { Log(...); close(fd); return -1; } + if (Y) { Log(...); close(fd); return -1; } + ... + return fd; + @endcode + + Change this to + @code + ats_scoped_fd fd(open(...); + if (X) { Log(...) return; } // fd is closed upon return. + if (Y) { Log(...) return; } // fd is closed upon return. + fd.release(); // fd will not be automatically closed after this. + return fd; + @endcode + + The @a TRAITS class must have the following members. + + @code + value_type; // Declaration type of the resource. + RT initValue(); // Return canonical initialization value for RT. + bool isValid(RT); // Check for validity. Can take a reference or const reference. + void destroy(RT); // Cleanup. Can take a reference. + @endcode + + @c isValid should return @c true if the resource instance is valid and @c false if it is not valid. + + @c initValue must be a constant value of @a RT such that @c isValid(INVALID) is @c false. This + is used to initialize the object when the container is empty. + + @c destroy should perform cleanup on the object. + + @internal One might think the initialization value should be a constant but you can't initialize + non-integral class constants (such as pointers) in C++ (you can in C++ eleventy but we can't + require that). We can only hope the compiler is smart enough to optimize out functions returning + constants. + +*/ + +template < + typename TRAITS ///< Traits object. +> +class ats_scoped_resource +{ +public: + typedef TRAITS Traits; ///< Make template arg available. + typedef typename TRAITS::value_type value_type; ///< Import value type. + typedef ats_scoped_resource self; ///< Self reference type. + +public: + + /// Default constructor - an empty container. + ats_scoped_resource() : _r(Traits::initValue()) {} + + /// Construct with contained resource. + explicit ats_scoped_resource(value_type rt) : _r(rt) {} + + /// Destructor. + ~ats_scoped_resource() { + if (Traits::isValid(_r)) + Traits::destroy(_r); + } + + /// Automatic conversion to resource type. + operator value_type () const { + return _r; + } + + /** Release resource from this container. + After this call, the resource will @b not cleaned up when this container is destructed. + + @note Although direct assignment is forbidden due to the non-obvious semantics, a pointer can + be moved (@b not copied) from one instance to another using this method. + @code + new_ptr = old_ptr.release(); + @endcode + This is by design. + + @return The no longer contained resource. + */ + value_type release() { + value_type zret = _r; + _r = Traits::initValue(); + return zret; + } + + /** Place a new resource in the container. + Any resource currently contained is destroyed. + */ + self& operator = (value_type rt) { + if (Traits::isValid(_r)) Traits::destroy(_r); + _r = rt; + return *this; + } + + bool operator == (value_type rt) const { + return _r == rt; + } + + bool operator != (value_type rt) const { + return _r != rt; + } + +protected: + value_type _r; ///< Resource. +private: + ats_scoped_resource(self const&); ///< Copy constructor not permitted. + self& operator = (self const&); ///< Self assignment not permitted. + +}; + +namespace detail { +/** Traits for @c ats_scoped_resource for file descriptors. + */ + struct SCOPED_FD_TRAITS + { + typedef int value_type; + static int initValue() { return -1; } + static bool isValid(int fd) { return fd >= 0; } + static void destroy(int fd) { close(fd); } + }; +} +/** File descriptor as a scoped resource. + + @internal This needs to be a class and not just a @c typedef because the + pseudo-bool operator is required to avoid ambiguity for non-pointer + resources, but creates ambiguity for pointer resources. + */ +class ats_scoped_fd : public ats_scoped_resource<detail::SCOPED_FD_TRAITS> +{ +public: + typedef ats_scoped_resource<detail::SCOPED_FD_TRAITS> super; ///< Super type. + typedef ats_scoped_fd self; ///< Self reference type. + typedef bool (self::*pseudo_bool)() const; ///< Bool operator type. + + /// Assign a file descriptor @a fd. + self& operator = (int fd) { + super::operator=(fd); + return *this; + } + + /// Enable direct validity check in an @c if statement w/o ambiguity with @c int conversion. + operator pseudo_bool () const { + return Traits::isValid(_r) ? &self::operator! : 0; + } + + /// Not valid check. + bool operator ! () const { + return ! Traits::isValid(_r); + } +}; + +namespace detail { +/** Traits for @c ats_scoped_resource for pointers from @c ats_malloc. + */ + template < + typename T ///< Underlying type (not the pointer type). + > + struct SCOPED_MALLOC_TRAITS + { + typedef T* value_type; + static T* initValue() { return NULL; } + static bool isValid(T* t) { return 0 != t; } + static void destroy(T* t) { ats_free(t); } + }; + + /// Traits for @c ats_scoped_resource for objects using @c new and @c delete. + template < + typename T ///< Underlying type - not the pointer type. + > + struct SCOPED_OBJECT_TRAITS + { + typedef T* value_type; + static T* initValue() { return NULL; } + static bool isValid(T* t) { return 0 != t; } + static void destroy(T* t) { delete t; } + }; +} + +/** Specialization of @c ats_scoped_resource for strings. + This contains an allocated string that is cleaned up if not explicitly released. +*/ +class ats_scoped_str : public ats_scoped_resource<detail::SCOPED_MALLOC_TRAITS<char> > +{ + public: + typedef ats_scoped_resource<detail::SCOPED_MALLOC_TRAITS<char> > super; ///< Super type. + typedef ats_scoped_str self; ///< Self reference type. + + /// Default constructor (no string). + ats_scoped_str() + { } + /// Construct and allocate @a n bytes for a string. + explicit ats_scoped_str(size_t n) : super(static_cast<char*>(ats_malloc(n))) + { } + /// Put string @a s in this container for cleanup. + explicit ats_scoped_str(char* s) : super(s) + { } + /// Assign a string @a s to this container. + self& operator = (char* s) { + super::operator=(s); + return *this; + } +}; + +/** Specialization of @c ats_scoped_resource for pointers allocated with @c ats_malloc. + + @note This is a drop in replacement for @c xptr<T>. + */ +template < + typename T ///< Underlying (not pointer) type. +> +class ats_scoped_mem : public ats_scoped_resource<detail::SCOPED_MALLOC_TRAITS<T> > +{ +public: + typedef ats_scoped_resource<detail::SCOPED_MALLOC_TRAITS<T> > super; ///< Super type. + typedef ats_scoped_mem self; ///< Self reference. + + self& operator = (T* ptr) { + super::operator=(ptr); + return *this; + } +}; + +/** Specialization of @c ats_scoped_resource for objects. + This handles a pointer to an object created by @c new and destroyed by @c delete. +*/ + +template < + typename T /// Underlying (not pointer) type. +> +class ats_scoped_obj : public ats_scoped_resource<detail::SCOPED_OBJECT_TRAITS<T> > +{ +public: + typedef ats_scoped_resource<detail::SCOPED_OBJECT_TRAITS<T> > super; ///< Super type. + typedef ats_scoped_obj self; ///< Self reference. + + self& operator = (T* obj) { + super::operator=(obj); + return *this; + } +}; + +/** Combine two strings as file paths. + Trailing and leading separators for @a lhs and @a rhs respectively + are handled to yield exactly one separator. + @return A newly @x ats_malloc string of the combined paths. +*/ +inline char* +path_join (ats_scoped_str const& lhs, ats_scoped_str const& rhs) +{ + size_t ln = strlen(lhs); + size_t rn = strlen(rhs); + char const* rptr = rhs; // May need to be modified. + + if (ln && lhs[ln-1] == '/') --ln; // drop trailing separator. + if (rn && *rptr == '/') --rn, ++rptr; // drop leading separator. + + ats_scoped_str x(ln + rn + 2); + + memcpy(x, lhs, ln); + x[ln] = '/'; + memcpy(x + ln + 1, rptr, rn); + x[ln+rn+1] = 0; // terminate string. + + return x.release(); +} #endif /* __cplusplus */ #endif
