Skip to content

Add init functions to support thread safe init of impls #102

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 14 commits into from
Mar 26, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ on:
- 'main'

env:
BUILDER_VERSION: v0.9.72
BUILDER_VERSION: v0.9.76
BUILDER_SOURCE: releases
BUILDER_HOST: https://d19elf31gohf1l.cloudfront.net
PACKAGE_NAME: aws-checksums
Expand Down Expand Up @@ -322,7 +322,7 @@ jobs:
role-to-assume: ${{ env.CRT_CI_ROLE }}
aws-region: ${{ env.AWS_DEFAULT_REGION }}
- uses: actions/checkout@v4
- uses: uraimo/run-on-arch-action@v2
- uses: uraimo/run-on-arch-action@v3
name: Run commands
id: runcmd
with:
Expand Down
26 changes: 26 additions & 0 deletions include/aws/checksums/checksums.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#ifndef AWS_CHECKSUMS_CHECKSUMS_H
#define AWS_CHECKSUMS_CHECKSUMS_H

#include <aws/common/common.h>

#include <aws/checksums/exports.h>

/**
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/

/**
* Initializes internal data structures used by aws-checksums.
* MUST be called before using any functionality in aws-checksums.
* Note: historically aws-checksums lazily initialized stuff and things worked without init.
* However, DO NOT rely on that behavior and explicitly call init instead.
*/
AWS_CHECKSUMS_API void aws_checksums_library_init(struct aws_allocator *allocator);

/**
* Shuts down the internal data structures used by aws-checksums.
*/
AWS_CHECKSUMS_API void aws_checksums_library_clean_up(void);

#endif /* AWS_CHECKSUMS_CHECKSUMS_H */
11 changes: 5 additions & 6 deletions include/aws/checksums/exports.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/
#if defined(AWS_C_RT_USE_WINDOWS_DLL_SEMANTICS) || defined(_WIN32)
#if defined(AWS_CRT_USE_WINDOWS_DLL_SEMANTICS) || defined(_WIN32)
# ifdef AWS_CHECKSUMS_USE_IMPORT_EXPORT
# ifdef AWS_CHECKSUMS_EXPORTS
# define AWS_CHECKSUMS_API __declspec(dllexport)
Expand All @@ -14,13 +14,12 @@
# else
# define AWS_CHECKSUMS_API
# endif /* AWS_CHECKSUMS_USE_IMPORT_EXPORT */
#else /* defined (AWS_C_RT_USE_WINDOWS_DLL_SEMANTICS) || defined (_WIN32) */
# if ((__GNUC__ >= 4) || defined(__clang__)) && defined(AWS_CHECKSUMS_USE_IMPORT_EXPORT) && \
defined(AWS_CHECKSUMS_EXPORTS)
#else /* defined (AWS_CRT_USE_WINDOWS_DLL_SEMANTICS) || defined (_WIN32) */
# if defined(AWS_CHECKSUMS_USE_IMPORT_EXPORT) && defined(AWS_CHECKSUMS_EXPORTS)
# define AWS_CHECKSUMS_API __attribute__((visibility("default")))
# else
# define AWS_CHECKSUMS_API
# endif /* __GNUC__ >= 4 || defined(__clang__) */
#endif /* defined (AWS_C_RT_USE_WINDOWS_DLL_SEMANTICS) || defined (_WIN32) */
# endif
#endif /* defined (AWS_CRT_USE_WINDOWS_DLL_SEMANTICS) || defined (_WIN32) */

#endif /* AWS_CHECKSUMS_EXPORTS_H */
14 changes: 14 additions & 0 deletions include/aws/checksums/private/crc_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,18 @@ static inline uint64_t aws_bswap64_if_be(uint64_t x) {
#endif
}

/**
* Force resolution of any global variables for CRC32.
* Note: in usual flow those are resolved on the first call to crc32 functions, which
* which might be deemed non-thread safe by some tools.
*/
void aws_checksums_crc32_init(void);

/**
* Force resolution of any global variables for CRC64.
* Note: in usual flow those are resolved on the first call to crc64 functions, which
* which might be deemed non-thread safe by some tools.
*/
void aws_checksums_crc64_init(void);

