1+ #include " ../../test/gtest.h"
2+ #include " ../vdma/shm.cpp"
3+ #include < photon/photon.h>
4+ #include < photon/common/alog.h>
5+ #include < photon/thread/thread11.h>
6+ #include < photon/thread/workerpool.h>
7+
8+ #include < vector>
9+
10+ TEST (Buffer, encode_decode) {
11+ std::string str;
12+ photon::SharedMemoryBuffer::encode_to (str, 111 , 222 );
13+ uint64_t idx = 0 ;
14+ size_t bufsz = 0 ;
15+ photon::SharedMemoryBuffer::decode_from (str, &idx, &bufsz);
16+ EXPECT_EQ (111 , idx);
17+ EXPECT_EQ (222 , bufsz);
18+ }
19+
20+ TEST (Target, basic) {
21+ photon::vDMATarget* target = photon::new_shm_vdma_target (" foo" , 65536 , 4096 );
22+ DEFER (delete target);
23+ photon::vDMAInitiator* initiator= photon::new_shm_vdma_initiator (" foo" , 65536 );
24+ DEFER (delete initiator);
25+
26+ std::string id_0_4096, id_1_4096, id_2_4096, id_3_4096;
27+ photon::SharedMemoryBuffer::encode_to (id_0_4096, 0 , 4096 );
28+ photon::SharedMemoryBuffer::encode_to (id_1_4096, 1 , 4096 );
29+ photon::SharedMemoryBuffer::encode_to (id_2_4096, 2 , 4096 );
30+ photon::SharedMemoryBuffer::encode_to (id_3_4096, 3 , 4096 );
31+
32+ // in this version, alloc size must same as unit
33+ photon::vDMABuffer* buffer = target->alloc (512 );
34+ EXPECT_EQ (buffer, nullptr );
35+
36+ // target alloc
37+ photon::vDMABuffer* t_buffer0 = target->alloc (4096 );
38+ EXPECT_EQ (reinterpret_cast <photon::SharedMemoryBuffer*>(t_buffer0)->idx (), 0 );
39+ EXPECT_EQ (t_buffer0->buf_size (), 4096 );
40+ EXPECT_EQ (t_buffer0->id ().size (), id_0_4096.size ());
41+ EXPECT_EQ (memcmp (t_buffer0->id ().data (), id_0_4096.data (), id_0_4096.size ()), 0 );
42+
43+ photon::vDMABuffer* t_buffer1 = target->alloc (4096 );
44+ EXPECT_EQ (t_buffer1->id ().size (), id_1_4096.size ());
45+ EXPECT_EQ (memcmp (t_buffer1->id ().data (), id_1_4096.data (), id_1_4096.size ()), 0 );
46+ EXPECT_EQ (t_buffer1->buf_size (), 4096 );
47+ EXPECT_EQ (t_buffer1->is_valid (), true );
48+ EXPECT_EQ (t_buffer1->is_registered (), true );
49+ EXPECT_EQ (t_buffer1->address (), (char *)t_buffer0->address ()+4096 );
50+
51+ photon::vDMABuffer* t_buffer2 = target->alloc (4096 );
52+ EXPECT_EQ (t_buffer2->id ().size (), id_2_4096.size ());
53+ EXPECT_EQ (memcmp (t_buffer2->id ().data (), id_2_4096.data (), id_2_4096.size ()), 0 );
54+ EXPECT_EQ (t_buffer2->address (), (char *)t_buffer1->address ()+4096 );
55+
56+ // initiator map
57+ photon::vDMABuffer* i_buffer0 = initiator->map (t_buffer0->id ());
58+ EXPECT_EQ (i_buffer0->buf_size (), t_buffer0->buf_size ());
59+ EXPECT_EQ (i_buffer0->id ().size (), t_buffer0->id ().size ());
60+ EXPECT_EQ (memcmp (i_buffer0->id ().data (), t_buffer0->id ().data (), 16 ), 0 );
61+
62+ char tmp[4096 ];
63+ memset (tmp, 0x5F , 4096 );
64+ memcpy (t_buffer0->address (), tmp, 4096 );
65+ EXPECT_EQ (memcmp (i_buffer0->address (), tmp, 4096 ), 0 );
66+ memset (tmp, 0x22 , 4096 );
67+ memcpy (i_buffer0->address (), tmp, 4096 );
68+ EXPECT_EQ (memcmp (t_buffer0->address (), tmp, 4096 ), 0 );
69+
70+ initiator->unmap (i_buffer0);
71+ photon::vDMABuffer* t_buffer3 = target->alloc (4096 );
72+ EXPECT_EQ (t_buffer3->id (), id_3_4096);
73+
74+ i_buffer0 = initiator->map (t_buffer0->id ());
75+ photon::vDMABuffer* i_buffer1 = initiator->map (t_buffer1->id ());
76+
77+ initiator->unmap (i_buffer0);
78+ initiator->unmap (i_buffer1);
79+
80+ target->dealloc (t_buffer0);
81+ target->dealloc (t_buffer1);
82+ target->dealloc (t_buffer2);
83+ target->dealloc (t_buffer3);
84+ }
85+
86+ void func1 (photon::vDMATarget* target, int id) {
87+ auto buf = target->alloc (4096 );
88+ LOG_INFO (" thread " , id, " get buf " , reinterpret_cast <photon::SharedMemoryBuffer*>(buf)->idx ());
89+ photon::thread_sleep (1 );
90+ target->dealloc (buf);
91+ LOG_INFO (" thread " , id, " release buf" );
92+ }
93+
94+ TEST (Target, single_vcpu_multi_thread_alloc_1) {
95+ // 16 buffer, 16 thread(alloc - sleep 1s - free)
96+ // The result should be that each thread is allocated a different buffer
97+ auto target = photon::new_shm_vdma_target (" foo" , 65536 , 4096 );
98+ std::vector<photon::join_handle*> threads;
99+ for (int i=0 ; i<16 ; i++) {
100+ threads.push_back (photon::thread_enable_join (photon::thread_create11 (func1, target, i)));
101+ }
102+ LOG_INFO (" all create success" );
103+ for (int i=0 ; i<16 ; i++) {
104+ photon::thread_join (threads[i]);
105+ }
106+ }
107+
108+ void func2 (photon::vDMATarget* target, int id) {
109+ std::vector<int > stats (16 , 0 );
110+ for (int i=0 ; i<1000000 ; i++) {
111+ auto buf = target->alloc (4096 );
112+ if (!buf) {
113+ LOG_INFO (" some wrong " , id, " not get buffer in the middle" );
114+ }
115+ for (int j=0 ; j<10 ; j++) {
116+ photon::thread_yield ();
117+ }
118+ stats[reinterpret_cast <photon::SharedMemoryBuffer*>(buf)->idx ()] += 1 ;
119+ target->dealloc (buf);
120+ }
121+ std::string output;
122+ for (const auto & t : stats) output.append (std::to_string (t) + " " );
123+ LOG_INFO (id, " res " , output.c_str ());
124+ }
125+
126+ TEST (Target, single_vcpu_multi_thread_alloc_2) {
127+ // 16 buffer, 16 thread (repeat "alloc-free" 1000000 times)
128+ // alloc should be all success, test hold-release lock
129+ auto target = photon::new_shm_vdma_target (" foo" , 65536 , 4096 );
130+ std::vector<photon::join_handle*> threads;
131+ for (int i=0 ; i<16 ; i++) {
132+ threads.push_back (photon::thread_enable_join (photon::thread_create11 (func2, target, i)));
133+ }
134+ LOG_INFO (" all create success" );
135+ for (int i=0 ; i<16 ; i++) {
136+ photon::thread_join (threads[i]);
137+ }
138+ }
139+
140+ void func3 (photon::vDMATarget* target, int id) {
141+ auto buf = target->alloc (4096 );
142+ if (buf) {
143+ LOG_INFO (" thread " , id, " get buf " , reinterpret_cast <photon::SharedMemoryBuffer*>(buf)->idx ());
144+ photon::thread_sleep (10 );
145+ target->dealloc (buf);
146+ LOG_INFO (" thread " , id, " release buf" );
147+ }
148+ else {
149+ LOG_INFO (" thread " , id, " not get buf" );
150+ }
151+ }
152+
153+ TEST (Target, single_vcpu_multi_thread_alloc_3) {
154+ // 16 buffer, 20 thread (alloc - sleep 10s - dealloc)
155+ // only 16 thread can get buffer, other 4 thread will retry.
156+ // now, sleep time is very long
157+ // so, other 4 will get a nullptr in the end.
158+ // After a while, 16 thread release their buffer.
159+ auto target = photon::new_shm_vdma_target (" foo" , 65536 , 4096 );
160+ std::vector<photon::join_handle*> threads;
161+ for (int i=0 ; i<20 ; i++) {
162+ threads.push_back (photon::thread_enable_join (photon::thread_create11 (func3, target, i)));
163+ }
164+ LOG_INFO (" all create success" );
165+ for (int i=0 ; i<20 ; i++) {
166+ photon::thread_join (threads[i]);
167+ }
168+ }
169+
170+ void func4 (photon::vDMATarget* target, int id) {
171+ auto buf = target->alloc (4096 );
172+ if (buf) {
173+ LOG_INFO (" thread " , id, " get buf " , reinterpret_cast <photon::SharedMemoryBuffer*>(buf)->idx ());
174+ photon::thread_usleep (1 );
175+ target->dealloc (buf);
176+ LOG_INFO (" thread " , id, " release buf" );
177+ }
178+ else {
179+ LOG_INFO (" thread " , id, " not get buf" );
180+ }
181+ }
182+
183+ TEST (Target, single_vcpu_multi_thread_alloc_4) {
184+ // 16 buffer, 20 thread (alloc - sleep 1s - dealloc)
185+ // only 16 thread can get buffer at first, other thread will retry
186+ // sleep time is short, other 4 thread may get a buffer
187+ auto target = photon::new_shm_vdma_target (" foo" , 65536 , 4096 );
188+ std::vector<photon::join_handle*> threads;
189+ for (int i=0 ; i<20 ; i++) {
190+ threads.push_back (photon::thread_enable_join (photon::thread_create11 (func4, target, i)));
191+ }
192+ LOG_INFO (" all create success" );
193+ for (int i=0 ; i<20 ; i++) {
194+ photon::thread_join (threads[i]);
195+ }
196+ }
197+
198+ TEST (Target, multi_vcpu_multi_thread) {
199+ // 16 buffer, 4 vcpu, each 16 thread (total 64 thread), 10000 task
200+ auto target = photon::new_shm_vdma_target (" foo" , 65536 , 4096 );
201+ photon::WorkPool wp (4 , photon::INIT_EVENT_DEFAULT , photon::INIT_IO_NONE , 32 );
202+ photon::semaphore sem (0 );
203+ // rough statstic
204+ std::vector<int > stats (16 , 0 );
205+ int n_notget = 0 ;
206+ for (int i=0 ; i<10000 ; i++) {
207+ wp.async_call (new auto ([&]{
208+ auto buf = target->alloc (4096 );
209+ if (buf) {
210+ int buf_idx = reinterpret_cast <photon::SharedMemoryBuffer*>(buf)->idx ();
211+ LOG_INFO (" task " , i, " get buf " , buf_idx);
212+ photon::thread_usleep (1 );
213+ target->dealloc (buf);
214+ LOG_INFO (" task " , i, " release buf " , buf_idx);
215+ stats[buf_idx] += 1 ;
216+ }
217+ else {
218+ LOG_INFO (" task " , i, " not get buf" );
219+ n_notget++;
220+ }
221+ sem.signal (1 );
222+ }));
223+ }
224+
225+ sem.wait (10000 ); // wait most async task to complete
226+
227+ std::string output;
228+ for (int i=0 ; i<16 ; i++) output.append (std::to_string (stats[i]) + " " );
229+ LOG_INFO (output.c_str ());
230+ LOG_INFO (" get failed: " , n_notget);
231+ }
232+
233+ int main (int argc, char **argv){
234+ srand (time (nullptr ));
235+ ::testing::InitGoogleTest (&argc, argv);
236+ if (photon::init (photon::INIT_EVENT_DEFAULT , photon::INIT_IO_NONE ))
237+ return -1 ;
238+ DEFER (photon::fini ());
239+ return RUN_ALL_TESTS ();
240+ }
0 commit comments