From dbdf495f61d099521bab45cf93f6f7d8dfb5c06f Mon Sep 17 00:00:00 2001 From: "Mark D. Roth" Date: Thu, 18 Jan 2018 11:21:12 -0800 Subject: Split lib/support into lib/gpr and lib/gpr++. --- src/core/lib/gpr/README.md | 8 + src/core/lib/gpr/alloc.cc | 102 ++++++++++ src/core/lib/gpr/arena.cc | 83 ++++++++ src/core/lib/gpr/arena.h | 39 ++++ src/core/lib/gpr/atm.cc | 32 ++++ src/core/lib/gpr/avl.cc | 300 +++++++++++++++++++++++++++++ src/core/lib/gpr/cmdline.cc | 330 ++++++++++++++++++++++++++++++++ src/core/lib/gpr/cpu_iphone.cc | 36 ++++ src/core/lib/gpr/cpu_linux.cc | 78 ++++++++ src/core/lib/gpr/cpu_posix.cc | 80 ++++++++ src/core/lib/gpr/cpu_windows.cc | 33 ++++ src/core/lib/gpr/env.h | 41 ++++ src/core/lib/gpr/env_linux.cc | 82 ++++++++ src/core/lib/gpr/env_posix.cc | 47 +++++ src/core/lib/gpr/env_windows.cc | 72 +++++++ src/core/lib/gpr/fork.cc | 62 ++++++ src/core/lib/gpr/fork.h | 35 ++++ src/core/lib/gpr/host_port.cc | 95 +++++++++ src/core/lib/gpr/log.cc | 94 +++++++++ src/core/lib/gpr/log_android.cc | 72 +++++++ src/core/lib/gpr/log_linux.cc | 92 +++++++++ src/core/lib/gpr/log_posix.cc | 90 +++++++++ src/core/lib/gpr/log_windows.cc | 97 ++++++++++ src/core/lib/gpr/mpscq.cc | 114 +++++++++++ src/core/lib/gpr/mpscq.h | 84 ++++++++ src/core/lib/gpr/murmur_hash.cc | 78 ++++++++ src/core/lib/gpr/murmur_hash.h | 29 +++ src/core/lib/gpr/spinlock.h | 44 +++++ src/core/lib/gpr/string.cc | 315 ++++++++++++++++++++++++++++++ src/core/lib/gpr/string.h | 109 +++++++++++ src/core/lib/gpr/string_posix.cc | 72 +++++++ src/core/lib/gpr/string_util_windows.cc | 82 ++++++++ src/core/lib/gpr/string_windows.cc | 69 +++++++ src/core/lib/gpr/string_windows.h | 32 ++++ src/core/lib/gpr/subprocess_posix.cc | 99 ++++++++++ src/core/lib/gpr/subprocess_windows.cc | 126 ++++++++++++ src/core/lib/gpr/sync.cc | 122 ++++++++++++ src/core/lib/gpr/sync_posix.cc | 111 +++++++++++ src/core/lib/gpr/sync_windows.cc | 118 ++++++++++++ src/core/lib/gpr/thd.cc | 49 +++++ src/core/lib/gpr/thd_internal.h | 30 +++ src/core/lib/gpr/thd_posix.cc | 152 +++++++++++++++ src/core/lib/gpr/thd_windows.cc | 105 ++++++++++ src/core/lib/gpr/time.cc | 247 ++++++++++++++++++++++++ src/core/lib/gpr/time_posix.cc | 166 ++++++++++++++++ src/core/lib/gpr/time_precise.cc | 76 ++++++++ src/core/lib/gpr/time_precise.h | 27 +++ src/core/lib/gpr/time_windows.cc | 98 ++++++++++ src/core/lib/gpr/tls_pthread.cc | 30 +++ src/core/lib/gpr/tmpfile.h | 30 +++ src/core/lib/gpr/tmpfile_msys.cc | 58 ++++++ src/core/lib/gpr/tmpfile_posix.cc | 70 +++++++ src/core/lib/gpr/tmpfile_windows.cc | 69 +++++++ src/core/lib/gpr/wrap_memcpy.cc | 42 ++++ 54 files changed, 4853 insertions(+) create mode 100644 src/core/lib/gpr/README.md create mode 100644 src/core/lib/gpr/alloc.cc create mode 100644 src/core/lib/gpr/arena.cc create mode 100644 src/core/lib/gpr/arena.h create mode 100644 src/core/lib/gpr/atm.cc create mode 100644 src/core/lib/gpr/avl.cc create mode 100644 src/core/lib/gpr/cmdline.cc create mode 100644 src/core/lib/gpr/cpu_iphone.cc create mode 100644 src/core/lib/gpr/cpu_linux.cc create mode 100644 src/core/lib/gpr/cpu_posix.cc create mode 100644 src/core/lib/gpr/cpu_windows.cc create mode 100644 src/core/lib/gpr/env.h create mode 100644 src/core/lib/gpr/env_linux.cc create mode 100644 src/core/lib/gpr/env_posix.cc create mode 100644 src/core/lib/gpr/env_windows.cc create mode 100644 src/core/lib/gpr/fork.cc create mode 100644 src/core/lib/gpr/fork.h create mode 100644 src/core/lib/gpr/host_port.cc create mode 100644 src/core/lib/gpr/log.cc create mode 100644 src/core/lib/gpr/log_android.cc create mode 100644 src/core/lib/gpr/log_linux.cc create mode 100644 src/core/lib/gpr/log_posix.cc create mode 100644 src/core/lib/gpr/log_windows.cc create mode 100644 src/core/lib/gpr/mpscq.cc create mode 100644 src/core/lib/gpr/mpscq.h create mode 100644 src/core/lib/gpr/murmur_hash.cc create mode 100644 src/core/lib/gpr/murmur_hash.h create mode 100644 src/core/lib/gpr/spinlock.h create mode 100644 src/core/lib/gpr/string.cc create mode 100644 src/core/lib/gpr/string.h create mode 100644 src/core/lib/gpr/string_posix.cc create mode 100644 src/core/lib/gpr/string_util_windows.cc create mode 100644 src/core/lib/gpr/string_windows.cc create mode 100644 src/core/lib/gpr/string_windows.h create mode 100644 src/core/lib/gpr/subprocess_posix.cc create mode 100644 src/core/lib/gpr/subprocess_windows.cc create mode 100644 src/core/lib/gpr/sync.cc create mode 100644 src/core/lib/gpr/sync_posix.cc create mode 100644 src/core/lib/gpr/sync_windows.cc create mode 100644 src/core/lib/gpr/thd.cc create mode 100644 src/core/lib/gpr/thd_internal.h create mode 100644 src/core/lib/gpr/thd_posix.cc create mode 100644 src/core/lib/gpr/thd_windows.cc create mode 100644 src/core/lib/gpr/time.cc create mode 100644 src/core/lib/gpr/time_posix.cc create mode 100644 src/core/lib/gpr/time_precise.cc create mode 100644 src/core/lib/gpr/time_precise.h create mode 100644 src/core/lib/gpr/time_windows.cc create mode 100644 src/core/lib/gpr/tls_pthread.cc create mode 100644 src/core/lib/gpr/tmpfile.h create mode 100644 src/core/lib/gpr/tmpfile_msys.cc create mode 100644 src/core/lib/gpr/tmpfile_posix.cc create mode 100644 src/core/lib/gpr/tmpfile_windows.cc create mode 100644 src/core/lib/gpr/wrap_memcpy.cc (limited to 'src/core/lib/gpr') diff --git a/src/core/lib/gpr/README.md b/src/core/lib/gpr/README.md new file mode 100644 index 0000000000..21fb0c796d --- /dev/null +++ b/src/core/lib/gpr/README.md @@ -0,0 +1,8 @@ +# GPR - Google Portable Runtime for C + +The files in this directory contain basic utility code and platform +abstractions for C code. None of this code is gRPC-specific; anything +here may also be useful for other open source projects written in C. + +Note that this is one of the few places in src/core where we allow +the use of portability macros. diff --git a/src/core/lib/gpr/alloc.cc b/src/core/lib/gpr/alloc.cc new file mode 100644 index 0000000000..518bdb99f7 --- /dev/null +++ b/src/core/lib/gpr/alloc.cc @@ -0,0 +1,102 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include +#include +#include "src/core/lib/profiling/timers.h" + +static void* zalloc_with_calloc(size_t sz) { return calloc(sz, 1); } + +static void* zalloc_with_gpr_malloc(size_t sz) { + void* p = gpr_malloc(sz); + memset(p, 0, sz); + return p; +} + +static gpr_allocation_functions g_alloc_functions = {malloc, zalloc_with_calloc, + realloc, free}; + +gpr_allocation_functions gpr_get_allocation_functions() { + return g_alloc_functions; +} + +void gpr_set_allocation_functions(gpr_allocation_functions functions) { + GPR_ASSERT(functions.malloc_fn != nullptr); + GPR_ASSERT(functions.realloc_fn != nullptr); + GPR_ASSERT(functions.free_fn != nullptr); + if (functions.zalloc_fn == nullptr) { + functions.zalloc_fn = zalloc_with_gpr_malloc; + } + g_alloc_functions = functions; +} + +void* gpr_malloc(size_t size) { + void* p; + if (size == 0) return nullptr; + GPR_TIMER_BEGIN("gpr_malloc", 0); + p = g_alloc_functions.malloc_fn(size); + if (!p) { + abort(); + } + GPR_TIMER_END("gpr_malloc", 0); + return p; +} + +void* gpr_zalloc(size_t size) { + void* p; + if (size == 0) return nullptr; + GPR_TIMER_BEGIN("gpr_zalloc", 0); + p = g_alloc_functions.zalloc_fn(size); + if (!p) { + abort(); + } + GPR_TIMER_END("gpr_zalloc", 0); + return p; +} + +void gpr_free(void* p) { + GPR_TIMER_BEGIN("gpr_free", 0); + g_alloc_functions.free_fn(p); + GPR_TIMER_END("gpr_free", 0); +} + +void* gpr_realloc(void* p, size_t size) { + if ((size == 0) && (p == nullptr)) return nullptr; + GPR_TIMER_BEGIN("gpr_realloc", 0); + p = g_alloc_functions.realloc_fn(p, size); + if (!p) { + abort(); + } + GPR_TIMER_END("gpr_realloc", 0); + return p; +} + +void* gpr_malloc_aligned(size_t size, size_t alignment_log) { + size_t alignment = ((size_t)1) << alignment_log; + size_t extra = alignment - 1 + sizeof(void*); + void* p = gpr_malloc(size + extra); + void** ret = (void**)(((uintptr_t)p + extra) & ~(alignment - 1)); + ret[-1] = p; + return (void*)ret; +} + +void gpr_free_aligned(void* ptr) { gpr_free(((void**)ptr)[-1]); } diff --git a/src/core/lib/gpr/arena.cc b/src/core/lib/gpr/arena.cc new file mode 100644 index 0000000000..177c176732 --- /dev/null +++ b/src/core/lib/gpr/arena.cc @@ -0,0 +1,83 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/gpr/arena.h" +#include +#include +#include +#include + +#define ROUND_UP_TO_ALIGNMENT_SIZE(x) \ + (((x) + GPR_MAX_ALIGNMENT - 1u) & ~(GPR_MAX_ALIGNMENT - 1u)) + +typedef struct zone { + size_t size_begin; + size_t size_end; + gpr_atm next_atm; +} zone; + +struct gpr_arena { + gpr_atm size_so_far; + zone initial_zone; +}; + +gpr_arena* gpr_arena_create(size_t initial_size) { + initial_size = ROUND_UP_TO_ALIGNMENT_SIZE(initial_size); + gpr_arena* a = (gpr_arena*)gpr_zalloc(sizeof(gpr_arena) + initial_size); + a->initial_zone.size_end = initial_size; + return a; +} + +size_t gpr_arena_destroy(gpr_arena* arena) { + gpr_atm size = gpr_atm_no_barrier_load(&arena->size_so_far); + zone* z = (zone*)gpr_atm_no_barrier_load(&arena->initial_zone.next_atm); + gpr_free(arena); + while (z) { + zone* next_z = (zone*)gpr_atm_no_barrier_load(&z->next_atm); + gpr_free(z); + z = next_z; + } + return (size_t)size; +} + +void* gpr_arena_alloc(gpr_arena* arena, size_t size) { + size = ROUND_UP_TO_ALIGNMENT_SIZE(size); + size_t start = + (size_t)gpr_atm_no_barrier_fetch_add(&arena->size_so_far, size); + zone* z = &arena->initial_zone; + while (start > z->size_end) { + zone* next_z = (zone*)gpr_atm_acq_load(&z->next_atm); + if (next_z == nullptr) { + size_t next_z_size = (size_t)gpr_atm_no_barrier_load(&arena->size_so_far); + next_z = (zone*)gpr_zalloc(sizeof(zone) + next_z_size); + next_z->size_begin = z->size_end; + next_z->size_end = z->size_end + next_z_size; + if (!gpr_atm_rel_cas(&z->next_atm, (gpr_atm)NULL, (gpr_atm)next_z)) { + gpr_free(next_z); + next_z = (zone*)gpr_atm_acq_load(&z->next_atm); + } + } + z = next_z; + } + if (start + size > z->size_end) { + return gpr_arena_alloc(arena, size); + } + GPR_ASSERT(start >= z->size_begin); + GPR_ASSERT(start + size <= z->size_end); + return ((char*)(z + 1)) + start - z->size_begin; +} diff --git a/src/core/lib/gpr/arena.h b/src/core/lib/gpr/arena.h new file mode 100644 index 0000000000..339771c0e3 --- /dev/null +++ b/src/core/lib/gpr/arena.h @@ -0,0 +1,39 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// \file Arena based allocator +// Allows very fast allocation of memory, but that memory cannot be freed until +// the arena as a whole is freed +// Tracks the total memory allocated against it, so that future arenas can +// pre-allocate the right amount of memory + +#ifndef GRPC_CORE_LIB_GPR_ARENA_H +#define GRPC_CORE_LIB_GPR_ARENA_H + +#include + +typedef struct gpr_arena gpr_arena; + +// Create an arena, with \a initial_size bytes in the first allocated buffer +gpr_arena* gpr_arena_create(size_t initial_size); +// Allocate \a size bytes from the arena +void* gpr_arena_alloc(gpr_arena* arena, size_t size); +// Destroy an arena, returning the total number of bytes allocated +size_t gpr_arena_destroy(gpr_arena* arena); + +#endif /* GRPC_CORE_LIB_GPR_ARENA_H */ diff --git a/src/core/lib/gpr/atm.cc b/src/core/lib/gpr/atm.cc new file mode 100644 index 0000000000..15bfe52d64 --- /dev/null +++ b/src/core/lib/gpr/atm.cc @@ -0,0 +1,32 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include + +gpr_atm gpr_atm_no_barrier_clamped_add(gpr_atm* value, gpr_atm delta, + gpr_atm min, gpr_atm max) { + gpr_atm current_value; + gpr_atm new_value; + do { + current_value = gpr_atm_no_barrier_load(value); + new_value = GPR_CLAMP(current_value + delta, min, max); + if (new_value == current_value) break; + } while (!gpr_atm_no_barrier_cas(value, current_value, new_value)); + return new_value; +} diff --git a/src/core/lib/gpr/avl.cc b/src/core/lib/gpr/avl.cc new file mode 100644 index 0000000000..0b67a21f2f --- /dev/null +++ b/src/core/lib/gpr/avl.cc @@ -0,0 +1,300 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +#include +#include +#include + +gpr_avl gpr_avl_create(const gpr_avl_vtable* vtable) { + gpr_avl out; + out.vtable = vtable; + out.root = nullptr; + return out; +} + +static gpr_avl_node* ref_node(gpr_avl_node* node) { + if (node) { + gpr_ref(&node->refs); + } + return node; +} + +static void unref_node(const gpr_avl_vtable* vtable, gpr_avl_node* node, + void* user_data) { + if (node == nullptr) { + return; + } + if (gpr_unref(&node->refs)) { + vtable->destroy_key(node->key, user_data); + vtable->destroy_value(node->value, user_data); + unref_node(vtable, node->left, user_data); + unref_node(vtable, node->right, user_data); + gpr_free(node); + } +} + +static long node_height(gpr_avl_node* node) { + return node == nullptr ? 0 : node->height; +} + +#ifndef NDEBUG +static long calculate_height(gpr_avl_node* node) { + return node == nullptr ? 0 + : 1 + GPR_MAX(calculate_height(node->left), + calculate_height(node->right)); +} + +static gpr_avl_node* assert_invariants(gpr_avl_node* n) { + if (n == nullptr) return nullptr; + assert_invariants(n->left); + assert_invariants(n->right); + assert(calculate_height(n) == n->height); + assert(labs(node_height(n->left) - node_height(n->right)) <= 1); + return n; +} +#else +static gpr_avl_node* assert_invariants(gpr_avl_node* n) { return n; } +#endif + +gpr_avl_node* new_node(void* key, void* value, gpr_avl_node* left, + gpr_avl_node* right) { + gpr_avl_node* node = (gpr_avl_node*)gpr_malloc(sizeof(*node)); + gpr_ref_init(&node->refs, 1); + node->key = key; + node->value = value; + node->left = assert_invariants(left); + node->right = assert_invariants(right); + node->height = 1 + GPR_MAX(node_height(left), node_height(right)); + return node; +} + +static gpr_avl_node* get(const gpr_avl_vtable* vtable, gpr_avl_node* node, + void* key, void* user_data) { + long cmp; + + if (node == nullptr) { + return nullptr; + } + + cmp = vtable->compare_keys(node->key, key, user_data); + if (cmp == 0) { + return node; + } else if (cmp > 0) { + return get(vtable, node->left, key, user_data); + } else { + return get(vtable, node->right, key, user_data); + } +} + +void* gpr_avl_get(gpr_avl avl, void* key, void* user_data) { + gpr_avl_node* node = get(avl.vtable, avl.root, key, user_data); + return node ? node->value : nullptr; +} + +int gpr_avl_maybe_get(gpr_avl avl, void* key, void** value, void* user_data) { + gpr_avl_node* node = get(avl.vtable, avl.root, key, user_data); + if (node != nullptr) { + *value = node->value; + return 1; + } + return 0; +} + +static gpr_avl_node* rotate_left(const gpr_avl_vtable* vtable, void* key, + void* value, gpr_avl_node* left, + gpr_avl_node* right, void* user_data) { + gpr_avl_node* n = new_node(vtable->copy_key(right->key, user_data), + vtable->copy_value(right->value, user_data), + new_node(key, value, left, ref_node(right->left)), + ref_node(right->right)); + unref_node(vtable, right, user_data); + return n; +} + +static gpr_avl_node* rotate_right(const gpr_avl_vtable* vtable, void* key, + void* value, gpr_avl_node* left, + gpr_avl_node* right, void* user_data) { + gpr_avl_node* n = + new_node(vtable->copy_key(left->key, user_data), + vtable->copy_value(left->value, user_data), ref_node(left->left), + new_node(key, value, ref_node(left->right), right)); + unref_node(vtable, left, user_data); + return n; +} + +static gpr_avl_node* rotate_left_right(const gpr_avl_vtable* vtable, void* key, + void* value, gpr_avl_node* left, + gpr_avl_node* right, void* user_data) { + /* rotate_right(..., rotate_left(left), right) */ + gpr_avl_node* n = + new_node(vtable->copy_key(left->right->key, user_data), + vtable->copy_value(left->right->value, user_data), + new_node(vtable->copy_key(left->key, user_data), + vtable->copy_value(left->value, user_data), + ref_node(left->left), ref_node(left->right->left)), + new_node(key, value, ref_node(left->right->right), right)); + unref_node(vtable, left, user_data); + return n; +} + +static gpr_avl_node* rotate_right_left(const gpr_avl_vtable* vtable, void* key, + void* value, gpr_avl_node* left, + gpr_avl_node* right, void* user_data) { + /* rotate_left(..., left, rotate_right(right)) */ + gpr_avl_node* n = + new_node(vtable->copy_key(right->left->key, user_data), + vtable->copy_value(right->left->value, user_data), + new_node(key, value, left, ref_node(right->left->left)), + new_node(vtable->copy_key(right->key, user_data), + vtable->copy_value(right->value, user_data), + ref_node(right->left->right), ref_node(right->right))); + unref_node(vtable, right, user_data); + return n; +} + +static gpr_avl_node* rebalance(const gpr_avl_vtable* vtable, void* key, + void* value, gpr_avl_node* left, + gpr_avl_node* right, void* user_data) { + switch (node_height(left) - node_height(right)) { + case 2: + if (node_height(left->left) - node_height(left->right) == -1) { + return assert_invariants( + rotate_left_right(vtable, key, value, left, right, user_data)); + } else { + return assert_invariants( + rotate_right(vtable, key, value, left, right, user_data)); + } + case -2: + if (node_height(right->left) - node_height(right->right) == 1) { + return assert_invariants( + rotate_right_left(vtable, key, value, left, right, user_data)); + } else { + return assert_invariants( + rotate_left(vtable, key, value, left, right, user_data)); + } + default: + return assert_invariants(new_node(key, value, left, right)); + } +} + +static gpr_avl_node* add_key(const gpr_avl_vtable* vtable, gpr_avl_node* node, + void* key, void* value, void* user_data) { + long cmp; + if (node == nullptr) { + return new_node(key, value, nullptr, nullptr); + } + cmp = vtable->compare_keys(node->key, key, user_data); + if (cmp == 0) { + return new_node(key, value, ref_node(node->left), ref_node(node->right)); + } else if (cmp > 0) { + return rebalance(vtable, vtable->copy_key(node->key, user_data), + vtable->copy_value(node->value, user_data), + add_key(vtable, node->left, key, value, user_data), + ref_node(node->right), user_data); + } else { + return rebalance( + vtable, vtable->copy_key(node->key, user_data), + vtable->copy_value(node->value, user_data), ref_node(node->left), + add_key(vtable, node->right, key, value, user_data), user_data); + } +} + +gpr_avl gpr_avl_add(gpr_avl avl, void* key, void* value, void* user_data) { + gpr_avl_node* old_root = avl.root; + avl.root = add_key(avl.vtable, avl.root, key, value, user_data); + assert_invariants(avl.root); + unref_node(avl.vtable, old_root, user_data); + return avl; +} + +static gpr_avl_node* in_order_head(gpr_avl_node* node) { + while (node->left != nullptr) { + node = node->left; + } + return node; +} + +static gpr_avl_node* in_order_tail(gpr_avl_node* node) { + while (node->right != nullptr) { + node = node->right; + } + return node; +} + +static gpr_avl_node* remove_key(const gpr_avl_vtable* vtable, + gpr_avl_node* node, void* key, + void* user_data) { + long cmp; + if (node == nullptr) { + return nullptr; + } + cmp = vtable->compare_keys(node->key, key, user_data); + if (cmp == 0) { + if (node->left == nullptr) { + return ref_node(node->right); + } else if (node->right == nullptr) { + return ref_node(node->left); + } else if (node->left->height < node->right->height) { + gpr_avl_node* h = in_order_head(node->right); + return rebalance( + vtable, vtable->copy_key(h->key, user_data), + vtable->copy_value(h->value, user_data), ref_node(node->left), + remove_key(vtable, node->right, h->key, user_data), user_data); + } else { + gpr_avl_node* h = in_order_tail(node->left); + return rebalance(vtable, vtable->copy_key(h->key, user_data), + vtable->copy_value(h->value, user_data), + remove_key(vtable, node->left, h->key, user_data), + ref_node(node->right), user_data); + } + } else if (cmp > 0) { + return rebalance(vtable, vtable->copy_key(node->key, user_data), + vtable->copy_value(node->value, user_data), + remove_key(vtable, node->left, key, user_data), + ref_node(node->right), user_data); + } else { + return rebalance( + vtable, vtable->copy_key(node->key, user_data), + vtable->copy_value(node->value, user_data), ref_node(node->left), + remove_key(vtable, node->right, key, user_data), user_data); + } +} + +gpr_avl gpr_avl_remove(gpr_avl avl, void* key, void* user_data) { + gpr_avl_node* old_root = avl.root; + avl.root = remove_key(avl.vtable, avl.root, key, user_data); + assert_invariants(avl.root); + unref_node(avl.vtable, old_root, user_data); + return avl; +} + +gpr_avl gpr_avl_ref(gpr_avl avl, void* user_data) { + ref_node(avl.root); + return avl; +} + +void gpr_avl_unref(gpr_avl avl, void* user_data) { + unref_node(avl.vtable, avl.root, user_data); +} + +int gpr_avl_is_empty(gpr_avl avl) { return avl.root == nullptr; } diff --git a/src/core/lib/gpr/cmdline.cc b/src/core/lib/gpr/cmdline.cc new file mode 100644 index 0000000000..4118f9a72a --- /dev/null +++ b/src/core/lib/gpr/cmdline.cc @@ -0,0 +1,330 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +#include +#include +#include +#include "src/core/lib/gpr/string.h" + +typedef enum { ARGTYPE_INT, ARGTYPE_BOOL, ARGTYPE_STRING } argtype; + +typedef struct arg { + const char* name; + const char* help; + argtype type; + void* value; + struct arg* next; +} arg; + +struct gpr_cmdline { + const char* description; + arg* args; + const char* argv0; + + const char* extra_arg_name; + const char* extra_arg_help; + void (*extra_arg)(void* user_data, const char* arg); + void* extra_arg_user_data; + + int (*state)(gpr_cmdline* cl, char* arg); + arg* cur_arg; + + int survive_failure; +}; + +static int normal_state(gpr_cmdline* cl, char* arg); + +gpr_cmdline* gpr_cmdline_create(const char* description) { + gpr_cmdline* cl = (gpr_cmdline*)gpr_zalloc(sizeof(gpr_cmdline)); + + cl->description = description; + cl->state = normal_state; + + return cl; +} + +void gpr_cmdline_set_survive_failure(gpr_cmdline* cl) { + cl->survive_failure = 1; +} + +void gpr_cmdline_destroy(gpr_cmdline* cl) { + while (cl->args) { + arg* a = cl->args; + cl->args = a->next; + gpr_free(a); + } + gpr_free(cl); +} + +static void add_arg(gpr_cmdline* cl, const char* name, const char* help, + argtype type, void* value) { + arg* a; + + for (a = cl->args; a; a = a->next) { + GPR_ASSERT(0 != strcmp(a->name, name)); + } + + a = (arg*)gpr_zalloc(sizeof(arg)); + a->name = name; + a->help = help; + a->type = type; + a->value = value; + a->next = cl->args; + cl->args = a; +} + +void gpr_cmdline_add_int(gpr_cmdline* cl, const char* name, const char* help, + int* value) { + add_arg(cl, name, help, ARGTYPE_INT, value); +} + +void gpr_cmdline_add_flag(gpr_cmdline* cl, const char* name, const char* help, + int* value) { + add_arg(cl, name, help, ARGTYPE_BOOL, value); +} + +void gpr_cmdline_add_string(gpr_cmdline* cl, const char* name, const char* help, + const char** value) { + add_arg(cl, name, help, ARGTYPE_STRING, value); +} + +void gpr_cmdline_on_extra_arg( + gpr_cmdline* cl, const char* name, const char* help, + void (*on_extra_arg)(void* user_data, const char* arg), void* user_data) { + GPR_ASSERT(!cl->extra_arg); + GPR_ASSERT(on_extra_arg); + + cl->extra_arg = on_extra_arg; + cl->extra_arg_user_data = user_data; + cl->extra_arg_name = name; + cl->extra_arg_help = help; +} + +/* recursively descend argument list, adding the last element + to s first - so that arguments are added in the order they were + added to the list by api calls */ +static void add_args_to_usage(gpr_strvec* s, arg* a) { + char* tmp; + + if (!a) return; + add_args_to_usage(s, a->next); + + switch (a->type) { + case ARGTYPE_BOOL: + gpr_asprintf(&tmp, " [--%s|--no-%s]", a->name, a->name); + gpr_strvec_add(s, tmp); + break; + case ARGTYPE_STRING: + gpr_asprintf(&tmp, " [--%s=string]", a->name); + gpr_strvec_add(s, tmp); + break; + case ARGTYPE_INT: + gpr_asprintf(&tmp, " [--%s=int]", a->name); + gpr_strvec_add(s, tmp); + break; + } +} + +char* gpr_cmdline_usage_string(gpr_cmdline* cl, const char* argv0) { + /* TODO(ctiller): make this prettier */ + gpr_strvec s; + char* tmp; + const char* name = strrchr(argv0, '/'); + + if (name) { + name++; + } else { + name = argv0; + } + + gpr_strvec_init(&s); + + gpr_asprintf(&tmp, "Usage: %s", name); + gpr_strvec_add(&s, tmp); + add_args_to_usage(&s, cl->args); + if (cl->extra_arg) { + gpr_asprintf(&tmp, " [%s...]", cl->extra_arg_name); + gpr_strvec_add(&s, tmp); + } + gpr_strvec_add(&s, gpr_strdup("\n")); + + tmp = gpr_strvec_flatten(&s, nullptr); + gpr_strvec_destroy(&s); + return tmp; +} + +static int print_usage_and_die(gpr_cmdline* cl) { + char* usage = gpr_cmdline_usage_string(cl, cl->argv0); + fprintf(stderr, "%s", usage); + gpr_free(usage); + if (!cl->survive_failure) { + exit(1); + } + return 0; +} + +static int extra_state(gpr_cmdline* cl, char* str) { + if (!cl->extra_arg) { + return print_usage_and_die(cl); + } + cl->extra_arg(cl->extra_arg_user_data, str); + return 1; +} + +static arg* find_arg(gpr_cmdline* cl, char* name) { + arg* a; + + for (a = cl->args; a; a = a->next) { + if (0 == strcmp(a->name, name)) { + break; + } + } + + if (!a) { + fprintf(stderr, "Unknown argument: %s\n", name); + return nullptr; + } + + return a; +} + +static int value_state(gpr_cmdline* cl, char* str) { + long intval; + char* end; + + GPR_ASSERT(cl->cur_arg); + + switch (cl->cur_arg->type) { + case ARGTYPE_INT: + intval = strtol(str, &end, 0); + if (*end || intval < INT_MIN || intval > INT_MAX) { + fprintf(stderr, "expected integer, got '%s' for %s\n", str, + cl->cur_arg->name); + return print_usage_and_die(cl); + } + *(int*)cl->cur_arg->value = (int)intval; + break; + case ARGTYPE_BOOL: + if (0 == strcmp(str, "1") || 0 == strcmp(str, "true")) { + *(int*)cl->cur_arg->value = 1; + } else if (0 == strcmp(str, "0") || 0 == strcmp(str, "false")) { + *(int*)cl->cur_arg->value = 0; + } else { + fprintf(stderr, "expected boolean, got '%s' for %s\n", str, + cl->cur_arg->name); + return print_usage_and_die(cl); + } + break; + case ARGTYPE_STRING: + *(char**)cl->cur_arg->value = str; + break; + } + + cl->state = normal_state; + return 1; +} + +static int normal_state(gpr_cmdline* cl, char* str) { + char* eq = nullptr; + char* tmp = nullptr; + char* arg_name = nullptr; + int r = 1; + + if (0 == strcmp(str, "-help") || 0 == strcmp(str, "--help") || + 0 == strcmp(str, "-h")) { + return print_usage_and_die(cl); + } + + cl->cur_arg = nullptr; + + if (str[0] == '-') { + if (str[1] == '-') { + if (str[2] == 0) { + /* handle '--' to move to just extra args */ + cl->state = extra_state; + return 1; + } + str += 2; + } else { + str += 1; + } + /* first byte of str is now past the leading '-' or '--' */ + if (str[0] == 'n' && str[1] == 'o' && str[2] == '-') { + /* str is of the form '--no-foo' - it's a flag disable */ + str += 3; + cl->cur_arg = find_arg(cl, str); + if (cl->cur_arg == nullptr) { + return print_usage_and_die(cl); + } + if (cl->cur_arg->type != ARGTYPE_BOOL) { + fprintf(stderr, "%s is not a flag argument\n", str); + return print_usage_and_die(cl); + } + *(int*)cl->cur_arg->value = 0; + return 1; /* early out */ + } + eq = strchr(str, '='); + if (eq != nullptr) { + /* copy the string into a temp buffer and extract the name */ + tmp = arg_name = (char*)gpr_malloc((size_t)(eq - str + 1)); + memcpy(arg_name, str, (size_t)(eq - str)); + arg_name[eq - str] = 0; + } else { + arg_name = str; + } + cl->cur_arg = find_arg(cl, arg_name); + if (cl->cur_arg == nullptr) { + return print_usage_and_die(cl); + } + if (eq != nullptr) { + /* str was of the type --foo=value, parse the value */ + r = value_state(cl, eq + 1); + } else if (cl->cur_arg->type != ARGTYPE_BOOL) { + /* flag types don't have a '--foo value' variant, other types do */ + cl->state = value_state; + } else { + /* flag parameter: just set the value */ + *(int*)cl->cur_arg->value = 1; + } + } else { + r = extra_state(cl, str); + } + + gpr_free(tmp); + return r; +} + +int gpr_cmdline_parse(gpr_cmdline* cl, int argc, char** argv) { + int i; + + GPR_ASSERT(argc >= 1); + cl->argv0 = argv[0]; + + for (i = 1; i < argc; i++) { + if (!cl->state(cl, argv[i])) { + return 0; + } + } + return 1; +} diff --git a/src/core/lib/gpr/cpu_iphone.cc b/src/core/lib/gpr/cpu_iphone.cc new file mode 100644 index 0000000000..2847e03ba5 --- /dev/null +++ b/src/core/lib/gpr/cpu_iphone.cc @@ -0,0 +1,36 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +#ifdef GPR_CPU_IPHONE + +/* Probably 2 instead of 1, but see comment on gpr_cpu_current_cpu. */ +unsigned gpr_cpu_num_cores(void) { return 1; } + +/* Most code that's using this is using it to shard across work queues. So + unless profiling shows it's a problem or there appears a way to detect the + currently running CPU core, let's have it shard the default way. + Note that the interface in cpu.h lets gpr_cpu_num_cores return 0, but doing + it makes it impossible for gpr_cpu_current_cpu to satisfy its stated range, + and some code might be relying on it. */ +unsigned gpr_cpu_current_cpu(void) { return 0; } + +#endif /* GPR_CPU_IPHONE */ diff --git a/src/core/lib/gpr/cpu_linux.cc b/src/core/lib/gpr/cpu_linux.cc new file mode 100644 index 0000000000..21b1a71dc9 --- /dev/null +++ b/src/core/lib/gpr/cpu_linux.cc @@ -0,0 +1,78 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif /* _GNU_SOURCE */ + +#include + +#ifdef GPR_CPU_LINUX + +#include +#include +#include +#include + +#include +#include +#include + +static int ncpus = 0; + +static void init_num_cpus() { +#ifndef GPR_MUSL_LIBC_COMPAT + if (sched_getcpu() < 0) { + gpr_log(GPR_ERROR, "Error determining current CPU: %s\n", strerror(errno)); + ncpus = 1; + return; + } +#endif + /* This must be signed. sysconf returns -1 when the number cannot be + determined */ + ncpus = (int)sysconf(_SC_NPROCESSORS_ONLN); + if (ncpus < 1) { + gpr_log(GPR_ERROR, "Cannot determine number of CPUs: assuming 1"); + ncpus = 1; + } +} + +unsigned gpr_cpu_num_cores(void) { + static gpr_once once = GPR_ONCE_INIT; + gpr_once_init(&once, init_num_cpus); + return (unsigned)ncpus; +} + +unsigned gpr_cpu_current_cpu(void) { +#ifdef GPR_MUSL_LIBC_COMPAT + // sched_getcpu() is undefined on musl + return 0; +#else + if (gpr_cpu_num_cores() == 1) { + return 0; + } + int cpu = sched_getcpu(); + if (cpu < 0) { + gpr_log(GPR_ERROR, "Error determining current CPU: %s\n", strerror(errno)); + return 0; + } + return (unsigned)cpu; +#endif +} + +#endif /* GPR_CPU_LINUX */ diff --git a/src/core/lib/gpr/cpu_posix.cc b/src/core/lib/gpr/cpu_posix.cc new file mode 100644 index 0000000000..bca14a0c12 --- /dev/null +++ b/src/core/lib/gpr/cpu_posix.cc @@ -0,0 +1,80 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#if defined(GPR_CPU_POSIX) + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +static long ncpus = 0; + +static pthread_key_t thread_id_key; + +static void init_ncpus() { + ncpus = sysconf(_SC_NPROCESSORS_ONLN); + if (ncpus < 1 || ncpus > INT32_MAX) { + gpr_log(GPR_ERROR, "Cannot determine number of CPUs: assuming 1"); + ncpus = 1; + } +} + +unsigned gpr_cpu_num_cores(void) { + static gpr_once once = GPR_ONCE_INIT; + gpr_once_init(&once, init_ncpus); + return (unsigned)ncpus; +} + +static void delete_thread_id(void* value) { + if (value) { + gpr_free(value); + } +} + +static void init_thread_id_key(void) { + pthread_key_create(&thread_id_key, delete_thread_id); +} + +unsigned gpr_cpu_current_cpu(void) { + /* NOTE: there's no way I know to return the actual cpu index portably... + most code that's using this is using it to shard across work queues though, + so here we use thread identity instead to achieve a similar though not + identical effect */ + static gpr_once once = GPR_ONCE_INIT; + gpr_once_init(&once, init_thread_id_key); + + unsigned int* thread_id = + static_cast(pthread_getspecific(thread_id_key)); + if (thread_id == nullptr) { + thread_id = static_cast(gpr_malloc(sizeof(unsigned int))); + pthread_setspecific(thread_id_key, thread_id); + } + + return (unsigned)GPR_HASH_POINTER(thread_id, gpr_cpu_num_cores()); +} + +#endif /* GPR_CPU_POSIX */ diff --git a/src/core/lib/gpr/cpu_windows.cc b/src/core/lib/gpr/cpu_windows.cc new file mode 100644 index 0000000000..8d89453403 --- /dev/null +++ b/src/core/lib/gpr/cpu_windows.cc @@ -0,0 +1,33 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#ifdef GPR_WINDOWS +#include +#include + +unsigned gpr_cpu_num_cores(void) { + SYSTEM_INFO si; + GetSystemInfo(&si); + return si.dwNumberOfProcessors; +} + +unsigned gpr_cpu_current_cpu(void) { return GetCurrentProcessorNumber(); } + +#endif /* GPR_WINDOWS */ diff --git a/src/core/lib/gpr/env.h b/src/core/lib/gpr/env.h new file mode 100644 index 0000000000..7f35104be3 --- /dev/null +++ b/src/core/lib/gpr/env.h @@ -0,0 +1,41 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_GPR_ENV_H +#define GRPC_CORE_LIB_GPR_ENV_H + +#include + +/* Env utility functions */ + +/* Gets the environment variable value with the specified name. + Returns a newly allocated string. It is the responsability of the caller to + gpr_free the return value if not NULL (which means that the environment + variable exists). */ +char* gpr_getenv(const char* name); + +/* Sets the the environment with the specified name to the specified value. */ +void gpr_setenv(const char* name, const char* value); + +/* This is a version of gpr_getenv that does not produce any output if it has to + use an insecure version of the function. It is ONLY to be used to solve the + problem in which we need to check an env variable to configure the verbosity + level of logging. So DO NOT USE THIS. */ +const char* gpr_getenv_silent(const char* name, char** dst); + +#endif /* GRPC_CORE_LIB_GPR_ENV_H */ diff --git a/src/core/lib/gpr/env_linux.cc b/src/core/lib/gpr/env_linux.cc new file mode 100644 index 0000000000..17902c3d0b --- /dev/null +++ b/src/core/lib/gpr/env_linux.cc @@ -0,0 +1,82 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* for secure_getenv. */ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include + +#ifdef GPR_LINUX_ENV + +#include "src/core/lib/gpr/env.h" + +#include +#include +#include +#include + +#include +#include +#include + +#include "src/core/lib/gpr/string.h" + +const char* gpr_getenv_silent(const char* name, char** dst) { + const char* insecure_func_used = nullptr; + char* result = nullptr; +#if defined(GPR_BACKWARDS_COMPATIBILITY_MODE) + typedef char* (*getenv_type)(const char*); + static getenv_type getenv_func = NULL; + /* Check to see which getenv variant is supported (go from most + * to least secure) */ + const char* names[] = {"secure_getenv", "__secure_getenv", "getenv"}; + for (size_t i = 0; getenv_func == NULL && i < GPR_ARRAY_SIZE(names); i++) { + getenv_func = (getenv_type)dlsym(RTLD_DEFAULT, names[i]); + if (getenv_func != NULL && strstr(names[i], "secure") == NULL) { + insecure_func_used = names[i]; + } + } + result = getenv_func(name); +#elif __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 17) + result = secure_getenv(name); +#else + result = getenv(name); + insecure_func_used = "getenv"; +#endif + *dst = result == nullptr ? result : gpr_strdup(result); + return insecure_func_used; +} + +char* gpr_getenv(const char* name) { + char* result = nullptr; + const char* insecure_func_used = gpr_getenv_silent(name, &result); + if (insecure_func_used != nullptr) { + gpr_log(GPR_DEBUG, "Warning: insecure environment read function '%s' used", + insecure_func_used); + } + return result; +} + +void gpr_setenv(const char* name, const char* value) { + int res = setenv(name, value, 1); + GPR_ASSERT(res == 0); +} + +#endif /* GPR_LINUX_ENV */ diff --git a/src/core/lib/gpr/env_posix.cc b/src/core/lib/gpr/env_posix.cc new file mode 100644 index 0000000000..599f85aa72 --- /dev/null +++ b/src/core/lib/gpr/env_posix.cc @@ -0,0 +1,47 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#ifdef GPR_POSIX_ENV + +#include "src/core/lib/gpr/env.h" + +#include + +#include + +#include +#include "src/core/lib/gpr/string.h" + +const char* gpr_getenv_silent(const char* name, char** dst) { + *dst = gpr_getenv(name); + return nullptr; +} + +char* gpr_getenv(const char* name) { + char* result = getenv(name); + return result == nullptr ? result : gpr_strdup(result); +} + +void gpr_setenv(const char* name, const char* value) { + int res = setenv(name, value, 1); + GPR_ASSERT(res == 0); +} + +#endif /* GPR_POSIX_ENV */ diff --git a/src/core/lib/gpr/env_windows.cc b/src/core/lib/gpr/env_windows.cc new file mode 100644 index 0000000000..9ca1e02d03 --- /dev/null +++ b/src/core/lib/gpr/env_windows.cc @@ -0,0 +1,72 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#ifdef GPR_WINDOWS_ENV + +#include + +#include "src/core/lib/gpr/env.h" +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/gpr/string_windows.h" + +#include +#include +#include + +const char* gpr_getenv_silent(const char* name, char** dst) { + *dst = gpr_getenv(name); + return NULL; +} + +char* gpr_getenv(const char* name) { + char* result = NULL; + DWORD size; + LPTSTR tresult = NULL; + LPTSTR tname = gpr_char_to_tchar(name); + DWORD ret; + + ret = GetEnvironmentVariable(tname, NULL, 0); + if (ret == 0) { + gpr_free(tname); + return NULL; + } + size = ret * (DWORD)sizeof(TCHAR); + tresult = (LPTSTR)gpr_malloc(size); + ret = GetEnvironmentVariable(tname, tresult, size); + gpr_free(tname); + if (ret == 0) { + gpr_free(tresult); + return NULL; + } + result = gpr_tchar_to_char(tresult); + gpr_free(tresult); + return result; +} + +void gpr_setenv(const char* name, const char* value) { + LPTSTR tname = gpr_char_to_tchar(name); + LPTSTR tvalue = gpr_char_to_tchar(value); + BOOL res = SetEnvironmentVariable(tname, tvalue); + gpr_free(tname); + gpr_free(tvalue); + GPR_ASSERT(res); +} + +#endif /* GPR_WINDOWS_ENV */ diff --git a/src/core/lib/gpr/fork.cc b/src/core/lib/gpr/fork.cc new file mode 100644 index 0000000000..c47e686378 --- /dev/null +++ b/src/core/lib/gpr/fork.cc @@ -0,0 +1,62 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/gpr/fork.h" + +#include + +#include +#include + +#include "src/core/lib/gpr/env.h" + +/* + * NOTE: FORKING IS NOT GENERALLY SUPPORTED, THIS IS ONLY INTENDED TO WORK + * AROUND VERY SPECIFIC USE CASES. + */ + +static int override_fork_support_enabled = -1; +static int fork_support_enabled; + +void grpc_fork_support_init() { +#ifdef GRPC_ENABLE_FORK_SUPPORT + fork_support_enabled = 1; +#else + fork_support_enabled = 0; + char* env = gpr_getenv("GRPC_ENABLE_FORK_SUPPORT"); + if (env != nullptr) { + static const char* truthy[] = {"yes", "Yes", "YES", "true", + "True", "TRUE", "1"}; + for (size_t i = 0; i < GPR_ARRAY_SIZE(truthy); i++) { + if (0 == strcmp(env, truthy[i])) { + fork_support_enabled = 1; + } + } + gpr_free(env); + } +#endif + if (override_fork_support_enabled != -1) { + fork_support_enabled = override_fork_support_enabled; + } +} + +int grpc_fork_support_enabled() { return fork_support_enabled; } + +void grpc_enable_fork_support(int enable) { + override_fork_support_enabled = enable; +} diff --git a/src/core/lib/gpr/fork.h b/src/core/lib/gpr/fork.h new file mode 100644 index 0000000000..94c61bb836 --- /dev/null +++ b/src/core/lib/gpr/fork.h @@ -0,0 +1,35 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_GPR_FORK_H +#define GRPC_CORE_LIB_GPR_FORK_H + +/* + * NOTE: FORKING IS NOT GENERALLY SUPPORTED, THIS IS ONLY INTENDED TO WORK + * AROUND VERY SPECIFIC USE CASES. + */ + +void grpc_fork_support_init(void); + +int grpc_fork_support_enabled(void); + +// Test only: Must be called before grpc_init(), and overrides +// environment variables/compile flags +void grpc_enable_fork_support(int enable); + +#endif /* GRPC_CORE_LIB_GPR_FORK_H */ diff --git a/src/core/lib/gpr/host_port.cc b/src/core/lib/gpr/host_port.cc new file mode 100644 index 0000000000..29178279d3 --- /dev/null +++ b/src/core/lib/gpr/host_port.cc @@ -0,0 +1,95 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +#include +#include +#include +#include "src/core/lib/gpr/string.h" + +int gpr_join_host_port(char** out, const char* host, int port) { + if (host[0] != '[' && strchr(host, ':') != nullptr) { + /* IPv6 literals must be enclosed in brackets. */ + return gpr_asprintf(out, "[%s]:%d", host, port); + } else { + /* Ordinary non-bracketed host:port. */ + return gpr_asprintf(out, "%s:%d", host, port); + } +} + +int gpr_split_host_port(const char* name, char** host, char** port) { + const char* host_start; + size_t host_len; + const char* port_start; + + *host = nullptr; + *port = nullptr; + + if (name[0] == '[') { + /* Parse a bracketed host, typically an IPv6 literal. */ + const char* rbracket = strchr(name, ']'); + if (rbracket == nullptr) { + /* Unmatched [ */ + return 0; + } + if (rbracket[1] == '\0') { + /* ] */ + port_start = nullptr; + } else if (rbracket[1] == ':') { + /* ]: */ + port_start = rbracket + 2; + } else { + /* ] */ + return 0; + } + host_start = name + 1; + host_len = (size_t)(rbracket - host_start); + if (memchr(host_start, ':', host_len) == nullptr) { + /* Require all bracketed hosts to contain a colon, because a hostname or + IPv4 address should never use brackets. */ + return 0; + } + } else { + const char* colon = strchr(name, ':'); + if (colon != nullptr && strchr(colon + 1, ':') == nullptr) { + /* Exactly 1 colon. Split into host:port. */ + host_start = name; + host_len = (size_t)(colon - name); + port_start = colon + 1; + } else { + /* 0 or 2+ colons. Bare hostname or IPv6 litearal. */ + host_start = name; + host_len = strlen(name); + port_start = nullptr; + } + } + + /* Allocate return values. */ + *host = (char*)gpr_malloc(host_len + 1); + memcpy(*host, host_start, host_len); + (*host)[host_len] = '\0'; + + if (port_start != nullptr) { + *port = gpr_strdup(port_start); + } + + return 1; +} diff --git a/src/core/lib/gpr/log.cc b/src/core/lib/gpr/log.cc new file mode 100644 index 0000000000..19c0f6c34d --- /dev/null +++ b/src/core/lib/gpr/log.cc @@ -0,0 +1,94 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include +#include + +#include "src/core/lib/gpr/env.h" +#include "src/core/lib/gpr/string.h" + +#include +#include + +void gpr_default_log(gpr_log_func_args* args); +static gpr_atm g_log_func = (gpr_atm)gpr_default_log; +static gpr_atm g_min_severity_to_print = GPR_LOG_VERBOSITY_UNSET; + +const char* gpr_log_severity_string(gpr_log_severity severity) { + switch (severity) { + case GPR_LOG_SEVERITY_DEBUG: + return "D"; + case GPR_LOG_SEVERITY_INFO: + return "I"; + case GPR_LOG_SEVERITY_ERROR: + return "E"; + } + GPR_UNREACHABLE_CODE(return "UNKNOWN"); +} + +void gpr_log_message(const char* file, int line, gpr_log_severity severity, + const char* message) { + if ((gpr_atm)severity < gpr_atm_no_barrier_load(&g_min_severity_to_print)) { + return; + } + + gpr_log_func_args lfargs; + memset(&lfargs, 0, sizeof(lfargs)); + lfargs.file = file; + lfargs.line = line; + lfargs.severity = severity; + lfargs.message = message; + ((gpr_log_func)gpr_atm_no_barrier_load(&g_log_func))(&lfargs); +} + +void gpr_set_log_verbosity(gpr_log_severity min_severity_to_print) { + gpr_atm_no_barrier_store(&g_min_severity_to_print, + (gpr_atm)min_severity_to_print); +} + +void gpr_log_verbosity_init() { + char* verbosity = nullptr; + const char* insecure_getenv = gpr_getenv_silent("GRPC_VERBOSITY", &verbosity); + + gpr_atm min_severity_to_print = GPR_LOG_SEVERITY_ERROR; + if (verbosity != nullptr) { + if (gpr_stricmp(verbosity, "DEBUG") == 0) { + min_severity_to_print = (gpr_atm)GPR_LOG_SEVERITY_DEBUG; + } else if (gpr_stricmp(verbosity, "INFO") == 0) { + min_severity_to_print = (gpr_atm)GPR_LOG_SEVERITY_INFO; + } else if (gpr_stricmp(verbosity, "ERROR") == 0) { + min_severity_to_print = (gpr_atm)GPR_LOG_SEVERITY_ERROR; + } + gpr_free(verbosity); + } + if ((gpr_atm_no_barrier_load(&g_min_severity_to_print)) == + GPR_LOG_VERBOSITY_UNSET) { + gpr_atm_no_barrier_store(&g_min_severity_to_print, min_severity_to_print); + } + + if (insecure_getenv != nullptr) { + gpr_log(GPR_DEBUG, "Warning: insecure environment read function '%s' used", + insecure_getenv); + } +} + +void gpr_set_log_function(gpr_log_func f) { + gpr_atm_no_barrier_store(&g_log_func, (gpr_atm)(f ? f : gpr_default_log)); +} diff --git a/src/core/lib/gpr/log_android.cc b/src/core/lib/gpr/log_android.cc new file mode 100644 index 0000000000..0d3ac0fe52 --- /dev/null +++ b/src/core/lib/gpr/log_android.cc @@ -0,0 +1,72 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#ifdef GPR_ANDROID + +#include +#include +#include +#include +#include +#include + +static android_LogPriority severity_to_log_priority(gpr_log_severity severity) { + switch (severity) { + case GPR_LOG_SEVERITY_DEBUG: + return ANDROID_LOG_DEBUG; + case GPR_LOG_SEVERITY_INFO: + return ANDROID_LOG_INFO; + case GPR_LOG_SEVERITY_ERROR: + return ANDROID_LOG_ERROR; + } + return ANDROID_LOG_DEFAULT; +} + +void gpr_log(const char* file, int line, gpr_log_severity severity, + const char* format, ...) { + char* message = NULL; + va_list args; + va_start(args, format); + vasprintf(&message, format, args); + va_end(args); + gpr_log_message(file, line, severity, message); + free(message); +} + +void gpr_default_log(gpr_log_func_args* args) { + const char* final_slash; + const char* display_file; + char* output = NULL; + + final_slash = strrchr(args->file, '/'); + if (final_slash == NULL) + display_file = args->file; + else + display_file = final_slash + 1; + + asprintf(&output, "%s:%d] %s", display_file, args->line, args->message); + + __android_log_write(severity_to_log_priority(args->severity), "GRPC", output); + + /* allocated by asprintf => use free, not gpr_free */ + free(output); +} + +#endif /* GPR_ANDROID */ diff --git a/src/core/lib/gpr/log_linux.cc b/src/core/lib/gpr/log_linux.cc new file mode 100644 index 0000000000..6b1f1c71e4 --- /dev/null +++ b/src/core/lib/gpr/log_linux.cc @@ -0,0 +1,92 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef _POSIX_SOURCE +#define _POSIX_SOURCE +#endif + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include + +#ifdef GPR_LINUX_LOG + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static long gettid(void) { return syscall(__NR_gettid); } + +void gpr_log(const char* file, int line, gpr_log_severity severity, + const char* format, ...) { + char* message = nullptr; + va_list args; + va_start(args, format); + if (vasprintf(&message, format, args) == -1) { + va_end(args); + return; + } + va_end(args); + gpr_log_message(file, line, severity, message); + /* message has been allocated by vasprintf above, and needs free */ + free(message); +} + +void gpr_default_log(gpr_log_func_args* args) { + const char* final_slash; + char* prefix; + const char* display_file; + char time_buffer[64]; + time_t timer; + gpr_timespec now = gpr_now(GPR_CLOCK_REALTIME); + struct tm tm; + static __thread long tid = 0; + if (tid == 0) tid = gettid(); + + timer = (time_t)now.tv_sec; + final_slash = strrchr(args->file, '/'); + if (final_slash == nullptr) + display_file = args->file; + else + display_file = final_slash + 1; + + if (!localtime_r(&timer, &tm)) { + strcpy(time_buffer, "error:localtime"); + } else if (0 == + strftime(time_buffer, sizeof(time_buffer), "%m%d %H:%M:%S", &tm)) { + strcpy(time_buffer, "error:strftime"); + } + + gpr_asprintf(&prefix, "%s%s.%09" PRId32 " %7ld %s:%d]", + gpr_log_severity_string(args->severity), time_buffer, + now.tv_nsec, tid, display_file, args->line); + + fprintf(stderr, "%-60s %s\n", prefix, args->message); + gpr_free(prefix); +} + +#endif /* GPR_LINUX_LOG */ diff --git a/src/core/lib/gpr/log_posix.cc b/src/core/lib/gpr/log_posix.cc new file mode 100644 index 0000000000..6f93cdefcd --- /dev/null +++ b/src/core/lib/gpr/log_posix.cc @@ -0,0 +1,90 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#ifdef GPR_POSIX_LOG + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static intptr_t gettid(void) { return (intptr_t)pthread_self(); } + +void gpr_log(const char* file, int line, gpr_log_severity severity, + const char* format, ...) { + char buf[64]; + char* allocated = nullptr; + char* message = nullptr; + int ret; + va_list args; + va_start(args, format); + ret = vsnprintf(buf, sizeof(buf), format, args); + va_end(args); + if (ret < 0) { + message = nullptr; + } else if ((size_t)ret <= sizeof(buf) - 1) { + message = buf; + } else { + message = allocated = (char*)gpr_malloc((size_t)ret + 1); + va_start(args, format); + vsnprintf(message, (size_t)(ret + 1), format, args); + va_end(args); + } + gpr_log_message(file, line, severity, message); + gpr_free(allocated); +} + +void gpr_default_log(gpr_log_func_args* args) { + const char* final_slash; + const char* display_file; + char time_buffer[64]; + time_t timer; + gpr_timespec now = gpr_now(GPR_CLOCK_REALTIME); + struct tm tm; + + timer = (time_t)now.tv_sec; + final_slash = strrchr(args->file, '/'); + if (final_slash == nullptr) + display_file = args->file; + else + display_file = final_slash + 1; + + if (!localtime_r(&timer, &tm)) { + strcpy(time_buffer, "error:localtime"); + } else if (0 == + strftime(time_buffer, sizeof(time_buffer), "%m%d %H:%M:%S", &tm)) { + strcpy(time_buffer, "error:strftime"); + } + + char* prefix; + gpr_asprintf(&prefix, "%s%s.%09d %7tu %s:%d]", + gpr_log_severity_string(args->severity), time_buffer, + (int)(now.tv_nsec), gettid(), display_file, args->line); + + fprintf(stderr, "%-70s %s\n", prefix, args->message); + gpr_free(prefix); +} + +#endif /* defined(GPR_POSIX_LOG) */ diff --git a/src/core/lib/gpr/log_windows.cc b/src/core/lib/gpr/log_windows.cc new file mode 100644 index 0000000000..caaa973f5a --- /dev/null +++ b/src/core/lib/gpr/log_windows.cc @@ -0,0 +1,97 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#ifdef GPR_WINDOWS_LOG + +#include +#include + +#include +#include +#include +#include +#include + +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/gpr/string_windows.h" + +void gpr_log(const char* file, int line, gpr_log_severity severity, + const char* format, ...) { + char* message = NULL; + va_list args; + int ret; + + /* Determine the length. */ + va_start(args, format); + ret = _vscprintf(format, args); + va_end(args); + if (ret < 0) { + message = NULL; + } else { + /* Allocate a new buffer, with space for the NUL terminator. */ + size_t strp_buflen = (size_t)ret + 1; + message = (char*)gpr_malloc(strp_buflen); + + /* Print to the buffer. */ + va_start(args, format); + ret = vsnprintf_s(message, strp_buflen, _TRUNCATE, format, args); + va_end(args); + if ((size_t)ret != strp_buflen - 1) { + /* This should never happen. */ + gpr_free(message); + message = NULL; + } + } + + gpr_log_message(file, line, severity, message); + gpr_free(message); +} + +/* Simple starter implementation */ +void gpr_default_log(gpr_log_func_args* args) { + const char* final_slash; + const char* display_file; + char time_buffer[64]; + time_t timer; + gpr_timespec now = gpr_now(GPR_CLOCK_REALTIME); + struct tm tm; + + timer = (time_t)now.tv_sec; + final_slash = strrchr(args->file, '\\'); + if (final_slash == NULL) + display_file = args->file; + else + display_file = final_slash + 1; + + if (localtime_s(&tm, &timer)) { + strcpy(time_buffer, "error:localtime"); + } else if (0 == + strftime(time_buffer, sizeof(time_buffer), "%m%d %H:%M:%S", &tm)) { + strcpy(time_buffer, "error:strftime"); + } + + fprintf(stderr, "%s%s.%09u %5lu %s:%d] %s\n", + gpr_log_severity_string(args->severity), time_buffer, + (int)(now.tv_nsec), GetCurrentThreadId(), display_file, args->line, + args->message); + fflush(stderr); +} + +#endif /* GPR_WINDOWS_LOG */ diff --git a/src/core/lib/gpr/mpscq.cc b/src/core/lib/gpr/mpscq.cc new file mode 100644 index 0000000000..34fc050a11 --- /dev/null +++ b/src/core/lib/gpr/mpscq.cc @@ -0,0 +1,114 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/gpr/mpscq.h" + +#include + +void gpr_mpscq_init(gpr_mpscq* q) { + gpr_atm_no_barrier_store(&q->head, (gpr_atm)&q->stub); + q->tail = &q->stub; + gpr_atm_no_barrier_store(&q->stub.next, (gpr_atm)NULL); +} + +void gpr_mpscq_destroy(gpr_mpscq* q) { + GPR_ASSERT(gpr_atm_no_barrier_load(&q->head) == (gpr_atm)&q->stub); + GPR_ASSERT(q->tail == &q->stub); +} + +bool gpr_mpscq_push(gpr_mpscq* q, gpr_mpscq_node* n) { + gpr_atm_no_barrier_store(&n->next, (gpr_atm)NULL); + gpr_mpscq_node* prev = + (gpr_mpscq_node*)gpr_atm_full_xchg(&q->head, (gpr_atm)n); + gpr_atm_rel_store(&prev->next, (gpr_atm)n); + return prev == &q->stub; +} + +gpr_mpscq_node* gpr_mpscq_pop(gpr_mpscq* q) { + bool empty; + return gpr_mpscq_pop_and_check_end(q, &empty); +} + +gpr_mpscq_node* gpr_mpscq_pop_and_check_end(gpr_mpscq* q, bool* empty) { + gpr_mpscq_node* tail = q->tail; + gpr_mpscq_node* next = (gpr_mpscq_node*)gpr_atm_acq_load(&tail->next); + if (tail == &q->stub) { + // indicates the list is actually (ephemerally) empty + if (next == nullptr) { + *empty = true; + return nullptr; + } + q->tail = next; + tail = next; + next = (gpr_mpscq_node*)gpr_atm_acq_load(&tail->next); + } + if (next != nullptr) { + *empty = false; + q->tail = next; + return tail; + } + gpr_mpscq_node* head = (gpr_mpscq_node*)gpr_atm_acq_load(&q->head); + if (tail != head) { + *empty = false; + // indicates a retry is in order: we're still adding + return nullptr; + } + gpr_mpscq_push(q, &q->stub); + next = (gpr_mpscq_node*)gpr_atm_acq_load(&tail->next); + if (next != nullptr) { + q->tail = next; + return tail; + } + // indicates a retry is in order: we're still adding + *empty = false; + return nullptr; +} + +void gpr_locked_mpscq_init(gpr_locked_mpscq* q) { + gpr_mpscq_init(&q->queue); + gpr_mu_init(&q->mu); +} + +void gpr_locked_mpscq_destroy(gpr_locked_mpscq* q) { + gpr_mpscq_destroy(&q->queue); + gpr_mu_destroy(&q->mu); +} + +bool gpr_locked_mpscq_push(gpr_locked_mpscq* q, gpr_mpscq_node* n) { + return gpr_mpscq_push(&q->queue, n); +} + +gpr_mpscq_node* gpr_locked_mpscq_try_pop(gpr_locked_mpscq* q) { + if (gpr_mu_trylock(&q->mu)) { + gpr_mpscq_node* n = gpr_mpscq_pop(&q->queue); + gpr_mu_unlock(&q->mu); + return n; + } + return nullptr; +} + +gpr_mpscq_node* gpr_locked_mpscq_pop(gpr_locked_mpscq* q) { + gpr_mu_lock(&q->mu); + bool empty = false; + gpr_mpscq_node* n; + do { + n = gpr_mpscq_pop_and_check_end(&q->queue, &empty); + } while (n == nullptr && !empty); + gpr_mu_unlock(&q->mu); + return n; +} diff --git a/src/core/lib/gpr/mpscq.h b/src/core/lib/gpr/mpscq.h new file mode 100644 index 0000000000..4409c5c9f5 --- /dev/null +++ b/src/core/lib/gpr/mpscq.h @@ -0,0 +1,84 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_GPR_MPSCQ_H +#define GRPC_CORE_LIB_GPR_MPSCQ_H + +#include +#include +#include +#include + +// Multiple-producer single-consumer lock free queue, based upon the +// implementation from Dmitry Vyukov here: +// http://www.1024cores.net/home/lock-free-algorithms/queues/intrusive-mpsc-node-based-queue + +// List node (include this in a data structure at the top, and add application +// fields after it - to simulate inheritance) +typedef struct gpr_mpscq_node { + gpr_atm next; +} gpr_mpscq_node; + +// Actual queue type +typedef struct gpr_mpscq { + gpr_atm head; + // make sure head & tail don't share a cacheline + char padding[GPR_CACHELINE_SIZE]; + gpr_mpscq_node* tail; + gpr_mpscq_node stub; +} gpr_mpscq; + +void gpr_mpscq_init(gpr_mpscq* q); +void gpr_mpscq_destroy(gpr_mpscq* q); +// Push a node +// Thread safe - can be called from multiple threads concurrently +// Returns true if this was possibly the first node (may return true +// sporadically, will not return false sporadically) +bool gpr_mpscq_push(gpr_mpscq* q, gpr_mpscq_node* n); +// Pop a node (returns NULL if no node is ready - which doesn't indicate that +// the queue is empty!!) +// Thread compatible - can only be called from one thread at a time +gpr_mpscq_node* gpr_mpscq_pop(gpr_mpscq* q); +// Pop a node; sets *empty to true if the queue is empty, or false if it is not +gpr_mpscq_node* gpr_mpscq_pop_and_check_end(gpr_mpscq* q, bool* empty); + +// An mpscq with a lock: it's safe to pop from multiple threads, but doing +// only one thread will succeed concurrently +typedef struct gpr_locked_mpscq { + gpr_mpscq queue; + gpr_mu mu; +} gpr_locked_mpscq; + +void gpr_locked_mpscq_init(gpr_locked_mpscq* q); +void gpr_locked_mpscq_destroy(gpr_locked_mpscq* q); +// Push a node +// Thread safe - can be called from multiple threads concurrently +// Returns true if this was possibly the first node (may return true +// sporadically, will not return false sporadically) +bool gpr_locked_mpscq_push(gpr_locked_mpscq* q, gpr_mpscq_node* n); + +// Pop a node (returns NULL if no node is ready - which doesn't indicate that +// the queue is empty!!) +// Thread safe - can be called from multiple threads concurrently +gpr_mpscq_node* gpr_locked_mpscq_try_pop(gpr_locked_mpscq* q); + +// Pop a node. Returns NULL only if the queue was empty at some point after +// calling this function +gpr_mpscq_node* gpr_locked_mpscq_pop(gpr_locked_mpscq* q); + +#endif /* GRPC_CORE_LIB_GPR_MPSCQ_H */ diff --git a/src/core/lib/gpr/murmur_hash.cc b/src/core/lib/gpr/murmur_hash.cc new file mode 100644 index 0000000000..3f5e04d211 --- /dev/null +++ b/src/core/lib/gpr/murmur_hash.cc @@ -0,0 +1,78 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/gpr/murmur_hash.h" + +#include + +#define ROTL32(x, r) ((x) << (r)) | ((x) >> (32 - (r))) + +#define FMIX32(h) \ + (h) ^= (h) >> 16; \ + (h) *= 0x85ebca6b; \ + (h) ^= (h) >> 13; \ + (h) *= 0xc2b2ae35; \ + (h) ^= (h) >> 16; + +uint32_t gpr_murmur_hash3(const void* key, size_t len, uint32_t seed) { + uint32_t h1 = seed; + uint32_t k1; + + const uint32_t c1 = 0xcc9e2d51; + const uint32_t c2 = 0x1b873593; + + const uint8_t* keyptr = (const uint8_t*)key; + const size_t bsize = sizeof(k1); + const size_t nblocks = len / bsize; + + /* body */ + for (size_t i = 0; i < nblocks; i++, keyptr += bsize) { + memcpy(&k1, keyptr, bsize); + + k1 *= c1; + k1 = ROTL32(k1, 15); + k1 *= c2; + + h1 ^= k1; + h1 = ROTL32(h1, 13); + h1 = h1 * 5 + 0xe6546b64; + } + + k1 = 0; + + /* tail */ + switch (len & 3) { + case 3: + k1 ^= ((uint32_t)keyptr[2]) << 16; + /* fallthrough */ + case 2: + k1 ^= ((uint32_t)keyptr[1]) << 8; + /* fallthrough */ + case 1: + k1 ^= keyptr[0]; + k1 *= c1; + k1 = ROTL32(k1, 15); + k1 *= c2; + h1 ^= k1; + }; + + /* finalization */ + h1 ^= (uint32_t)len; + FMIX32(h1); + return h1; +} diff --git a/src/core/lib/gpr/murmur_hash.h b/src/core/lib/gpr/murmur_hash.h new file mode 100644 index 0000000000..8004889a9a --- /dev/null +++ b/src/core/lib/gpr/murmur_hash.h @@ -0,0 +1,29 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_GPR_MURMUR_HASH_H +#define GRPC_CORE_LIB_GPR_MURMUR_HASH_H + +#include + +#include + +/* compute the hash of key (length len) */ +uint32_t gpr_murmur_hash3(const void* key, size_t len, uint32_t seed); + +#endif /* GRPC_CORE_LIB_GPR_MURMUR_HASH_H */ diff --git a/src/core/lib/gpr/spinlock.h b/src/core/lib/gpr/spinlock.h new file mode 100644 index 0000000000..f03be1d791 --- /dev/null +++ b/src/core/lib/gpr/spinlock.h @@ -0,0 +1,44 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_GPR_SPINLOCK_H +#define GRPC_CORE_LIB_GPR_SPINLOCK_H + +#include + +/* Simple spinlock. No backoff strategy, gpr_spinlock_lock is almost always + a concurrency code smell. */ +typedef struct { + gpr_atm atm; +} gpr_spinlock; + +#ifdef __cplusplus +#define GPR_SPINLOCK_INITIALIZER (gpr_spinlock{0}) +#else +#define GPR_SPINLOCK_INITIALIZER ((gpr_spinlock){0}) +#endif +#define GPR_SPINLOCK_STATIC_INITIALIZER \ + { 0 } + +#define gpr_spinlock_trylock(lock) (gpr_atm_acq_cas(&(lock)->atm, 0, 1)) +#define gpr_spinlock_unlock(lock) (gpr_atm_rel_store(&(lock)->atm, 0)) +#define gpr_spinlock_lock(lock) \ + do { \ + } while (!gpr_spinlock_trylock((lock))) + +#endif /* GRPC_CORE_LIB_GPR_SPINLOCK_H */ diff --git a/src/core/lib/gpr/string.cc b/src/core/lib/gpr/string.cc new file mode 100644 index 0000000000..919d957d1b --- /dev/null +++ b/src/core/lib/gpr/string.cc @@ -0,0 +1,315 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/gpr/string.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +char* gpr_strdup(const char* src) { + char* dst; + size_t len; + + if (!src) { + return nullptr; + } + + len = strlen(src) + 1; + dst = (char*)gpr_malloc(len); + + memcpy(dst, src, len); + + return dst; +} + +typedef struct { + size_t capacity; + size_t length; + char* data; +} dump_out; + +static dump_out dump_out_create(void) { + dump_out r = {0, 0, nullptr}; + return r; +} + +static void dump_out_append(dump_out* out, char c) { + if (out->length == out->capacity) { + out->capacity = GPR_MAX(8, 2 * out->capacity); + out->data = (char*)gpr_realloc(out->data, out->capacity); + } + out->data[out->length++] = c; +} + +static void hexdump(dump_out* out, const char* buf, size_t len) { + static const char* hex = "0123456789abcdef"; + + const uint8_t* const beg = (const uint8_t*)buf; + const uint8_t* const end = beg + len; + const uint8_t* cur; + + for (cur = beg; cur != end; ++cur) { + if (cur != beg) dump_out_append(out, ' '); + dump_out_append(out, hex[*cur >> 4]); + dump_out_append(out, hex[*cur & 0xf]); + } +} + +static void asciidump(dump_out* out, const char* buf, size_t len) { + const uint8_t* const beg = (const uint8_t*)buf; + const uint8_t* const end = beg + len; + const uint8_t* cur; + int out_was_empty = (out->length == 0); + if (!out_was_empty) { + dump_out_append(out, ' '); + dump_out_append(out, '\''); + } + for (cur = beg; cur != end; ++cur) { + dump_out_append(out, (char)(isprint(*cur) ? *(char*)cur : '.')); + } + if (!out_was_empty) { + dump_out_append(out, '\''); + } +} + +char* gpr_dump(const char* buf, size_t len, uint32_t flags) { + dump_out out = dump_out_create(); + if (flags & GPR_DUMP_HEX) { + hexdump(&out, buf, len); + } + if (flags & GPR_DUMP_ASCII) { + asciidump(&out, buf, len); + } + dump_out_append(&out, 0); + return out.data; +} + +int gpr_parse_bytes_to_uint32(const char* buf, size_t len, uint32_t* result) { + uint32_t out = 0; + uint32_t new_val; + size_t i; + + if (len == 0) return 0; /* must have some bytes */ + + for (i = 0; i < len; i++) { + if (buf[i] < '0' || buf[i] > '9') return 0; /* bad char */ + new_val = 10 * out + (uint32_t)(buf[i] - '0'); + if (new_val < out) return 0; /* overflow */ + out = new_val; + } + + *result = out; + return 1; +} + +void gpr_reverse_bytes(char* str, int len) { + char *p1, *p2; + for (p1 = str, p2 = str + len - 1; p2 > p1; ++p1, --p2) { + char temp = *p1; + *p1 = *p2; + *p2 = temp; + } +} + +int gpr_ltoa(long value, char* string) { + long sign; + int i = 0; + + if (value == 0) { + string[0] = '0'; + string[1] = 0; + return 1; + } + + sign = value < 0 ? -1 : 1; + while (value) { + string[i++] = (char)('0' + sign * (value % 10)); + value /= 10; + } + if (sign < 0) string[i++] = '-'; + gpr_reverse_bytes(string, i); + string[i] = 0; + return i; +} + +int int64_ttoa(int64_t value, char* string) { + int64_t sign; + int i = 0; + + if (value == 0) { + string[0] = '0'; + string[1] = 0; + return 1; + } + + sign = value < 0 ? -1 : 1; + while (value) { + string[i++] = (char)('0' + sign * (value % 10)); + value /= 10; + } + if (sign < 0) string[i++] = '-'; + gpr_reverse_bytes(string, i); + string[i] = 0; + return i; +} + +int gpr_parse_nonnegative_int(const char* value) { + char* end; + long result = strtol(value, &end, 0); + if (*end != '\0' || result < 0 || result > INT_MAX) return -1; + return (int)result; +} + +char* gpr_leftpad(const char* str, char flag, size_t length) { + const size_t str_length = strlen(str); + const size_t out_length = str_length > length ? str_length : length; + char* out = (char*)gpr_malloc(out_length + 1); + memset(out, flag, out_length - str_length); + memcpy(out + out_length - str_length, str, str_length); + out[out_length] = 0; + return out; +} + +char* gpr_strjoin(const char** strs, size_t nstrs, size_t* final_length) { + return gpr_strjoin_sep(strs, nstrs, "", final_length); +} + +char* gpr_strjoin_sep(const char** strs, size_t nstrs, const char* sep, + size_t* final_length) { + const size_t sep_len = strlen(sep); + size_t out_length = 0; + size_t i; + char* out; + for (i = 0; i < nstrs; i++) { + out_length += strlen(strs[i]); + } + out_length += 1; /* null terminator */ + if (nstrs > 0) { + out_length += sep_len * (nstrs - 1); /* separators */ + } + out = (char*)gpr_malloc(out_length); + out_length = 0; + for (i = 0; i < nstrs; i++) { + const size_t slen = strlen(strs[i]); + if (i != 0) { + memcpy(out + out_length, sep, sep_len); + out_length += sep_len; + } + memcpy(out + out_length, strs[i], slen); + out_length += slen; + } + out[out_length] = 0; + if (final_length != nullptr) { + *final_length = out_length; + } + return out; +} + +void gpr_strvec_init(gpr_strvec* sv) { memset(sv, 0, sizeof(*sv)); } + +void gpr_strvec_destroy(gpr_strvec* sv) { + size_t i; + for (i = 0; i < sv->count; i++) { + gpr_free(sv->strs[i]); + } + gpr_free(sv->strs); +} + +void gpr_strvec_add(gpr_strvec* sv, char* str) { + if (sv->count == sv->capacity) { + sv->capacity = GPR_MAX(sv->capacity + 8, sv->capacity * 2); + sv->strs = (char**)gpr_realloc(sv->strs, sizeof(char*) * sv->capacity); + } + sv->strs[sv->count++] = str; +} + +char* gpr_strvec_flatten(gpr_strvec* sv, size_t* final_length) { + return gpr_strjoin((const char**)sv->strs, sv->count, final_length); +} + +int gpr_stricmp(const char* a, const char* b) { + int ca, cb; + do { + ca = tolower(*a); + cb = tolower(*b); + ++a; + ++b; + } while (ca == cb && ca && cb); + return ca - cb; +} + +static void add_string_to_split(const char* beg, const char* end, char*** strs, + size_t* nstrs, size_t* capstrs) { + char* out = (char*)gpr_malloc((size_t)(end - beg) + 1); + memcpy(out, beg, (size_t)(end - beg)); + out[end - beg] = 0; + if (*nstrs == *capstrs) { + *capstrs = GPR_MAX(8, 2 * *capstrs); + *strs = (char**)gpr_realloc(*strs, sizeof(*strs) * *capstrs); + } + (*strs)[*nstrs] = out; + ++*nstrs; +} + +void gpr_string_split(const char* input, const char* sep, char*** strs, + size_t* nstrs) { + const char* next; + *strs = nullptr; + *nstrs = 0; + size_t capstrs = 0; + while ((next = strstr(input, sep))) { + add_string_to_split(input, next, strs, nstrs, &capstrs); + input = next + strlen(sep); + } + add_string_to_split(input, input + strlen(input), strs, nstrs, &capstrs); +} + +void* gpr_memrchr(const void* s, int c, size_t n) { + if (s == nullptr) return nullptr; + char* b = (char*)s; + size_t i; + for (i = 0; i < n; i++) { + if (b[n - i - 1] == c) { + return &b[n - i - 1]; + } + } + return nullptr; +} + +bool gpr_is_true(const char* s) { + size_t i; + if (s == nullptr) { + return false; + } + static const char* truthy[] = {"yes", "true", "1"}; + for (i = 0; i < GPR_ARRAY_SIZE(truthy); i++) { + if (0 == gpr_stricmp(s, truthy[i])) { + return true; + } + } + return false; +} diff --git a/src/core/lib/gpr/string.h b/src/core/lib/gpr/string.h new file mode 100644 index 0000000000..ef3a8c6086 --- /dev/null +++ b/src/core/lib/gpr/string.h @@ -0,0 +1,109 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_GPR_STRING_H +#define GRPC_CORE_LIB_GPR_STRING_H + +#include +#include + +#include + +/* String utility functions */ + +/* Flags for gpr_dump function. */ +#define GPR_DUMP_HEX 0x00000001 +#define GPR_DUMP_ASCII 0x00000002 + +/* Converts array buf, of length len, into a C string according to the flags. + Result should be freed with gpr_free() */ +char* gpr_dump(const char* buf, size_t len, uint32_t flags); + +/* Parses an array of bytes into an integer (base 10). Returns 1 on success, + 0 on failure. */ +int gpr_parse_bytes_to_uint32(const char* data, size_t length, + uint32_t* result); + +/* Minimum buffer size for calling ltoa */ +#define GPR_LTOA_MIN_BUFSIZE (3 * sizeof(long)) + +/* Convert a long to a string in base 10; returns the length of the + output string (or 0 on failure). + output must be at least GPR_LTOA_MIN_BUFSIZE bytes long. */ +int gpr_ltoa(long value, char* output); + +/* Minimum buffer size for calling int64toa */ +#define GPR_INT64TOA_MIN_BUFSIZE (3 * sizeof(int64_t)) + +/* Convert an int64 to a string in base 10; returns the length of the +output string (or 0 on failure). +output must be at least GPR_INT64TOA_MIN_BUFSIZE bytes long. +NOTE: This function ensures sufficient bit width even on Win x64, +where long is 32bit is size.*/ +int int64_ttoa(int64_t value, char* output); + +// Parses a non-negative number from a value string. Returns -1 on error. +int gpr_parse_nonnegative_int(const char* value); + +/* Reverse a run of bytes */ +void gpr_reverse_bytes(char* str, int len); + +/* Pad a string with flag characters. The given length specifies the minimum + field width. The input string is never truncated. */ +char* gpr_leftpad(const char* str, char flag, size_t length); + +/* Join a set of strings, returning the resulting string. + Total combined length (excluding null terminator) is returned in total_length + if it is non-null. */ +char* gpr_strjoin(const char** strs, size_t nstrs, size_t* total_length); + +/* Join a set of strings using a separator, returning the resulting string. + Total combined length (excluding null terminator) is returned in total_length + if it is non-null. */ +char* gpr_strjoin_sep(const char** strs, size_t nstrs, const char* sep, + size_t* total_length); + +void gpr_string_split(const char* input, const char* sep, char*** strs, + size_t* nstrs); + +/* A vector of strings... for building up a final string one piece at a time */ +typedef struct { + char** strs; + size_t count; + size_t capacity; +} gpr_strvec; + +/* Initialize/destroy */ +void gpr_strvec_init(gpr_strvec* strs); +void gpr_strvec_destroy(gpr_strvec* strs); +/* Add a string to a strvec, takes ownership of the string */ +void gpr_strvec_add(gpr_strvec* strs, char* add); +/* Return a joined string with all added substrings, optionally setting + total_length as per gpr_strjoin */ +char* gpr_strvec_flatten(gpr_strvec* strs, size_t* total_length); + +/** Case insensitive string comparison... return <0 if lower(a)0 if lower(a)>lower(b) */ +int gpr_stricmp(const char* a, const char* b); + +void* gpr_memrchr(const void* s, int c, size_t n); + +/** Return true if lower(s) equals "true", "yes" or "1", otherwise false. */ +bool gpr_is_true(const char* s); + +#endif /* GRPC_CORE_LIB_GPR_STRING_H */ diff --git a/src/core/lib/gpr/string_posix.cc b/src/core/lib/gpr/string_posix.cc new file mode 100644 index 0000000000..8b818e39b9 --- /dev/null +++ b/src/core/lib/gpr/string_posix.cc @@ -0,0 +1,72 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#ifdef GPR_POSIX_STRING + +#include +#include +#include + +#include +#include + +int gpr_asprintf(char** strp, const char* format, ...) { + va_list args; + int ret; + char buf[64]; + size_t strp_buflen; + + /* Use a constant-sized buffer to determine the length. */ + va_start(args, format); + ret = vsnprintf(buf, sizeof(buf), format, args); + va_end(args); + if (ret < 0) { + *strp = nullptr; + return -1; + } + + /* Allocate a new buffer, with space for the NUL terminator. */ + strp_buflen = (size_t)ret + 1; + if ((*strp = (char*)gpr_malloc(strp_buflen)) == nullptr) { + /* This shouldn't happen, because gpr_malloc() calls abort(). */ + return -1; + } + + /* Return early if we have all the bytes. */ + if (strp_buflen <= sizeof(buf)) { + memcpy(*strp, buf, strp_buflen); + return ret; + } + + /* Try again using the larger buffer. */ + va_start(args, format); + ret = vsnprintf(*strp, strp_buflen, format, args); + va_end(args); + if ((size_t)ret == strp_buflen - 1) { + return ret; + } + + /* This should never happen. */ + gpr_free(*strp); + *strp = nullptr; + return -1; +} + +#endif /* GPR_POSIX_STRING */ diff --git a/src/core/lib/gpr/string_util_windows.cc b/src/core/lib/gpr/string_util_windows.cc new file mode 100644 index 0000000000..8c8c99cd21 --- /dev/null +++ b/src/core/lib/gpr/string_util_windows.cc @@ -0,0 +1,82 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* Posix code for gpr snprintf support. */ + +#include + +#ifdef GPR_WINDOWS + +/* Some platforms (namely msys) need wchar to be included BEFORE + anything else, especially strsafe.h. */ +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/gpr/string_windows.h" + +#if defined UNICODE || defined _UNICODE +LPTSTR +gpr_char_to_tchar(LPCSTR input) { + LPTSTR ret; + int needed = MultiByteToWideChar(CP_UTF8, 0, input, -1, NULL, 0); + if (needed <= 0) return NULL; + ret = (LPTSTR)gpr_malloc((unsigned)needed * sizeof(TCHAR)); + MultiByteToWideChar(CP_UTF8, 0, input, -1, ret, needed); + return ret; +} + +LPSTR +gpr_tchar_to_char(LPCTSTR input) { + LPSTR ret; + int needed = WideCharToMultiByte(CP_UTF8, 0, input, -1, NULL, 0, NULL, NULL); + if (needed <= 0) return NULL; + ret = (LPSTR)gpr_malloc((unsigned)needed); + WideCharToMultiByte(CP_UTF8, 0, input, -1, ret, needed, NULL, NULL); + return ret; +} +#else +LPSTR gpr_tchar_to_char(LPCTSTR input) { return (LPSTR)gpr_strdup(input); } + +LPTSTR gpr_char_to_tchar(LPCTSTR input) { return (LPTSTR)gpr_strdup(input); } +#endif + +char* gpr_format_message(int messageid) { + LPTSTR tmessage; + char* message; + DWORD status = FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, (DWORD)messageid, MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT), + (LPTSTR)(&tmessage), 0, NULL); + if (status == 0) return gpr_strdup("Unable to retrieve error string"); + message = gpr_tchar_to_char(tmessage); + LocalFree(tmessage); + return message; +} + +#endif /* GPR_WINDOWS */ diff --git a/src/core/lib/gpr/string_windows.cc b/src/core/lib/gpr/string_windows.cc new file mode 100644 index 0000000000..25bfd412e4 --- /dev/null +++ b/src/core/lib/gpr/string_windows.cc @@ -0,0 +1,69 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* Windows code for gpr snprintf support. */ + +#include + +#ifdef GPR_WINDOWS_STRING + +#include +#include +#include + +#include +#include + +#include "src/core/lib/gpr/string.h" + +int gpr_asprintf(char** strp, const char* format, ...) { + va_list args; + int ret; + size_t strp_buflen; + + /* Determine the length. */ + va_start(args, format); + ret = _vscprintf(format, args); + va_end(args); + if (ret < 0) { + *strp = NULL; + return -1; + } + + /* Allocate a new buffer, with space for the NUL terminator. */ + strp_buflen = (size_t)ret + 1; + if ((*strp = (char*)gpr_malloc(strp_buflen)) == NULL) { + /* This shouldn't happen, because gpr_malloc() calls abort(). */ + return -1; + } + + /* Print to the buffer. */ + va_start(args, format); + ret = vsnprintf_s(*strp, strp_buflen, _TRUNCATE, format, args); + va_end(args); + if ((size_t)ret == strp_buflen - 1) { + return ret; + } + + /* This should never happen. */ + gpr_free(*strp); + *strp = NULL; + return -1; +} + +#endif /* GPR_WINDOWS_STRING */ diff --git a/src/core/lib/gpr/string_windows.h b/src/core/lib/gpr/string_windows.h new file mode 100644 index 0000000000..e370f802cf --- /dev/null +++ b/src/core/lib/gpr/string_windows.h @@ -0,0 +1,32 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_GPR_STRING_WINDOWS_H +#define GRPC_CORE_LIB_GPR_STRING_WINDOWS_H + +#include + +#ifdef GPR_WINDOWS + +/* These allocate new strings using gpr_malloc to convert from and to utf-8. */ +LPTSTR gpr_char_to_tchar(LPCSTR input); +LPSTR gpr_tchar_to_char(LPCTSTR input); + +#endif /* GPR_WINDOWS */ + +#endif /* GRPC_CORE_LIB_GPR_STRING_WINDOWS_H */ diff --git a/src/core/lib/gpr/subprocess_posix.cc b/src/core/lib/gpr/subprocess_posix.cc new file mode 100644 index 0000000000..dc046b6499 --- /dev/null +++ b/src/core/lib/gpr/subprocess_posix.cc @@ -0,0 +1,99 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#ifdef GPR_POSIX_SUBPROCESS + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +struct gpr_subprocess { + int pid; + bool joined; +}; + +const char* gpr_subprocess_binary_extension() { return ""; } + +gpr_subprocess* gpr_subprocess_create(int argc, const char** argv) { + gpr_subprocess* r; + int pid; + char** exec_args; + + pid = fork(); + if (pid == -1) { + return nullptr; + } else if (pid == 0) { + exec_args = (char**)gpr_malloc(((size_t)argc + 1) * sizeof(char*)); + memcpy(exec_args, argv, (size_t)argc * sizeof(char*)); + exec_args[argc] = nullptr; + execv(exec_args[0], exec_args); + /* if we reach here, an error has occurred */ + gpr_log(GPR_ERROR, "execv '%s' failed: %s", exec_args[0], strerror(errno)); + _exit(1); + return nullptr; + } else { + r = (gpr_subprocess*)gpr_zalloc(sizeof(gpr_subprocess)); + r->pid = pid; + return r; + } +} + +void gpr_subprocess_destroy(gpr_subprocess* p) { + if (!p->joined) { + kill(p->pid, SIGKILL); + gpr_subprocess_join(p); + } + gpr_free(p); +} + +int gpr_subprocess_join(gpr_subprocess* p) { + int status; +retry: + if (waitpid(p->pid, &status, 0) == -1) { + if (errno == EINTR) { + goto retry; + } + gpr_log(GPR_ERROR, "waitpid failed for pid %d: %s", p->pid, + strerror(errno)); + return -1; + } + p->joined = true; + return status; +} + +void gpr_subprocess_interrupt(gpr_subprocess* p) { + if (!p->joined) { + kill(p->pid, SIGINT); + } +} + +#endif /* GPR_POSIX_SUBPROCESS */ diff --git a/src/core/lib/gpr/subprocess_windows.cc b/src/core/lib/gpr/subprocess_windows.cc new file mode 100644 index 0000000000..1947d475e3 --- /dev/null +++ b/src/core/lib/gpr/subprocess_windows.cc @@ -0,0 +1,126 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#ifdef GPR_WINDOWS_SUBPROCESS + +#include +#include +#include + +#include +#include +#include +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/gpr/string_windows.h" + +struct gpr_subprocess { + PROCESS_INFORMATION pi; + int joined; + int interrupted; +}; + +const char* gpr_subprocess_binary_extension() { return ".exe"; } + +gpr_subprocess* gpr_subprocess_create(int argc, const char** argv) { + gpr_subprocess* r; + + STARTUPINFO si; + PROCESS_INFORMATION pi; + + char* args = gpr_strjoin_sep(argv, (size_t)argc, " ", NULL); + TCHAR* args_tchar; + + args_tchar = gpr_char_to_tchar(args); + gpr_free(args); + + memset(&si, 0, sizeof(si)); + si.cb = sizeof(si); + memset(&pi, 0, sizeof(pi)); + + if (!CreateProcess(NULL, args_tchar, NULL, NULL, FALSE, + CREATE_NEW_PROCESS_GROUP, NULL, NULL, &si, &pi)) { + gpr_free(args_tchar); + return NULL; + } + gpr_free(args_tchar); + + r = (gpr_subprocess*)gpr_malloc(sizeof(gpr_subprocess)); + memset(r, 0, sizeof(*r)); + r->pi = pi; + return r; +} + +void gpr_subprocess_destroy(gpr_subprocess* p) { + if (p) { + if (!p->joined) { + gpr_subprocess_interrupt(p); + gpr_subprocess_join(p); + } + if (p->pi.hProcess) { + CloseHandle(p->pi.hProcess); + } + if (p->pi.hThread) { + CloseHandle(p->pi.hThread); + } + gpr_free(p); + } +} + +int gpr_subprocess_join(gpr_subprocess* p) { + DWORD dwExitCode; + if (GetExitCodeProcess(p->pi.hProcess, &dwExitCode)) { + if (dwExitCode == STILL_ACTIVE) { + if (WaitForSingleObject(p->pi.hProcess, INFINITE) == WAIT_OBJECT_0) { + p->joined = 1; + goto getExitCode; + } + return -1; // failed to join + } else { + goto getExitCode; + } + } else { + return -1; // failed to get exit code + } + +getExitCode: + if (p->interrupted) { + return 0; + } + if (GetExitCodeProcess(p->pi.hProcess, &dwExitCode)) { + return (int)dwExitCode; + } else { + return -1; // failed to get exit code + } +} + +void gpr_subprocess_interrupt(gpr_subprocess* p) { + DWORD dwExitCode; + if (GetExitCodeProcess(p->pi.hProcess, &dwExitCode)) { + if (dwExitCode == STILL_ACTIVE) { + gpr_log(GPR_INFO, "sending ctrl-break"); + GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, p->pi.dwProcessId); + p->joined = 1; + p->interrupted = 1; + } + } + return; +} + +#endif /* GPR_WINDOWS_SUBPROCESS */ diff --git a/src/core/lib/gpr/sync.cc b/src/core/lib/gpr/sync.cc new file mode 100644 index 0000000000..347ffcd00e --- /dev/null +++ b/src/core/lib/gpr/sync.cc @@ -0,0 +1,122 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* Generic implementation of synchronization primitives. */ + +#include +#include +#include + +#include + +/* Number of mutexes to allocate for events, to avoid lock contention. + Should be a prime. */ +enum { event_sync_partitions = 31 }; + +/* Events are partitioned by address to avoid lock contention. */ +static struct sync_array_s { + gpr_mu mu; + gpr_cv cv; +} sync_array[event_sync_partitions]; + +/* This routine is executed once on first use, via event_once */ +static gpr_once event_once = GPR_ONCE_INIT; +static void event_initialize(void) { + int i; + for (i = 0; i != event_sync_partitions; i++) { + gpr_mu_init(&sync_array[i].mu); + gpr_cv_init(&sync_array[i].cv); + } +} + +/* Hash ev into an element of sync_array[]. */ +static struct sync_array_s* hash(gpr_event* ev) { + return &sync_array[((uintptr_t)ev) % event_sync_partitions]; +} + +void gpr_event_init(gpr_event* ev) { + gpr_once_init(&event_once, &event_initialize); + ev->state = 0; +} + +void gpr_event_set(gpr_event* ev, void* value) { + struct sync_array_s* s = hash(ev); + gpr_mu_lock(&s->mu); + GPR_ASSERT(gpr_atm_acq_load(&ev->state) == 0); + gpr_atm_rel_store(&ev->state, (gpr_atm)value); + gpr_cv_broadcast(&s->cv); + gpr_mu_unlock(&s->mu); + GPR_ASSERT(value != nullptr); +} + +void* gpr_event_get(gpr_event* ev) { + return (void*)gpr_atm_acq_load(&ev->state); +} + +void* gpr_event_wait(gpr_event* ev, gpr_timespec abs_deadline) { + void* result = (void*)gpr_atm_acq_load(&ev->state); + if (result == nullptr) { + struct sync_array_s* s = hash(ev); + gpr_mu_lock(&s->mu); + do { + result = (void*)gpr_atm_acq_load(&ev->state); + } while (result == nullptr && !gpr_cv_wait(&s->cv, &s->mu, abs_deadline)); + gpr_mu_unlock(&s->mu); + } + return result; +} + +void gpr_ref_init(gpr_refcount* r, int n) { gpr_atm_rel_store(&r->count, n); } + +void gpr_ref(gpr_refcount* r) { gpr_atm_no_barrier_fetch_add(&r->count, 1); } + +void gpr_ref_non_zero(gpr_refcount* r) { +#ifndef NDEBUG + gpr_atm prior = gpr_atm_no_barrier_fetch_add(&r->count, 1); + assert(prior > 0); +#else + gpr_ref(r); +#endif +} + +void gpr_refn(gpr_refcount* r, int n) { + gpr_atm_no_barrier_fetch_add(&r->count, n); +} + +int gpr_unref(gpr_refcount* r) { + gpr_atm prior = gpr_atm_full_fetch_add(&r->count, -1); + GPR_ASSERT(prior > 0); + return prior == 1; +} + +int gpr_ref_is_unique(gpr_refcount* r) { + return gpr_atm_acq_load(&r->count) == 1; +} + +void gpr_stats_init(gpr_stats_counter* c, intptr_t n) { + gpr_atm_rel_store(&c->value, n); +} + +void gpr_stats_inc(gpr_stats_counter* c, intptr_t inc) { + gpr_atm_no_barrier_fetch_add(&c->value, inc); +} + +intptr_t gpr_stats_read(const gpr_stats_counter* c) { + /* don't need acquire-load, but we have no no-barrier load yet */ + return gpr_atm_acq_load(&c->value); +} diff --git a/src/core/lib/gpr/sync_posix.cc b/src/core/lib/gpr/sync_posix.cc new file mode 100644 index 0000000000..c3f6b10463 --- /dev/null +++ b/src/core/lib/gpr/sync_posix.cc @@ -0,0 +1,111 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#ifdef GPR_POSIX_SYNC + +#include +#include +#include +#include +#include +#include "src/core/lib/profiling/timers.h" + +#ifdef GPR_LOW_LEVEL_COUNTERS +gpr_atm gpr_mu_locks = 0; +gpr_atm gpr_counter_atm_cas = 0; +gpr_atm gpr_counter_atm_add = 0; +#endif + +void gpr_mu_init(gpr_mu* mu) { + GPR_ASSERT(pthread_mutex_init(mu, nullptr) == 0); +} + +void gpr_mu_destroy(gpr_mu* mu) { GPR_ASSERT(pthread_mutex_destroy(mu) == 0); } + +void gpr_mu_lock(gpr_mu* mu) { +#ifdef GPR_LOW_LEVEL_COUNTERS + GPR_ATM_INC_COUNTER(gpr_mu_locks); +#endif + GPR_TIMER_BEGIN("gpr_mu_lock", 0); + GPR_ASSERT(pthread_mutex_lock(mu) == 0); + GPR_TIMER_END("gpr_mu_lock", 0); +} + +void gpr_mu_unlock(gpr_mu* mu) { + GPR_TIMER_BEGIN("gpr_mu_unlock", 0); + GPR_ASSERT(pthread_mutex_unlock(mu) == 0); + GPR_TIMER_END("gpr_mu_unlock", 0); +} + +int gpr_mu_trylock(gpr_mu* mu) { + int err; + GPR_TIMER_BEGIN("gpr_mu_trylock", 0); + err = pthread_mutex_trylock(mu); + GPR_ASSERT(err == 0 || err == EBUSY); + GPR_TIMER_END("gpr_mu_trylock", 0); + return err == 0; +} + +/*----------------------------------------*/ + +void gpr_cv_init(gpr_cv* cv) { + pthread_condattr_t attr; + GPR_ASSERT(pthread_condattr_init(&attr) == 0); +#if GPR_LINUX + GPR_ASSERT(pthread_condattr_setclock(&attr, CLOCK_MONOTONIC) == 0); +#endif // GPR_LINUX + GPR_ASSERT(pthread_cond_init(cv, &attr) == 0); +} + +void gpr_cv_destroy(gpr_cv* cv) { GPR_ASSERT(pthread_cond_destroy(cv) == 0); } + +int gpr_cv_wait(gpr_cv* cv, gpr_mu* mu, gpr_timespec abs_deadline) { + int err = 0; + if (gpr_time_cmp(abs_deadline, gpr_inf_future(abs_deadline.clock_type)) == + 0) { + err = pthread_cond_wait(cv, mu); + } else { + struct timespec abs_deadline_ts; +#if GPR_LINUX + abs_deadline = gpr_convert_clock_type(abs_deadline, GPR_CLOCK_MONOTONIC); +#else + abs_deadline = gpr_convert_clock_type(abs_deadline, GPR_CLOCK_REALTIME); +#endif // GPR_LINUX + abs_deadline_ts.tv_sec = (time_t)abs_deadline.tv_sec; + abs_deadline_ts.tv_nsec = abs_deadline.tv_nsec; + err = pthread_cond_timedwait(cv, mu, &abs_deadline_ts); + } + GPR_ASSERT(err == 0 || err == ETIMEDOUT || err == EAGAIN); + return err == ETIMEDOUT; +} + +void gpr_cv_signal(gpr_cv* cv) { GPR_ASSERT(pthread_cond_signal(cv) == 0); } + +void gpr_cv_broadcast(gpr_cv* cv) { + GPR_ASSERT(pthread_cond_broadcast(cv) == 0); +} + +/*----------------------------------------*/ + +void gpr_once_init(gpr_once* once, void (*init_function)(void)) { + GPR_ASSERT(pthread_once(once, init_function) == 0); +} + +#endif /* GRP_POSIX_SYNC */ diff --git a/src/core/lib/gpr/sync_windows.cc b/src/core/lib/gpr/sync_windows.cc new file mode 100644 index 0000000000..7cd41633d5 --- /dev/null +++ b/src/core/lib/gpr/sync_windows.cc @@ -0,0 +1,118 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* Win32 code for gpr synchronization support. */ + +#include + +#ifdef GPR_WINDOWS + +#include +#include +#include + +void gpr_mu_init(gpr_mu* mu) { + InitializeCriticalSection(&mu->cs); + mu->locked = 0; +} + +void gpr_mu_destroy(gpr_mu* mu) { DeleteCriticalSection(&mu->cs); } + +void gpr_mu_lock(gpr_mu* mu) { + EnterCriticalSection(&mu->cs); + GPR_ASSERT(!mu->locked); + mu->locked = 1; +} + +void gpr_mu_unlock(gpr_mu* mu) { + mu->locked = 0; + LeaveCriticalSection(&mu->cs); +} + +int gpr_mu_trylock(gpr_mu* mu) { + int result = TryEnterCriticalSection(&mu->cs); + if (result) { + if (mu->locked) { /* This thread already holds the lock. */ + LeaveCriticalSection(&mu->cs); /* Decrement lock count. */ + result = 0; /* Indicate failure */ + } + mu->locked = 1; + } + return result; +} + +/*----------------------------------------*/ + +void gpr_cv_init(gpr_cv* cv) { InitializeConditionVariable(cv); } + +void gpr_cv_destroy(gpr_cv* cv) { + /* Condition variables don't need destruction in Win32. */ +} + +int gpr_cv_wait(gpr_cv* cv, gpr_mu* mu, gpr_timespec abs_deadline) { + int timeout = 0; + DWORD timeout_max_ms; + mu->locked = 0; + if (gpr_time_cmp(abs_deadline, gpr_inf_future(abs_deadline.clock_type)) == + 0) { + SleepConditionVariableCS(cv, &mu->cs, INFINITE); + } else { + abs_deadline = gpr_convert_clock_type(abs_deadline, GPR_CLOCK_REALTIME); + gpr_timespec now = gpr_now(abs_deadline.clock_type); + int64_t now_ms = (int64_t)now.tv_sec * 1000 + now.tv_nsec / 1000000; + int64_t deadline_ms = + (int64_t)abs_deadline.tv_sec * 1000 + abs_deadline.tv_nsec / 1000000; + if (now_ms >= deadline_ms) { + timeout = 1; + } else { + if ((deadline_ms - now_ms) >= INFINITE) { + timeout_max_ms = INFINITE - 1; + } else { + timeout_max_ms = (DWORD)(deadline_ms - now_ms); + } + timeout = (SleepConditionVariableCS(cv, &mu->cs, timeout_max_ms) == 0 && + GetLastError() == ERROR_TIMEOUT); + } + } + mu->locked = 1; + return timeout; +} + +void gpr_cv_signal(gpr_cv* cv) { WakeConditionVariable(cv); } + +void gpr_cv_broadcast(gpr_cv* cv) { WakeAllConditionVariable(cv); } + +/*----------------------------------------*/ + +static void* dummy; +struct run_once_func_arg { + void (*init_function)(void); +}; +static BOOL CALLBACK run_once_func(gpr_once* once, void* v, void** pv) { + struct run_once_func_arg* arg = (struct run_once_func_arg*)v; + (*arg->init_function)(); + return 1; +} + +void gpr_once_init(gpr_once* once, void (*init_function)(void)) { + struct run_once_func_arg arg; + arg.init_function = init_function; + InitOnceExecuteOnce(once, run_once_func, &arg, &dummy); +} + +#endif /* GPR_WINDOWS */ diff --git a/src/core/lib/gpr/thd.cc b/src/core/lib/gpr/thd.cc new file mode 100644 index 0000000000..ca62615d65 --- /dev/null +++ b/src/core/lib/gpr/thd.cc @@ -0,0 +1,49 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* Posix implementation for gpr threads. */ + +#include + +#include + +enum { GPR_THD_JOINABLE = 1 }; + +gpr_thd_options gpr_thd_options_default(void) { + gpr_thd_options options; + memset(&options, 0, sizeof(options)); + return options; +} + +void gpr_thd_options_set_detached(gpr_thd_options* options) { + options->flags &= ~GPR_THD_JOINABLE; +} + +void gpr_thd_options_set_joinable(gpr_thd_options* options) { + options->flags |= GPR_THD_JOINABLE; +} + +int gpr_thd_options_is_detached(const gpr_thd_options* options) { + if (!options) return 1; + return (options->flags & GPR_THD_JOINABLE) == 0; +} + +int gpr_thd_options_is_joinable(const gpr_thd_options* options) { + if (!options) return 0; + return (options->flags & GPR_THD_JOINABLE) == GPR_THD_JOINABLE; +} diff --git a/src/core/lib/gpr/thd_internal.h b/src/core/lib/gpr/thd_internal.h new file mode 100644 index 0000000000..692c00c8e1 --- /dev/null +++ b/src/core/lib/gpr/thd_internal.h @@ -0,0 +1,30 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_GPR_THD_INTERNAL_H +#define GRPC_CORE_LIB_GPR_THD_INTERNAL_H + +#include + +/* Internal interfaces between modules within the gpr support library. */ +void gpr_thd_init(); + +/* Wait for all outstanding threads to finish, up to deadline */ +int gpr_await_threads(gpr_timespec deadline); + +#endif /* GRPC_CORE_LIB_GPR_THD_INTERNAL_H */ diff --git a/src/core/lib/gpr/thd_posix.cc b/src/core/lib/gpr/thd_posix.cc new file mode 100644 index 0000000000..cfff0df6de --- /dev/null +++ b/src/core/lib/gpr/thd_posix.cc @@ -0,0 +1,152 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* Posix implementation for gpr threads. */ + +#include + +#ifdef GPR_POSIX_SYNC + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "src/core/lib/gpr/fork.h" + +static gpr_mu g_mu; +static gpr_cv g_cv; +static int g_thread_count; +static int g_awaiting_threads; + +struct thd_arg { + void (*body)(void* arg); /* body of a thread */ + void* arg; /* argument to a thread */ + const char* name; /* name of thread. Can be nullptr. */ +}; + +static void inc_thd_count(); +static void dec_thd_count(); + +/* Body of every thread started via gpr_thd_new. */ +static void* thread_body(void* v) { + struct thd_arg a = *(struct thd_arg*)v; + free(v); + if (a.name != nullptr) { +#if GPR_APPLE_PTHREAD_NAME + /* Apple supports 64 characters, and will truncate if it's longer. */ + pthread_setname_np(a.name); +#elif GPR_LINUX_PTHREAD_NAME + /* Linux supports 16 characters max, and will error if it's longer. */ + char buf[16]; + size_t buf_len = GPR_ARRAY_SIZE(buf) - 1; + strncpy(buf, a.name, buf_len); + buf[buf_len] = '\0'; + pthread_setname_np(pthread_self(), buf); +#endif // GPR_APPLE_PTHREAD_NAME + } + (*a.body)(a.arg); + dec_thd_count(); + return nullptr; +} + +int gpr_thd_new(gpr_thd_id* t, const char* thd_name, + void (*thd_body)(void* arg), void* arg, + const gpr_thd_options* options) { + int thread_started; + pthread_attr_t attr; + pthread_t p; + /* don't use gpr_malloc as we may cause an infinite recursion with + * the profiling code */ + struct thd_arg* a = (struct thd_arg*)malloc(sizeof(*a)); + GPR_ASSERT(a != nullptr); + a->body = thd_body; + a->arg = arg; + a->name = thd_name; + inc_thd_count(); + + GPR_ASSERT(pthread_attr_init(&attr) == 0); + if (gpr_thd_options_is_detached(options)) { + GPR_ASSERT(pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED) == + 0); + } else { + GPR_ASSERT(pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE) == + 0); + } + thread_started = (pthread_create(&p, &attr, &thread_body, a) == 0); + GPR_ASSERT(pthread_attr_destroy(&attr) == 0); + if (!thread_started) { + /* don't use gpr_free, as this was allocated using malloc (see above) */ + free(a); + dec_thd_count(); + } + *t = (gpr_thd_id)p; + return thread_started; +} + +gpr_thd_id gpr_thd_currentid(void) { return (gpr_thd_id)pthread_self(); } + +void gpr_thd_join(gpr_thd_id t) { pthread_join((pthread_t)t, nullptr); } + +/***************************************** + * Only used when fork support is enabled + */ + +static void inc_thd_count() { + if (grpc_fork_support_enabled()) { + gpr_mu_lock(&g_mu); + g_thread_count++; + gpr_mu_unlock(&g_mu); + } +} + +static void dec_thd_count() { + if (grpc_fork_support_enabled()) { + gpr_mu_lock(&g_mu); + g_thread_count--; + if (g_awaiting_threads && g_thread_count == 0) { + gpr_cv_signal(&g_cv); + } + gpr_mu_unlock(&g_mu); + } +} + +void gpr_thd_init() { + gpr_mu_init(&g_mu); + gpr_cv_init(&g_cv); + g_thread_count = 0; + g_awaiting_threads = 0; +} + +int gpr_await_threads(gpr_timespec deadline) { + gpr_mu_lock(&g_mu); + g_awaiting_threads = 1; + int res = 0; + if (g_thread_count > 0) { + res = gpr_cv_wait(&g_cv, &g_mu, deadline); + } + g_awaiting_threads = 0; + gpr_mu_unlock(&g_mu); + return res == 0; +} + +#endif /* GPR_POSIX_SYNC */ diff --git a/src/core/lib/gpr/thd_windows.cc b/src/core/lib/gpr/thd_windows.cc new file mode 100644 index 0000000000..f920770f32 --- /dev/null +++ b/src/core/lib/gpr/thd_windows.cc @@ -0,0 +1,105 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* Windows implementation for gpr threads. */ + +#include + +#ifdef GPR_WINDOWS + +#include +#include +#include +#include + +#if defined(_MSC_VER) +#define thread_local __declspec(thread) +#elif defined(__GNUC__) +#define thread_local __thread +#else +#error "Unknown compiler - please file a bug report" +#endif + +struct thd_info { + void (*body)(void* arg); /* body of a thread */ + void* arg; /* argument to a thread */ + HANDLE join_event; /* if joinable, the join event */ + int joinable; /* true if not detached */ +}; + +static thread_local struct thd_info* g_thd_info; + +/* Destroys a thread info */ +static void destroy_thread(struct thd_info* t) { + if (t->joinable) CloseHandle(t->join_event); + gpr_free(t); +} + +void gpr_thd_init(void) {} + +/* Body of every thread started via gpr_thd_new. */ +static DWORD WINAPI thread_body(void* v) { + g_thd_info = (struct thd_info*)v; + g_thd_info->body(g_thd_info->arg); + if (g_thd_info->joinable) { + BOOL ret = SetEvent(g_thd_info->join_event); + GPR_ASSERT(ret); + } else { + destroy_thread(g_thd_info); + } + return 0; +} + +int gpr_thd_new(gpr_thd_id* t, const char* thd_name, + void (*thd_body)(void* arg), void* arg, + const gpr_thd_options* options) { + HANDLE handle; + struct thd_info* info = (struct thd_info*)gpr_malloc(sizeof(*info)); + info->body = thd_body; + info->arg = arg; + *t = 0; + if (gpr_thd_options_is_joinable(options)) { + info->joinable = 1; + info->join_event = CreateEvent(NULL, FALSE, FALSE, NULL); + if (info->join_event == NULL) { + gpr_free(info); + return 0; + } + } else { + info->joinable = 0; + } + handle = CreateThread(NULL, 64 * 1024, thread_body, info, 0, NULL); + if (handle == NULL) { + destroy_thread(info); + } else { + *t = (gpr_thd_id)info; + CloseHandle(handle); + } + return handle != NULL; +} + +gpr_thd_id gpr_thd_currentid(void) { return (gpr_thd_id)g_thd_info; } + +void gpr_thd_join(gpr_thd_id t) { + struct thd_info* info = (struct thd_info*)t; + DWORD ret = WaitForSingleObject(info->join_event, INFINITE); + GPR_ASSERT(ret == WAIT_OBJECT_0); + destroy_thread(info); +} + +#endif /* GPR_WINDOWS */ diff --git a/src/core/lib/gpr/time.cc b/src/core/lib/gpr/time.cc new file mode 100644 index 0000000000..6903674d75 --- /dev/null +++ b/src/core/lib/gpr/time.cc @@ -0,0 +1,247 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* Generic implementation of time calls. */ + +#include +#include +#include +#include +#include + +int gpr_time_cmp(gpr_timespec a, gpr_timespec b) { + int cmp = (a.tv_sec > b.tv_sec) - (a.tv_sec < b.tv_sec); + GPR_ASSERT(a.clock_type == b.clock_type); + if (cmp == 0 && a.tv_sec != INT64_MAX && a.tv_sec != INT64_MIN) { + cmp = (a.tv_nsec > b.tv_nsec) - (a.tv_nsec < b.tv_nsec); + } + return cmp; +} + +gpr_timespec gpr_time_min(gpr_timespec a, gpr_timespec b) { + return gpr_time_cmp(a, b) < 0 ? a : b; +} + +gpr_timespec gpr_time_max(gpr_timespec a, gpr_timespec b) { + return gpr_time_cmp(a, b) > 0 ? a : b; +} + +gpr_timespec gpr_time_0(gpr_clock_type type) { + gpr_timespec out; + out.tv_sec = 0; + out.tv_nsec = 0; + out.clock_type = type; + return out; +} + +gpr_timespec gpr_inf_future(gpr_clock_type type) { + gpr_timespec out; + out.tv_sec = INT64_MAX; + out.tv_nsec = 0; + out.clock_type = type; + return out; +} + +gpr_timespec gpr_inf_past(gpr_clock_type type) { + gpr_timespec out; + out.tv_sec = INT64_MIN; + out.tv_nsec = 0; + out.clock_type = type; + return out; +} + +static gpr_timespec to_seconds_from_sub_second_time(int64_t time_in_units, + int64_t units_per_sec, + gpr_clock_type type) { + gpr_timespec out; + if (time_in_units == INT64_MAX) { + out = gpr_inf_future(type); + } else if (time_in_units == INT64_MIN) { + out = gpr_inf_past(type); + } else { + if (time_in_units >= 0) { + out.tv_sec = time_in_units / units_per_sec; + } else { + out.tv_sec = (-((units_per_sec - 1) - (time_in_units + units_per_sec)) / + units_per_sec) - + 1; + } + out.tv_nsec = (int32_t)((time_in_units - out.tv_sec * units_per_sec) * + GPR_NS_PER_SEC / units_per_sec); + out.clock_type = type; + } + return out; +} + +static gpr_timespec to_seconds_from_above_second_time(int64_t time_in_units, + int64_t secs_per_unit, + gpr_clock_type type) { + gpr_timespec out; + if (time_in_units >= INT64_MAX / secs_per_unit) { + out = gpr_inf_future(type); + } else if (time_in_units <= INT64_MIN / secs_per_unit) { + out = gpr_inf_past(type); + } else { + out.tv_sec = time_in_units * secs_per_unit; + out.tv_nsec = 0; + out.clock_type = type; + } + return out; +} + +gpr_timespec gpr_time_from_nanos(int64_t ns, gpr_clock_type type) { + return to_seconds_from_sub_second_time(ns, GPR_NS_PER_SEC, type); +} + +gpr_timespec gpr_time_from_micros(int64_t us, gpr_clock_type type) { + return to_seconds_from_sub_second_time(us, GPR_US_PER_SEC, type); +} + +gpr_timespec gpr_time_from_millis(int64_t ms, gpr_clock_type type) { + return to_seconds_from_sub_second_time(ms, GPR_MS_PER_SEC, type); +} + +gpr_timespec gpr_time_from_seconds(int64_t s, gpr_clock_type type) { + return to_seconds_from_sub_second_time(s, 1, type); +} + +gpr_timespec gpr_time_from_minutes(int64_t m, gpr_clock_type type) { + return to_seconds_from_above_second_time(m, 60, type); +} + +gpr_timespec gpr_time_from_hours(int64_t h, gpr_clock_type type) { + return to_seconds_from_above_second_time(h, 3600, type); +} + +gpr_timespec gpr_time_add(gpr_timespec a, gpr_timespec b) { + gpr_timespec sum; + int64_t inc = 0; + GPR_ASSERT(b.clock_type == GPR_TIMESPAN); + sum.clock_type = a.clock_type; + sum.tv_nsec = a.tv_nsec + b.tv_nsec; + if (sum.tv_nsec >= GPR_NS_PER_SEC) { + sum.tv_nsec -= GPR_NS_PER_SEC; + inc++; + } + if (a.tv_sec == INT64_MAX || a.tv_sec == INT64_MIN) { + sum = a; + } else if (b.tv_sec == INT64_MAX || + (b.tv_sec >= 0 && a.tv_sec >= INT64_MAX - b.tv_sec)) { + sum = gpr_inf_future(sum.clock_type); + } else if (b.tv_sec == INT64_MIN || + (b.tv_sec <= 0 && a.tv_sec <= INT64_MIN - b.tv_sec)) { + sum = gpr_inf_past(sum.clock_type); + } else { + sum.tv_sec = a.tv_sec + b.tv_sec; + if (inc != 0 && sum.tv_sec == INT64_MAX - 1) { + sum = gpr_inf_future(sum.clock_type); + } else { + sum.tv_sec += inc; + } + } + return sum; +} + +gpr_timespec gpr_time_sub(gpr_timespec a, gpr_timespec b) { + gpr_timespec diff; + int64_t dec = 0; + if (b.clock_type == GPR_TIMESPAN) { + diff.clock_type = a.clock_type; + } else { + GPR_ASSERT(a.clock_type == b.clock_type); + diff.clock_type = GPR_TIMESPAN; + } + diff.tv_nsec = a.tv_nsec - b.tv_nsec; + if (diff.tv_nsec < 0) { + diff.tv_nsec += GPR_NS_PER_SEC; + dec++; + } + if (a.tv_sec == INT64_MAX || a.tv_sec == INT64_MIN) { + diff = a; + } else if (b.tv_sec == INT64_MIN || + (b.tv_sec <= 0 && a.tv_sec >= INT64_MAX + b.tv_sec)) { + diff = gpr_inf_future(GPR_CLOCK_REALTIME); + } else if (b.tv_sec == INT64_MAX || + (b.tv_sec >= 0 && a.tv_sec <= INT64_MIN + b.tv_sec)) { + diff = gpr_inf_past(GPR_CLOCK_REALTIME); + } else { + diff.tv_sec = a.tv_sec - b.tv_sec; + if (dec != 0 && diff.tv_sec == INT64_MIN + 1) { + diff = gpr_inf_past(GPR_CLOCK_REALTIME); + } else { + diff.tv_sec -= dec; + } + } + return diff; +} + +int gpr_time_similar(gpr_timespec a, gpr_timespec b, gpr_timespec threshold) { + int cmp_ab; + + GPR_ASSERT(a.clock_type == b.clock_type); + GPR_ASSERT(threshold.clock_type == GPR_TIMESPAN); + + cmp_ab = gpr_time_cmp(a, b); + if (cmp_ab == 0) return 1; + if (cmp_ab < 0) { + return gpr_time_cmp(gpr_time_sub(b, a), threshold) <= 0; + } else { + return gpr_time_cmp(gpr_time_sub(a, b), threshold) <= 0; + } +} + +int32_t gpr_time_to_millis(gpr_timespec t) { + if (t.tv_sec >= 2147483) { + if (t.tv_sec == 2147483 && t.tv_nsec < 648 * GPR_NS_PER_MS) { + return 2147483 * GPR_MS_PER_SEC + t.tv_nsec / GPR_NS_PER_MS; + } + return 2147483647; + } else if (t.tv_sec <= -2147483) { + /* TODO(ctiller): correct handling here (it's so far in the past do we + care?) */ + return -2147483647; + } else { + return (int32_t)(t.tv_sec * GPR_MS_PER_SEC + t.tv_nsec / GPR_NS_PER_MS); + } +} + +double gpr_timespec_to_micros(gpr_timespec t) { + return (double)t.tv_sec * GPR_US_PER_SEC + t.tv_nsec * 1e-3; +} + +gpr_timespec gpr_convert_clock_type(gpr_timespec t, gpr_clock_type clock_type) { + if (t.clock_type == clock_type) { + return t; + } + + if (t.tv_sec == INT64_MAX || t.tv_sec == INT64_MIN) { + t.clock_type = clock_type; + return t; + } + + if (clock_type == GPR_TIMESPAN) { + return gpr_time_sub(t, gpr_now(t.clock_type)); + } + + if (t.clock_type == GPR_TIMESPAN) { + return gpr_time_add(gpr_now(clock_type), t); + } + + return gpr_time_add(gpr_now(clock_type), + gpr_time_sub(t, gpr_now(t.clock_type))); +} diff --git a/src/core/lib/gpr/time_posix.cc b/src/core/lib/gpr/time_posix.cc new file mode 100644 index 0000000000..9c7e86b080 --- /dev/null +++ b/src/core/lib/gpr/time_posix.cc @@ -0,0 +1,166 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include "src/core/lib/gpr/time_precise.h" + +#ifdef GPR_POSIX_TIME + +#include +#include +#include +#ifdef __linux__ +#include +#endif +#include +#include +#include + +static struct timespec timespec_from_gpr(gpr_timespec gts) { + struct timespec rv; + if (sizeof(time_t) < sizeof(int64_t)) { + /* fine to assert, as this is only used in gpr_sleep_until */ + GPR_ASSERT(gts.tv_sec <= INT32_MAX && gts.tv_sec >= INT32_MIN); + } + rv.tv_sec = (time_t)gts.tv_sec; + rv.tv_nsec = gts.tv_nsec; + return rv; +} + +#if _POSIX_TIMERS > 0 || defined(__OpenBSD__) +static gpr_timespec gpr_from_timespec(struct timespec ts, + gpr_clock_type clock_type) { + /* + * timespec.tv_sec can have smaller size than gpr_timespec.tv_sec, + * but we are only using this function to implement gpr_now + * so there's no need to handle "infinity" values. + */ + gpr_timespec rv; + rv.tv_sec = ts.tv_sec; + rv.tv_nsec = (int32_t)ts.tv_nsec; + rv.clock_type = clock_type; + return rv; +} + +/** maps gpr_clock_type --> clockid_t for clock_gettime */ +static const clockid_t clockid_for_gpr_clock[] = {CLOCK_MONOTONIC, + CLOCK_REALTIME}; + +void gpr_time_init(void) { gpr_precise_clock_init(); } + +static gpr_timespec now_impl(gpr_clock_type clock_type) { + struct timespec now; + GPR_ASSERT(clock_type != GPR_TIMESPAN); + if (clock_type == GPR_CLOCK_PRECISE) { + gpr_timespec ret; + gpr_precise_clock_now(&ret); + return ret; + } else { +#if defined(GPR_BACKWARDS_COMPATIBILITY_MODE) && defined(__linux__) + /* avoid ABI problems by invoking syscalls directly */ + syscall(SYS_clock_gettime, clockid_for_gpr_clock[clock_type], &now); +#else + clock_gettime(clockid_for_gpr_clock[clock_type], &now); +#endif + return gpr_from_timespec(now, clock_type); + } +} +#else + /* For some reason Apple's OSes haven't implemented clock_gettime. */ + +#include +#include +#include + +static double g_time_scale; +static uint64_t g_time_start; + +void gpr_time_init(void) { + mach_timebase_info_data_t tb = {0, 1}; + gpr_precise_clock_init(); + mach_timebase_info(&tb); + g_time_scale = tb.numer; + g_time_scale /= tb.denom; + g_time_start = mach_absolute_time(); +} + +static gpr_timespec now_impl(gpr_clock_type clock) { + gpr_timespec now; + struct timeval now_tv; + double now_dbl; + + now.clock_type = clock; + switch (clock) { + case GPR_CLOCK_REALTIME: + gettimeofday(&now_tv, nullptr); + now.tv_sec = now_tv.tv_sec; + now.tv_nsec = now_tv.tv_usec * 1000; + break; + case GPR_CLOCK_MONOTONIC: + now_dbl = ((double)(mach_absolute_time() - g_time_start)) * g_time_scale; + now.tv_sec = (int64_t)(now_dbl * 1e-9); + now.tv_nsec = (int32_t)(now_dbl - ((double)now.tv_sec) * 1e9); + break; + case GPR_CLOCK_PRECISE: + gpr_precise_clock_now(&now); + break; + case GPR_TIMESPAN: + abort(); + } + + return now; +} +#endif + +gpr_timespec (*gpr_now_impl)(gpr_clock_type clock_type) = now_impl; + +#ifdef GPR_LOW_LEVEL_COUNTERS +gpr_atm gpr_now_call_count; +#endif + +gpr_timespec gpr_now(gpr_clock_type clock_type) { +#ifdef GPR_LOW_LEVEL_COUNTERS + __atomic_fetch_add(&gpr_now_call_count, 1, __ATOMIC_RELAXED); +#endif + return gpr_now_impl(clock_type); +} + +void gpr_sleep_until(gpr_timespec until) { + gpr_timespec now; + gpr_timespec delta; + struct timespec delta_ts; + int ns_result; + + for (;;) { + /* We could simplify by using clock_nanosleep instead, but it might be + * slightly less portable. */ + now = gpr_now(until.clock_type); + if (gpr_time_cmp(until, now) <= 0) { + return; + } + + delta = gpr_time_sub(until, now); + delta_ts = timespec_from_gpr(delta); + ns_result = nanosleep(&delta_ts, nullptr); + if (ns_result == 0) { + break; + } + } +} + +#endif /* GPR_POSIX_TIME */ diff --git a/src/core/lib/gpr/time_precise.cc b/src/core/lib/gpr/time_precise.cc new file mode 100644 index 0000000000..3c7aaabc40 --- /dev/null +++ b/src/core/lib/gpr/time_precise.cc @@ -0,0 +1,76 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include + +#include "src/core/lib/gpr/time_precise.h" + +#ifdef GRPC_TIMERS_RDTSC +#if defined(__i386__) +static void gpr_get_cycle_counter(int64_t int* clk) { + int64_t int ret; + __asm__ volatile("rdtsc" : "=A"(ret)); + *clk = ret; +} + +// ---------------------------------------------------------------- +#elif defined(__x86_64__) || defined(__amd64__) +static void gpr_get_cycle_counter(int64_t* clk) { + uint64_t low, high; + __asm__ volatile("rdtsc" : "=a"(low), "=d"(high)); + *clk = (int64_t)(high << 32) | (int64_t)low; +} +#endif + +static double cycles_per_second = 0; +static int64_t start_cycle; +void gpr_precise_clock_init(void) { + time_t start; + int64_t end_cycle; + gpr_log(GPR_DEBUG, "Calibrating timers"); + start = time(NULL); + while (time(NULL) == start) + ; + gpr_get_cycle_counter(&start_cycle); + while (time(NULL) <= start + 10) + ; + gpr_get_cycle_counter(&end_cycle); + cycles_per_second = (double)(end_cycle - start_cycle) / 10.0; + gpr_log(GPR_DEBUG, "... cycles_per_second = %f\n", cycles_per_second); +} + +void gpr_precise_clock_now(gpr_timespec* clk) { + int64_t counter; + double secs; + gpr_get_cycle_counter(&counter); + secs = (double)(counter - start_cycle) / cycles_per_second; + clk->clock_type = GPR_CLOCK_PRECISE; + clk->tv_sec = (int64_t)secs; + clk->tv_nsec = (int32_t)(1e9 * (secs - (double)clk->tv_sec)); +} + +#else /* GRPC_TIMERS_RDTSC */ +void gpr_precise_clock_init(void) {} + +void gpr_precise_clock_now(gpr_timespec* clk) { + *clk = gpr_now(GPR_CLOCK_REALTIME); + clk->clock_type = GPR_CLOCK_PRECISE; +} +#endif /* GRPC_TIMERS_RDTSC */ diff --git a/src/core/lib/gpr/time_precise.h b/src/core/lib/gpr/time_precise.h new file mode 100644 index 0000000000..acc4ee3d1b --- /dev/null +++ b/src/core/lib/gpr/time_precise.h @@ -0,0 +1,27 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_GPR_TIME_PRECISE_H +#define GRPC_CORE_LIB_GPR_TIME_PRECISE_H + +#include + +void gpr_precise_clock_init(void); +void gpr_precise_clock_now(gpr_timespec* clk); + +#endif /* GRPC_CORE_LIB_GPR_TIME_PRECISE_H */ diff --git a/src/core/lib/gpr/time_windows.cc b/src/core/lib/gpr/time_windows.cc new file mode 100644 index 0000000000..247cc16468 --- /dev/null +++ b/src/core/lib/gpr/time_windows.cc @@ -0,0 +1,98 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* Win32 code for gpr time support. */ + +#include + +#ifdef GPR_WINDOWS_TIME + +#include +#include +#include +#include +#include + +#include "src/core/lib/gpr/time_precise.h" + +static LARGE_INTEGER g_start_time; +static double g_time_scale; + +void gpr_time_init(void) { + LARGE_INTEGER frequency; + QueryPerformanceFrequency(&frequency); + QueryPerformanceCounter(&g_start_time); + g_time_scale = 1.0 / (double)frequency.QuadPart; +} + +static gpr_timespec now_impl(gpr_clock_type clock) { + gpr_timespec now_tv; + LONGLONG diff; + struct _timeb now_tb; + LARGE_INTEGER timestamp; + double now_dbl; + now_tv.clock_type = clock; + switch (clock) { + case GPR_CLOCK_REALTIME: + _ftime_s(&now_tb); + now_tv.tv_sec = (int64_t)now_tb.time; + now_tv.tv_nsec = now_tb.millitm * 1000000; + break; + case GPR_CLOCK_MONOTONIC: + case GPR_CLOCK_PRECISE: + QueryPerformanceCounter(×tamp); + diff = timestamp.QuadPart - g_start_time.QuadPart; + now_dbl = (double)diff * g_time_scale; + now_tv.tv_sec = (int64_t)now_dbl; + now_tv.tv_nsec = (int32_t)((now_dbl - (double)now_tv.tv_sec) * 1e9); + break; + case GPR_TIMESPAN: + abort(); + break; + } + return now_tv; +} + +gpr_timespec (*gpr_now_impl)(gpr_clock_type clock_type) = now_impl; + +gpr_timespec gpr_now(gpr_clock_type clock_type) { + return gpr_now_impl(clock_type); +} + +void gpr_sleep_until(gpr_timespec until) { + gpr_timespec now; + gpr_timespec delta; + int64_t sleep_millis; + + for (;;) { + /* We could simplify by using clock_nanosleep instead, but it might be + * slightly less portable. */ + now = gpr_now(until.clock_type); + if (gpr_time_cmp(until, now) <= 0) { + return; + } + + delta = gpr_time_sub(until, now); + sleep_millis = + delta.tv_sec * GPR_MS_PER_SEC + delta.tv_nsec / GPR_NS_PER_MS; + GPR_ASSERT((sleep_millis >= 0) && (sleep_millis <= INT_MAX)); + Sleep((DWORD)sleep_millis); + } +} + +#endif /* GPR_WINDOWS_TIME */ diff --git a/src/core/lib/gpr/tls_pthread.cc b/src/core/lib/gpr/tls_pthread.cc new file mode 100644 index 0000000000..ebeef2a8c2 --- /dev/null +++ b/src/core/lib/gpr/tls_pthread.cc @@ -0,0 +1,30 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#ifdef GPR_PTHREAD_TLS + +#include + +intptr_t gpr_tls_set(struct gpr_pthread_thread_local* tls, intptr_t value) { + GPR_ASSERT(0 == pthread_setspecific(tls->key, (void*)value)); + return value; +} + +#endif /* GPR_PTHREAD_TLS */ diff --git a/src/core/lib/gpr/tmpfile.h b/src/core/lib/gpr/tmpfile.h new file mode 100644 index 0000000000..f47ec7aa63 --- /dev/null +++ b/src/core/lib/gpr/tmpfile.h @@ -0,0 +1,30 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_GPR_TMPFILE_H +#define GRPC_CORE_LIB_GPR_TMPFILE_H + +#include + +/* Creates a temporary file from a prefix. + If tmp_filename is not NULL, *tmp_filename is assigned the name of the + created file and it is the responsibility of the caller to gpr_free it + unless an error occurs in which case it will be set to NULL. */ +FILE* gpr_tmpfile(const char* prefix, char** tmp_filename); + +#endif /* GRPC_CORE_LIB_GPR_TMPFILE_H */ diff --git a/src/core/lib/gpr/tmpfile_msys.cc b/src/core/lib/gpr/tmpfile_msys.cc new file mode 100644 index 0000000000..76cd886f3a --- /dev/null +++ b/src/core/lib/gpr/tmpfile_msys.cc @@ -0,0 +1,58 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#ifdef GPR_MSYS_TMPFILE + +#include +#include +#include +#include + +#include +#include +#include + +#include "src/core/lib/gpr/string_windows.h" +#include "src/core/lib/gpr/tmpfile.h" + +FILE* gpr_tmpfile(const char* prefix, char** tmp_filename_out) { + FILE* result = NULL; + char tmp_filename[MAX_PATH]; + UINT success; + + if (tmp_filename_out != NULL) *tmp_filename_out = NULL; + + /* Generate a unique filename with our template + temporary path. */ + success = GetTempFileNameA(".", prefix, 0, tmp_filename); + fprintf(stderr, "success = %d\n", success); + + if (success) { + /* Open a file there. */ + result = fopen(tmp_filename, "wb+"); + fprintf(stderr, "result = %p\n", result); + } + if (result != NULL && tmp_filename_out) { + *tmp_filename_out = gpr_strdup(tmp_filename); + } + + return result; +} + +#endif /* GPR_MSYS_TMPFILE */ diff --git a/src/core/lib/gpr/tmpfile_posix.cc b/src/core/lib/gpr/tmpfile_posix.cc new file mode 100644 index 0000000000..ffdad335d6 --- /dev/null +++ b/src/core/lib/gpr/tmpfile_posix.cc @@ -0,0 +1,70 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#ifdef GPR_POSIX_TMPFILE + +#include "src/core/lib/gpr/tmpfile.h" + +#include +#include +#include +#include + +#include +#include +#include + +#include "src/core/lib/gpr/string.h" + +FILE* gpr_tmpfile(const char* prefix, char** tmp_filename) { + FILE* result = nullptr; + char* filename_template; + int fd; + + if (tmp_filename != nullptr) *tmp_filename = nullptr; + + gpr_asprintf(&filename_template, "/tmp/%s_XXXXXX", prefix); + GPR_ASSERT(filename_template != nullptr); + + fd = mkstemp(filename_template); + if (fd == -1) { + gpr_log(GPR_ERROR, "mkstemp failed for filename_template %s with error %s.", + filename_template, strerror(errno)); + goto end; + } + result = fdopen(fd, "w+"); + if (result == nullptr) { + gpr_log(GPR_ERROR, "Could not open file %s from fd %d (error = %s).", + filename_template, fd, strerror(errno)); + unlink(filename_template); + close(fd); + goto end; + } + +end: + if (result != nullptr && tmp_filename != nullptr) { + *tmp_filename = filename_template; + } else { + gpr_free(filename_template); + } + return result; +} + +#endif /* GPR_POSIX_TMPFILE */ diff --git a/src/core/lib/gpr/tmpfile_windows.cc b/src/core/lib/gpr/tmpfile_windows.cc new file mode 100644 index 0000000000..d486808418 --- /dev/null +++ b/src/core/lib/gpr/tmpfile_windows.cc @@ -0,0 +1,69 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#ifdef GPR_WINDOWS_TMPFILE + +#include +#include +#include +#include + +#include +#include +#include + +#include "src/core/lib/gpr/string_windows.h" +#include "src/core/lib/gpr/tmpfile.h" + +FILE* gpr_tmpfile(const char* prefix, char** tmp_filename_out) { + FILE* result = NULL; + LPTSTR template_string = NULL; + TCHAR tmp_path[MAX_PATH]; + TCHAR tmp_filename[MAX_PATH]; + DWORD status; + UINT success; + + if (tmp_filename_out != NULL) *tmp_filename_out = NULL; + + /* Convert our prefix to TCHAR. */ + template_string = gpr_char_to_tchar(prefix); + GPR_ASSERT(template_string); + + /* Get the path to the best temporary folder available. */ + status = GetTempPath(MAX_PATH, tmp_path); + if (status == 0 || status > MAX_PATH) goto end; + + /* Generate a unique filename with our template + temporary path. */ + success = GetTempFileName(tmp_path, template_string, 0, tmp_filename); + if (!success) goto end; + + /* Open a file there. */ + if (_tfopen_s(&result, tmp_filename, TEXT("wb+")) != 0) goto end; + +end: + if (result && tmp_filename_out) { + *tmp_filename_out = gpr_tchar_to_char(tmp_filename); + } + + gpr_free(template_string); + return result; +} + +#endif /* GPR_WINDOWS_TMPFILE */ diff --git a/src/core/lib/gpr/wrap_memcpy.cc b/src/core/lib/gpr/wrap_memcpy.cc new file mode 100644 index 0000000000..9b8608e056 --- /dev/null +++ b/src/core/lib/gpr/wrap_memcpy.cc @@ -0,0 +1,42 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +/* Provide a wrapped memcpy for targets that need to be backwards + * compatible with older libc's. + * + * Enable by setting LDFLAGS=-Wl,-wrap,memcpy when linking. + */ + +extern "C" { +#ifdef __linux__ +#if defined(__x86_64__) && !defined(GPR_MUSL_LIBC_COMPAT) +__asm__(".symver memcpy,memcpy@GLIBC_2.2.5"); +void* __wrap_memcpy(void* destination, const void* source, size_t num) { + return memcpy(destination, source, num); +} +#else /* !__x86_64__ */ +void* __wrap_memcpy(void* destination, const void* source, size_t num) { + return memmove(destination, source, num); +} +#endif +#endif +} -- cgit v1.2.3