MB-20054: Regression test - bucket is deleted with DCPBackfill running
[ep-engine.git] / src / fakes / fake_executorpool.h
1 /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3  *     Copyright 2016 Couchbase, Inc.
4  *
5  *   Licensed under the Apache License, Version 2.0 (the "License");
6  *   you may not use this file except in compliance with the License.
7  *   You may obtain a copy of the License at
8  *
9  *       http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *   Unless required by applicable law or agreed to in writing, software
12  *   distributed under the License is distributed on an "AS IS" BASIS,
13  *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *   See the License for the specific language governing permissions and
15  *   limitations under the License.
16  */
17
18 /*
19  * FakeExecutorPool / FakeExecutorThread
20  *
21  * A pair of classes which act as a fake ExecutorPool for testing purposes.
22  * Only executes tasks when explicitly told, and only on the main thread.
23  *
24  * See SingleThreadedEPStoreTest for basic usage.
25  *
26  * TODO: Improve usage documentation.
27  */
28
29 #pragma once
30
31 #include "executorpool.h"
32 #include "executorthread.h"
33
34 #include <gtest/gtest.h>
35
36 class SingleThreadedExecutorPool : public ExecutorPool {
37 public:
38
39     /* Registers an instance of this class as "the" executorpool (i.e. what
40      * you get when you call ExecutorPool::get()).
41      *
42      * This *must* be called before the normal ExecutorPool is created.
43      */
44     static void replaceExecutorPoolWithFake() {
45         LockHolder lh(initGuard);
46         ExecutorPool* tmp = ExecutorPool::instance;
47         if (tmp != NULL) {
48             throw std::runtime_error("replaceExecutorPoolWithFake: "
49                     "ExecutorPool instance already created - cowardly refusing to continue!");
50         }
51
52         EventuallyPersistentEngine *epe =
53                 ObjectRegistry::onSwitchThread(NULL, true);
54         tmp = new SingleThreadedExecutorPool(NUM_TASK_GROUPS);
55         ObjectRegistry::onSwitchThread(epe);
56         instance = tmp;
57     }
58
59     SingleThreadedExecutorPool(size_t nTaskSets)
60         : ExecutorPool(/*threads*/0, nTaskSets, 0, 0, 0, 0) {
61     }
62
63     bool _startWorkers() {
64         // Don't actually start any worker threads (all work will be done
65         // synchronously in the same thread) - but we do need to set
66         // maxWorkers to at least 1 otherwise ExecutorPool::tryNewWork() will
67         // never return any work.
68
69         maxWorkers[WRITER_TASK_IDX] = 1;
70         maxWorkers[READER_TASK_IDX] = 1;
71         maxWorkers[AUXIO_TASK_IDX]  = 1;
72         maxWorkers[NONIO_TASK_IDX]  = 1;
73
74         return true;
75     }
76
77     // Helper methods to access normally protected state of ExecutorPool
78
79     TaskQ& getLpTaskQ() {
80         return lpTaskQ;
81     }
82 };
83
84 /* A fake execution 'thread', to be used with the FakeExecutorPool Allows
85  * execution of tasks synchronously in the current thread.
86  */
87 class FakeExecutorThread : public ExecutorThread {
88 public:
89     FakeExecutorThread(ExecutorPool* manager_, int startingQueue)
90         : ExecutorThread(manager_, startingQueue, "mock_executor") {
91     }
92
93     void runCurrentTask() {
94         // Only supports one-shot tasks
95         EXPECT_FALSE(currentTask->run());
96         completeCurrentTask();
97     }
98
99     // 'completes' the current task; useful if the caller wants to seperately
100     // run() the current task and then tidy up afterwards.
101     void completeCurrentTask() {
102         manager->doneWork(curTaskType);
103         manager->cancel(currentTask->getId(), true);
104         currentTask.reset();
105     }
106
107     ExTask& getCurrentTask() {
108         return currentTask;
109     }
110
111     void updateCurrentTime() {
112         now = gethrtime();
113     }
114 };