1 /* 2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #ifndef SRC_TRACE_PROCESSOR_SQLITE_MODULE_LIFECYCLE_MANAGER_H_ 18 #define SRC_TRACE_PROCESSOR_SQLITE_MODULE_LIFECYCLE_MANAGER_H_ 19 20 #include <memory> 21 #include <string> 22 #include <string_view> 23 24 #include "perfetto/base/logging.h" 25 #include "perfetto/ext/base/flat_hash_map.h" 26 27 namespace perfetto::trace_processor::sqlite { 28 29 // Helper class which abstracts away management of per-vtab state of an SQLite 30 // virtual table module. 31 // 32 // SQLite has some subtle semantics around lifecycle of vtabs which makes state 33 // management complex. This class attempts to encapsulate some of that 34 // complexity as a central place where we can document the quirks. 35 // 36 // Usage of this class: 37 // struct MyModule : sqlite::Module<MyModule> { 38 // struct Context { 39 // // Store the manager in the context object. 40 // ModuleStateManager<MyModule> manager. 41 // ... (other fields) 42 // } 43 // struct Vtab : sqlite::Module<MyModule>::Vtab { 44 // // Store the per-vtab-state pointer in the vtab object. 45 // ModuleStateManager<MyModule>::PerVtabState* state; 46 // ... (other fields) 47 // } 48 // static void OnCreate(...) { 49 // ... 50 // // Call OnCreate on the manager object and store the returned pointer. 51 // tab->state = ctx->manager.OnCreate(argv); 52 // ... 53 // } 54 // static void OnDestroy(...) { 55 // ... 56 // // Call OnDestroy with the stored state pointer. 57 // sqlite::ModuleStateManager<MyModule>::OnDestroy(tab->state); 58 // ... 59 // } 60 // // Do the same in OnConnect and OnDisconnect as in OnCreate and OnDestroy 61 // // respectively. 62 // static void OnConnect(...) 63 // static void OnDisconnect(...) 64 // } 65 template <typename Module> 66 class ModuleStateManager { 67 public: 68 // Per-vtab state. The pointer to this class should be stored in the Vtab. 69 struct PerVtabState { 70 private: 71 // The below fields should only be accessed by the manager, use GetState to 72 // access the state from outside this class. 73 friend class ModuleStateManager<Module>; 74 75 ModuleStateManager* manager; 76 bool disconnected = false; 77 std::string table_name; 78 std::unique_ptr<typename Module::State> state; 79 }; 80 81 // Lifecycle method to be called from Module::Create. OnCreate(const char * const * argv,std::unique_ptr<typename Module::State> state)82 [[nodiscard]] PerVtabState* OnCreate( 83 const char* const* argv, 84 std::unique_ptr<typename Module::State> state) { 85 auto it_and_inserted = state_by_name_.Insert(argv[2], nullptr); 86 PERFETTO_CHECK( 87 it_and_inserted.second || 88 (it_and_inserted.first && it_and_inserted.first->get()->disconnected)); 89 90 auto s = std::make_unique<PerVtabState>(); 91 auto* s_ptr = s.get(); 92 *it_and_inserted.first = std::move(s); 93 94 s_ptr->manager = this; 95 s_ptr->table_name = argv[2]; 96 s_ptr->state = std::move(state); 97 return it_and_inserted.first->get(); 98 } 99 100 // Lifecycle method to be called from Module::Connect. OnConnect(const char * const * argv)101 [[nodiscard]] PerVtabState* OnConnect(const char* const* argv) { 102 auto* ptr = state_by_name_.Find(argv[2]); 103 PERFETTO_CHECK(ptr); 104 ptr->get()->disconnected = false; 105 return ptr->get(); 106 } 107 108 // Lifecycle method to be called from Module::Disconnect. OnDisconnect(PerVtabState * state)109 static void OnDisconnect(PerVtabState* state) { 110 auto* ptr = state->manager->state_by_name_.Find(state->table_name); 111 PERFETTO_CHECK(ptr); 112 ptr->get()->disconnected = true; 113 } 114 115 // Lifecycle method to be called from Module::Destroy. OnDestroy(PerVtabState * state)116 static void OnDestroy(PerVtabState* state) { 117 PERFETTO_CHECK(state->manager->state_by_name_.Erase(state->table_name)); 118 } 119 120 // Method to be called from module callbacks to extract the module state 121 // from the manager state. GetState(PerVtabState * s)122 static typename Module::State* GetState(PerVtabState* s) { 123 return s->state.get(); 124 } 125 126 // Looks up the state of a module by name. This function should only be called 127 // for speculative lookups from outside the module implementation: use 128 // |GetState| inside the sqlite::Module implementation. FindStateByName(std::string_view name)129 typename Module::State* FindStateByName(std::string_view name) { 130 if (auto ptr = state_by_name_.Find(std::string(name)); ptr) { 131 return GetState(ptr->get()); 132 } 133 return nullptr; 134 } 135 136 private: 137 base::FlatHashMap<std::string, std::unique_ptr<PerVtabState>> state_by_name_; 138 }; 139 140 } // namespace perfetto::trace_processor::sqlite 141 142 #endif // SRC_TRACE_PROCESSOR_SQLITE_MODULE_LIFECYCLE_MANAGER_H_ 143