]>
git.proxmox.com Git - ceph.git/blob - ceph/src/jaegertracing/thrift/lib/cpp/test/TFileTransportTest.cpp
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
20 #define _GNU_SOURCE // needed for getopt_long
23 #include <thrift/thrift-config.h>
25 #ifdef HAVE_SYS_TIME_H
29 #include <boost/test/unit_test.hpp>
31 #include <thrift/transport/TFileTransport.h>
36 #include <sys/types.h>
41 using namespace apache::thrift::transport
;
43 /**************************************************************************
45 **************************************************************************/
47 static const char* tmp_dir
= "/tmp";
52 /**************************************************************************
54 **************************************************************************/
57 * Class to record calls to fsync
65 typedef std::list
<FsyncCall
> CallList
;
72 THRIFT_GETTIMEOFDAY(&call
.time
, nullptr);
73 calls_
.push_back(call
);
76 const CallList
* getCalls() const { return &calls_
; }
83 * Helper class to clean up temporary files
87 TempFile(const char* directory
, const char* prefix
) {
90 size_t path_len
= strlen(prefix
) + 8;
91 path_
= new char[path_len
];
92 snprintf(path_
, path_len
, "%sXXXXXX", prefix
);
93 if (_mktemp_s(path_
,path_len
) == 0) {
94 fd_
= open(path_
,O_CREAT
| O_RDWR
| O_BINARY
,S_IREAD
| S_IWRITE
);
96 throw apache::thrift::TException("_mktemp_s() failed");
99 throw apache::thrift::TException("_mktemp_s() failed");
102 size_t path_len
= strlen(directory
) + strlen(prefix
) + 8;
103 path_
= new char[path_len
];
104 snprintf(path_
, path_len
, "%s/%sXXXXXX", directory
, prefix
);
106 fd_
= mkstemp(path_
);
108 throw apache::thrift::TException("mkstemp() failed");
118 const char* getPath() const { return path_
; }
120 int getFD() const { return fd_
; }
144 // Use our own version of fsync() for testing.
145 // This returns immediately, so timing in test_destructor() isn't affected by
146 // waiting on the actual filesystem.
147 extern "C" int fsync(int fd
) {
149 fsync_log
->fsync(fd
);
154 int time_diff(const struct timeval
* t1
, const struct timeval
* t2
) {
155 return (t2
->tv_usec
- t1
->tv_usec
) + (t2
->tv_sec
- t1
->tv_sec
) * 1000000;
158 /**************************************************************************
160 **************************************************************************/
163 * Make sure the TFileTransport destructor exits "quickly".
165 * Previous versions had a bug causing the writer thread not to exit
168 * It's kind of lame that we just check to see how long the destructor takes in
169 * wall-clock time. This could result in false failures on slower systems, or
170 * on heavily loaded machines.
172 BOOST_AUTO_TEST_CASE(test_destructor
) {
173 TempFile
f(tmp_dir
, "thrift.TFileTransportTest.");
175 unsigned int const NUM_ITERATIONS
= 1000;
177 unsigned int num_over
= 0;
178 for (unsigned int n
= 0; n
< NUM_ITERATIONS
; ++n
) {
179 BOOST_CHECK_EQUAL(0, ftruncate(f
.getFD(), 0));
181 TFileTransport
* transport
= new TFileTransport(f
.getPath());
183 // write something so that the writer thread gets started
184 transport
->write(reinterpret_cast<const uint8_t*>("foo"), 3);
186 // Every other iteration, also call flush(), just in case that potentially
187 // has any effect on how the writer thread wakes up.
193 * Time the call to the destructor
195 struct timeval start
;
198 THRIFT_GETTIMEOFDAY(&start
, nullptr);
200 THRIFT_GETTIMEOFDAY(&end
, nullptr);
202 int delta
= time_diff(&start
, &end
);
204 // If any attempt takes more than 500ms, treat that as a failure.
205 // Treat this as a fatal failure, so we'll return now instead of
206 // looping over a very slow operation.
207 BOOST_WARN( delta
< 500000 );
209 // Normally, it takes less than 100ms on my dev box.
210 // However, if the box is heavily loaded, some of the test runs
211 // take longer, since we're just waiting for our turn on the CPU.
212 if (delta
> 100000) {
217 // Make sure fewer than 10% of the runs took longer than 1000us
218 BOOST_WARN(num_over
< (NUM_ITERATIONS
/ 10));
222 * Make sure setFlushMaxUs() is honored.
224 void test_flush_max_us_impl(uint32_t flush_us
, uint32_t write_us
, uint32_t test_us
) {
225 // TFileTransport only calls fsync() if data has been written,
226 // so make sure the write interval is smaller than the flush interval.
227 BOOST_WARN(write_us
< flush_us
);
229 TempFile
f(tmp_dir
, "thrift.TFileTransportTest.");
231 // Record calls to fsync()
235 TFileTransport
* transport
= new TFileTransport(f
.getPath());
236 // Don't flush because of # of bytes written
237 transport
->setFlushMaxBytes(0xffffffff);
239 uint32_t buflen
= sizeof(buf
);
241 // Set the flush interval
242 transport
->setFlushMaxUs(flush_us
);
244 // Make one call to write, to start the writer thread now.
245 // (If we just let the thread get created during our test loop,
246 // the thread creation sometimes takes long enough to make the first
247 // fsync interval fail the check.)
248 transport
->write(buf
, buflen
);
250 // Add one entry to the fsync log, just to mark the start time
253 // Loop doing write(), sleep(), ...
254 uint32_t total_time
= 0;
256 transport
->write(buf
, buflen
);
257 if (total_time
> test_us
) {
261 total_time
+= write_us
;
266 // Stop logging new fsync() calls
269 // Examine the fsync() log
271 // TFileTransport uses pthread_cond_timedwait(), which only has millisecond
272 // resolution. In my testing, it normally wakes up about 1 millisecond late.
273 // However, sometimes it takes a bit longer. Allow 5ms leeway.
274 int max_allowed_delta
= flush_us
+ 5000;
276 const FsyncLog::CallList
* calls
= log
.getCalls();
277 // We added 1 fsync call above.
278 // Make sure TFileTransport called fsync at least once
279 BOOST_WARN_GE(calls
->size(), static_cast<FsyncLog::CallList::size_type
>(1));
281 const struct timeval
* prev_time
= nullptr;
282 for (const auto & call
: *calls
) {
284 int delta
= time_diff(prev_time
, &call
.time
);
285 BOOST_WARN( delta
< max_allowed_delta
);
287 prev_time
= &call
.time
;
291 BOOST_AUTO_TEST_CASE(test_flush_max_us1
) {
292 // fsync every 10ms, write every 5ms, for 500ms
293 test_flush_max_us_impl(10000, 5000, 500000);
296 BOOST_AUTO_TEST_CASE(test_flush_max_us2
) {
297 // fsync every 50ms, write every 20ms, for 500ms
298 test_flush_max_us_impl(50000, 20000, 500000);
301 BOOST_AUTO_TEST_CASE(test_flush_max_us3
) {
302 // fsync every 400ms, write every 300ms, for 1s
303 test_flush_max_us_impl(400000, 300000, 1000000);
307 * Make sure flush() is fast when there is nothing to do.
309 * TFileTransport used to have a bug where flush() would wait for the fsync
312 BOOST_AUTO_TEST_CASE(test_noop_flush
) {
313 TempFile
f(tmp_dir
, "thrift.TFileTransportTest.");
314 TFileTransport
transport(f
.getPath());
316 // Write something to start the writer thread.
318 transport
.write(buf
, 1);
320 struct timeval start
;
321 THRIFT_GETTIMEOFDAY(&start
, nullptr);
323 for (unsigned int n
= 0; n
< 10; ++n
) {
327 THRIFT_GETTIMEOFDAY(&now
, nullptr);
329 // Fail if at any point we've been running for longer than half a second.
330 // (With the buggy code, TFileTransport used to take 3 seconds per flush())
332 // Use a fatal fail so we break out early, rather than continuing to make
333 // many more slow flush() calls.
334 int delta
= time_diff(&start
, &now
);
335 BOOST_WARN( delta
< 2000000 );
339 /**************************************************************************
340 * General Initialization
341 **************************************************************************/
343 void print_usage(FILE* f
, const char* argv0
) {
344 fprintf(f
, "Usage: %s [boost_options] [options]\n", argv0
);
345 fprintf(f
, "Options:\n");
346 fprintf(f
, " --tmp-dir=DIR, -t DIR\n");
347 fprintf(f
, " --help\n");
350 void parse_args(int argc
, char* argv
[]) {
351 struct option long_opts
[]
352 = {{"help", false, nullptr, 'h'}, {"tmp-dir", true, nullptr, 't'}, {nullptr, 0, nullptr, 0}};
356 int optchar
= getopt_long(argc
, argv
, "ht:", long_opts
, nullptr);
366 print_usage(stdout
, argv
[0]);
371 // Only happens if someone adds another option to the optarg string,
372 // but doesn't update the switch statement to handle it.
373 fprintf(stderr
, "unknown option \"-%c\"\n", optchar
);
379 #ifdef BOOST_TEST_DYN_LINK
380 static int myArgc
= 0;
381 static char **myArgv
= nullptr;
383 bool init_unit_test_suite() {
384 boost::unit_test::framework::master_test_suite().p_name
.value
= "TFileTransportTest";
387 parse_args(myArgc
,myArgv
);
391 int main( int argc
, char* argv
[] ) {
394 return ::boost::unit_test::unit_test_main(&init_unit_test_suite
,argc
,argv
);
397 boost::unit_test::test_suite
* init_unit_test_suite(int argc
, char* argv
[]) {
398 boost::unit_test::framework::master_test_suite().p_name
.value
= "TFileTransportTest";
401 parse_args(argc
, argv
);