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 */
176bbec722Sjim
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 */
266bbec722Sjim
276bbec722Sjim #include "engine_manager.h"
286bbec722Sjim #include "default_engine_internal.h"
296bbec722Sjim
30d4b3338bSTrond Norbye #include <chrono>
316fcf60d9SJim Walker #include <memory>
326bbec722Sjim
336fcf60d9SJim Walker static std::unique_ptr<EngineManager> engineManager;
346bbec722Sjim
EngineManager()356bbec722Sjim EngineManager::EngineManager()
36d4b3338bSTrond Norbye : scrubberTask(*this),
376bbec722Sjim shuttingdown(false) {}
386bbec722Sjim
~EngineManager()396bbec722Sjim EngineManager::~EngineManager() {
406bbec722Sjim shutdown();
416bbec722Sjim }
426bbec722Sjim
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 }
486bbec722Sjim
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);
606bbec722Sjim
616bbec722Sjim return newEngine;
62d4b3338bSTrond Norbye } catch (const std::bad_alloc&) {
63d4b3338bSTrond Norbye return nullptr;
646bbec722Sjim }
656bbec722Sjim }
666bbec722Sjim
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 }
736bbec722Sjim
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 }
806bbec722Sjim
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 }
1606bbec722Sjim
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 }
1766bbec722Sjim
engine_manager_delete_engine(struct default_engine * engine)1776bbec722Sjim void engine_manager_delete_engine(struct default_engine* engine) {
1786fcf60d9SJim Walker getEngineManager().requestDestroyEngine(engine);
1796bbec722Sjim }
1806bbec722Sjim
engine_manager_scrub_engine(struct default_engine * engine)1816bbec722Sjim void engine_manager_scrub_engine(struct default_engine* engine) {
1826fcf60d9SJim Walker getEngineManager().scrubEngine(engine);
1836bbec722Sjim }
1846bbec722Sjim
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 }
192