File indexing completed on 2024-05-19 05:42:10

0001 // ct_lvtmdb_databaseobject.h                                         -*-C++-*-
0002 
0003 /*
0004 // Copyright 2023 Codethink Ltd <codethink@codethink.co.uk>
0005 // SPDX-License-Identifier: Apache-2.0
0006 //
0007 // Licensed under the Apache License, Version 2.0 (the "License");
0008 // you may not use this file except in compliance with the License.
0009 // You may obtain a copy of the License at
0010 //
0011 //     http://www.apache.org/licenses/LICENSE-2.0
0012 //
0013 // Unless required by applicable law or agreed to in writing, software
0014 // distributed under the License is distributed on an "AS IS" BASIS,
0015 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0016 // See the License for the specific language governing permissions and
0017 // limitations under the License.
0018 */
0019 
0020 #ifndef INCLUDED_CT_LVTMDB_DATABASEOBJECT
0021 #define INCLUDED_CT_LVTMDB_DATABASEOBJECT
0022 
0023 //@PURPOSE: Share code between lvtmdb::*Object
0024 
0025 #include <lvtmdb_export.h>
0026 
0027 #include <ct_lvtmdb_lockable.h>
0028 
0029 #include <algorithm>
0030 #include <string>
0031 #include <type_traits>
0032 
0033 namespace Codethink::lvtmdb {
0034 
0035 // =====================
0036 // class DatabaseObject
0037 // =====================
0038 
0039 class LVTMDB_EXPORT DatabaseObject : public Lockable {
0040     // See locking discipline in superclass. That applies here.
0041 
0042   private:
0043     std::string d_qualifiedName;
0044     // Full path of this variable
0045 
0046     std::string d_name;
0047     // Name of this variable
0048 
0049   public:
0050     // CREATORS
0051     DatabaseObject(std::string qualifiedName, std::string name);
0052 
0053     ~DatabaseObject() noexcept override;
0054 
0055     DatabaseObject(DatabaseObject&& other) noexcept;
0056 
0057     DatabaseObject& operator=(DatabaseObject&& other) noexcept;
0058 
0059     // ACCESSORS
0060 #ifdef LVTMDB_LOCK_DEBUGGING
0061     [[nodiscard]] const std::string& debugName() const;
0062 #endif
0063 
0064     [[nodiscard]] const std::string& qualifiedName() const;
0065 
0066     [[nodiscard]] const std::string& name() const;
0067 
0068     // CLASS METHODS
0069     template<class DERIVED>
0070     static void addPeerRelationship(DERIVED *source,
0071                                     DERIVED *target,
0072                                     std::vector<DERIVED *>& sourceList,
0073                                     std::vector<DERIVED *>& targetList)
0074     // add a peer relationship between source and target
0075     // for example, when adding a package dependency relationship:
0076     //     DERIVED would be PackageObject
0077     //     sourceList would be source.d_forwardDeps
0078     //     targetList would be target.d_reverseDeps
0079     {
0080         static_assert(std::is_base_of_v<DatabaseObject, DERIVED>);
0081 
0082         if (source == target) {
0083             // deadlock
0084             return;
0085         }
0086 
0087         // check if the relationship already exists. This lets us avoid getting any
0088         // exclusive locks
0089         {
0090             auto lock = source->readOnlyLock();
0091             (void) lock; // cppcheck
0092             const auto it = std::find(sourceList.begin(), sourceList.end(), target);
0093             if (it != sourceList.end()) {
0094                 return;
0095             }
0096         }
0097 
0098         auto locks = Lockable::rwLockTwo(source, target);
0099         (void) locks; // cppcheck
0100 
0101         // we already checked for duplicates above
0102         sourceList.push_back(target);
0103         targetList.push_back(source);
0104     }
0105 
0106     template<class DERIVED>
0107     static void removePeerRelationship(DERIVED *source,
0108                                        DERIVED *target,
0109                                        std::vector<DERIVED *>& sourceList,
0110                                        std::vector<DERIVED *>& targetList)
0111     // remove a peer relationship between source and target
0112     {
0113         static_assert(std::is_base_of_v<DatabaseObject, DERIVED>);
0114 
0115         if (source == target) {
0116             // deadlock
0117             return;
0118         }
0119 
0120         auto locks = Lockable::rwLockTwo(source, target);
0121         (void) locks; // cppcheck
0122 
0123         // we already checked for duplicates above
0124         sourceList.erase(std::remove(sourceList.begin(), sourceList.end(), target), sourceList.end());
0125         targetList.erase(std::remove(targetList.begin(), targetList.end(), source), targetList.end());
0126     }
0127 };
0128 
0129 } // namespace Codethink::lvtmdb
0130 
0131 #endif // INCLUDED_CT_LVTMDB_DATABASEOBJECT