1 /*
2 ** Copyright 2018 Bloomberg Finance L.P.
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 #include <gtest/gtest.h>
17 #include <quantum/quantum.h>
18 #include <vector>
19 #include <unordered_set>
20
21 using namespace Bloomberg;
22
23 struct MockTaskId : public quantum::TaskId
24 {
25 struct ThisThreadTag{};
MockTaskIdMockTaskId26 MockTaskId() : quantum::TaskId() {}
MockTaskIdMockTaskId27 explicit MockTaskId(ThisThreadTag) : quantum::TaskId(quantum::TaskId::ThisThreadTag{}) {}
MockTaskIdMockTaskId28 explicit MockTaskId(quantum::CoroContextTag t) : quantum::TaskId(t){}
MockTaskIdMockTaskId29 explicit MockTaskId(quantum::ThreadContextTag t) : quantum::TaskId(t){}
assignMockTaskId30 void assign() { assignCurrentThread(); }
31 };
32
33 //==============================================================================
34 // TEST CASES
35 //==============================================================================
TEST(TaskId,DefaultInitialization)36 TEST(TaskId, DefaultInitialization)
37 {
38 MockTaskId idMain; //empty
39 MockTaskId idMain2(MockTaskId::ThisThreadTag{}); //has main's thread id
40 MockTaskId idCoro(quantum::CoroContextTag{});
41 MockTaskId idCoroCopy = idCoro;
42 idCoro.assign(); //set current thread id
43 MockTaskId idCoro2(quantum::CoroContextTag{});
44 MockTaskId idIo(quantum::ThreadContextTag{});
45 idIo.assign(); //set current thread id
46 MockTaskId idIo2(quantum::ThreadContextTag{});
47
48 EXPECT_NE(idMain, idMain2);
49 EXPECT_EQ(idMain2, quantum::local::taskId());
50 EXPECT_NE(idCoro, idCoro2);
51 EXPECT_EQ(idCoro, idCoroCopy); //do not compare thread ids, only coroutine ids
52 EXPECT_NE(idIo, idIo2);
53 EXPECT_NE(idCoro, idIo);
54 EXPECT_NE(idCoro, idMain);
55 EXPECT_NE(idIo, idMain);
56
57 //check if it's a coroutine
58 EXPECT_TRUE(idCoro.isCoroutine());
59 EXPECT_FALSE(idMain.isCoroutine());
60 EXPECT_FALSE(idIo.isCoroutine());
61
62 //check the thread ids
63 EXPECT_NE(idMain.threadId(), idCoro.threadId());
64 EXPECT_EQ(idMain2.threadId(), idCoro.threadId());
65 EXPECT_EQ(idCoro.threadId(), idIo.threadId());
66 EXPECT_EQ(std::this_thread::get_id(), idCoro.threadId());
67 EXPECT_EQ(std::thread::id(), idCoro2.threadId());
68 EXPECT_EQ(0, idMain.id());
69 EXPECT_NE(idCoro.id(), idCoro2.id());
70 EXPECT_NE(idIo.id(), idIo2.id());
71 }
72
TEST(TaskId,Uniqueness)73 TEST(TaskId, Uniqueness)
74 {
75 std::vector<MockTaskId> coro, io;
76 MockTaskId firstCoro(quantum::CoroContextTag{});
77 for (int i = 0; i < 10; ++i) {
78 coro.emplace_back(MockTaskId(quantum::CoroContextTag{}));
79 }
80 //Check if all id's are different and are in decreasing order
81 for (const auto& id : coro) {
82 EXPECT_EQ(firstCoro.id()-1, id.id());
83 firstCoro = id;
84 }
85
86 //IO ids are in increasing order
87 MockTaskId firstIo(quantum::ThreadContextTag{});
88 for (int i = 0; i < 10; ++i) {
89 io.emplace_back(MockTaskId(quantum::ThreadContextTag{}));
90 }
91 //Check if all id's are different and are in decreasing order
92 for (const auto& id : io) {
93 EXPECT_EQ(firstIo.id()+1, id.id());
94 firstIo = id;
95 }
96 }
97
TEST(TaskId,LocalContext)98 TEST(TaskId, LocalContext)
99 {
100 quantum::Configuration config;
101 config.setNumCoroutineThreads(2);
102 config.setNumIoThreads(2);
103 quantum::Dispatcher dispatcher(config);
104 std::vector<quantum::TaskId> coroIds, ioIds;
105
106 auto coroFunc = [](quantum::VoidCoroContextPtr ctx) mutable->quantum::TaskId {
107 return ctx->taskId();
108 };
109 auto ioFunc = []() mutable->quantum::TaskId {
110 return quantum::local::taskId();
111 };
112 //Collect task ids
113 coroIds.emplace_back(dispatcher.post(0,false,coroFunc)->get());
114 coroIds.emplace_back(dispatcher.post(0,false,coroFunc)->get());
115 coroIds.emplace_back(dispatcher.post(1,false,coroFunc)->get());
116 coroIds.emplace_back(dispatcher.post(1,false,coroFunc)->get());
117 ioIds.emplace_back(dispatcher.postAsyncIo(0,false,ioFunc)->get());
118 ioIds.emplace_back(dispatcher.postAsyncIo(0,false,ioFunc)->get());
119 ioIds.emplace_back(dispatcher.postAsyncIo(1,false,ioFunc)->get());
120 ioIds.emplace_back(dispatcher.postAsyncIo(1,false,ioFunc)->get());
121
122 //Compare
123 EXPECT_TRUE(coroIds[0].isCoroutine());
124 for (size_t i = 1; i < coroIds.size(); ++i) {
125 EXPECT_TRUE(coroIds[i].isCoroutine());
126 EXPECT_EQ(coroIds[i-1].id()-1, coroIds[i].id());
127 }
128 //Make sure the executing thread id is properly set
129 EXPECT_EQ(coroIds[0].threadId(), coroIds[1].threadId());
130 EXPECT_EQ(coroIds[2].threadId(), coroIds[3].threadId());
131 EXPECT_NE(coroIds[0].threadId(), coroIds[2].threadId());
132 EXPECT_NE(quantum::TaskId().threadId(), coroIds[0].threadId());
133
134 EXPECT_FALSE(ioIds[0].isCoroutine());
135 for (size_t i = 1; i < ioIds.size(); ++i) {
136 EXPECT_FALSE(ioIds[i].isCoroutine());
137 EXPECT_EQ(ioIds[i-1].id()+1, ioIds[i].id());
138 }
139 //Make sure the executing thread id is properly set
140 EXPECT_EQ(ioIds[0].threadId(), ioIds[1].threadId());
141 EXPECT_EQ(ioIds[2].threadId(), ioIds[3].threadId());
142 EXPECT_NE(ioIds[0].threadId(), ioIds[2].threadId());
143 EXPECT_NE(quantum::TaskId().threadId(), ioIds[0].threadId());
144
145 //Check hashing
146 std::unordered_set<quantum::TaskId> idSet;
147 idSet.insert(coroIds.begin(), coroIds.end());
148 idSet.insert(ioIds.begin(), ioIds.end());
149 EXPECT_EQ(coroIds.size() + ioIds.size(), idSet.size());
150 }
151
152