| 
					
				 | 
			
			
				@@ -0,0 +1,589 @@ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/* 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * Copyright 2015-2016, Google Inc. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * All rights reserved. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * Redistribution and use in source and binary forms, with or without 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * modification, are permitted provided that the following conditions are 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * met: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *     * Redistributions of source code must retain the above copyright 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * notice, this list of conditions and the following disclaimer. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *     * Redistributions in binary form must reproduce the above 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * copyright notice, this list of conditions and the following disclaimer 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * in the documentation and/or other materials provided with the 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * distribution. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *     * Neither the name of Google Inc. nor the names of its 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * contributors may be used to endorse or promote products derived from 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * this software without specific prior written permission. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#include "src/core/census/log.h" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#include <grpc/support/cpu.h> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#include <grpc/support/log.h> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#include <grpc/support/port_platform.h> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#include <grpc/support/sync.h> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#include <grpc/support/thd.h> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#include <grpc/support/time.h> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#include <grpc/support/useful.h> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#include <stdio.h> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#include <stdlib.h> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#include <string.h> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#include "test/core/util/test_config.h" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// Change this to non-zero if you want more output. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#define VERBOSE 0 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// Log size to use for all tests. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#define LOG_SIZE_IN_MB 1 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#define LOG_SIZE_IN_BYTES (LOG_SIZE_IN_MB << 20) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// Fills in 'record' of size 'size'. Each byte in record is filled in with the 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// same value. The value is extracted from 'record' pointer. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static void write_record(char* record, size_t size) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  char data = (char)((uintptr_t)record % 255); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  memset(record, data, size); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// Reads fixed size records. Returns the number of records read in 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// 'num_records'. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static void read_records(size_t record_size, const char* buffer, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                         size_t buffer_size, int* num_records) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  GPR_ASSERT(buffer_size >= record_size); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  GPR_ASSERT(buffer_size % record_size == 0); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  *num_records = (int)(buffer_size / record_size); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  for (int i = 0; i < *num_records; ++i) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    const char* record = buffer + (record_size * (size_t)i); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    char data = (char)((uintptr_t)record % 255); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    for (size_t j = 0; j < record_size; ++j) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      GPR_ASSERT(data == record[j]); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// Tries to write the specified number of records. Stops when the log gets 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// full. Returns the number of records written. Spins for random 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// number of times, up to 'max_spin_count', between writes. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static int write_records_to_log(int writer_id, size_t record_size, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                int num_records, int max_spin_count) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  int counter = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  for (int i = 0; i < num_records; ++i) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    int spin_count = max_spin_count ? rand() % max_spin_count : 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (VERBOSE && (counter++ == num_records / 10)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      printf("   Writer %d: %d out of %d written\n", writer_id, i, num_records); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      counter = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    char* record = (char*)(census_log_start_write(record_size)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (record == NULL) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      return i; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    write_record(record, record_size); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    census_log_end_write(record, record_size); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    for (int j = 0; j < spin_count; ++j) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      GPR_ASSERT(j >= 0); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  return num_records; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// Performs a single read iteration. Returns the number of records read. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static int perform_read_iteration(size_t record_size) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  const void* read_buffer = NULL; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  size_t bytes_available; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  int records_read = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  census_log_init_reader(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  while ((read_buffer = census_log_read_next(&bytes_available))) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    int num_records = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    read_records(record_size, (const char*)read_buffer, bytes_available, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                 &num_records); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    records_read += num_records; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  return records_read; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// Asserts that the log is empty. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static void assert_log_empty(void) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  census_log_init_reader(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  size_t bytes_available; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  GPR_ASSERT(census_log_read_next(&bytes_available) == NULL); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// Fills the log and verifies data. If 'no fragmentation' is true, records 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// are sized such that CENSUS_LOG_2_MAX_RECORD_SIZE is a multiple of record 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// size. If not a circular log, verifies that the number of records written 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// match the number of records read. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static void fill_log(size_t log_size, int no_fragmentation, int circular_log) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  size_t size; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (no_fragmentation) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    int log2size = rand() % (CENSUS_LOG_2_MAX_RECORD_SIZE + 1); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    size = ((size_t)1 << log2size); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    while (1) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      size = 1 + ((size_t)rand() % CENSUS_LOG_MAX_RECORD_SIZE); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      if (CENSUS_LOG_MAX_RECORD_SIZE % size) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        break; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  int records_written = 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      write_records_to_log(0 /* writer id */, size, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                           (int)((log_size / size) * 2), 0 /* spin count */); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  int records_read = perform_read_iteration(size); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (!circular_log) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    GPR_ASSERT(records_written == records_read); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  assert_log_empty(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// Structure to pass args to writer_thread 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+typedef struct writer_thread_args { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  // Index of this thread in the writers vector. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  int index; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  // Record size. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  size_t record_size; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  // Number of records to write. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  int num_records; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  // Used to signal when writer is complete 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  gpr_cv* done; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  gpr_mu* mu; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  int* count; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} writer_thread_args; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// Writes the given number of records of random size (up to kMaxRecordSize) and 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// random data to the specified log. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static void writer_thread(void* arg) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  writer_thread_args* args = (writer_thread_args*)arg; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  // Maximum number of times to spin between writes. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  static const int MAX_SPIN_COUNT = 50; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  int records_written = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (VERBOSE) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    printf("   Writer %d starting\n", args->index); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  while (records_written < args->num_records) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    records_written += write_records_to_log(args->index, args->record_size, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                            args->num_records - records_written, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                            MAX_SPIN_COUNT); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (records_written < args->num_records) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      // Ran out of log space. Sleep for a bit and let the reader catch up. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      // This should never happen for circular logs. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      if (VERBOSE) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        printf( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            "   Writer %d stalled due to out-of-space: %d out of %d " 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            "written\n", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            args->index, records_written, args->num_records); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      gpr_sleep_until(GRPC_TIMEOUT_MILLIS_TO_DEADLINE(10)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  // Done. Decrement count and signal. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  gpr_mu_lock(args->mu); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  (*args->count)--; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  gpr_cv_signal(args->done); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (VERBOSE) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    printf("   Writer %d done\n", args->index); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  gpr_mu_unlock(args->mu); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// struct to pass args to reader_thread 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+typedef struct reader_thread_args { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  // Record size. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  size_t record_size; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  // Interval between read iterations. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  int read_iteration_interval_in_msec; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  // Total number of records. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  int total_records; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  // Signalled when reader should stop. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  gpr_cv stop; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  int stop_flag; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  // Used to signal when reader has finished 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  gpr_cv* done; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  gpr_mu* mu; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  int running; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} reader_thread_args; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// Reads and verifies the specified number of records. Reader can also be 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// stopped via gpr_cv_signal(&args->stop). Sleeps for 'read_interval_in_msec' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// between read iterations. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static void reader_thread(void* arg) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  reader_thread_args* args = (reader_thread_args*)arg; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (VERBOSE) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    printf("   Reader starting\n"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  gpr_timespec interval = gpr_time_from_micros( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      args->read_iteration_interval_in_msec * 1000, GPR_TIMESPAN); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  gpr_mu_lock(args->mu); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  int records_read = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  int num_iterations = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  int counter = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  while (!args->stop_flag && records_read < args->total_records) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    gpr_cv_wait(&args->stop, args->mu, interval); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (!args->stop_flag) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      records_read += perform_read_iteration(args->record_size); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      GPR_ASSERT(records_read <= args->total_records); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      if (VERBOSE && (counter++ == 100000)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        printf("   Reader: %d out of %d read\n", records_read, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+               args->total_records); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        counter = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      ++num_iterations; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  // Done 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  args->running = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  gpr_cv_signal(args->done); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (VERBOSE) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    printf("   Reader: records: %d, iterations: %d\n", records_read, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+           num_iterations); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  gpr_mu_unlock(args->mu); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// Creates NUM_WRITERS writers where each writer writes NUM_RECORDS_PER_WRITER 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// records. Also, starts a reader that iterates over and reads blocks every 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// READ_ITERATION_INTERVAL_IN_MSEC. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// Number of writers. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#define NUM_WRITERS 5 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static void multiple_writers_single_reader(int circular_log) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  // Sleep interval between read iterations. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  static const int READ_ITERATION_INTERVAL_IN_MSEC = 10; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  // Maximum record size. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  static const size_t MAX_RECORD_SIZE = 20; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  // Number of records written by each writer. This is sized such that we 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  // will write through the entire log ~10 times. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  const int NUM_RECORDS_PER_WRITER = 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      (int)((10 * census_log_remaining_space()) / (MAX_RECORD_SIZE / 2)) / 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      NUM_WRITERS; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  size_t record_size = ((size_t)rand() % MAX_RECORD_SIZE) + 1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  // Create and start writers. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  writer_thread_args writers[NUM_WRITERS]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  int writers_count = NUM_WRITERS; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  gpr_cv writers_done; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  gpr_mu writers_mu;  // protects writers_done and writers_count 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  gpr_cv_init(&writers_done); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  gpr_mu_init(&writers_mu); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  gpr_thd_id id; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  for (int i = 0; i < NUM_WRITERS; ++i) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    writers[i].index = i; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    writers[i].record_size = record_size; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    writers[i].num_records = NUM_RECORDS_PER_WRITER; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    writers[i].done = &writers_done; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    writers[i].count = &writers_count; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    writers[i].mu = &writers_mu; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    gpr_thd_new(&id, &writer_thread, &writers[i], NULL); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  // Start reader. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  gpr_cv reader_done; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  gpr_mu reader_mu;  // protects reader_done and reader.running 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  reader_thread_args reader; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  reader.record_size = record_size; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  reader.read_iteration_interval_in_msec = READ_ITERATION_INTERVAL_IN_MSEC; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  reader.total_records = NUM_WRITERS * NUM_RECORDS_PER_WRITER; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  reader.stop_flag = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  gpr_cv_init(&reader.stop); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  gpr_cv_init(&reader_done); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  reader.done = &reader_done; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  gpr_mu_init(&reader_mu); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  reader.mu = &reader_mu; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  reader.running = 1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  gpr_thd_new(&id, &reader_thread, &reader, NULL); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  // Wait for writers to finish. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  gpr_mu_lock(&writers_mu); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  while (writers_count != 0) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    gpr_cv_wait(&writers_done, &writers_mu, gpr_inf_future(GPR_CLOCK_REALTIME)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  gpr_mu_unlock(&writers_mu); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  gpr_mu_destroy(&writers_mu); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  gpr_cv_destroy(&writers_done); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  gpr_mu_lock(&reader_mu); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (circular_log) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // Stop reader. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    reader.stop_flag = 1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    gpr_cv_signal(&reader.stop); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  // wait for reader to finish 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  while (reader.running) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    gpr_cv_wait(&reader_done, &reader_mu, gpr_inf_future(GPR_CLOCK_REALTIME)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (circular_log) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // Assert that there were no out-of-space errors. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    GPR_ASSERT(0 == census_log_out_of_space_count()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  gpr_mu_unlock(&reader_mu); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  gpr_mu_destroy(&reader_mu); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  gpr_cv_destroy(&reader_done); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (VERBOSE) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    printf("   Reader: finished\n"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static void setup_test(int circular_log) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  census_log_initialize(LOG_SIZE_IN_MB, circular_log); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  GPR_ASSERT(census_log_remaining_space() == LOG_SIZE_IN_BYTES); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// 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(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// 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(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// 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(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// 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 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 non-circular log with records sized such that size is a multiple of 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// CENSUS_LOG_MAX_RECORD_SIZE (no per-block fragmentation). 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+void test_fill_log_no_fragmentation(void) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  printf("Starting test: fill log no fragmentation\n"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  const int circular = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  setup_test(circular); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  fill_log(LOG_SIZE_IN_BYTES, 1 /* no fragmentation */, circular); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  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(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// Fills non-circular log with records that may straddle end of a block. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+void test_fill_log_with_straddling_records(void) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  printf("Starting test: fill log with straddling records\n"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  const int circular = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  setup_test(circular); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  fill_log(LOG_SIZE_IN_BYTES, 0 /* block straddling records */, circular); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  census_log_shutdown(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// 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_circular_log(void) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  printf("Starting test: multiple writers circular log\n"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  const int circular = 1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  setup_test(circular); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  multiple_writers_single_reader(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(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// 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); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+int main(int argc, char** argv) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  grpc_test_init(argc, argv); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  gpr_time_init(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  srand((unsigned)gpr_now(GPR_CLOCK_REALTIME).tv_nsec); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  test_invalid_record_size(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  test_end_write_with_different_size(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  test_read_pending_record(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  test_read_beyond_pending_record(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  test_detached_while_reading(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  test_fill_log_no_fragmentation(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  test_fill_circular_log_no_fragmentation(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  test_fill_log_with_straddling_records(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  test_fill_circular_log_with_straddling_records(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  test_small_log(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  test_multiple_writers(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  test_multiple_writers_circular_log(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  test_performance(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  return 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 |