16bbec722Sjim /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
26bbec722Sjim /*
3d4b3338bSTrond Norbye  *     Copyright 2016 Couchbase, Inc
46bbec722Sjim  *
56bbec722Sjim  *   Licensed under the Apache License, Version 2.0 (the "License");
66bbec722Sjim  *   you may not use this file except in compliance with the License.
76bbec722Sjim  *   You may obtain a copy of the License at
86bbec722Sjim  *
96bbec722Sjim  *       http://www.apache.org/licenses/LICENSE-2.0
106bbec722Sjim  *
116bbec722Sjim  *   Unless required by applicable law or agreed to in writing, software
126bbec722Sjim  *   distributed under the License is distributed on an "AS IS" BASIS,
136bbec722Sjim  *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
146bbec722Sjim  *   See the License for the specific language governing permissions and
156bbec722Sjim  *   limitations under the License.
166bbec722Sjim  */
186bbec722Sjim /*
19d4b3338bSTrond Norbye  * Engine manager provides methods for creating and deleting of engine
20d4b3338bSTrond Norbye  * handles/structs and the creation and safe teardown of the scrubber thread.
21d4b3338bSTrond Norbye  *
22d4b3338bSTrond Norbye  *  Note: A single scrubber exists for the purposes of running a user requested
23d4b3338bSTrond Norbye  *  scrub and for background deletion of bucket items when a bucket is
24d4b3338bSTrond Norbye  *  destroyed.
256bbec722Sjim  */
276bbec722Sjim #include "engine_manager.h"
286bbec722Sjim #include "default_engine_internal.h"
30d4b3338bSTrond Norbye #include <chrono>
316fcf60d9SJim Walker #include <memory>
336fcf60d9SJim Walker static std::unique_ptr<EngineManager> engineManager;
EngineManager()356bbec722Sjim EngineManager::EngineManager()
36d4b3338bSTrond Norbye   : scrubberTask(*this),
376bbec722Sjim     shuttingdown(false) {}
~EngineManager()396bbec722Sjim EngineManager::~EngineManager() {
406bbec722Sjim     shutdown();
416bbec722Sjim }
createEngine()436bbec722Sjim struct default_engine* EngineManager::createEngine() {
44d4b3338bSTrond Norbye     std::lock_guard<std::mutex> lck(lock);
45d4b3338bSTrond Norbye     if (shuttingdown) {
46d4b3338bSTrond Norbye         return nullptr;
476bbec722Sjim     }
49d4b3338bSTrond Norbye     try {
50ca61c5fbSTrond Norbye         static bucket_id_t bucket_id;
51d4b3338bSTrond Norbye 
52d4b3338bSTrond Norbye         struct default_engine* newEngine = new struct default_engine();
53ca61c5fbSTrond Norbye         if (bucket_id + 1 == 0) {
54ca61c5fbSTrond Norbye             // We've used all of the available id's
55ca61c5fbSTrond Norbye             delete newEngine;
56ca61c5fbSTrond Norbye             return nullptr;
57ca61c5fbSTrond Norbye         }
58ca61c5fbSTrond Norbye         default_engine_constructor(newEngine, bucket_id++);
596bbec722Sjim         engines.insert(newEngine);
616bbec722Sjim         return newEngine;
62d4b3338bSTrond Norbye     } catch (const std::bad_alloc&) {
63d4b3338bSTrond Norbye         return nullptr;
646bbec722Sjim     }
656bbec722Sjim }
requestDestroyEngine(struct default_engine * engine)676bbec722Sjim void EngineManager::requestDestroyEngine(struct default_engine* engine) {
68d4b3338bSTrond Norbye     std::lock_guard<std::mutex> lck(lock);
696bbec722Sjim     if (!shuttingdown) {
706bbec722Sjim         scrubberTask.placeOnWorkQueue(engine, true);
716bbec722Sjim     }
726bbec722Sjim }
scrubEngine(struct default_engine * engine)746bbec722Sjim void EngineManager::scrubEngine(struct default_engine* engine) {
75d4b3338bSTrond Norbye     std::lock_guard<std::mutex> lck(lock);
766bbec722Sjim     if (!shuttingdown) {
776bbec722Sjim         scrubberTask.placeOnWorkQueue(engine, false);
786bbec722Sjim     }
796bbec722Sjim }
waitForScrubberToBeIdle(std::unique_lock<std::mutex> & lck)81d4b3338bSTrond Norbye void EngineManager::waitForScrubberToBeIdle(std::unique_lock<std::mutex>& lck) {
82d4b3338bSTrond Norbye     if (!lck.owns_lock()) {
83d4b3338bSTrond Norbye         throw std::logic_error("EngineManager::waitForScrubberToBeIdle: Lock must be held");
84d4b3338bSTrond Norbye     }
85d4b3338bSTrond Norbye 
86d4b3338bSTrond Norbye     while (!scrubberTask.isIdle()) {
87d4b3338bSTrond Norbye         auto& task = scrubberTask;
88d4b3338bSTrond Norbye         // There is a race for the isIdle call, and I don't want to solve it
89d4b3338bSTrond Norbye         // by using a mutex as that would result in the use of trying to
90d4b3338bSTrond Norbye         // acquire multiple locks (which is a highway to deadlocks ;-)
91d4b3338bSTrond Norbye         //
92d4b3338bSTrond Norbye         // The scrubber does *not* hold the for the scrubber while calling
93d4b3338bSTrond Norbye         // notify on this condition variable.. And the state is then Scrubbing
94d4b3338bSTrond Norbye         // That means that this thread will wake, grab the mutex and check
95d4b3338bSTrond Norbye         // the state which is still Scrubbing and go back to sleep (depending
96d4b3338bSTrond Norbye         // on the scheduling order)..
97d4b3338bSTrond Norbye         cond.wait_for(lck,
98d4b3338bSTrond Norbye                       std::chrono::milliseconds(10),
99d4b3338bSTrond Norbye                       [&task] {
100d4b3338bSTrond Norbye             return task.isIdle();
101d4b3338bSTrond Norbye         });
102d4b3338bSTrond Norbye     }
103d4b3338bSTrond Norbye }
104d4b3338bSTrond Norbye 
1056bbec722Sjim /*
1066bbec722Sjim  * Join the scrubber and delete any data which wasn't cleaned by clients
1076bbec722Sjim  */
shutdown()1086bbec722Sjim void EngineManager::shutdown() {
109d4b3338bSTrond Norbye     std::unique_lock<std::mutex> lck(lock);
110d4b3338bSTrond Norbye     if (!shuttingdown) {
1116bbec722Sjim         shuttingdown = true;
112d4b3338bSTrond Norbye 
113d4b3338bSTrond Norbye         // Wait until the scrubber is done with all of its tasks
114d4b3338bSTrond Norbye         waitForScrubberToBeIdle(lck);
115d4b3338bSTrond Norbye 
116d4b3338bSTrond Norbye         // Do we have any engines defined?
117d4b3338bSTrond Norbye         if (!engines.empty()) {
118d4b3338bSTrond Norbye             // Tell it to go ahead and scrub all engines
119d4b3338bSTrond Norbye             for (auto engine : engines) {
120d4b3338bSTrond Norbye                 scrubberTask.placeOnWorkQueue(engine, true);
121d4b3338bSTrond Norbye             }
122d4b3338bSTrond Norbye 
123d4b3338bSTrond Norbye             // Wait for all of the engines to be deleted
124d4b3338bSTrond Norbye             auto& set = engines;
125*fe458a8fSTrond Norbye             // Ideally we should use cond.wait() here, but I _HAVE_ seen
126*fe458a8fSTrond Norbye             // this wait stuck on our commit validator
127*fe458a8fSTrond Norbye             // builders on.. tata WINDOWS
128*fe458a8fSTrond Norbye             // Given that it means that we need to log into the commit
129*fe458a8fSTrond Norbye             // validator builders to manually kill the process in order for
130*fe458a8fSTrond Norbye             // other builds to succeed we'll just timeout and recheck every
131*fe458a8fSTrond Norbye             // once and a while until we figure out why we might miss a
132*fe458a8fSTrond Norbye             // notification signal.
133*fe458a8fSTrond Norbye             cond.wait_for(lck, std::chrono::milliseconds(100), [&set] {
134d4b3338bSTrond Norbye                 return set.empty();
135d4b3338bSTrond Norbye             });
136d4b3338bSTrond Norbye 
137d4b3338bSTrond Norbye             // wait for the scrubber to become idle again
138d4b3338bSTrond Norbye             waitForScrubberToBeIdle(lck);
139d4b3338bSTrond Norbye         }
140d4b3338bSTrond Norbye 
1416bbec722Sjim         scrubberTask.shutdown();
1426bbec722Sjim         scrubberTask.joinThread();
143d4b3338bSTrond Norbye     }
144d4b3338bSTrond Norbye }
145d4b3338bSTrond Norbye 
notifyScrubComplete(struct default_engine * engine,bool destroy)146d4b3338bSTrond Norbye void EngineManager::notifyScrubComplete(struct default_engine* engine,
147d4b3338bSTrond Norbye                                         bool destroy) {
148d4b3338bSTrond Norbye     if (destroy) {
149d4b3338bSTrond Norbye         destroy_engine_instance(engine);
150d4b3338bSTrond Norbye     }
151d4b3338bSTrond Norbye 
1526bbec722Sjim     std::lock_guard<std::mutex> lck(lock);
153d4b3338bSTrond Norbye     if (destroy) {
154d4b3338bSTrond Norbye         engines.erase(engine);
1556bbec722Sjim         delete engine;
1566bbec722Sjim     }
157d4b3338bSTrond Norbye 
158d4b3338bSTrond Norbye     cond.notify_one();
1596bbec722Sjim }
getEngineManager()1616fcf60d9SJim Walker EngineManager& getEngineManager() {
1626fcf60d9SJim Walker     static std::mutex createLock;
1636fcf60d9SJim Walker     if (engineManager.get() == nullptr) {
1646fcf60d9SJim Walker         std::lock_guard<std::mutex> lg(createLock);
1656fcf60d9SJim Walker         if (engineManager.get() == nullptr) {
1666fcf60d9SJim Walker             engineManager.reset(new EngineManager());
1676fcf60d9SJim Walker         }
1686fcf60d9SJim Walker     }
1696fcf60d9SJim Walker     return *engineManager.get();
1706fcf60d9SJim Walker }
1716fcf60d9SJim Walker 
1726bbec722Sjim // C API methods follow.
engine_manager_create_engine()1736bbec722Sjim struct default_engine* engine_manager_create_engine() {
1746fcf60d9SJim Walker     return getEngineManager().createEngine();
1756bbec722Sjim }
engine_manager_delete_engine(struct default_engine * engine)1776bbec722Sjim void engine_manager_delete_engine(struct default_engine* engine) {
1786fcf60d9SJim Walker     getEngineManager().requestDestroyEngine(engine);
1796bbec722Sjim }
engine_manager_scrub_engine(struct default_engine * engine)1816bbec722Sjim void engine_manager_scrub_engine(struct default_engine* engine) {
1826fcf60d9SJim Walker     getEngineManager().scrubEngine(engine);
1836bbec722Sjim }
engine_manager_shutdown()1856bbec722Sjim void engine_manager_shutdown() {
1866bbec722Sjim     // will block waiting for scrubber to finish
187d4b3338bSTrond Norbye     // Note that it would be tempting to just call reset on the unique_ptr,
188d4b3338bSTrond Norbye     // but then we could recreate the object on accident by calling
189d4b3338bSTrond Norbye     // one of the other functions which in turn call getEngineManager()
1906fcf60d9SJim Walker     getEngineManager().shutdown();
1916bbec722Sjim }