From 06f9b1d63b49b7567ee77614e25d67b3093ca3a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Wei=C3=9F?= Date: Tue, 21 Nov 2017 18:32:33 +0000 Subject: [PATCH] merge ConcurrencyHelpers --- Sources/CNIOAtomics/include/c-atomics.h | 234 +++++++++ Sources/CNIOAtomics/include/cpp_magic.h | 452 ++++++++++++++++++ Sources/CNIOAtomics/src/c-atomics.c | 104 ++++ Sources/NIO/Channel.swift | 2 +- Sources/NIO/EventLoop.swift | 2 +- Sources/NIO/EventLoopFuture.swift | 2 +- Sources/NIOConcurrencyHelpers/atomics.swift | 183 +++++++ Sources/NIOConcurrencyHelpers/lock.swift | 140 ++++++ Tests/LinuxMain.swift | 2 + .../NIOConcurrencyHelpersTests+XCTest.swift | 45 ++ .../NIOConcurrencyHelpersTests.swift | 404 ++++++++++++++++ .../NIOHTTP1Tests/HTTPServerClientTest.swift | 2 +- Tests/NIOTests/ChannelPipelineTest.swift | 2 +- Tests/NIOTests/EchoServerClientTest.swift | 2 +- Tests/NIOTests/EventLoopTest.swift | 2 +- 15 files changed, 1571 insertions(+), 7 deletions(-) create mode 100644 Sources/CNIOAtomics/include/c-atomics.h create mode 100644 Sources/CNIOAtomics/include/cpp_magic.h create mode 100644 Sources/CNIOAtomics/src/c-atomics.c create mode 100644 Sources/NIOConcurrencyHelpers/atomics.swift create mode 100644 Sources/NIOConcurrencyHelpers/lock.swift create mode 100644 Tests/NIOConcurrencyHelpersTests/NIOConcurrencyHelpersTests+XCTest.swift create mode 100644 Tests/NIOConcurrencyHelpersTests/NIOConcurrencyHelpersTests.swift diff --git a/Sources/CNIOAtomics/include/c-atomics.h b/Sources/CNIOAtomics/include/c-atomics.h new file mode 100644 index 00000000..f85ee283 --- /dev/null +++ b/Sources/CNIOAtomics/include/c-atomics.h @@ -0,0 +1,234 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the SwiftNIO open source project +// +// Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of SwiftNIO project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +#include +#include +#include + +struct catmc_atomic__Bool; +struct catmc_atomic__Bool * _Nonnull catmc_atomic__Bool_create(bool value); +void catmc_atomic__Bool_destroy(struct catmc_atomic__Bool * _Nonnull atomic); +bool catmc_atomic__Bool_compare_and_exchange(struct catmc_atomic__Bool * _Nonnull atomic, bool expected, bool desired); +bool catmc_atomic__Bool_add(struct catmc_atomic__Bool * _Nonnull atomic, bool value); +bool catmc_atomic__Bool_sub(struct catmc_atomic__Bool * _Nonnull atomic, bool value); +bool catmc_atomic__Bool_exchange(struct catmc_atomic__Bool * _Nonnull atomic, bool value); +bool catmc_atomic__Bool_load(struct catmc_atomic__Bool * _Nonnull atomic); +void catmc_atomic__Bool_store(struct catmc_atomic__Bool * _Nonnull atomic, bool value); +struct catmc_atomic_char; +struct catmc_atomic_char * _Nonnull catmc_atomic_char_create(char value); +void catmc_atomic_char_destroy(struct catmc_atomic_char * _Nonnull atomic); +bool catmc_atomic_char_compare_and_exchange(struct catmc_atomic_char * _Nonnull atomic, char expected, char desired); +char catmc_atomic_char_add(struct catmc_atomic_char * _Nonnull atomic, char value); +char catmc_atomic_char_sub(struct catmc_atomic_char * _Nonnull atomic, char value); +char catmc_atomic_char_exchange(struct catmc_atomic_char * _Nonnull atomic, char value); +char catmc_atomic_char_load(struct catmc_atomic_char * _Nonnull atomic); +void catmc_atomic_char_store(struct catmc_atomic_char * _Nonnull atomic, char value); +struct catmc_atomic_short; +struct catmc_atomic_short * _Nonnull catmc_atomic_short_create(short value); +void catmc_atomic_short_destroy(struct catmc_atomic_short * _Nonnull atomic); +bool catmc_atomic_short_compare_and_exchange(struct catmc_atomic_short * _Nonnull atomic, short expected, short desired); +short catmc_atomic_short_add(struct catmc_atomic_short * _Nonnull atomic, short value); +short catmc_atomic_short_sub(struct catmc_atomic_short * _Nonnull atomic, short value); +short catmc_atomic_short_exchange(struct catmc_atomic_short * _Nonnull atomic, short value); +short catmc_atomic_short_load(struct catmc_atomic_short * _Nonnull atomic); +void catmc_atomic_short_store(struct catmc_atomic_short * _Nonnull atomic, short value); +struct catmc_atomic_int; +struct catmc_atomic_int * _Nonnull catmc_atomic_int_create(int value); +void catmc_atomic_int_destroy(struct catmc_atomic_int * _Nonnull atomic); +bool catmc_atomic_int_compare_and_exchange(struct catmc_atomic_int * _Nonnull atomic, int expected, int desired); +int catmc_atomic_int_add(struct catmc_atomic_int * _Nonnull atomic, int value); +int catmc_atomic_int_sub(struct catmc_atomic_int * _Nonnull atomic, int value); +int catmc_atomic_int_exchange(struct catmc_atomic_int * _Nonnull atomic, int value); +int catmc_atomic_int_load(struct catmc_atomic_int * _Nonnull atomic); +void catmc_atomic_int_store(struct catmc_atomic_int * _Nonnull atomic, int value); +struct catmc_atomic_long; +struct catmc_atomic_long * _Nonnull catmc_atomic_long_create(long value); +void catmc_atomic_long_destroy(struct catmc_atomic_long * _Nonnull atomic); +bool catmc_atomic_long_compare_and_exchange(struct catmc_atomic_long * _Nonnull atomic, long expected, long desired); +long catmc_atomic_long_add(struct catmc_atomic_long * _Nonnull atomic, long value); +long catmc_atomic_long_sub(struct catmc_atomic_long * _Nonnull atomic, long value); +long catmc_atomic_long_exchange(struct catmc_atomic_long * _Nonnull atomic, long value); +long catmc_atomic_long_load(struct catmc_atomic_long * _Nonnull atomic); +void catmc_atomic_long_store(struct catmc_atomic_long * _Nonnull atomic, long value); +struct catmc_atomic_long_long; +struct catmc_atomic_long_long * _Nonnull catmc_atomic_long_long_create(long long value); +void catmc_atomic_long_long_destroy(struct catmc_atomic_long_long * _Nonnull atomic); +bool catmc_atomic_long_long_compare_and_exchange(struct catmc_atomic_long_long * _Nonnull atomic, long long expected, long long desired); +long long catmc_atomic_long_long_add(struct catmc_atomic_long_long * _Nonnull atomic, long long value); +long long catmc_atomic_long_long_sub(struct catmc_atomic_long_long * _Nonnull atomic, long long value); +long long catmc_atomic_long_long_exchange(struct catmc_atomic_long_long * _Nonnull atomic, long long value); +long long catmc_atomic_long_long_load(struct catmc_atomic_long_long * _Nonnull atomic); +void catmc_atomic_long_long_store(struct catmc_atomic_long_long * _Nonnull atomic, long long value); +struct catmc_atomic_signed_char; +struct catmc_atomic_signed_char * _Nonnull catmc_atomic_signed_char_create(signed char value); +void catmc_atomic_signed_char_destroy(struct catmc_atomic_signed_char * _Nonnull atomic); +bool catmc_atomic_signed_char_compare_and_exchange(struct catmc_atomic_signed_char * _Nonnull atomic, signed char expected, signed char desired); +signed char catmc_atomic_signed_char_add(struct catmc_atomic_signed_char * _Nonnull atomic, signed char value); +signed char catmc_atomic_signed_char_sub(struct catmc_atomic_signed_char * _Nonnull atomic, signed char value); +signed char catmc_atomic_signed_char_exchange(struct catmc_atomic_signed_char * _Nonnull atomic, signed char value); +signed char catmc_atomic_signed_char_load(struct catmc_atomic_signed_char * _Nonnull atomic); +void catmc_atomic_signed_char_store(struct catmc_atomic_signed_char * _Nonnull atomic, signed char value); +struct catmc_atomic_signed_short; +struct catmc_atomic_signed_short * _Nonnull catmc_atomic_signed_short_create(signed short value); +void catmc_atomic_signed_short_destroy(struct catmc_atomic_signed_short * _Nonnull atomic); +bool catmc_atomic_signed_short_compare_and_exchange(struct catmc_atomic_signed_short * _Nonnull atomic, signed short expected, signed short desired); +signed short catmc_atomic_signed_short_add(struct catmc_atomic_signed_short * _Nonnull atomic, signed short value); +signed short catmc_atomic_signed_short_sub(struct catmc_atomic_signed_short * _Nonnull atomic, signed short value); +signed short catmc_atomic_signed_short_exchange(struct catmc_atomic_signed_short * _Nonnull atomic, signed short value); +signed short catmc_atomic_signed_short_load(struct catmc_atomic_signed_short * _Nonnull atomic); +void catmc_atomic_signed_short_store(struct catmc_atomic_signed_short * _Nonnull atomic, signed short value); +struct catmc_atomic_signed_int; +struct catmc_atomic_signed_int * _Nonnull catmc_atomic_signed_int_create(signed int value); +void catmc_atomic_signed_int_destroy(struct catmc_atomic_signed_int * _Nonnull atomic); +bool catmc_atomic_signed_int_compare_and_exchange(struct catmc_atomic_signed_int * _Nonnull atomic, signed int expected, signed int desired); +signed int catmc_atomic_signed_int_add(struct catmc_atomic_signed_int * _Nonnull atomic, signed int value); +signed int catmc_atomic_signed_int_sub(struct catmc_atomic_signed_int * _Nonnull atomic, signed int value); +signed int catmc_atomic_signed_int_exchange(struct catmc_atomic_signed_int * _Nonnull atomic, signed int value); +signed int catmc_atomic_signed_int_load(struct catmc_atomic_signed_int * _Nonnull atomic); +void catmc_atomic_signed_int_store(struct catmc_atomic_signed_int * _Nonnull atomic, signed int value); +struct catmc_atomic_signed_long; +struct catmc_atomic_signed_long * _Nonnull catmc_atomic_signed_long_create(signed long value); +void catmc_atomic_signed_long_destroy(struct catmc_atomic_signed_long * _Nonnull atomic); +bool catmc_atomic_signed_long_compare_and_exchange(struct catmc_atomic_signed_long * _Nonnull atomic, signed long expected, signed long desired); +signed long catmc_atomic_signed_long_add(struct catmc_atomic_signed_long * _Nonnull atomic, signed long value); +signed long catmc_atomic_signed_long_sub(struct catmc_atomic_signed_long * _Nonnull atomic, signed long value); +signed long catmc_atomic_signed_long_exchange(struct catmc_atomic_signed_long * _Nonnull atomic, signed long value); +signed long catmc_atomic_signed_long_load(struct catmc_atomic_signed_long * _Nonnull atomic); +void catmc_atomic_signed_long_store(struct catmc_atomic_signed_long * _Nonnull atomic, signed long value); +struct catmc_atomic_signed_long_long; +struct catmc_atomic_signed_long_long * _Nonnull catmc_atomic_signed_long_long_create(signed long long value); +void catmc_atomic_signed_long_long_destroy(struct catmc_atomic_signed_long_long * _Nonnull atomic); +bool catmc_atomic_signed_long_long_compare_and_exchange(struct catmc_atomic_signed_long_long * _Nonnull atomic, signed long long expected, signed long long desired); +signed long long catmc_atomic_signed_long_long_add(struct catmc_atomic_signed_long_long * _Nonnull atomic, signed long long value); +signed long long catmc_atomic_signed_long_long_sub(struct catmc_atomic_signed_long_long * _Nonnull atomic, signed long long value); +signed long long catmc_atomic_signed_long_long_exchange(struct catmc_atomic_signed_long_long * _Nonnull atomic, signed long long value); +signed long long catmc_atomic_signed_long_long_load(struct catmc_atomic_signed_long_long * _Nonnull atomic); +void catmc_atomic_signed_long_long_store(struct catmc_atomic_signed_long_long * _Nonnull atomic, signed long long value); +struct catmc_atomic_unsigned_char; +struct catmc_atomic_unsigned_char * _Nonnull catmc_atomic_unsigned_char_create(unsigned char value); +void catmc_atomic_unsigned_char_destroy(struct catmc_atomic_unsigned_char * _Nonnull atomic); +bool catmc_atomic_unsigned_char_compare_and_exchange(struct catmc_atomic_unsigned_char * _Nonnull atomic, unsigned char expected, unsigned char desired); +unsigned char catmc_atomic_unsigned_char_add(struct catmc_atomic_unsigned_char * _Nonnull atomic, unsigned char value); +unsigned char catmc_atomic_unsigned_char_sub(struct catmc_atomic_unsigned_char * _Nonnull atomic, unsigned char value); +unsigned char catmc_atomic_unsigned_char_exchange(struct catmc_atomic_unsigned_char * _Nonnull atomic, unsigned char value); +unsigned char catmc_atomic_unsigned_char_load(struct catmc_atomic_unsigned_char * _Nonnull atomic); +void catmc_atomic_unsigned_char_store(struct catmc_atomic_unsigned_char * _Nonnull atomic, unsigned char value); +struct catmc_atomic_unsigned_short; +struct catmc_atomic_unsigned_short * _Nonnull catmc_atomic_unsigned_short_create(unsigned short value); +void catmc_atomic_unsigned_short_destroy(struct catmc_atomic_unsigned_short * _Nonnull atomic); +bool catmc_atomic_unsigned_short_compare_and_exchange(struct catmc_atomic_unsigned_short * _Nonnull atomic, unsigned short expected, unsigned short desired); +unsigned short catmc_atomic_unsigned_short_add(struct catmc_atomic_unsigned_short * _Nonnull atomic, unsigned short value); +unsigned short catmc_atomic_unsigned_short_sub(struct catmc_atomic_unsigned_short * _Nonnull atomic, unsigned short value); +unsigned short catmc_atomic_unsigned_short_exchange(struct catmc_atomic_unsigned_short * _Nonnull atomic, unsigned short value); +unsigned short catmc_atomic_unsigned_short_load(struct catmc_atomic_unsigned_short * _Nonnull atomic); +void catmc_atomic_unsigned_short_store(struct catmc_atomic_unsigned_short * _Nonnull atomic, unsigned short value); +struct catmc_atomic_unsigned_int; +struct catmc_atomic_unsigned_int * _Nonnull catmc_atomic_unsigned_int_create(unsigned int value); +void catmc_atomic_unsigned_int_destroy(struct catmc_atomic_unsigned_int * _Nonnull atomic); +bool catmc_atomic_unsigned_int_compare_and_exchange(struct catmc_atomic_unsigned_int * _Nonnull atomic, unsigned int expected, unsigned int desired); +unsigned int catmc_atomic_unsigned_int_add(struct catmc_atomic_unsigned_int * _Nonnull atomic, unsigned int value); +unsigned int catmc_atomic_unsigned_int_sub(struct catmc_atomic_unsigned_int * _Nonnull atomic, unsigned int value); +unsigned int catmc_atomic_unsigned_int_exchange(struct catmc_atomic_unsigned_int * _Nonnull atomic, unsigned int value); +unsigned int catmc_atomic_unsigned_int_load(struct catmc_atomic_unsigned_int * _Nonnull atomic); +void catmc_atomic_unsigned_int_store(struct catmc_atomic_unsigned_int * _Nonnull atomic, unsigned int value); +struct catmc_atomic_unsigned_long; +struct catmc_atomic_unsigned_long * _Nonnull catmc_atomic_unsigned_long_create(unsigned long value); +void catmc_atomic_unsigned_long_destroy(struct catmc_atomic_unsigned_long * _Nonnull atomic); +bool catmc_atomic_unsigned_long_compare_and_exchange(struct catmc_atomic_unsigned_long * _Nonnull atomic, unsigned long expected, unsigned long desired); +unsigned long catmc_atomic_unsigned_long_add(struct catmc_atomic_unsigned_long * _Nonnull atomic, unsigned long value); +unsigned long catmc_atomic_unsigned_long_sub(struct catmc_atomic_unsigned_long * _Nonnull atomic, unsigned long value); +unsigned long catmc_atomic_unsigned_long_exchange(struct catmc_atomic_unsigned_long * _Nonnull atomic, unsigned long value); +unsigned long catmc_atomic_unsigned_long_load(struct catmc_atomic_unsigned_long * _Nonnull atomic); +void catmc_atomic_unsigned_long_store(struct catmc_atomic_unsigned_long * _Nonnull atomic, unsigned long value); +struct catmc_atomic_unsigned_long_long; +struct catmc_atomic_unsigned_long_long * _Nonnull catmc_atomic_unsigned_long_long_create(unsigned long long value); +void catmc_atomic_unsigned_long_long_destroy(struct catmc_atomic_unsigned_long_long * _Nonnull atomic); +bool catmc_atomic_unsigned_long_long_compare_and_exchange(struct catmc_atomic_unsigned_long_long * _Nonnull atomic, unsigned long long expected, unsigned long long desired); +unsigned long long catmc_atomic_unsigned_long_long_add(struct catmc_atomic_unsigned_long_long * _Nonnull atomic, unsigned long long value); +unsigned long long catmc_atomic_unsigned_long_long_sub(struct catmc_atomic_unsigned_long_long * _Nonnull atomic, unsigned long long value); +unsigned long long catmc_atomic_unsigned_long_long_exchange(struct catmc_atomic_unsigned_long_long * _Nonnull atomic, unsigned long long value); +unsigned long long catmc_atomic_unsigned_long_long_load(struct catmc_atomic_unsigned_long_long * _Nonnull atomic); +void catmc_atomic_unsigned_long_long_store(struct catmc_atomic_unsigned_long_long * _Nonnull atomic, unsigned long long value); +struct catmc_atomic_int_least8_t; +struct catmc_atomic_int_least8_t * _Nonnull catmc_atomic_int_least8_t_create(int_least8_t value); +void catmc_atomic_int_least8_t_destroy(struct catmc_atomic_int_least8_t * _Nonnull atomic); +bool catmc_atomic_int_least8_t_compare_and_exchange(struct catmc_atomic_int_least8_t * _Nonnull atomic, int_least8_t expected, int_least8_t desired); +int_least8_t catmc_atomic_int_least8_t_add(struct catmc_atomic_int_least8_t * _Nonnull atomic, int_least8_t value); +int_least8_t catmc_atomic_int_least8_t_sub(struct catmc_atomic_int_least8_t * _Nonnull atomic, int_least8_t value); +int_least8_t catmc_atomic_int_least8_t_exchange(struct catmc_atomic_int_least8_t * _Nonnull atomic, int_least8_t value); +int_least8_t catmc_atomic_int_least8_t_load(struct catmc_atomic_int_least8_t * _Nonnull atomic); +void catmc_atomic_int_least8_t_store(struct catmc_atomic_int_least8_t * _Nonnull atomic, int_least8_t value); +struct catmc_atomic_uint_least8_t; +struct catmc_atomic_uint_least8_t * _Nonnull catmc_atomic_uint_least8_t_create(uint_least8_t value); +void catmc_atomic_uint_least8_t_destroy(struct catmc_atomic_uint_least8_t * _Nonnull atomic); +bool catmc_atomic_uint_least8_t_compare_and_exchange(struct catmc_atomic_uint_least8_t * _Nonnull atomic, uint_least8_t expected, uint_least8_t desired); +uint_least8_t catmc_atomic_uint_least8_t_add(struct catmc_atomic_uint_least8_t * _Nonnull atomic, uint_least8_t value); +uint_least8_t catmc_atomic_uint_least8_t_sub(struct catmc_atomic_uint_least8_t * _Nonnull atomic, uint_least8_t value); +uint_least8_t catmc_atomic_uint_least8_t_exchange(struct catmc_atomic_uint_least8_t * _Nonnull atomic, uint_least8_t value); +uint_least8_t catmc_atomic_uint_least8_t_load(struct catmc_atomic_uint_least8_t * _Nonnull atomic); +void catmc_atomic_uint_least8_t_store(struct catmc_atomic_uint_least8_t * _Nonnull atomic, uint_least8_t value); +struct catmc_atomic_int_least16_t; +struct catmc_atomic_int_least16_t * _Nonnull catmc_atomic_int_least16_t_create(int_least16_t value); +void catmc_atomic_int_least16_t_destroy(struct catmc_atomic_int_least16_t * _Nonnull atomic); +bool catmc_atomic_int_least16_t_compare_and_exchange(struct catmc_atomic_int_least16_t * _Nonnull atomic, int_least16_t expected, int_least16_t desired); +int_least16_t catmc_atomic_int_least16_t_add(struct catmc_atomic_int_least16_t * _Nonnull atomic, int_least16_t value); +int_least16_t catmc_atomic_int_least16_t_sub(struct catmc_atomic_int_least16_t * _Nonnull atomic, int_least16_t value); +int_least16_t catmc_atomic_int_least16_t_exchange(struct catmc_atomic_int_least16_t * _Nonnull atomic, int_least16_t value); +int_least16_t catmc_atomic_int_least16_t_load(struct catmc_atomic_int_least16_t * _Nonnull atomic); +void catmc_atomic_int_least16_t_store(struct catmc_atomic_int_least16_t * _Nonnull atomic, int_least16_t value); +struct catmc_atomic_uint_least16_t; +struct catmc_atomic_uint_least16_t * _Nonnull catmc_atomic_uint_least16_t_create(uint_least16_t value); +void catmc_atomic_uint_least16_t_destroy(struct catmc_atomic_uint_least16_t * _Nonnull atomic); +bool catmc_atomic_uint_least16_t_compare_and_exchange(struct catmc_atomic_uint_least16_t * _Nonnull atomic, uint_least16_t expected, uint_least16_t desired); +uint_least16_t catmc_atomic_uint_least16_t_add(struct catmc_atomic_uint_least16_t * _Nonnull atomic, uint_least16_t value); +uint_least16_t catmc_atomic_uint_least16_t_sub(struct catmc_atomic_uint_least16_t * _Nonnull atomic, uint_least16_t value); +uint_least16_t catmc_atomic_uint_least16_t_exchange(struct catmc_atomic_uint_least16_t * _Nonnull atomic, uint_least16_t value); +uint_least16_t catmc_atomic_uint_least16_t_load(struct catmc_atomic_uint_least16_t * _Nonnull atomic); +void catmc_atomic_uint_least16_t_store(struct catmc_atomic_uint_least16_t * _Nonnull atomic, uint_least16_t value); +struct catmc_atomic_int_least32_t; +struct catmc_atomic_int_least32_t * _Nonnull catmc_atomic_int_least32_t_create(int_least32_t value); +void catmc_atomic_int_least32_t_destroy(struct catmc_atomic_int_least32_t * _Nonnull atomic); +bool catmc_atomic_int_least32_t_compare_and_exchange(struct catmc_atomic_int_least32_t * _Nonnull atomic, int_least32_t expected, int_least32_t desired); +int_least32_t catmc_atomic_int_least32_t_add(struct catmc_atomic_int_least32_t * _Nonnull atomic, int_least32_t value); +int_least32_t catmc_atomic_int_least32_t_sub(struct catmc_atomic_int_least32_t * _Nonnull atomic, int_least32_t value); +int_least32_t catmc_atomic_int_least32_t_exchange(struct catmc_atomic_int_least32_t * _Nonnull atomic, int_least32_t value); +int_least32_t catmc_atomic_int_least32_t_load(struct catmc_atomic_int_least32_t * _Nonnull atomic); +void catmc_atomic_int_least32_t_store(struct catmc_atomic_int_least32_t * _Nonnull atomic, int_least32_t value); +struct catmc_atomic_uint_least32_t; +struct catmc_atomic_uint_least32_t * _Nonnull catmc_atomic_uint_least32_t_create(uint_least32_t value); +void catmc_atomic_uint_least32_t_destroy(struct catmc_atomic_uint_least32_t * _Nonnull atomic); +bool catmc_atomic_uint_least32_t_compare_and_exchange(struct catmc_atomic_uint_least32_t * _Nonnull atomic, uint_least32_t expected, uint_least32_t desired); +uint_least32_t catmc_atomic_uint_least32_t_add(struct catmc_atomic_uint_least32_t * _Nonnull atomic, uint_least32_t value); +uint_least32_t catmc_atomic_uint_least32_t_sub(struct catmc_atomic_uint_least32_t * _Nonnull atomic, uint_least32_t value); +uint_least32_t catmc_atomic_uint_least32_t_exchange(struct catmc_atomic_uint_least32_t * _Nonnull atomic, uint_least32_t value); +uint_least32_t catmc_atomic_uint_least32_t_load(struct catmc_atomic_uint_least32_t * _Nonnull atomic); +void catmc_atomic_uint_least32_t_store(struct catmc_atomic_uint_least32_t * _Nonnull atomic, uint_least32_t value); +struct catmc_atomic_int_least64_t; +struct catmc_atomic_int_least64_t * _Nonnull catmc_atomic_int_least64_t_create(int_least64_t value); +void catmc_atomic_int_least64_t_destroy(struct catmc_atomic_int_least64_t * _Nonnull atomic); +bool catmc_atomic_int_least64_t_compare_and_exchange(struct catmc_atomic_int_least64_t * _Nonnull atomic, int_least64_t expected, int_least64_t desired); +int_least64_t catmc_atomic_int_least64_t_add(struct catmc_atomic_int_least64_t * _Nonnull atomic, int_least64_t value); +int_least64_t catmc_atomic_int_least64_t_sub(struct catmc_atomic_int_least64_t * _Nonnull atomic, int_least64_t value); +int_least64_t catmc_atomic_int_least64_t_exchange(struct catmc_atomic_int_least64_t * _Nonnull atomic, int_least64_t value); +int_least64_t catmc_atomic_int_least64_t_load(struct catmc_atomic_int_least64_t * _Nonnull atomic); +void catmc_atomic_int_least64_t_store(struct catmc_atomic_int_least64_t * _Nonnull atomic, int_least64_t value); +struct catmc_atomic_uint_least64_t; +struct catmc_atomic_uint_least64_t * _Nonnull catmc_atomic_uint_least64_t_create(uint_least64_t value); +void catmc_atomic_uint_least64_t_destroy(struct catmc_atomic_uint_least64_t * _Nonnull atomic); +bool catmc_atomic_uint_least64_t_compare_and_exchange(struct catmc_atomic_uint_least64_t * _Nonnull atomic, uint_least64_t expected, uint_least64_t desired); +uint_least64_t catmc_atomic_uint_least64_t_add(struct catmc_atomic_uint_least64_t * _Nonnull atomic, uint_least64_t value); +uint_least64_t catmc_atomic_uint_least64_t_sub(struct catmc_atomic_uint_least64_t * _Nonnull atomic, uint_least64_t value); +uint_least64_t catmc_atomic_uint_least64_t_exchange(struct catmc_atomic_uint_least64_t * _Nonnull atomic, uint_least64_t value); +uint_least64_t catmc_atomic_uint_least64_t_load(struct catmc_atomic_uint_least64_t * _Nonnull atomic); +void catmc_atomic_uint_least64_t_store(struct catmc_atomic_uint_least64_t * _Nonnull atomic, uint_least64_t value); diff --git a/Sources/CNIOAtomics/include/cpp_magic.h b/Sources/CNIOAtomics/include/cpp_magic.h new file mode 100644 index 00000000..3bccf362 --- /dev/null +++ b/Sources/CNIOAtomics/include/cpp_magic.h @@ -0,0 +1,452 @@ +// +// this is https://github.com/18sg/uSHET/blob/master/lib/cpp_magic.h +// LICENSE: MIT +// +// + +/** + * This header file contains a library of advanced C Pre-Processor (CPP) macros + * which implement various useful functions, such as iteration, in the + * pre-processor. + * + * Though the file name (quite validly) labels this as magic, there should be + * enough documentation in the comments for a reader only casually familiar + * with the CPP to be able to understand how everything works. + * + * The majority of the magic tricks used in this file are based on those + * described by pfultz2 in his "Cloak" library: + * + * https://github.com/pfultz2/Cloak/wiki/C-Preprocessor-tricks,-tips,-and-idioms + * + * Major differences are a greater level of detailed explanation in this + * implementation and also a refusal to include any macros which require a O(N) + * macro definitions to handle O(N) arguments (with the exception of DEFERn). + */ + +#ifndef CPP_MAGIC_H +#define CPP_MAGIC_H + +/** + * Force the pre-processor to expand the macro a large number of times. Usage: + * + * EVAL(expression) + * + * This is useful when you have a macro which evaluates to a valid macro + * expression which is not subsequently expanded in the same pass. A contrived, + * but easy to understand, example of such a macro follows. Note that though + * this example is contrived, this behaviour is abused to implement bounded + * recursion in macros such as FOR. + * + * #define A(x) x+1 + * #define EMPTY + * #define NOT_QUITE_RIGHT(x) A EMPTY (x) + * NOT_QUITE_RIGHT(999) + * + * Here's what happens inside the C preprocessor: + * + * 1. It sees a macro "NOT_QUITE_RIGHT" and performs a single macro expansion + * pass on its arguments. Since the argument is "999" and this isn't a macro, + * this is a boring step resulting in no change. + * 2. The NOT_QUITE_RIGHT macro is substituted for its definition giving "A + * EMPTY() (x)". + * 3. The expander moves from left-to-right trying to expand the macro: + * The first token, A, cannot be expanded since there are no brackets + * immediately following it. The second token EMPTY(), however, can be + * expanded (recursively in this manner) and is replaced with "". + * 4. Expansion continues from the start of the substituted test (which in this + * case is just empty), and sees "(999)" but since no macro name is present, + * nothing is done. This results in a final expansion of "A (999)". + * + * Unfortunately, this doesn't quite meet expectations since you may expect that + * "A (999)" would have been expanded into "999+1". Unfortunately this requires + * a second expansion pass but luckily we can force the macro processor to make + * more passes by abusing the first step of macro expansion: the preprocessor + * expands arguments in their own pass. If we define a macro which does nothing + * except produce its arguments e.g.: + * + * #define PASS_THROUGH(...) __VA_ARGS__ + * + * We can now do "PASS_THROUGH(NOT_QUITE_RIGHT(999))" causing "NOT_QUITE_RIGHT" to be + * expanded to "A (999)", as described above, when the arguments are expanded. + * Now when the body of PASS_THROUGH is expanded, "A (999)" gets expanded to + * "999+1". + * + * The EVAL defined below is essentially equivalent to a large nesting of + * "PASS_THROUGH(PASS_THROUGH(PASS_THROUGH(..." which results in the + * preprocessor making a large number of expansion passes over the given + * expression. + */ +#define EVAL(...) EVAL1024(__VA_ARGS__) +#define EVAL1024(...) EVAL512(EVAL512(__VA_ARGS__)) +#define EVAL512(...) EVAL256(EVAL256(__VA_ARGS__)) +#define EVAL256(...) EVAL128(EVAL128(__VA_ARGS__)) +#define EVAL128(...) EVAL64(EVAL64(__VA_ARGS__)) +#define EVAL64(...) EVAL32(EVAL32(__VA_ARGS__)) +#define EVAL32(...) EVAL16(EVAL16(__VA_ARGS__)) +#define EVAL16(...) EVAL8(EVAL8(__VA_ARGS__)) +#define EVAL8(...) EVAL4(EVAL4(__VA_ARGS__)) +#define EVAL4(...) EVAL2(EVAL2(__VA_ARGS__)) +#define EVAL2(...) EVAL1(EVAL1(__VA_ARGS__)) +#define EVAL1(...) __VA_ARGS__ + + +/** + * Macros which expand to common values + */ +#define PASS(...) __VA_ARGS__ +#define EMPTY() +#define COMMA() , +#define PLUS() + +#define ZERO() 0 +#define ONE() 1 + +/** + * Causes a function-style macro to require an additional pass to be expanded. + * + * This is useful, for example, when trying to implement recursion since the + * recursive step must not be expanded in a single pass as the pre-processor + * will catch it and prevent it. + * + * Usage: + * + * DEFER1(IN_NEXT_PASS)(args, to, the, macro) + * + * How it works: + * + * 1. When DEFER1 is expanded, first its arguments are expanded which are + * simply IN_NEXT_PASS. Since this is a function-style macro and it has no + * arguments, nothing will happen. + * 2. The body of DEFER1 will now be expanded resulting in EMPTY() being + * deleted. This results in "IN_NEXT_PASS (args, to, the macro)". Note that + * since the macro expander has already passed IN_NEXT_PASS by the time it + * expands EMPTY() and so it won't spot that the brackets which remain can be + * applied to IN_NEXT_PASS. + * 3. At this point the macro expansion completes. If one more pass is made, + * IN_NEXT_PASS(args, to, the, macro) will be expanded as desired. + */ +#define DEFER1(id) id EMPTY() + +/** + * As with DEFER1 except here n additional passes are required for DEFERn. + * + * The mechanism is analogous. + * + * Note that there doesn't appear to be a way of combining DEFERn macros in + * order to achieve exponentially increasing defers e.g. as is done by EVAL. + */ +#define DEFER2(id) id EMPTY EMPTY()() +#define DEFER3(id) id EMPTY EMPTY EMPTY()()() +#define DEFER4(id) id EMPTY EMPTY EMPTY EMPTY()()()() +#define DEFER5(id) id EMPTY EMPTY EMPTY EMPTY EMPTY()()()()() +#define DEFER6(id) id EMPTY EMPTY EMPTY EMPTY EMPTY EMPTY()()()()()() +#define DEFER7(id) id EMPTY EMPTY EMPTY EMPTY EMPTY EMPTY EMPTY()()()()()()() +#define DEFER8(id) id EMPTY EMPTY EMPTY EMPTY EMPTY EMPTY EMPTY EMPTY()()()()()()()() + + +/** + * Indirection around the standard ## concatenation operator. This simply + * ensures that the arguments are expanded (once) before concatenation. + */ +#define CAT(a, ...) a ## __VA_ARGS__ +#define CAT3(a, b, ...) a ## b ## __VA_ARGS__ + + +/** + * Get the first argument and ignore the rest. + */ +#define FIRST(a, ...) a + +/** + * Get the second argument and ignore the rest. + */ +#define SECOND(a, b, ...) b + +/** + * Expects a single input (not containing commas). Returns 1 if the input is + * PROBE() and otherwise returns 0. + * + * This can be useful as the basis of a NOT function. + * + * This macro abuses the fact that PROBE() contains a comma while other valid + * inputs must not. + */ +#define IS_PROBE(...) SECOND(__VA_ARGS__, 0) +#define PROBE() ~, 1 + + +/** + * Logical negation. 0 is defined as false and everything else as true. + * + * When 0, _NOT_0 will be found which evaluates to the PROBE. When 1 (or any other + * value) is given, an appropriately named macro won't be found and the + * concatenated string will be produced. IS_PROBE then simply checks to see if + * the PROBE was returned, cleanly converting the argument into a 1 or 0. + */ +#define NOT(x) IS_PROBE(CAT(_NOT_, x)) +#define _NOT_0 PROBE() + +/** + * Macro version of C's famous "cast to bool" operator (i.e. !!) which takes + * anything and casts it to 0 if it is 0 and 1 otherwise. + */ +#define BOOL(x) NOT(NOT(x)) + +/** + * Logical OR. Simply performs a lookup. + */ +#define OR(a,b) CAT3(_OR_, a, b) +#define _OR_00 0 +#define _OR_01 1 +#define _OR_10 1 +#define _OR_11 1 + +/** + * Logical AND. Simply performs a lookup. + */ +#define AND(a,b) CAT3(_AND_, a, b) +#define _AND_00 0 +#define _AND_01 0 +#define _AND_10 0 +#define _AND_11 1 + + +/** + * Macro if statement. Usage: + * + * IF(c)(expansion when true) + * + * Here's how: + * + * 1. The preprocessor expands the arguments to _IF casting the condition to '0' + * or '1'. + * 2. The casted condition is concatencated with _IF_ giving _IF_0 or _IF_1. + * 3. The _IF_0 and _IF_1 macros either returns the argument or doesn't (e.g. + * they implement the "choice selection" part of the macro). + * 4. Note that the "true" clause is in the extra set of brackets; thus these + * become the arguments to _IF_0 or _IF_1 and thus a selection is made! + */ +#define IF(c) _IF(BOOL(c)) +#define _IF(c) CAT(_IF_,c) +#define _IF_0(...) +#define _IF_1(...) __VA_ARGS__ + +/** + * Macro if/else statement. Usage: + * + * IF_ELSE(c)( \ + * expansion when true, \ + * expansion when false \ + * ) + * + * The mechanism is analogous to IF. + */ +#define IF_ELSE(c) _IF_ELSE(BOOL(c)) +#define _IF_ELSE(c) CAT(_IF_ELSE_,c) +#define _IF_ELSE_0(t,f) f +#define _IF_ELSE_1(t,f) t + + +/** + * Macro which checks if it has any arguments. Returns '0' if there are no + * arguments, '1' otherwise. + * + * Limitation: HAS_ARGS(,1,2,3) returns 0 -- this check essentially only checks + * that the first argument exists. + * + * This macro works as follows: + * + * 1. _END_OF_ARGUMENTS_ is concatenated with the first argument. + * 2. If the first argument is not present then only "_END_OF_ARGUMENTS_" will + * remain, otherwise "_END_OF_ARGUMENTS something_here" will remain. This + * remaining argument can start with parentheses. + * 3. In the former case, the _END_OF_ARGUMENTS_(0) macro expands to a + * 0 when it is expanded. In the latter, a non-zero result remains. If the + * first argument started with parentheses these will mostly not contain + * only a single 0, but e.g a C cast or some arithmetic operation that will + * cause the BOOL in _END_OF_ARGUMENTS_ to be one. + * 4. BOOL is used to force non-zero results into 1 giving the clean 0 or 1 + * output required. + */ +#define HAS_ARGS(...) BOOL(FIRST(_END_OF_ARGUMENTS_ __VA_ARGS__)(0)) +#define _END_OF_ARGUMENTS_(...) BOOL(FIRST(__VA_ARGS__)) + + +/** + * Macro map/list comprehension. Usage: + * + * MAP(op, sep, ...) + * + * Produces a 'sep()'-separated list of the result of op(arg) for each arg. + * + * Example Usage: + * + * #define MAKE_HAPPY(x) happy_##x + * #define COMMA() , + * MAP(MAKE_HAPPY, COMMA, 1,2,3) + * + * Which expands to: + * + * happy_1 , happy_2 , happy_3 + * + * How it works: + * + * 1. The MAP macro simply maps the inner MAP_INNER function in an EVAL which + * forces it to be expanded a large number of times, thus enabling many steps + * of iteration (see step 6). + * 2. The MAP_INNER macro is substituted for its body. + * 3. In the body, op(cur_val) is substituted giving the value for this + * iteration. + * 4. The IF macro expands according to whether further iterations are required. + * This expansion either produces _IF_0 or _IF_1. + * 5. Since the IF is followed by a set of brackets containing the "if true" + * clause, these become the argument to _IF_0 or _IF_1. At this point, the + * macro in the brackets will be expanded giving the separator followed by + * _MAP_INNER EMPTY()()(op, sep, __VA_ARGS__). + * 5. If the IF was not taken, the above will simply be discarded and everything + * stops. If the IF is taken, The expression is then processed a second time + * yielding "_MAP_INNER()(op, sep, __VA_ARGS__)". Note that this call looks + * very similar to the essentially the same as the original call except the + * first argument has been dropped. + * 6. At this point expansion of MAP_INNER will terminate. However, since we can + * force more rounds of expansion using EVAL1. In the argument-expansion pass + * of the EVAL1, _MAP_INNER() is expanded to MAP_INNER which is then expanded + * using the arguments which follow it as in step 2-5. This is followed by a + * second expansion pass as the substitution of EVAL1() is expanded executing + * 2-5 a second time. This results in up to two iterations occurring. Using + * many nested EVAL1 macros, i.e. the very-deeply-nested EVAL macro, will in + * this manner produce further iterations, hence the outer MAP macro doing + * this for us. + * + * Important tricks used: + * + * * If we directly produce "MAP_INNER" in an expansion of MAP_INNER, a special + * case in the preprocessor will prevent it being expanded in the future, even + * if we EVAL. As a result, the MAP_INNER macro carefully only expands to + * something containing "_MAP_INNER()" which requires a further expansion step + * to invoke MAP_INNER and thus implementing the recursion. + * * To prevent _MAP_INNER being expanded within the macro we must first defer its + * expansion during its initial pass as an argument to _IF_0 or _IF_1. We must + * then defer its expansion a second time as part of the body of the _IF_0. As + * a result hence the DEFER2. + * * _MAP_INNER seemingly gets away with producing itself because it actually only + * produces MAP_INNER. It just happens that when _MAP_INNER() is expanded in + * this case it is followed by some arguments which get consumed by MAP_INNER + * and produce a _MAP_INNER. As such, the macro expander never marks + * _MAP_INNER as expanding to itself and thus it will still be expanded in + * future productions of itself. + */ +#define MAP(...) \ + IF(HAS_ARGS(__VA_ARGS__))(EVAL(MAP_INNER(__VA_ARGS__))) +#define MAP_INNER(op,sep,cur_val, ...) \ + op(cur_val) \ + IF(HAS_ARGS(__VA_ARGS__))( \ + sep() DEFER2(_MAP_INNER)()(op, sep, ##__VA_ARGS__) \ + ) +#define _MAP_INNER() MAP_INNER + + +/** + * This is a variant of the MAP macro which also includes as an argument to the + * operation a valid C variable name which is different for each iteration. + * + * Usage: + * MAP_WITH_ID(op, sep, ...) + * + * Where op is a macro op(val, id) which takes a list value and an ID. This ID + * will simply be a unary number using the digit "I", that is, I, II, III, IIII, + * and so on. + * + * Example: + * + * #define MAKE_STATIC_VAR(type, name) static type name; + * MAP_WITH_ID(MAKE_STATIC_VAR, EMPTY, int, int, int, bool, char) + * + * Which expands to: + * + * static int I; static int II; static int III; static bool IIII; static char IIIII; + * + * The mechanism is analogous to the MAP macro. + */ +#define MAP_WITH_ID(op,sep,...) \ + IF(HAS_ARGS(__VA_ARGS__))(EVAL(MAP_WITH_ID_INNER(op,sep,I, ##__VA_ARGS__))) +#define MAP_WITH_ID_INNER(op,sep,id,cur_val, ...) \ + op(cur_val,id) \ + IF(HAS_ARGS(__VA_ARGS__))( \ + sep() DEFER2(_MAP_WITH_ID_INNER)()(op, sep, CAT(id,I), ##__VA_ARGS__) \ + ) +#define _MAP_WITH_ID_INNER() MAP_WITH_ID_INNER + + +/** + * This is a variant of the MAP macro which iterates over pairs rather than + * singletons. + * + * Usage: + * MAP_PAIRS(op, sep, ...) + * + * Where op is a macro op(val_1, val_2) which takes two list values. + * + * Example: + * + * #define MAKE_STATIC_VAR(type, name) static type name; + * MAP_PAIRS(MAKE_STATIC_VAR, EMPTY, char, my_char, int, my_int) + * + * Which expands to: + * + * static char my_char; static int my_int; + * + * The mechanism is analogous to the MAP macro. + */ +#define MAP_PAIRS(op,sep,...) \ + IF(HAS_ARGS(__VA_ARGS__))(EVAL(MAP_PAIRS_INNER(op,sep,__VA_ARGS__))) +#define MAP_PAIRS_INNER(op,sep,cur_val_1, cur_val_2, ...) \ + op(cur_val_1,cur_val_2) \ + IF(HAS_ARGS(__VA_ARGS__))( \ + sep() DEFER2(_MAP_PAIRS_INNER)()(op, sep, __VA_ARGS__) \ + ) +#define _MAP_PAIRS_INNER() MAP_PAIRS_INNER + +/** + * This is a variant of the MAP macro which iterates over a two-element sliding + * window. + * + * Usage: + * MAP_SLIDE(op, last_op, sep, ...) + * + * Where op is a macro op(val_1, val_2) which takes the two list values + * currently in the window. last_op is a macro taking a single value which is + * called for the last argument. + * + * Example: + * + * #define SIMON_SAYS_OP(simon, next) IF(NOT(simon()))(next) + * #define SIMON_SAYS_LAST_OP(val) last_but_not_least_##val + * #define SIMON_SAYS() 0 + * + * MAP_SLIDE(SIMON_SAYS_OP, SIMON_SAYS_LAST_OP, EMPTY, wiggle, SIMON_SAYS, dance, move, SIMON_SAYS, boogie, stop) + * + * Which expands to: + * + * dance boogie last_but_not_least_stop + * + * The mechanism is analogous to the MAP macro. + */ +#define MAP_SLIDE(op,last_op,sep,...) \ + IF(HAS_ARGS(__VA_ARGS__))(EVAL(MAP_SLIDE_INNER(op,last_op,sep,__VA_ARGS__))) +#define MAP_SLIDE_INNER(op,last_op,sep,cur_val, ...) \ + IF(HAS_ARGS(__VA_ARGS__))(op(cur_val,FIRST(__VA_ARGS__))) \ + IF(NOT(HAS_ARGS(__VA_ARGS__)))(last_op(cur_val)) \ + IF(HAS_ARGS(__VA_ARGS__))( \ + sep() DEFER2(_MAP_SLIDE_INNER)()(op, last_op, sep, __VA_ARGS__) \ + ) +#define _MAP_SLIDE_INNER() MAP_SLIDE_INNER + + +/** + * Strip any excess commas from a set of arguments. + */ +#define REMOVE_TRAILING_COMMAS(...) \ + MAP(PASS, COMMA, __VA_ARGS__) + + +#endif diff --git a/Sources/CNIOAtomics/src/c-atomics.c b/Sources/CNIOAtomics/src/c-atomics.c new file mode 100644 index 00000000..04e671a3 --- /dev/null +++ b/Sources/CNIOAtomics/src/c-atomics.c @@ -0,0 +1,104 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the SwiftNIO open source project +// +// Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of SwiftNIO project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +#include +#include +#include +#include +#include + +#include "../include/c-atomics.h" +#include "../include/cpp_magic.h" + +struct catmc_atomic_flag { + atomic_flag _flag; +}; + +struct catmc_atomic_flag *catmc_atomic_flag_create(bool value) { + struct catmc_atomic_flag *flag = malloc(sizeof(*flag)); + flag->_flag = (__typeof__(flag->_flag))ATOMIC_FLAG_INIT; + if (value) { + (void)atomic_flag_test_and_set_explicit(&flag->_flag, memory_order_relaxed); + } else { + atomic_flag_clear_explicit(&flag->_flag, memory_order_relaxed); + } + return flag; +} + +void catmc_atomic_flag_destroy(struct catmc_atomic_flag *flag) { + free(flag); +} + +#define MAKE(type) /* +*/ struct catmc_atomic_##type { /* +*/ _Atomic type value; /* +*/ }; /* +*/ /* +*/ struct catmc_atomic_##type *catmc_atomic_##type##_create(type value) { /* +*/ struct catmc_atomic_##type *wrapper = malloc(sizeof(*wrapper)); /* +*/ atomic_init(&wrapper->value, value); /* +*/ return wrapper; /* +*/ } /* +*/ /* +*/ void catmc_atomic_##type##_destroy(struct catmc_atomic_##type *wrapper) { /* +*/ free(wrapper); /* +*/ } /* +*/ /* +*/ bool catmc_atomic_##type##_compare_and_exchange(struct catmc_atomic_##type *wrapper, type expected, type desired) { /* +*/ type expected_copy = expected; /* +*/ return atomic_compare_exchange_strong(&wrapper->value, &expected_copy, desired); /* +*/ } /* +*/ /* +*/ type catmc_atomic_##type##_add(struct catmc_atomic_##type *wrapper, type value) { /* +*/ return atomic_fetch_add_explicit(&wrapper->value, value, memory_order_relaxed); /* +*/ } /* +*/ /* +*/ type catmc_atomic_##type##_sub(struct catmc_atomic_##type *wrapper, type value) { /* +*/ return atomic_fetch_sub_explicit(&wrapper->value, value, memory_order_relaxed); /* +*/ } /* +*/ /* +*/ type catmc_atomic_##type##_exchange(struct catmc_atomic_##type *wrapper, type value) { /* +*/ return atomic_exchange_explicit(&wrapper->value, value, memory_order_relaxed); /* +*/ } /* +*/ /* +*/ type catmc_atomic_##type##_load(struct catmc_atomic_##type *wrapper) { /* +*/ return atomic_load_explicit(&wrapper->value, memory_order_relaxed); /* +*/ } /* +*/ /* +*/ void catmc_atomic_##type##_store(struct catmc_atomic_##type *wrapper, type value) { /* +*/ atomic_store_explicit(&wrapper->value, value, memory_order_relaxed); /* +*/ } + +typedef signed char signed_char; +typedef signed short signed_short; +typedef signed int signed_int; +typedef signed long signed_long; +typedef signed long long signed_long_long; +typedef unsigned char unsigned_char; +typedef unsigned short unsigned_short; +typedef unsigned int unsigned_int; +typedef unsigned long unsigned_long; +typedef unsigned long long unsigned_long_long; +typedef long long long_long; + +MAP(MAKE,EMPTY, + bool, + char, short, int, long, long_long, + signed_char, signed_short, signed_int, signed_long, signed_long_long, + unsigned_char, unsigned_short, unsigned_int, unsigned_long, unsigned_long_long, + int_least8_t, uint_least8_t, + int_least16_t, uint_least16_t, + int_least32_t, uint_least32_t, + int_least64_t, uint_least64_t + ) diff --git a/Sources/NIO/Channel.swift b/Sources/NIO/Channel.swift index 2c47b21b..d12ea5d5 100644 --- a/Sources/NIO/Channel.swift +++ b/Sources/NIO/Channel.swift @@ -12,7 +12,7 @@ // //===----------------------------------------------------------------------===// -import ConcurrencyHelpers +import NIOConcurrencyHelpers #if os(Linux) import Glibc diff --git a/Sources/NIO/EventLoop.swift b/Sources/NIO/EventLoop.swift index d153c813..334fd4ad 100644 --- a/Sources/NIO/EventLoop.swift +++ b/Sources/NIO/EventLoop.swift @@ -12,7 +12,7 @@ // //===----------------------------------------------------------------------===// -import ConcurrencyHelpers +import NIOConcurrencyHelpers import Dispatch import class Foundation.Thread import SwiftPriorityQueue diff --git a/Sources/NIO/EventLoopFuture.swift b/Sources/NIO/EventLoopFuture.swift index 21e204f2..4e29a670 100644 --- a/Sources/NIO/EventLoopFuture.swift +++ b/Sources/NIO/EventLoopFuture.swift @@ -12,7 +12,7 @@ // //===----------------------------------------------------------------------===// -import ConcurrencyHelpers +import NIOConcurrencyHelpers public enum EventLoopFutureValue { diff --git a/Sources/NIOConcurrencyHelpers/atomics.swift b/Sources/NIOConcurrencyHelpers/atomics.swift new file mode 100644 index 00000000..21294dc8 --- /dev/null +++ b/Sources/NIOConcurrencyHelpers/atomics.swift @@ -0,0 +1,183 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the SwiftNIO open source project +// +// Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of SwiftNIO project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +import CNIOAtomics + +public final class Atomic { + private let value: OpaquePointer + + public init(value: T) { + self.value = T.atomic_create(value) + } + + public func compareAndExchange(expected: T, desired: T) -> Bool { + return T.atomic_compare_and_exchange(self.value, expected, desired) + } + + public func add(_ rhs: T) -> T { + return T.atomic_add(self.value, rhs) + } + + public func sub(_ rhs: T) -> T { + return T.atomic_sub(self.value, rhs) + } + + public func exchange(with value: T) -> T { + return T.atomic_exchange(self.value, value) + } + + public func load() -> T { + return T.atomic_load(self.value) + } + + public func store(_ value: T) -> Void { + T.atomic_store(self.value, value) + } + + deinit { + T.atomic_destroy(self.value) + } +} + +public protocol AtomicPrimitive { + static var atomic_create: (Self) -> OpaquePointer { get } + static var atomic_destroy: (OpaquePointer) -> Void { get } + static var atomic_compare_and_exchange: (OpaquePointer, Self, Self) -> Bool { get } + static var atomic_add: (OpaquePointer, Self) -> Self { get } + static var atomic_sub: (OpaquePointer, Self) -> Self { get } + static var atomic_exchange: (OpaquePointer, Self) -> Self { get } + static var atomic_load: (OpaquePointer) -> Self { get } + static var atomic_store: (OpaquePointer, Self) -> Void { get } +} + +extension Bool: AtomicPrimitive { + public static let atomic_create = catmc_atomic__Bool_create + public static let atomic_destroy = catmc_atomic__Bool_destroy + public static let atomic_compare_and_exchange = catmc_atomic__Bool_compare_and_exchange + public static let atomic_add = catmc_atomic__Bool_add + public static let atomic_sub = catmc_atomic__Bool_sub + public static let atomic_exchange = catmc_atomic__Bool_exchange + public static let atomic_load = catmc_atomic__Bool_load + public static let atomic_store = catmc_atomic__Bool_store +} + +extension Int8: AtomicPrimitive { + public static let atomic_create = catmc_atomic_int_least8_t_create + public static let atomic_destroy = catmc_atomic_int_least8_t_destroy + public static let atomic_compare_and_exchange = catmc_atomic_int_least8_t_compare_and_exchange + public static let atomic_add = catmc_atomic_int_least8_t_add + public static let atomic_sub = catmc_atomic_int_least8_t_sub + public static let atomic_exchange = catmc_atomic_int_least8_t_exchange + public static let atomic_load = catmc_atomic_int_least8_t_load + public static let atomic_store = catmc_atomic_int_least8_t_store +} + +extension UInt8: AtomicPrimitive { + public static let atomic_create = catmc_atomic_uint_least8_t_create + public static let atomic_destroy = catmc_atomic_uint_least8_t_destroy + public static let atomic_compare_and_exchange = catmc_atomic_uint_least8_t_compare_and_exchange + public static let atomic_add = catmc_atomic_uint_least8_t_add + public static let atomic_sub = catmc_atomic_uint_least8_t_sub + public static let atomic_exchange = catmc_atomic_uint_least8_t_exchange + public static let atomic_load = catmc_atomic_uint_least8_t_load + public static let atomic_store = catmc_atomic_uint_least8_t_store +} + +extension Int16: AtomicPrimitive { + public static let atomic_create = catmc_atomic_int_least16_t_create + public static let atomic_destroy = catmc_atomic_int_least16_t_destroy + public static let atomic_compare_and_exchange = catmc_atomic_int_least16_t_compare_and_exchange + public static let atomic_add = catmc_atomic_int_least16_t_add + public static let atomic_sub = catmc_atomic_int_least16_t_sub + public static let atomic_exchange = catmc_atomic_int_least16_t_exchange + public static let atomic_load = catmc_atomic_int_least16_t_load + public static let atomic_store = catmc_atomic_int_least16_t_store +} + +extension UInt16: AtomicPrimitive { + public static let atomic_create = catmc_atomic_uint_least16_t_create + public static let atomic_destroy = catmc_atomic_uint_least16_t_destroy + public static let atomic_compare_and_exchange = catmc_atomic_uint_least16_t_compare_and_exchange + public static let atomic_add = catmc_atomic_uint_least16_t_add + public static let atomic_sub = catmc_atomic_uint_least16_t_sub + public static let atomic_exchange = catmc_atomic_uint_least16_t_exchange + public static let atomic_load = catmc_atomic_uint_least16_t_load + public static let atomic_store = catmc_atomic_uint_least16_t_store +} + +extension Int32: AtomicPrimitive { + public static let atomic_create = catmc_atomic_int_least32_t_create + public static let atomic_destroy = catmc_atomic_int_least32_t_destroy + public static let atomic_compare_and_exchange = catmc_atomic_int_least32_t_compare_and_exchange + public static let atomic_add = catmc_atomic_int_least32_t_add + public static let atomic_sub = catmc_atomic_int_least32_t_sub + public static let atomic_exchange = catmc_atomic_int_least32_t_exchange + public static let atomic_load = catmc_atomic_int_least32_t_load + public static let atomic_store = catmc_atomic_int_least32_t_store +} + +extension UInt32: AtomicPrimitive { + public static let atomic_create = catmc_atomic_uint_least32_t_create + public static let atomic_destroy = catmc_atomic_uint_least32_t_destroy + public static let atomic_compare_and_exchange = catmc_atomic_uint_least32_t_compare_and_exchange + public static let atomic_add = catmc_atomic_uint_least32_t_add + public static let atomic_sub = catmc_atomic_uint_least32_t_sub + public static let atomic_exchange = catmc_atomic_uint_least32_t_exchange + public static let atomic_load = catmc_atomic_uint_least32_t_load + public static let atomic_store = catmc_atomic_uint_least32_t_store +} + +extension Int64: AtomicPrimitive { + public static let atomic_create = catmc_atomic_long_long_create + public static let atomic_destroy = catmc_atomic_long_long_destroy + public static let atomic_compare_and_exchange = catmc_atomic_long_long_compare_and_exchange + public static let atomic_add = catmc_atomic_long_long_add + public static let atomic_sub = catmc_atomic_long_long_sub + public static let atomic_exchange = catmc_atomic_long_long_exchange + public static let atomic_load = catmc_atomic_long_long_load + public static let atomic_store = catmc_atomic_long_long_store +} + +extension UInt64: AtomicPrimitive { + public static let atomic_create = catmc_atomic_unsigned_long_long_create + public static let atomic_destroy = catmc_atomic_unsigned_long_long_destroy + public static let atomic_compare_and_exchange = catmc_atomic_unsigned_long_long_compare_and_exchange + public static let atomic_add = catmc_atomic_unsigned_long_long_add + public static let atomic_sub = catmc_atomic_unsigned_long_long_sub + public static let atomic_exchange = catmc_atomic_unsigned_long_long_exchange + public static let atomic_load = catmc_atomic_unsigned_long_long_load + public static let atomic_store = catmc_atomic_unsigned_long_long_store +} + +extension Int: AtomicPrimitive { + public static let atomic_create = catmc_atomic_long_create + public static let atomic_destroy = catmc_atomic_long_destroy + public static let atomic_compare_and_exchange = catmc_atomic_long_compare_and_exchange + public static let atomic_add = catmc_atomic_long_add + public static let atomic_sub = catmc_atomic_long_sub + public static let atomic_exchange = catmc_atomic_long_exchange + public static let atomic_load = catmc_atomic_long_load + public static let atomic_store = catmc_atomic_long_store +} + +extension UInt: AtomicPrimitive { + public static let atomic_create = catmc_atomic_unsigned_long_create + public static let atomic_destroy = catmc_atomic_unsigned_long_destroy + public static let atomic_compare_and_exchange = catmc_atomic_unsigned_long_compare_and_exchange + public static let atomic_add = catmc_atomic_unsigned_long_add + public static let atomic_sub = catmc_atomic_unsigned_long_sub + public static let atomic_exchange = catmc_atomic_unsigned_long_exchange + public static let atomic_load = catmc_atomic_unsigned_long_load + public static let atomic_store = catmc_atomic_unsigned_long_store +} diff --git a/Sources/NIOConcurrencyHelpers/lock.swift b/Sources/NIOConcurrencyHelpers/lock.swift new file mode 100644 index 00000000..d862aac6 --- /dev/null +++ b/Sources/NIOConcurrencyHelpers/lock.swift @@ -0,0 +1,140 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the SwiftNIO open source project +// +// Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of SwiftNIO project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) +import Darwin +#else +import Glibc +#endif + +public final class Lock { + fileprivate let mutex: UnsafeMutablePointer = UnsafeMutablePointer.allocate(capacity: 1) + + public init() { + let err = pthread_mutex_init(self.mutex, nil) + precondition(err == 0) + } + + deinit { + mutex.deallocate(capacity: 1) + } + + public func lock() { + let err = pthread_mutex_lock(self.mutex) + precondition(err == 0) + } + + public func unlock() { + let err = pthread_mutex_unlock(self.mutex) + precondition(err == 0) + } +} + +extension Lock { + public func withLock(_ fn: () throws -> T) rethrows -> T { + self.lock() + defer { + self.unlock() + } + return try fn() + } + + // specialise Void return (for performance) + public func withLockVoid(_ fn: () throws -> Void) rethrows -> Void { + try self.withLock(fn) + } +} + +public final class ConditionLock { + private var _value: T + private let mutex: Lock + private let cond: UnsafeMutablePointer = UnsafeMutablePointer.allocate(capacity: 1) + + public init(value: T) { + self._value = value + self.mutex = Lock() + let err = pthread_cond_init(self.cond, nil) + precondition(err == 0) + } + + deinit { + self.cond.deallocate(capacity: 1) + } + + public func lock() { + self.mutex.lock() + } + + public func unlock() { + self.mutex.unlock() + } + + public var value: T { + self.lock() + defer { + self.unlock() + } + return self._value + } + + public func lock(whenValue wantedValue: T) { + self.lock() + while true { + if self._value == wantedValue { + break + } + let err = pthread_cond_wait(self.cond, self.mutex.mutex) + precondition(err == 0, "pthread_cond_wait error \(err)") + } + } + + public func lock(whenValue wantedValue: T, timeoutSeconds: Double) -> Bool{ + precondition(timeoutSeconds >= 0) + + let nsecPerSec: Int64 = 1000000000 + self.lock() + /* the timeout as a (seconds, nano seconds) pair */ + let timeoutNS = Int64(timeoutSeconds * Double(nsecPerSec)) + + var curTime = timeval() + gettimeofday(&curTime, nil) + + let allNSecs: Int64 = timeoutNS + Int64(curTime.tv_usec) * 1000 + var timeoutAbs = timespec(tv_sec: curTime.tv_sec + Int((allNSecs / nsecPerSec)), + tv_nsec: Int(allNSecs % nsecPerSec)) + assert(timeoutAbs.tv_nsec >= 0 && timeoutAbs.tv_nsec < Int(nsecPerSec)) + assert(timeoutAbs.tv_sec >= curTime.tv_sec) + while true { + if self._value == wantedValue { + return true + } + switch pthread_cond_timedwait(self.cond, self.mutex.mutex, &timeoutAbs) { + case 0: + continue + case ETIMEDOUT: + self.unlock() + return false + case let e: + fatalError("caught error \(e) when calling pthread_cond_timedwait") + } + } + } + + public func unlock(withValue newValue: T) { + self._value = newValue + self.unlock() + let r = pthread_cond_broadcast(self.cond) + precondition(r == 0) + } +} diff --git a/Tests/LinuxMain.swift b/Tests/LinuxMain.swift index 4ac7cdd7..fb499eba 100644 --- a/Tests/LinuxMain.swift +++ b/Tests/LinuxMain.swift @@ -23,6 +23,7 @@ import XCTest /// #if os(Linux) || os(FreeBSD) + @testable import NIOConcurrencyHelpersTests @testable import NIOHTTP1Tests @testable import NIOOpenSSLTests @testable import NIOTLSTests @@ -59,6 +60,7 @@ import XCTest testCase(SSLPrivateKeyTest.allTests), testCase(SniHandlerTest.allTests), testCase(SocketAddressTest.allTests), + testCase(NIOConcurrencyHelpersTests.allTests), testCase(SystemTest.allTests), testCase(TLSConfigurationTest.allTests), testCase(TypeAssistedChannelHandlerTest.allTests), diff --git a/Tests/NIOConcurrencyHelpersTests/NIOConcurrencyHelpersTests+XCTest.swift b/Tests/NIOConcurrencyHelpersTests/NIOConcurrencyHelpersTests+XCTest.swift new file mode 100644 index 00000000..61ccfd85 --- /dev/null +++ b/Tests/NIOConcurrencyHelpersTests/NIOConcurrencyHelpersTests+XCTest.swift @@ -0,0 +1,45 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the SwiftNIO open source project +// +// Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of SwiftNIO project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// +/// +/// NIOConcurrencyHelpersTests+XCTest.swift +/// +import XCTest + +/// +/// NOTE: This file was generated by generate_linux_tests.rb +/// +/// Do NOT edit this file directly as it will be regenerated automatically when needed. +/// + +extension NIOConcurrencyHelpersTests { + + static var allTests : [(String, (NIOConcurrencyHelpersTests) -> () throws -> Void)] { + return [ + ("testLargeContentedAtomicSum", testLargeContentedAtomicSum), + ("testCompareAndExchangeBool", testCompareAndExchangeBool), + ("testAllOperationsBool", testAllOperationsBool), + ("testCompareAndExchangeUInts", testCompareAndExchangeUInts), + ("testCompareAndExchangeInts", testCompareAndExchangeInts), + ("testAddSub", testAddSub), + ("testExchange", testExchange), + ("testLoadStore", testLoadStore), + ("testLockMutualExclusion", testLockMutualExclusion), + ("testWithLockMutualExclusion", testWithLockMutualExclusion), + ("testConditionLockMutualExclusion", testConditionLockMutualExclusion), + ("testConditionLock", testConditionLock), + ("testConditionLockWithDifferentConditions", testConditionLockWithDifferentConditions), + ] + } +} + diff --git a/Tests/NIOConcurrencyHelpersTests/NIOConcurrencyHelpersTests.swift b/Tests/NIOConcurrencyHelpersTests/NIOConcurrencyHelpersTests.swift new file mode 100644 index 00000000..b7b33643 --- /dev/null +++ b/Tests/NIOConcurrencyHelpersTests/NIOConcurrencyHelpersTests.swift @@ -0,0 +1,404 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the SwiftNIO open source project +// +// Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of SwiftNIO project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// +#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) +import Darwin +#else +import Glibc +#endif +import Dispatch +import XCTest +@testable import NIOConcurrencyHelpers + +class NIOConcurrencyHelpersTests: XCTestCase { + private func sumOfIntegers(until n: UInt64) -> UInt64 { + return n*(n+1)/2 + } + + func testLargeContentedAtomicSum() { + let noAsyncs: UInt64 = 64 + let noCounts: UInt64 = 200_000 + + let q = DispatchQueue(label: "q", attributes: .concurrent) + let g = DispatchGroup() + let ai = NIOConcurrencyHelpers.Atomic(value: 0) + for thread in 1...noAsyncs { + q.async(group: g) { + for _ in 0..(value: true) + + XCTAssertFalse(ab.compareAndExchange(expected: false, desired: false)) + XCTAssertTrue(ab.compareAndExchange(expected: true, desired: true)) + + XCTAssertFalse(ab.compareAndExchange(expected: false, desired: false)) + XCTAssertTrue(ab.compareAndExchange(expected: true, desired: false)) + + XCTAssertTrue(ab.compareAndExchange(expected: false, desired: false)) + XCTAssertTrue(ab.compareAndExchange(expected: false, desired: true)) + } + + func testAllOperationsBool() { + let ab = Atomic(value: false) + XCTAssertEqual(false, ab.load()) + ab.store(false) + XCTAssertEqual(false, ab.load()) + ab.store(true) + XCTAssertEqual(true, ab.load()) + ab.store(true) + XCTAssertEqual(true, ab.load()) + XCTAssertEqual(true, ab.exchange(with: true)) + XCTAssertEqual(true, ab.exchange(with: false)) + XCTAssertEqual(false, ab.exchange(with: false)) + XCTAssertTrue(ab.compareAndExchange(expected: false, desired: true)) + XCTAssertFalse(ab.compareAndExchange(expected: false, desired: true)) + } + + func testCompareAndExchangeUInts() { + func testFor(_ value: T.Type) { + let zero: T = 0 + let max = ~zero + + let ab = Atomic(value: max) + + XCTAssertFalse(ab.compareAndExchange(expected: zero, desired: zero)) + XCTAssertTrue(ab.compareAndExchange(expected: max, desired: max)) + + XCTAssertFalse(ab.compareAndExchange(expected: zero, desired: zero)) + XCTAssertTrue(ab.compareAndExchange(expected: max, desired: zero)) + + XCTAssertTrue(ab.compareAndExchange(expected: zero, desired: zero)) + XCTAssertTrue(ab.compareAndExchange(expected: zero, desired: max)) + + var counter = max + for _ in 0..<255 { + XCTAssertTrue(ab.compareAndExchange(expected: counter, desired: counter-1)) + counter = counter - 1 + } + } + + testFor(UInt8.self) + testFor(UInt16.self) + testFor(UInt32.self) + testFor(UInt64.self) + testFor(UInt.self) + } + + func testCompareAndExchangeInts() { + func testFor(_ value: T.Type) { + let zero: T = 0 + let upperBound: T = 127 + + let ab = Atomic(value: upperBound) + + XCTAssertFalse(ab.compareAndExchange(expected: zero, desired: zero)) + XCTAssertTrue(ab.compareAndExchange(expected: upperBound, desired: upperBound)) + + XCTAssertFalse(ab.compareAndExchange(expected: zero, desired: zero)) + XCTAssertTrue(ab.compareAndExchange(expected: upperBound, desired: zero)) + + XCTAssertTrue(ab.compareAndExchange(expected: zero, desired: zero)) + XCTAssertTrue(ab.compareAndExchange(expected: zero, desired: upperBound)) + + var counter = upperBound + for _ in 0..<255 { + XCTAssertTrue(ab.compareAndExchange(expected: counter, desired: counter-1)) + XCTAssertFalse(ab.compareAndExchange(expected: counter, desired: counter)) + counter = counter - 1 + } + } + + testFor(Int8.self) + testFor(Int16.self) + testFor(Int32.self) + testFor(Int64.self) + testFor(Int.self) + } + + func testAddSub() { + func testFor(_ value: T.Type) { + let zero: T = 0 + + let ab = Atomic(value: zero) + + XCTAssertEqual(0, ab.add(1)) + XCTAssertEqual(1, ab.add(41)) + XCTAssertEqual(42, ab.add(23)) + + XCTAssertEqual(65, ab.load()) + + XCTAssertEqual(65, ab.sub(23)) + XCTAssertEqual(42, ab.sub(41)) + XCTAssertEqual(1, ab.sub(1)) + + XCTAssertEqual(0, ab.load()) + } + + testFor(Int8.self) + testFor(Int16.self) + testFor(Int32.self) + testFor(Int64.self) + testFor(Int.self) + testFor(UInt8.self) + testFor(UInt16.self) + testFor(UInt32.self) + testFor(UInt64.self) + testFor(UInt.self) + } + + func testExchange() { + func testFor(_ value: T.Type) { + let zero: T = 0 + + let ab = Atomic(value: zero) + + XCTAssertEqual(0, ab.exchange(with: 1)) + XCTAssertEqual(1, ab.exchange(with: 42)) + XCTAssertEqual(42, ab.exchange(with: 65)) + + XCTAssertEqual(65, ab.load()) + + XCTAssertEqual(65, ab.exchange(with: 42)) + XCTAssertEqual(42, ab.exchange(with: 1)) + XCTAssertEqual(1, ab.exchange(with: 0)) + + XCTAssertEqual(0, ab.load()) + } + + testFor(Int8.self) + testFor(Int16.self) + testFor(Int32.self) + testFor(Int64.self) + testFor(Int.self) + testFor(UInt8.self) + testFor(UInt16.self) + testFor(UInt32.self) + testFor(UInt64.self) + testFor(UInt.self) + } + + func testLoadStore() { + func testFor(_ value: T.Type) { + let zero: T = 0 + + let ab = Atomic(value: zero) + + XCTAssertEqual(0, ab.load()) + ab.store(42) + XCTAssertEqual(42, ab.load()) + ab.store(0) + XCTAssertEqual(0, ab.load()) + } + + testFor(Int8.self) + testFor(Int16.self) + testFor(Int32.self) + testFor(Int64.self) + testFor(Int.self) + testFor(UInt8.self) + testFor(UInt16.self) + testFor(UInt32.self) + testFor(UInt64.self) + testFor(UInt.self) + } + + func testLockMutualExclusion() { + let l = Lock() + + var x = 1 + let q = DispatchQueue(label: "q") + let g = DispatchGroup() + let sem1 = DispatchSemaphore(value: 0) + let sem2 = DispatchSemaphore(value: 0) + + l.lock() + + q.async(group: g) { + sem1.signal() + l.lock() + x = 2 + l.unlock() + sem2.signal() + } + + sem1.wait() + XCTAssertEqual(DispatchTimeoutResult.timedOut, + g.wait(timeout: .now() + 0.1)) + XCTAssertEqual(1, x) + + l.unlock() + sem2.wait() + + l.lock() + XCTAssertEqual(2, x) + l.unlock() + } + + func testWithLockMutualExclusion() { + let l = Lock() + + var x = 1 + let q = DispatchQueue(label: "q") + let g = DispatchGroup() + let sem1 = DispatchSemaphore(value: 0) + let sem2 = DispatchSemaphore(value: 0) + + l.withLock { + q.async(group: g) { + sem1.signal() + l.withLock { + x = 2 + } + sem2.signal() + } + + sem1.wait() + XCTAssertEqual(DispatchTimeoutResult.timedOut, + g.wait(timeout: .now() + 0.1)) + XCTAssertEqual(1, x) + } + sem2.wait() + + l.withLock { + XCTAssertEqual(2, x) + } + } + + func testConditionLockMutualExclusion() { + let l = ConditionLock(value: 0) + + var x = 1 + let q = DispatchQueue(label: "q") + let g = DispatchGroup() + let sem1 = DispatchSemaphore(value: 0) + let sem2 = DispatchSemaphore(value: 0) + + l.lock() + + q.async(group: g) { + sem1.signal() + l.lock() + x = 2 + l.unlock() + sem2.signal() + } + + sem1.wait() + XCTAssertEqual(DispatchTimeoutResult.timedOut, + g.wait(timeout: .now() + 0.1)) + XCTAssertEqual(1, x) + + l.unlock() + sem2.wait() + + l.lock() + XCTAssertEqual(2, x) + l.unlock() + } + + func testConditionLock() { + let l = ConditionLock(value: 0) + let q = DispatchQueue(label: "q") + let sem = DispatchSemaphore(value: 0) + + XCTAssertEqual(0, l.value) + + l.lock() + l.unlock(withValue: 1) + + XCTAssertEqual(1, l.value) + + q.async { + l.lock(whenValue: 2) + l.unlock(withValue: 3) + sem.signal() + } + + usleep(100_000) + + l.lock() + l.unlock(withValue: 2) + + sem.wait() + l.lock(whenValue: 3) + l.unlock() + + XCTAssertEqual(false, l.lock(whenValue: 4, timeoutSeconds: 0.1)) + + XCTAssertEqual(true, l.lock(whenValue: 3, timeoutSeconds: 0.01)) + l.unlock() + + q.async { + usleep(100_000) + + l.lock() + l.unlock(withValue: 4) + sem.signal() + } + + XCTAssertEqual(true, l.lock(whenValue: 4, timeoutSeconds: 10)) + l.unlock() + } + + func testConditionLockWithDifferentConditions() { + for _ in 0..<200 { + let l = ConditionLock(value: 0) + let q1 = DispatchQueue(label: "q1") + let q2 = DispatchQueue(label: "q2") + + let readySem = DispatchSemaphore(value: 0) + let doneSem = DispatchSemaphore(value: 0) + + q1.async { + readySem.signal() + + l.lock(whenValue: 1) + l.unlock() + XCTAssertEqual(1, l.value) + + doneSem.signal() + } + + q2.async { + readySem.signal() + + l.lock(whenValue: 2) + l.unlock() + XCTAssertEqual(2, l.value) + + doneSem.signal() + } + + readySem.wait() + readySem.wait() + l.lock() + l.unlock(withValue: 1) + + doneSem.wait() /* job on 'q1' is done */ + + XCTAssertEqual(1, l.value) + l.lock() + l.unlock(withValue: 2) + + doneSem.wait() /* job on 'q2' is done */ + } + } +} diff --git a/Tests/NIOHTTP1Tests/HTTPServerClientTest.swift b/Tests/NIOHTTP1Tests/HTTPServerClientTest.swift index 773919ac..e10de157 100644 --- a/Tests/NIOHTTP1Tests/HTTPServerClientTest.swift +++ b/Tests/NIOHTTP1Tests/HTTPServerClientTest.swift @@ -13,7 +13,7 @@ //===----------------------------------------------------------------------===// import XCTest -import ConcurrencyHelpers +import NIOConcurrencyHelpers import NIO import Dispatch @testable import NIOHTTP1 diff --git a/Tests/NIOTests/ChannelPipelineTest.swift b/Tests/NIOTests/ChannelPipelineTest.swift index 6ef63748..ec5ae20b 100644 --- a/Tests/NIOTests/ChannelPipelineTest.swift +++ b/Tests/NIOTests/ChannelPipelineTest.swift @@ -13,7 +13,7 @@ //===----------------------------------------------------------------------===// import XCTest -import ConcurrencyHelpers +import NIOConcurrencyHelpers @testable import NIO class ChannelPipelineTest: XCTestCase { diff --git a/Tests/NIOTests/EchoServerClientTest.swift b/Tests/NIOTests/EchoServerClientTest.swift index 88e6f9ff..d43b999f 100644 --- a/Tests/NIOTests/EchoServerClientTest.swift +++ b/Tests/NIOTests/EchoServerClientTest.swift @@ -13,7 +13,7 @@ //===----------------------------------------------------------------------===// import XCTest -import ConcurrencyHelpers +import NIOConcurrencyHelpers import Dispatch @testable import NIO diff --git a/Tests/NIOTests/EventLoopTest.swift b/Tests/NIOTests/EventLoopTest.swift index 0521baf0..7df9aa76 100644 --- a/Tests/NIOTests/EventLoopTest.swift +++ b/Tests/NIOTests/EventLoopTest.swift @@ -14,7 +14,7 @@ import XCTest import NIO import Dispatch -import ConcurrencyHelpers +import NIOConcurrencyHelpers public class EventLoopTest : XCTestCase {