The two main functions used for dictionary training using the COVER algorithm require initialization of a COVER_ctx_t where a call to qsort() is performed. The issue is that the standard C99 qsort() function doesn't offer a way to pass an extra parameter for the comparison function callback (e.g. a pointer to a context) and currently zstd relies on a *global* static variable to hold a pointer to a context needed to perform the sort operation. If a zstd library user invokes either ZDICT_trainFromBuffer_cover or ZDICT_optimizeTrainFromBuffer_cover from multiple threads, the global context may be overwritten before/during the call/execution to qsort() in the initialization of the COVER_ctx_t, thus yielding to crashes and other bad things (Tm) as reported on issue #4045. Enters qsort_r(): it was designed to address precisely this situation, to quote from the documention [1]: "the comparison function does not need to use global variables to pass through arbitrary arguments, and is therefore reentrant and safe to use in threads." It is available with small variations for multiple OSes (GNU, BSD[2], Windows[3]), and the ISO C11 [4] standard features on annex B-21 qsort_s() as part of the <stdlib.h>. Let's hope that compilers eventually catch up with it. For now, we have to handle the small variations in function parameters for each platform. The current fix solves the problem by allowing each executing thread pass its own COVER_ctx_t instance to qsort_r(), removing the use of a global pointer and allowing the code to be reentrant. Unfortunately for *BSD, we cannot leverage qsort_r() given that its API has changed on newer versions of FreeBSD (14.0) and the other BSD variants (e.g. NetBSD, OpenBSD) don't implement it. For such cases we provide a fallback that will work only requiring support for compilers implementing support for C90. [1] https://man7.org/linux/man-pages/man3/qsort_r.3.html [2] https://man.freebsd.org/cgi/man.cgi?query=qsort_r [3] https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/qsort-s?view=msvc-170 [4] https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1548.pdf
124 lines
2.9 KiB
C
124 lines
2.9 KiB
C
/*
|
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under both the BSD-style license (found in the
|
|
* LICENSE file in the root directory of this source tree) and the GPLv2 (found
|
|
* in the COPYING file in the root directory of this source tree).
|
|
* You may select, at your option, one of the above-listed licenses.
|
|
*/
|
|
|
|
/* This file provides common libc dependencies that zstd requires.
|
|
* The purpose is to allow replacing this file with a custom implementation
|
|
* to compile zstd without libc support.
|
|
*/
|
|
|
|
/* Need:
|
|
* NULL
|
|
* INT_MAX
|
|
* UINT_MAX
|
|
* ZSTD_memcpy()
|
|
* ZSTD_memset()
|
|
* ZSTD_memmove()
|
|
*/
|
|
#ifndef ZSTD_DEPS_COMMON
|
|
#define ZSTD_DEPS_COMMON
|
|
|
|
/* Even though we use qsort_r only for the dictionary builder, the macro
|
|
* _GNU_SOURCE has to be declared *before* the inclusion of any standard
|
|
* header and the script 'combine.sh' combines the whole zstd source code
|
|
* in a single file.
|
|
*/
|
|
#if defined(__linux) || defined(__linux__) || defined(linux) || defined(__gnu_linux__) || \
|
|
defined(__CYGWIN__) || defined(__MSYS__)
|
|
#if !defined(_GNU_SOURCE)
|
|
#define _GNU_SOURCE
|
|
#endif
|
|
#endif
|
|
|
|
#include <limits.h>
|
|
#include <stddef.h>
|
|
#include <string.h>
|
|
|
|
#if defined(__GNUC__) && __GNUC__ >= 4
|
|
# define ZSTD_memcpy(d,s,l) __builtin_memcpy((d),(s),(l))
|
|
# define ZSTD_memmove(d,s,l) __builtin_memmove((d),(s),(l))
|
|
# define ZSTD_memset(p,v,l) __builtin_memset((p),(v),(l))
|
|
#else
|
|
# define ZSTD_memcpy(d,s,l) memcpy((d),(s),(l))
|
|
# define ZSTD_memmove(d,s,l) memmove((d),(s),(l))
|
|
# define ZSTD_memset(p,v,l) memset((p),(v),(l))
|
|
#endif
|
|
|
|
#endif /* ZSTD_DEPS_COMMON */
|
|
|
|
/* Need:
|
|
* ZSTD_malloc()
|
|
* ZSTD_free()
|
|
* ZSTD_calloc()
|
|
*/
|
|
#ifdef ZSTD_DEPS_NEED_MALLOC
|
|
#ifndef ZSTD_DEPS_MALLOC
|
|
#define ZSTD_DEPS_MALLOC
|
|
|
|
#include <stdlib.h>
|
|
|
|
#define ZSTD_malloc(s) malloc(s)
|
|
#define ZSTD_calloc(n,s) calloc((n), (s))
|
|
#define ZSTD_free(p) free((p))
|
|
|
|
#endif /* ZSTD_DEPS_MALLOC */
|
|
#endif /* ZSTD_DEPS_NEED_MALLOC */
|
|
|
|
/*
|
|
* Provides 64-bit math support.
|
|
* Need:
|
|
* U64 ZSTD_div64(U64 dividend, U32 divisor)
|
|
*/
|
|
#ifdef ZSTD_DEPS_NEED_MATH64
|
|
#ifndef ZSTD_DEPS_MATH64
|
|
#define ZSTD_DEPS_MATH64
|
|
|
|
#define ZSTD_div64(dividend, divisor) ((dividend) / (divisor))
|
|
|
|
#endif /* ZSTD_DEPS_MATH64 */
|
|
#endif /* ZSTD_DEPS_NEED_MATH64 */
|
|
|
|
/* Need:
|
|
* assert()
|
|
*/
|
|
#ifdef ZSTD_DEPS_NEED_ASSERT
|
|
#ifndef ZSTD_DEPS_ASSERT
|
|
#define ZSTD_DEPS_ASSERT
|
|
|
|
#include <assert.h>
|
|
|
|
#endif /* ZSTD_DEPS_ASSERT */
|
|
#endif /* ZSTD_DEPS_NEED_ASSERT */
|
|
|
|
/* Need:
|
|
* ZSTD_DEBUG_PRINT()
|
|
*/
|
|
#ifdef ZSTD_DEPS_NEED_IO
|
|
#ifndef ZSTD_DEPS_IO
|
|
#define ZSTD_DEPS_IO
|
|
|
|
#include <stdio.h>
|
|
#define ZSTD_DEBUG_PRINT(...) fprintf(stderr, __VA_ARGS__)
|
|
|
|
#endif /* ZSTD_DEPS_IO */
|
|
#endif /* ZSTD_DEPS_NEED_IO */
|
|
|
|
/* Only requested when <stdint.h> is known to be present.
|
|
* Need:
|
|
* intptr_t
|
|
*/
|
|
#ifdef ZSTD_DEPS_NEED_STDINT
|
|
#ifndef ZSTD_DEPS_STDINT
|
|
#define ZSTD_DEPS_STDINT
|
|
|
|
#include <stdint.h>
|
|
|
|
#endif /* ZSTD_DEPS_STDINT */
|
|
#endif /* ZSTD_DEPS_NEED_STDINT */
|