1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 * Copyright (C) 2016 Red Hat Inc.
15 #include "dmclock_server.h"
16 #include "dmclock_util.h"
17 #include "gtest/gtest.h"
20 namespace dmc
= crimson::dmclock
;
23 // we need a request object; an empty one will do
32 * Allows us to test the code provided with the mutex provided locked.
34 static void test_locked(std::mutex
& mtx
, std::function
<void()> code
) {
35 std::unique_lock
<std::mutex
> l(mtx
);
40 TEST(dmclock_server
, bad_tag_deathtest
) {
42 using Queue
= dmc::PullPriorityQueue
<ClientId
,Request
>;
43 using QueueRef
= std::unique_ptr
<Queue
>;
45 ClientId client1
= 17;
46 ClientId client2
= 18;
48 double reservation
= 0.0;
51 dmc::ClientInfo
ci1(reservation
, weight
, 0.0);
52 dmc::ClientInfo
ci2(reservation
, weight
, 1.0);
54 auto client_info_f
= [&] (ClientId c
) -> dmc::ClientInfo
{
55 if (client1
== c
) return ci1
;
56 else if (client2
== c
) return ci2
;
58 ADD_FAILURE() << "got request from neither of two clients";
59 return ci1
; // must return
63 QueueRef
pq(new Queue(client_info_f
, false));
65 ReqParams
req_params(1,1);
67 EXPECT_DEATH_IF_SUPPORTED(pq
->add_request(req
, client1
, req_params
),
68 "Assertion.*reservation.*max_tag.*"
69 "proportion.*max_tag") <<
70 "we should fail if a client tries to generate a reservation tag "
71 "where reservation and proportion are both 0";
74 EXPECT_DEATH_IF_SUPPORTED(pq
->add_request(req
, client2
, req_params
),
75 "Assertion.*reservation.*max_tag.*"
76 "proportion.*max_tag") <<
77 "we should fail if a client tries to generate a reservation tag "
78 "where reservation and proportion are both 0";
82 TEST(dmclock_server
, client_idle_erase
) {
84 using Queue
= dmc::PushPriorityQueue
<ClientId
,Request
>;
86 double reservation
= 100.0;
88 dmc::ClientInfo
ci(reservation
, 1.0, 0.0);
89 auto client_info_f
= [&] (ClientId c
) -> dmc::ClientInfo
{ return ci
; };
90 auto server_ready_f
= [] () -> bool { return true; };
91 auto submit_req_f
= [] (const ClientId
& c
,
92 std::unique_ptr
<Request
> req
,
93 dmc::PhaseType phase
) {
97 Queue
pq(client_info_f
,
100 std::chrono::seconds(3),
101 std::chrono::seconds(5),
102 std::chrono::seconds(2),
105 auto lock_pq
= [&](std::function
<void()> code
) {
106 test_locked(pq
.data_mtx
, code
);
110 /* The timeline should be as follows:
112 * 0 seconds : request created
114 * 1 seconds : map is size 1, idle is false
116 * 2 seconds : clean notes first mark; +2 is base for further calcs
118 * 4 seconds : clean does nothing except makes another mark
120 * 5 seconds : when we're secheduled to idle (+2 + 3)
122 * 6 seconds : clean idles client
124 * 7 seconds : when we're secheduled to erase (+2 + 5)
126 * 7 seconds : verified client is idle
128 * 8 seconds : clean erases client info
130 * 9 seconds : verified client is erased
134 EXPECT_EQ(0u, pq
.client_map
.size()) <<
135 "client map initially has size 0";
139 dmc::ReqParams
req_params(1, 1);
140 pq
.add_request_time(req
, client
, req_params
, dmc::get_time());
142 std::this_thread::sleep_for(std::chrono::seconds(1));
145 EXPECT_EQ(1u, pq
.client_map
.size()) <<
146 "client map has 1 after 1 client";
147 EXPECT_FALSE(pq
.client_map
.at(client
)->idle
) <<
148 "initially client map entry shows not idle.";
151 std::this_thread::sleep_for(std::chrono::seconds(6));
154 EXPECT_TRUE(pq
.client_map
.at(client
)->idle
) <<
155 "after idle age client map entry shows idle.";
158 std::this_thread::sleep_for(std::chrono::seconds(2));
161 EXPECT_EQ(0u, pq
.client_map
.size()) <<
162 "client map loses its entry after erase age";
168 TEST(dmclock_server
, reservation_timing
) {
169 using ClientId
= int;
171 using Queue
= std::unique_ptr
<dmc::PriorityQueue
<ClientId
,Request
>>;
172 using std::chrono::steady_clock
;
176 std::vector
<dmc::Time
> times
;
177 std::mutex times_mtx
;
178 using Guard
= std::lock_guard
<decltype(times_mtx
)>;
180 // reservation every second
181 dmc::ClientInfo
ci(1.0, 0.0, 0.0);
184 auto client_info_f
= [&] (ClientId c
) -> dmc::ClientInfo
{ return ci
; };
185 auto server_ready_f
= [] () -> bool { return true; };
186 auto submit_req_f
= [&] (const ClientId
& c
,
187 std::unique_ptr
<Request
> req
,
188 dmc::PhaseType phase
) {
191 times
.emplace_back(dmc::get_time());
193 std::thread
complete([&](){ pq
->request_completed(); });
198 pq
= Queue(new dmc::PriorityQueue
<ClientId
,Request
>(client_info_f
,
204 ReqParams
<ClientId
> req_params(client
, 1, 1);
206 for (int i
= 0; i
< 5; ++i
) {
207 pq
->add_request_time(req
, req_params
, dmc::get_time());
212 std::this_thread::sleep_for(std::chrono::milliseconds(5500));
213 EXPECT_EQ(5, times
.size()) <<
214 "after 5.5 seconds, we should have 5 requests times at 1 second apart";
220 TEST(dmclock_server
, remove_by_req_filter
) {
231 using ClientId
= int;
232 using Queue
= dmc::PullPriorityQueue
<ClientId
,MyReq
>;
234 ClientId client1
= 17;
235 ClientId client2
= 98;
237 dmc::ClientInfo
info1(0.0, 1.0, 0.0);
239 auto client_info_f
= [&] (ClientId c
) -> dmc::ClientInfo
{
243 Queue
pq(client_info_f
, true);
245 EXPECT_EQ(0u, pq
.client_count());
246 EXPECT_EQ(0u, pq
.request_count());
248 ReqParams
req_params(1,1);
250 pq
.add_request(MyReq(1), client1
, req_params
);
251 pq
.add_request(MyReq(11), client1
, req_params
);
252 pq
.add_request(MyReq(2), client2
, req_params
);
253 pq
.add_request(MyReq(0), client2
, req_params
);
254 pq
.add_request(MyReq(13), client2
, req_params
);
255 pq
.add_request(MyReq(2), client2
, req_params
);
256 pq
.add_request(MyReq(13), client2
, req_params
);
257 pq
.add_request(MyReq(98), client2
, req_params
);
258 pq
.add_request(MyReq(44), client1
, req_params
);
260 EXPECT_EQ(2u, pq
.client_count());
261 EXPECT_EQ(9u, pq
.request_count());
263 pq
.remove_by_req_filter([](const MyReq
& r
) -> bool {return 1 == r
.id
% 2;});
265 EXPECT_EQ(5u, pq
.request_count());
267 std::list
<MyReq
> capture
;
268 pq
.remove_by_req_filter(
269 [&capture
] (const MyReq
& r
) -> bool {
271 capture
.push_front(r
);
279 EXPECT_EQ(0u, pq
.request_count());
280 EXPECT_EQ(5u, capture
.size());
282 for (auto i
: capture
) {
285 EXPECT_EQ(146, total
) << " sum of captured items should be 146";
289 TEST(dmclock_server
, remove_by_req_filter_ordering_forwards_visit
) {
300 using ClientId
= int;
301 using Queue
= dmc::PullPriorityQueue
<ClientId
,MyReq
>;
303 ClientId client1
= 17;
305 dmc::ClientInfo
info1(0.0, 1.0, 0.0);
307 auto client_info_f
= [&] (ClientId c
) -> dmc::ClientInfo
{
311 Queue
pq(client_info_f
, true);
313 EXPECT_EQ(0u, pq
.client_count());
314 EXPECT_EQ(0u, pq
.request_count());
316 ReqParams
req_params(1,1);
318 pq
.add_request(MyReq(1), client1
, req_params
);
319 pq
.add_request(MyReq(2), client1
, req_params
);
320 pq
.add_request(MyReq(3), client1
, req_params
);
321 pq
.add_request(MyReq(4), client1
, req_params
);
322 pq
.add_request(MyReq(5), client1
, req_params
);
323 pq
.add_request(MyReq(6), client1
, req_params
);
325 EXPECT_EQ(1u, pq
.client_count());
326 EXPECT_EQ(6u, pq
.request_count());
328 // remove odd ids in forward order and append to end
330 std::vector
<MyReq
> capture
;
331 pq
.remove_by_req_filter(
332 [&capture
] (const MyReq
& r
) -> bool {
334 capture
.push_back(r
);
342 EXPECT_EQ(3u, pq
.request_count());
343 EXPECT_EQ(3u, capture
.size());
344 EXPECT_EQ(1, capture
[0].id
) << "items should come out in forward order";
345 EXPECT_EQ(3, capture
[1].id
) << "items should come out in forward order";
346 EXPECT_EQ(5, capture
[2].id
) << "items should come out in forward order";
348 // remove even ids in reverse order but insert at front so comes
351 std::vector
<MyReq
> capture2
;
352 pq
.remove_by_req_filter(
353 [&capture2
] (const MyReq
& r
) -> bool {
355 capture2
.insert(capture2
.begin(), r
);
363 EXPECT_EQ(0u, pq
.request_count());
364 EXPECT_EQ(3u, capture2
.size());
365 EXPECT_EQ(6, capture2
[0].id
) << "items should come out in reverse order";
366 EXPECT_EQ(4, capture2
[1].id
) << "items should come out in reverse order";
367 EXPECT_EQ(2, capture2
[2].id
) << "items should come out in reverse order";
371 TEST(dmclock_server
, remove_by_req_filter_ordering_backwards_visit
) {
382 using ClientId
= int;
383 using Queue
= dmc::PullPriorityQueue
<ClientId
,MyReq
>;
385 ClientId client1
= 17;
387 dmc::ClientInfo
info1(0.0, 1.0, 0.0);
389 auto client_info_f
= [&] (ClientId c
) -> dmc::ClientInfo
{
393 Queue
pq(client_info_f
, true);
395 EXPECT_EQ(0u, pq
.client_count());
396 EXPECT_EQ(0u, pq
.request_count());
398 ReqParams
req_params(1,1);
400 pq
.add_request(MyReq(1), client1
, req_params
);
401 pq
.add_request(MyReq(2), client1
, req_params
);
402 pq
.add_request(MyReq(3), client1
, req_params
);
403 pq
.add_request(MyReq(4), client1
, req_params
);
404 pq
.add_request(MyReq(5), client1
, req_params
);
405 pq
.add_request(MyReq(6), client1
, req_params
);
407 EXPECT_EQ(1u, pq
.client_count());
408 EXPECT_EQ(6u, pq
.request_count());
410 // now remove odd ids in forward order
412 std::vector
<MyReq
> capture
;
413 pq
.remove_by_req_filter(
414 [&capture
] (const MyReq
& r
) -> bool {
416 capture
.insert(capture
.begin(), r
);
424 EXPECT_EQ(3u, pq
.request_count());
425 EXPECT_EQ(3u, capture
.size());
426 EXPECT_EQ(1, capture
[0].id
) << "items should come out in forward order";
427 EXPECT_EQ(3, capture
[1].id
) << "items should come out in forward order";
428 EXPECT_EQ(5, capture
[2].id
) << "items should come out in forward order";
430 // now remove even ids in reverse order
432 std::vector
<MyReq
> capture2
;
433 pq
.remove_by_req_filter(
434 [&capture2
] (const MyReq
& r
) -> bool {
436 capture2
.push_back(r
);
444 EXPECT_EQ(0u, pq
.request_count());
445 EXPECT_EQ(3u, capture2
.size());
446 EXPECT_EQ(6, capture2
[0].id
) << "items should come out in reverse order";
447 EXPECT_EQ(4, capture2
[1].id
) << "items should come out in reverse order";
448 EXPECT_EQ(2, capture2
[2].id
) << "items should come out in reverse order";
452 TEST(dmclock_server
, remove_by_client
) {
463 using ClientId
= int;
464 using Queue
= dmc::PullPriorityQueue
<ClientId
,MyReq
>;
466 ClientId client1
= 17;
467 ClientId client2
= 98;
469 dmc::ClientInfo
info1(0.0, 1.0, 0.0);
471 auto client_info_f
= [&] (ClientId c
) -> dmc::ClientInfo
{
475 Queue
pq(client_info_f
, true);
477 EXPECT_EQ(0u, pq
.client_count());
478 EXPECT_EQ(0u, pq
.request_count());
480 ReqParams
req_params(1,1);
482 pq
.add_request(MyReq(1), client1
, req_params
);
483 pq
.add_request(MyReq(11), client1
, req_params
);
484 pq
.add_request(MyReq(2), client2
, req_params
);
485 pq
.add_request(MyReq(0), client2
, req_params
);
486 pq
.add_request(MyReq(13), client2
, req_params
);
487 pq
.add_request(MyReq(2), client2
, req_params
);
488 pq
.add_request(MyReq(13), client2
, req_params
);
489 pq
.add_request(MyReq(98), client2
, req_params
);
490 pq
.add_request(MyReq(44), client1
, req_params
);
492 EXPECT_EQ(2u, pq
.client_count());
493 EXPECT_EQ(9u, pq
.request_count());
495 std::list
<MyReq
> removed
;
497 pq
.remove_by_client(client1
,
499 [&removed
] (const MyReq
& r
) {
500 removed
.push_front(r
);
503 EXPECT_EQ(3u, removed
.size());
504 EXPECT_EQ(1, removed
.front().id
);
506 EXPECT_EQ(11, removed
.front().id
);
508 EXPECT_EQ(44, removed
.front().id
);
511 EXPECT_EQ(6u, pq
.request_count());
513 Queue::PullReq pr
= pq
.pull_request();
514 EXPECT_TRUE(pr
.is_retn());
515 EXPECT_EQ(2, pr
.get_retn().request
->id
);
517 pr
= pq
.pull_request();
518 EXPECT_TRUE(pr
.is_retn());
519 EXPECT_EQ(0, pr
.get_retn().request
->id
);
521 pq
.remove_by_client(client2
);
522 EXPECT_EQ(0u, pq
.request_count()) <<
523 "after second client removed, none left";
527 TEST(dmclock_server_pull
, pull_weight
) {
528 using ClientId
= int;
529 using Queue
= dmc::PullPriorityQueue
<ClientId
,Request
>;
530 using QueueRef
= std::unique_ptr
<Queue
>;
532 ClientId client1
= 17;
533 ClientId client2
= 98;
535 dmc::ClientInfo
info1(0.0, 1.0, 0.0);
536 dmc::ClientInfo
info2(0.0, 2.0, 0.0);
540 auto client_info_f
= [&] (ClientId c
) -> dmc::ClientInfo
{
541 if (client1
== c
) return info1
;
542 else if (client2
== c
) return info2
;
544 ADD_FAILURE() << "client info looked up for non-existant client";
549 pq
= QueueRef(new Queue(client_info_f
, false));
552 ReqParams
req_params(1,1);
554 auto now
= dmc::get_time();
556 for (int i
= 0; i
< 5; ++i
) {
557 pq
->add_request(req
, client1
, req_params
);
558 pq
->add_request(req
, client2
, req_params
);
564 for (int i
= 0; i
< 6; ++i
) {
565 Queue::PullReq pr
= pq
->pull_request();
566 EXPECT_EQ(Queue::NextReqType::returning
, pr
.type
);
567 auto& retn
= boost::get
<Queue::PullReq::Retn
>(pr
.data
);
569 if (client1
== retn
.client
) ++c1_count
;
570 else if (client2
== retn
.client
) ++c2_count
;
571 else ADD_FAILURE() << "got request from neither of two clients";
573 EXPECT_EQ(PhaseType::priority
, retn
.phase
);
576 EXPECT_EQ(2, c1_count
) <<
577 "one-third of request should have come from first client";
578 EXPECT_EQ(4, c2_count
) <<
579 "two-thirds of request should have come from second client";
583 TEST(dmclock_server_pull
, pull_reservation
) {
584 using ClientId
= int;
585 using Queue
= dmc::PullPriorityQueue
<ClientId
,Request
>;
586 using QueueRef
= std::unique_ptr
<Queue
>;
588 ClientId client1
= 52;
589 ClientId client2
= 8;
591 dmc::ClientInfo
info1(2.0, 0.0, 0.0);
592 dmc::ClientInfo
info2(1.0, 0.0, 0.0);
594 auto client_info_f
= [&] (ClientId c
) -> dmc::ClientInfo
{
595 if (client1
== c
) return info1
;
596 else if (client2
== c
) return info2
;
598 ADD_FAILURE() << "client info looked up for non-existant client";
603 QueueRef
pq(new Queue(client_info_f
, false));
606 ReqParams
req_params(1,1);
608 // make sure all times are well before now
609 auto old_time
= dmc::get_time() - 100.0;
611 for (int i
= 0; i
< 5; ++i
) {
612 pq
->add_request_time(req
, client1
, req_params
, old_time
);
613 pq
->add_request_time(req
, client2
, req_params
, old_time
);
620 for (int i
= 0; i
< 6; ++i
) {
621 Queue::PullReq pr
= pq
->pull_request();
622 EXPECT_EQ(Queue::NextReqType::returning
, pr
.type
);
623 auto& retn
= boost::get
<Queue::PullReq::Retn
>(pr
.data
);
625 if (client1
== retn
.client
) ++c1_count
;
626 else if (client2
== retn
.client
) ++c2_count
;
627 else ADD_FAILURE() << "got request from neither of two clients";
629 EXPECT_EQ(PhaseType::reservation
, retn
.phase
);
632 EXPECT_EQ(4, c1_count
) <<
633 "two-thirds of request should have come from first client";
634 EXPECT_EQ(2, c2_count
) <<
635 "one-third of request should have come from second client";
636 } // dmclock_server_pull.pull_reservation
639 // This test shows what happens when a request can be ready (under
640 // limit) but not schedulable since proportion tag is 0. We expect
641 // to get some future and none responses.
642 TEST(dmclock_server_pull
, ready_and_under_limit
) {
643 using ClientId
= int;
644 using Queue
= dmc::PullPriorityQueue
<ClientId
,Request
>;
645 using QueueRef
= std::unique_ptr
<Queue
>;
647 ClientId client1
= 52;
648 ClientId client2
= 8;
650 dmc::ClientInfo
info1(1.0, 0.0, 0.0);
651 dmc::ClientInfo
info2(1.0, 0.0, 0.0);
653 auto client_info_f
= [&] (ClientId c
) -> dmc::ClientInfo
{
654 if (client1
== c
) return info1
;
655 else if (client2
== c
) return info2
;
657 ADD_FAILURE() << "client info looked up for non-existant client";
662 QueueRef
pq(new Queue(client_info_f
, false));
665 ReqParams
req_params(1,1);
667 // make sure all times are well before now
668 auto start_time
= dmc::get_time() - 100.0;
670 // add six requests; for same client reservations spaced one apart
671 for (int i
= 0; i
< 3; ++i
) {
672 pq
->add_request_time(req
, client1
, req_params
, start_time
);
673 pq
->add_request_time(req
, client2
, req_params
, start_time
);
676 Queue::PullReq pr
= pq
->pull_request(start_time
+ 0.5);
677 EXPECT_EQ(Queue::NextReqType::returning
, pr
.type
);
679 pr
= pq
->pull_request(start_time
+ 0.5);
680 EXPECT_EQ(Queue::NextReqType::returning
, pr
.type
);
682 pr
= pq
->pull_request(start_time
+ 0.5);
683 EXPECT_EQ(Queue::NextReqType::future
, pr
.type
) <<
684 "too soon for next reservation";
686 pr
= pq
->pull_request(start_time
+ 1.5);
687 EXPECT_EQ(Queue::NextReqType::returning
, pr
.type
);
689 pr
= pq
->pull_request(start_time
+ 1.5);
690 EXPECT_EQ(Queue::NextReqType::returning
, pr
.type
);
692 pr
= pq
->pull_request(start_time
+ 1.5);
693 EXPECT_EQ(Queue::NextReqType::future
, pr
.type
) <<
694 "too soon for next reservation";
696 pr
= pq
->pull_request(start_time
+ 2.5);
697 EXPECT_EQ(Queue::NextReqType::returning
, pr
.type
);
699 pr
= pq
->pull_request(start_time
+ 2.5);
700 EXPECT_EQ(Queue::NextReqType::returning
, pr
.type
);
702 pr
= pq
->pull_request(start_time
+ 2.5);
703 EXPECT_EQ(Queue::NextReqType::none
, pr
.type
) << "no more requests left";
707 TEST(dmclock_server_pull
, pull_none
) {
708 using ClientId
= int;
709 using Queue
= dmc::PullPriorityQueue
<ClientId
,Request
>;
710 using QueueRef
= std::unique_ptr
<Queue
>;
712 dmc::ClientInfo
info(1.0, 1.0, 1.0);
714 auto client_info_f
= [&] (ClientId c
) -> dmc::ClientInfo
{
718 QueueRef
pq(new Queue(client_info_f
, false));
721 ReqParams
req_params(1,1);
723 auto now
= dmc::get_time();
725 Queue::PullReq pr
= pq
->pull_request(now
+ 100);
727 EXPECT_EQ(Queue::NextReqType::none
, pr
.type
);
731 TEST(dmclock_server_pull
, pull_future
) {
732 using ClientId
= int;
733 using Queue
= dmc::PullPriorityQueue
<ClientId
,Request
>;
734 using QueueRef
= std::unique_ptr
<Queue
>;
736 ClientId client1
= 52;
737 // ClientId client2 = 8;
739 dmc::ClientInfo
info(1.0, 0.0, 1.0);
741 auto client_info_f
= [&] (ClientId c
) -> dmc::ClientInfo
{
745 QueueRef
pq(new Queue(client_info_f
, false));
748 ReqParams
req_params(1,1);
750 // make sure all times are well before now
751 auto now
= dmc::get_time();
753 pq
->add_request_time(req
, client1
, req_params
, now
+ 100);
754 Queue::PullReq pr
= pq
->pull_request(now
);
756 EXPECT_EQ(Queue::NextReqType::future
, pr
.type
);
758 Time when
= boost::get
<Time
>(pr
.data
);
759 EXPECT_EQ(now
+ 100, when
);
763 TEST(dmclock_server_pull
, pull_future_limit_break_weight
) {
764 using ClientId
= int;
765 using Queue
= dmc::PullPriorityQueue
<ClientId
,Request
>;
766 using QueueRef
= std::unique_ptr
<Queue
>;
768 ClientId client1
= 52;
769 // ClientId client2 = 8;
771 dmc::ClientInfo
info(0.0, 1.0, 1.0);
773 auto client_info_f
= [&] (ClientId c
) -> dmc::ClientInfo
{
777 QueueRef
pq(new Queue(client_info_f
, true));
780 ReqParams
req_params(1,1);
782 // make sure all times are well before now
783 auto now
= dmc::get_time();
785 pq
->add_request_time(req
, client1
, req_params
, now
+ 100);
786 Queue::PullReq pr
= pq
->pull_request(now
);
788 EXPECT_EQ(Queue::NextReqType::returning
, pr
.type
);
790 auto& retn
= boost::get
<Queue::PullReq::Retn
>(pr
.data
);
791 EXPECT_EQ(client1
, retn
.client
);
795 TEST(dmclock_server_pull
, pull_future_limit_break_reservation
) {
796 using ClientId
= int;
797 using Queue
= dmc::PullPriorityQueue
<ClientId
,Request
>;
798 using QueueRef
= std::unique_ptr
<Queue
>;
800 ClientId client1
= 52;
801 // ClientId client2 = 8;
803 dmc::ClientInfo
info(1.0, 0.0, 1.0);
805 auto client_info_f
= [&] (ClientId c
) -> dmc::ClientInfo
{
809 QueueRef
pq(new Queue(client_info_f
, true));
812 ReqParams
req_params(1,1);
814 // make sure all times are well before now
815 auto now
= dmc::get_time();
817 pq
->add_request_time(req
, client1
, req_params
, now
+ 100);
818 Queue::PullReq pr
= pq
->pull_request(now
);
820 EXPECT_EQ(Queue::NextReqType::returning
, pr
.type
);
822 auto& retn
= boost::get
<Queue::PullReq::Retn
>(pr
.data
);
823 EXPECT_EQ(client1
, retn
.client
);
825 } // namespace dmclock
826 } // namespace crimson