#endif /* AWS_CHECKSUMS_PRIVATE_CRC_UTIL_H */
27 changes: 27 additions & 0 deletions source/checksums.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/**
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/

#include <aws/checksums/checksums.h>
#include <aws/checksums/private/crc_util.h>

static bool s_checksums_library_initialized = false;

void aws_checksums_library_init(struct aws_allocator *allocator) {
if (!s_checksums_library_initialized) {
s_checksums_library_initialized = true;

aws_common_library_init(allocator);

aws_checksums_crc32_init();
aws_checksums_crc64_init();
}
}

void aws_checksums_library_clean_up(void) {
if (s_checksums_library_initialized) {
s_checksums_library_initialized = false;
aws_common_library_clean_up();
}
}
34 changes: 22 additions & 12 deletions source/crc.c → source/crc32.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@

large_buffer_apply_impl(crc32, uint32_t)

static uint32_t (*s_crc32c_fn_ptr)(const uint8_t *input, int length, uint32_t previous_crc32c) = 0;
static uint32_t (*s_crc32_fn_ptr)(const uint8_t *input, int length, uint32_t previous_crc32) = 0;
static uint32_t (*s_crc32c_fn_ptr)(const uint8_t *input, int length, uint32_t previous_crc32c) = NULL;
static uint32_t (*s_crc32_fn_ptr)(const uint8_t *input, int length, uint32_t previous_crc32) = NULL;

uint32_t aws_checksums_crc32(const uint8_t *input, int length, uint32_t previous_crc32) {
if (AWS_UNLIKELY(!s_crc32_fn_ptr)) {
void aws_checksums_crc32_init(void) {
if (s_crc32_fn_ptr == NULL) {
#if defined(AWS_USE_CPU_EXTENSIONS) && defined(AWS_ARCH_ARM64)
if (aws_cpu_has_feature(AWS_CPU_FEATURE_ARM_CRC)) {
s_crc32_fn_ptr = aws_checksums_crc32_armv8;
Expand All @@ -25,15 +25,8 @@ uint32_t aws_checksums_crc32(const uint8_t *input, int length, uint32_t previous
s_crc32_fn_ptr = aws_checksums_crc32_sw;
#endif
}
return s_crc32_fn_ptr(input, length, previous_crc32);
}

uint32_t aws_checksums_crc32_ex(const uint8_t *input, size_t length, uint32_t previous_crc32) {
return aws_large_buffer_apply_crc32(aws_checksums_crc32, input, length, previous_crc32);
}

uint32_t aws_checksums_crc32c(const uint8_t *input, int length, uint32_t previous_crc32c) {
if (AWS_UNLIKELY(!s_crc32c_fn_ptr)) {
if (s_crc32c_fn_ptr == NULL) {
#if defined(AWS_USE_CPU_EXTENSIONS) && defined(AWS_ARCH_INTEL_X64)
if (aws_cpu_has_feature(AWS_CPU_FEATURE_SSE_4_2)) {
s_crc32c_fn_ptr = aws_checksums_crc32c_intel_avx512_with_sse_fallback;
Expand All @@ -50,6 +43,23 @@ uint32_t aws_checksums_crc32c(const uint8_t *input, int length, uint32_t previou
s_crc32c_fn_ptr = aws_checksums_crc32c_sw;
#endif
}
}

uint32_t aws_checksums_crc32(const uint8_t *input, int length, uint32_t previous_crc32) {
if (AWS_UNLIKELY(s_crc32_fn_ptr == NULL)) {
aws_checksums_crc32_init();
}
return s_crc32_fn_ptr(input, length, previous_crc32);
}

uint32_t aws_checksums_crc32_ex(const uint8_t *input, size_t length, uint32_t previous_crc32) {
return aws_large_buffer_apply_crc32(aws_checksums_crc32, input, length, previous_crc32);
}

uint32_t aws_checksums_crc32c(const uint8_t *input, int length, uint32_t previous_crc32c) {
if (AWS_UNLIKELY(s_crc32c_fn_ptr == NULL)) {
aws_checksums_crc32_init();
}

return s_crc32c_fn_ptr(input, length, previous_crc32c);
}
Expand Down
13 changes: 10 additions & 3 deletions source/crc64.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
large_buffer_apply_impl(crc64, uint64_t)

AWS_ALIGNED_TYPEDEF(uint8_t, checksums_maxks_shifts_type[6][16], 16);

// Intel PSHUFB / ARM VTBL patterns for left/right shifts and masks
checksums_maxks_shifts_type aws_checksums_masks_shifts = {
{0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80}, //
Expand Down Expand Up @@ -91,10 +92,10 @@ checksums_constants aws_checksums_crc64nvme_constants = {
};
/* clang-format on */

static uint64_t (*s_crc64nvme_fn_ptr)(const uint8_t *input, int length, uint64_t prev_crc64) = 0;
static uint64_t (*s_crc64nvme_fn_ptr)(const uint8_t *input, int length, uint64_t prev_crc64) = NULL;

