// Repeat the straddling records and multiple writers tests with a small log. void test_small_log(void) { printf("Starting test: small log\n"); const int circular = 0; census_log_initialize(0, circular); size_t log_size = census_log_remaining_space(); GPR_ASSERT(log_size > 0); fill_log(log_size, 0, circular); census_log_shutdown(); census_log_initialize(0, circular); multiple_writers_single_reader(circular); census_log_shutdown(); }
void test_performance(void) { for (size_t write_size = 1; write_size < CENSUS_LOG_MAX_RECORD_SIZE; write_size *= 2) { setup_test(0); gpr_timespec start_time = gpr_now(GPR_CLOCK_REALTIME); int nrecords = 0; while (1) { void* record = census_log_start_write(write_size); if (record == NULL) { break; } census_log_end_write(record, write_size); nrecords++; } gpr_timespec write_time = gpr_time_sub(gpr_now(GPR_CLOCK_REALTIME), start_time); double write_time_micro = (double)write_time.tv_sec * 1000000 + (double)write_time.tv_nsec / 1000; census_log_shutdown(); printf( "Wrote %d %d byte records in %.3g microseconds: %g records/us " "(%g ns/record), %g gigabytes/s\n", nrecords, (int)write_size, write_time_micro, nrecords / write_time_micro, 1000 * write_time_micro / nrecords, (double)((int)write_size * nrecords) / write_time_micro / 1000); } }
// Fills circular log with records that may straddle end of a block. void test_fill_circular_log_with_straddling_records(void) { printf("Starting test: fill circular log with straddling records\n"); const int circular = 1; setup_test(circular); fill_log(LOG_SIZE_IN_BYTES, 0 /* block straddling records */, circular); census_log_shutdown(); }
// Tests scenario where multiple writers and a single reader are using a log // that is configured to discard old records. void test_multiple_writers(void) { printf("Starting test: multiple writers\n"); const int circular = 0; setup_test(circular); multiple_writers_single_reader(circular); census_log_shutdown(); }
// Tests scenario where block being read is detached from a core and put on the // dirty list. void test_detached_while_reading(void) { printf("Starting test: detached while reading\n"); setup_test(0); // Start a write. static const size_t DWR_RECORD_SIZE = 10; void* record_written = census_log_start_write(DWR_RECORD_SIZE); GPR_ASSERT(record_written != NULL); census_log_end_write(record_written, DWR_RECORD_SIZE); // Read this record. census_log_init_reader(); size_t bytes_available; const void* record_read = census_log_read_next(&bytes_available); GPR_ASSERT(record_read != NULL); GPR_ASSERT(DWR_RECORD_SIZE == bytes_available); // Now fill the log. This will move the block being read from core-local // array to the dirty list. while ((record_written = census_log_start_write(DWR_RECORD_SIZE))) { census_log_end_write(record_written, DWR_RECORD_SIZE); } // In this iteration, read_next() should only traverse blocks in the // core-local array. Therefore, we expect at most gpr_cpu_num_cores() more // blocks. As log is full, if read_next() is traversing the dirty list, we // will get more than gpr_cpu_num_cores() blocks. int block_read = 0; while ((record_read = census_log_read_next(&bytes_available))) { ++block_read; GPR_ASSERT(block_read <= (int)gpr_cpu_num_cores()); } census_log_shutdown(); }
// Fills circular log with records sized such that size is a multiple of // CENSUS_LOG_MAX_RECORD_SIZE (no per-block fragmentation). void test_fill_circular_log_no_fragmentation(void) { printf("Starting test: fill circular log no fragmentation\n"); const int circular = 1; setup_test(circular); fill_log(LOG_SIZE_IN_BYTES, 1 /* no fragmentation */, circular); census_log_shutdown(); }
// Tries reading beyond pending write. void test_read_beyond_pending_record(void) { printf("Starting test: read beyond pending record\n"); setup_test(0); // Start a write. const size_t incomplete_record_size = 10; void* incomplete_record = census_log_start_write(incomplete_record_size); GPR_ASSERT(incomplete_record != NULL); const size_t complete_record_size = 20; void* complete_record = census_log_start_write(complete_record_size); GPR_ASSERT(complete_record != NULL); GPR_ASSERT(complete_record != incomplete_record); census_log_end_write(complete_record, complete_record_size); // Now iterate over blocks to read completed records. census_log_init_reader(); size_t bytes_available; const void* record_read = census_log_read_next(&bytes_available); GPR_ASSERT(complete_record == record_read); GPR_ASSERT(complete_record_size == bytes_available); // Complete first record. census_log_end_write(incomplete_record, incomplete_record_size); // Have read past the incomplete record, so read_next() should return NULL. // NB: this test also assumes our thread did not get switched to a different // CPU between the two start_write calls record_read = census_log_read_next(&bytes_available); GPR_ASSERT(record_read == NULL); // Reset reader to get the newly completed record. census_log_init_reader(); record_read = census_log_read_next(&bytes_available); GPR_ASSERT(incomplete_record == record_read); GPR_ASSERT(incomplete_record_size == bytes_available); assert_log_empty(); census_log_shutdown(); }
/* Tests scenario where multiple writers and a single reader are using a log that is configured to discard old records. */ void test_multiple_writers_circular_log(void) { const int circular = 1; printf("Starting test: multiple writers circular log\n"); setup_test(circular); multiple_writers_single_reader(circular); census_log_shutdown(); }
// Tests end_write() with a different size than what was specified in // start_write(). void test_end_write_with_different_size(void) { static const size_t START_WRITE_SIZE = 10; static const size_t END_WRITE_SIZE = 7; printf("Starting test: end write with different size\n"); setup_test(0); void* record_written = census_log_start_write(START_WRITE_SIZE); GPR_ASSERT(record_written != NULL); census_log_end_write(record_written, END_WRITE_SIZE); census_log_init_reader(); size_t bytes_available; const void* record_read = census_log_read_next(&bytes_available); GPR_ASSERT(record_written == record_read); GPR_ASSERT(END_WRITE_SIZE == bytes_available); assert_log_empty(); census_log_shutdown(); }
// Attempts to create a record of invalid size (size > // CENSUS_LOG_MAX_RECORD_SIZE). void test_invalid_record_size(void) { static const size_t INVALID_SIZE = CENSUS_LOG_MAX_RECORD_SIZE + 1; static const size_t VALID_SIZE = 1; printf("Starting test: invalid record size\n"); setup_test(0); void* record = census_log_start_write(INVALID_SIZE); GPR_ASSERT(record == NULL); // Now try writing a valid record. record = census_log_start_write(VALID_SIZE); GPR_ASSERT(record != NULL); census_log_end_write(record, VALID_SIZE); // Verifies that available space went down by one block. In theory, this // check can fail if the thread is context switched to a new CPU during the // start_write execution (multiple blocks get allocated), but this has not // been observed in practice. // GPR_ASSERT(LOG_SIZE_IN_BYTES - CENSUS_LOG_MAX_RECORD_SIZE == // census_log_remaining_space()); census_log_shutdown(); }
// Verifies that pending records are not available via read_next(). void test_read_pending_record(void) { static const size_t PR_RECORD_SIZE = 1024; printf("Starting test: read pending record\n"); setup_test(0); // Start a write. void* record_written = census_log_start_write(PR_RECORD_SIZE); GPR_ASSERT(record_written != NULL); // As write is pending, read should fail. census_log_init_reader(); size_t bytes_available; const void* record_read = census_log_read_next(&bytes_available); GPR_ASSERT(record_read == NULL); // A read followed by end_write() should succeed. census_log_end_write(record_written, PR_RECORD_SIZE); census_log_init_reader(); record_read = census_log_read_next(&bytes_available); GPR_ASSERT(record_written == record_read); GPR_ASSERT(PR_RECORD_SIZE == bytes_available); assert_log_empty(); census_log_shutdown(); }