1 /* 2 * 3 * Copyright 2017 gRPC authors. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 */ 18 19 /* Test of gpr spin-lock support. */ 20 21 #include "src/core/lib/gpr/spinlock.h" 22 23 #include <stdio.h> 24 #include <stdlib.h> 25 26 #include <grpc/support/alloc.h> 27 #include <grpc/support/log.h> 28 #include <grpc/support/sync.h> 29 #include <grpc/support/time.h> 30 31 #include "src/core/lib/gprpp/thd.h" 32 #include "test/core/util/test_config.h" 33 34 /* ------------------------------------------------- */ 35 /* Tests for gpr_spinlock. */ 36 struct test { 37 int thread_count; /* number of threads */ 38 grpc_core::Thread* threads; 39 40 int64_t iterations; /* number of iterations per thread */ 41 int64_t counter; 42 int incr_step; /* how much to increment/decrement refcount each time */ 43 44 gpr_spinlock mu; /* protects iterations, counter */ 45 }; 46 47 /* Return pointer to a new struct test. */ 48 static struct test* test_new(int threads, int64_t iterations, int incr_step) { 49 struct test* m = static_cast<struct test*>(gpr_malloc(sizeof(*m))); 50 m->thread_count = threads; 51 m->threads = static_cast<grpc_core::Thread*>( 52 gpr_malloc(sizeof(*m->threads) * static_cast<size_t>(threads))); 53 m->iterations = iterations; 54 m->counter = 0; 55 m->thread_count = 0; 56 m->incr_step = incr_step; 57 m->mu = GPR_SPINLOCK_INITIALIZER; 58 return m; 59 } 60 61 /* Return pointer to a new struct test. */ 62 static void test_destroy(struct test* m) { 63 gpr_free(m->threads); 64 gpr_free(m); 65 } 66 67 /* Create m->threads threads, each running (*body)(m) */ 68 static void test_create_threads(struct test* m, void (*body)(void* arg)) { 69 int i; 70 for (i = 0; i != m->thread_count; i++) { 71 m->threads[i] = grpc_core::Thread("grpc_create_threads", body, m); 72 m->threads[i].Start(); 73 } 74 } 75 76 /* Wait until all threads report done. */ 77 static void test_wait(struct test* m) { 78 int i; 79 for (i = 0; i != m->thread_count; i++) { 80 m->threads[i].Join(); 81 } 82 } 83 84 /* Test several threads running (*body)(struct test *m) for increasing settings 85 of m->iterations, until about timeout_s to 2*timeout_s seconds have elapsed. 86 If extra!=NULL, run (*extra)(m) in an additional thread. 87 incr_step controls by how much m->refcount should be incremented/decremented 88 (if at all) each time in the tests. 89 */ 90 static void test(const char* name, void (*body)(void* m), int timeout_s, 91 int incr_step) { 92 int64_t iterations = 1024; 93 struct test* m; 94 gpr_timespec start = gpr_now(GPR_CLOCK_REALTIME); 95 gpr_timespec time_taken; 96 gpr_timespec deadline = gpr_time_add( 97 start, gpr_time_from_micros(static_cast<int64_t>(timeout_s) * 1000000, 98 GPR_TIMESPAN)); 99 fprintf(stderr, "%s:", name); 100 fflush(stderr); 101 while (gpr_time_cmp(gpr_now(GPR_CLOCK_REALTIME), deadline) < 0) { 102 if (iterations < INT64_MAX / 2) iterations <<= 1; 103 fprintf(stderr, " %ld", static_cast<long>(iterations)); 104 fflush(stderr); 105 m = test_new(10, iterations, incr_step); 106 test_create_threads(m, body); 107 test_wait(m); 108 if (m->counter != m->thread_count * m->iterations * m->incr_step) { 109 fprintf(stderr, "counter %ld threads %d iterations %ld\n", 110 static_cast<long>(m->counter), m->thread_count, 111 static_cast<long>(m->iterations)); 112 fflush(stderr); 113 GPR_ASSERT(0); 114 } 115 test_destroy(m); 116 } 117 time_taken = gpr_time_sub(gpr_now(GPR_CLOCK_REALTIME), start); 118 fprintf(stderr, " done %lld.%09d s\n", 119 static_cast<long long>(time_taken.tv_sec), 120 static_cast<int>(time_taken.tv_nsec)); 121 fflush(stderr); 122 } 123 124 /* Increment m->counter on each iteration; then mark thread as done. */ 125 static void inc(void* v /*=m*/) { 126 struct test* m = static_cast<struct test*>(v); 127 int64_t i; 128 for (i = 0; i != m->iterations; i++) { 129 gpr_spinlock_lock(&m->mu); 130 m->counter++; 131 gpr_spinlock_unlock(&m->mu); 132 } 133 } 134 135 /* Increment m->counter under lock acquired with trylock, m->iterations times; 136 then mark thread as done. */ 137 static void inctry(void* v /*=m*/) { 138 struct test* m = static_cast<struct test*>(v); 139 int64_t i; 140 for (i = 0; i != m->iterations;) { 141 if (gpr_spinlock_trylock(&m->mu)) { 142 m->counter++; 143 gpr_spinlock_unlock(&m->mu); 144 i++; 145 } 146 } 147 } 148 149 /* ------------------------------------------------- */ 150 151 int main(int argc, char* argv[]) { 152 grpc_test_init(argc, argv); 153 test("spinlock", &inc, 1, 1); 154 test("spinlock try", &inctry, 1, 1); 155 return 0; 156 } 157