uint64_t aws_checksums_crc64nvme(const uint8_t *input, int length, uint64_t prev_crc64) {
if (AWS_UNLIKELY(!s_crc64nvme_fn_ptr)) {
void aws_checksums_crc64_init(void) {
if (s_crc64nvme_fn_ptr == NULL) {
#if defined(AWS_USE_CPU_EXTENSIONS) && defined(AWS_ARCH_INTEL_X64) && !(defined(_MSC_VER) && _MSC_VER < 1920)
# if defined(AWS_HAVE_AVX512_INTRINSICS)
if (aws_cpu_has_feature(AWS_CPU_FEATURE_AVX512) && aws_cpu_has_feature(AWS_CPU_FEATURE_VPCLMULQDQ)) {
Expand Down Expand Up @@ -122,6 +123,12 @@ uint64_t aws_checksums_crc64nvme(const uint8_t *input, int length, uint64_t prev
s_crc64nvme_fn_ptr = aws_checksums_crc64nvme_sw;
#endif
}
}

uint64_t aws_checksums_crc64nvme(const uint8_t *input, int length, uint64_t prev_crc64) {
if (AWS_UNLIKELY(s_crc64nvme_fn_ptr == NULL)) {
aws_checksums_crc64_init();
}

return s_crc64nvme_fn_ptr(input, length, prev_crc64);
}
Expand Down
3 changes: 3 additions & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,11 @@ file(GLOB TESTS ${TEST_HDRS} ${TEST_SRC})

add_test_case(test_crc32c)
add_test_case(test_crc32)
add_test_case(test_crc32c_init)
add_test_case(test_crc32_init)
add_test_case(test_large_buffer_crc32)
add_test_case(test_crc64nvme)
add_test_case(test_crc64nvme_init)
add_test_case(test_large_buffer_crc64)

generate_test_driver(${PROJECT_NAME}-tests)
18 changes: 18 additions & 0 deletions tests/crc64_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
* SPDX-License-Identifier: Apache-2.0.
*/

#include <aws/checksums/checksums.h>
#include <aws/checksums/crc.h>
#include <aws/checksums/private/crc64_priv.h>
#include <aws/checksums/private/crc_util.h>
Expand Down Expand Up @@ -130,6 +131,23 @@ static int s_test_crc64nvme(struct aws_allocator *allocator, void *ctx) {

AWS_TEST_CASE(test_crc64nvme, s_test_crc64nvme)

static int s_test_crc64nvme_init(struct aws_allocator *allocator, void *ctx) {
(void)ctx;

aws_checksums_library_init(allocator);

int res = 0;

res |= s_test_known_crc64nvme(allocator, CRC_FUNC_NAME(crc64nvme_reference));
res |= s_test_known_crc64nvme(allocator, CRC_FUNC_NAME(aws_checksums_crc64nvme_sw));
res |= s_test_known_crc64nvme(allocator, CRC_FUNC_NAME(aws_checksums_crc64nvme));

aws_checksums_library_clean_up();

return res;
}
AWS_TEST_CASE(test_crc64nvme_init, s_test_crc64nvme_init)

static int s_test_large_buffer_crc64(struct aws_allocator *allocator, void *ctx) {
(void)ctx;
#if SIZE_BITS == 32 || defined(__OpenBSD__) /* openbsd fails to allocate big buffer */
Expand Down
35 changes: 35 additions & 0 deletions tests/crc_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
* SPDX-License-Identifier: Apache-2.0.
*/

#include <aws/checksums/checksums.h>
#include <aws/checksums/crc.h>
#include <aws/checksums/private/crc_priv.h>
#include <aws/checksums/private/crc_util.h>
Expand Down Expand Up @@ -172,6 +173,23 @@ static int s_test_crc32c(struct aws_allocator *allocator, void *ctx) {
}
AWS_TEST_CASE(test_crc32c, s_test_crc32c)

static int s_test_crc32c_init(struct aws_allocator *allocator, void *ctx) {
(void)ctx;

aws_checksums_library_init(allocator);

int res = 0;

res |= s_test_known_crc32c(allocator, CRC_FUNC_NAME(s_crc32c_reference));
res |= s_test_known_crc32c(allocator, CRC_FUNC_NAME(aws_checksums_crc32c_sw));
res |= s_test_known_crc32c(allocator, CRC_FUNC_NAME(aws_checksums_crc32c));

aws_checksums_library_clean_up();

return res;
}
AWS_TEST_CASE(test_crc32c_init, s_test_crc32c_init)

static int s_test_crc32(struct aws_allocator *allocator, void *ctx) {
(void)ctx;

Expand All @@ -185,6 +203,23 @@ static int s_test_crc32(struct aws_allocator *allocator, void *ctx) {
}
AWS_TEST_CASE(test_crc32, s_test_crc32)

static int s_test_crc32_init(struct aws_allocator *allocator, void *ctx) {
(void)ctx;

aws_checksums_library_init(allocator);

int res = 0;

res |= s_test_known_crc32(allocator, CRC_FUNC_NAME(s_crc32_reference));
res |= s_test_known_crc32(allocator, CRC_FUNC_NAME(aws_checksums_crc32_sw));
res |= s_test_known_crc32(allocator, CRC_FUNC_NAME(aws_checksums_crc32));

aws_checksums_library_clean_up();

return res;
}
AWS_TEST_CASE(test_crc32_init, s_test_crc32_init)

static int s_test_large_buffer_crc32(struct aws_allocator *allocator, void *ctx) {
(void)ctx;
#if SIZE_BITS == 32 || defined(__OpenBSD__) /* openbsd fails to allocate big buffer */
Expand Down