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