]> git.ipfire.org Git - thirdparty/squid.git/blobdiff - src/security/LockingPointer.h
SourceFormat Enforcement
[thirdparty/squid.git] / src / security / LockingPointer.h
index 74417f0d9cf0bb704c14ddae65028f6d97a449fb..e0395f412f5f7dd680ed5551f674e1957aee2b24 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 1996-2015 The Squid Software Foundation and contributors
+ * Copyright (C) 1996-2017 The Squid Software Foundation and contributors
  *
  * Squid software is distributed under GPLv2+ license and includes
  * contributions from numerous individuals and organizations.
@@ -9,7 +9,7 @@
 #ifndef SQUID_SRC_SECURITY_LOCKINGPOINTER_H
 #define SQUID_SRC_SECURITY_LOCKINGPOINTER_H
 
-#include "base/TidyPointer.h"
+#include "base/HardFun.h"
 
 #if USE_OPENSSL
 #if HAVE_OPENSSL_CRYPTO_H
@@ -24,7 +24,7 @@
             sk_object ## _pop_free(a, freefunction); \
         }
 
-#endif
+#endif /* USE_OPENSSL */
 
 // Macro to be used to define the C++ equivalent function of an extern "C"
 // function. The C++ function suffixed with the _cpp extension
 namespace Security
 {
 
+inline bool nilFunction(const void *) { return false; }
+typedef HardFun<bool, const void *, nilFunction> NilFunctor;
+
 /**
- * Add SSL locking (a.k.a. reference counting) and assignment to TidyPointer
+ * A shared pointer to a reference-counting Object with library-specific
+ * absorption, locking, and unlocking implementations. The API largely
+ * follows std::shared_ptr.
+ *
+ * The constructor and the resetWithoutLocking() method import a raw Object pointer.
+ * Normally, reset() would lock(), but libraries like OpenSSL
+ * pre-lock objects before they are fed to LockingPointer, necessitating
+ * this resetWithoutLocking() customization hook.
  */
-template <typename T, void (*DeAllocator)(T *t), int lock>
-class LockingPointer: public TidyPointer<T, DeAllocator>
+template <typename T, void (*UnLocker)(T *t), class Locker = NilFunctor>
+class LockingPointer
 {
 public:
-    typedef TidyPointer<T, DeAllocator> Parent;
-    typedef LockingPointer<T, DeAllocator, lock> SelfType;
+    /// a helper label to simplify this objects API definitions below
+    typedef Security::LockingPointer<T, UnLocker, Locker> SelfType;
+
+    /**
+     * Construct directly from a raw pointer.
+     * This action requires that the producer of that pointer has already
+     * created one reference lock for the object pointed to.
+     * Our destructor will do the matching unlock.
+     */
+    explicit LockingPointer(T *t = nullptr): raw(nullptr) {
+        // de-optimized for clarity about non-locking
+        resetWithoutLocking(t);
+    }
 
-    explicit LockingPointer(T *t = nullptr): Parent(t) {}
+    /// use the custom UnLocker to unlock any value still stored.
+    ~LockingPointer() { unlock(); }
 
-    explicit LockingPointer(const SelfType &o): Parent() {
+    // copy semantics are okay only when adding a lock reference
+    LockingPointer(const SelfType &o) : raw(nullptr) {
         resetAndLock(o.get());
     }
-
-    SelfType &operator =(const SelfType & o) {
+    const SelfType &operator =(const SelfType &o) {
         resetAndLock(o.get());
         return *this;
     }
 
-#if __cplusplus >= 201103L
-    explicit LockingPointer(LockingPointer<T, DeAllocator, lock> &&o): Parent(o.get()) {
-        *o.addr() = nullptr;
+    LockingPointer(SelfType &&o) : raw(nullptr) {
+        resetWithoutLocking(o.release());
     }
-
-    LockingPointer<T, DeAllocator, lock> &operator =(LockingPointer<T, DeAllocator, lock> &&o) {
-        if (o.get() != this->get()) {
-            this->reset(o.get());
-            *o.addr() = nullptr;
-        }
+    SelfType &operator =(SelfType &&o) {
+        if (o.get() != raw)
+            resetWithoutLocking(o.release());
         return *this;
     }
-#endif
+
+    bool operator !() const { return !raw; }
+    explicit operator bool() const { return raw; }
+    bool operator ==(const SelfType &o) const { return (o.get() == raw); }
+    bool operator !=(const SelfType &o) const { return (o.get() != raw); }
+
+    T *operator ->() const { return raw; }
+
+    /// Returns raw and possibly nullptr pointer
+    T *get() const { return raw; }
+
+    /// Reset raw pointer - unlock any previous one and save new one without locking.
+    void resetWithoutLocking(T *t) {
+        unlock();
+        raw = t;
+    }
 
     void resetAndLock(T *t) {
-        if (t != this->get()) {
-            this->reset(t);
-#if USE_OPENSSL
-            if (t)
-                CRYPTO_add(&t->references, 1, lock);
-#elif USE_GNUTLS
-            // XXX: GnuTLS does not provide locking ?
-#else
-            assert(false);
-#endif
+        if (t != get()) {
+            resetWithoutLocking(t);
+            lock(t);
+        }
+    }
+
+    /// Forget the raw pointer - unlock if any value was set. Become a nil pointer.
+    void reset() { unlock(); }
+
+    /// Forget the raw pointer without unlocking it. Become a nil pointer.
+    T *release() {
+        T *ret = raw;
+        raw = nullptr;
+        return ret;
+    }
+
+private:
+    /// The lock() method increments Object's reference counter.
+    void lock(T *t) {
+        if (t) {
+            Locker doLock;
+            doLock(t);
+        }
+    }
+
+    /// Become a nil pointer. Decrements any pointed-to Object's reference counter
+    /// using UnLocker which ideally destroys the object when the counter reaches zero.
+    void unlock() {
+        if (raw) {
+            UnLocker(raw);
+            raw = nullptr;
         }
     }
+
+    /**
+     * Normally, no other code will have this raw pointer.
+     *
+     * However, OpenSSL does some strange and not always consistent things.
+     * OpenSSL library may keep its own internal raw pointers and manage
+     * their reference counts independently, or it may not. This varies between
+     * API functions, though it is usually documented.
+     *
+     * This means the caller code needs to be carefuly written to use the correct
+     * reset method and avoid the raw-pointer constructor unless OpenSSL function
+     * producing the pointer is clearly documented as incrementing a lock for it.
+     */
+    T *raw;
 };
 
 } // namespace Security