Skip to content

Commit

Permalink
Disable exceptions when built with -fno-exceptions (#59)
Browse files Browse the repository at this point in the history
ParlayLib now removes all exception code when compiled with
-fno-exceptions. You can also explicitly disable exceptions just
inside ParlayLib with -DPARLAY_NO_EXCEPTIONS.
  • Loading branch information
DanielLiamAnderson authored Sep 11, 2023
1 parent 4579f39 commit 9e60787
Show file tree
Hide file tree
Showing 20 changed files with 216 additions and 35 deletions.
32 changes: 32 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,38 @@ jobs:
cd build
ctest -C Debug --no-tests=error --output-on-failure
noexcept:
name: ubuntu-22.04 GCC 12 Exceptions Disabled (Debug)
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v2

- name: Install Compiler
shell: bash
run: |
sudo add-apt-repository ppa:ubuntu-toolchain-r/test
sudo apt-get update
sudo apt-get -qq install gcc-12 g++-12
- name: Configure
shell: bash
run: |
mkdir build && cd build
CC=gcc-12 CXX=g++-12 cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_FLAGS="-fno-exceptions" -DPARLAY_TEST=On DPARLAY_BENCHMARK=On ..
- name: Build
shell: bash
run: |
cd build
cmake --build . --config Debug
- name: Test
shell: bash
run: |
cd build
ctest -C Debug --no-tests=error --output-on-failure
cppcheck:
name: Cppcheck
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
Expand Down
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
# -------------------------------------------------------------------

cmake_minimum_required(VERSION 3.14)
project(PARLAY VERSION 2.2.0
project(PARLAY VERSION 2.2.1
DESCRIPTION "A collection of parallel algorithms and other support for parallelism in C++"
LANGUAGES CXX)

Expand Down
4 changes: 4 additions & 0 deletions analysis/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,18 @@ configure_danalysis(
${PARLAY_INCLUDE_DIR}
${PARLAY_INCLUDE_DIR}/parlay
FILES
${PARLAY_INCLUDE_DIR}/parlay/internal/binary_search.h
${PARLAY_INCLUDE_DIR}/parlay/internal/block_allocator.h
${PARLAY_INCLUDE_DIR}/parlay/internal/block_delayed.h
${PARLAY_INCLUDE_DIR}/parlay/internal/bucket_sort.h
${PARLAY_INCLUDE_DIR}/parlay/internal/collect_reduce.h
${PARLAY_INCLUDE_DIR}/parlay/internal/counting_sort.h
${PARLAY_INCLUDE_DIR}/parlay/internal/file_map.h
${PARLAY_INCLUDE_DIR}/parlay/internal/group_by.h
${PARLAY_INCLUDE_DIR}/parlay/internal/heap_tree.h
${PARLAY_INCLUDE_DIR}/parlay/internal/integer_sort.h
${PARLAY_INCLUDE_DIR}/parlay/internal/pool_allocator.h
${PARLAY_INCLUDE_DIR}/parlay/internal/sample_sort.h
${PARLAY_INCLUDE_DIR}/parlay/internal/sequence_base.h
${PARLAY_INCLUDE_DIR}/parlay/internal/sequence_ops.h
${PARLAY_INCLUDE_DIR}/parlay/internal/stream_delayed.h
Expand Down
8 changes: 8 additions & 0 deletions include/parlay/alloc.h
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,11 @@ inline size_t alloc_padding_size(size_t n) { // in bytes

} // namespace internal

// GCC doesn't like placement new into the offset buffer
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wplacement-new"
#endif

// Allocate size bytes of uninitialized storage. Optionally ask for an
// aligned buffer of memory with the given alignment.
Expand Down Expand Up @@ -133,6 +138,9 @@ inline void p_free(void* ptr) {
internal::get_default_allocator().deallocate(buffer, size_t{1} << size_t(h.log_size));
}

#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic pop
#endif

// ----------------------------------------------------------------------------
// Container allocator
Expand Down
12 changes: 5 additions & 7 deletions include/parlay/delayed_sequence.h
Original file line number Diff line number Diff line change
Expand Up @@ -271,11 +271,9 @@ class delayed_sequence {
// Subscript access with bounds checking
T at(size_t i) const {
if (i < first || i >= last) {
throw std::out_of_range("Delayed sequence access out of"
"range at " + std::to_string(i) +
"for a sequence with bounds [" +
std::to_string(first) + ", " +
std::to_string(last) + ")");
throw_exception_or_terminate<std::out_of_range>("Delayed sequence access out of range at " + std::to_string(i) +
"for a sequence with bounds [" + std::to_string(first) + ", " +
std::to_string(last) + ")");
}
return f(i);
}
Expand All @@ -297,13 +295,13 @@ class delayed_sequence {
#endif

// Size
size_t size() const {
[[nodiscard]] size_t size() const {
assert(first <= last);
return last - first;
}

// Is empty?
bool empty() const {
[[nodiscard]] bool empty() const {
return size() == 0;
}

Expand Down
18 changes: 14 additions & 4 deletions include/parlay/internal/bucket_sort.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,20 @@
#ifndef PARLAY_BUCKET_SORT_H_
#define PARLAY_BUCKET_SORT_H_

#include <cassert>
#include <cstddef>

#include <limits>

#include "merge_sort.h"
#include "quicksort.h"
#include "sequence_ops.h"

#include "uninitialized_sequence.h"

#include "../parallel.h"
#include "../relocation.h"
#include "../sequence.h"
#include "../slice.h"
#include "../utilities.h"

namespace parlay {
Expand All @@ -30,9 +40,9 @@ void radix_step_(slice<InIterator, InIterator> A,
counts[i] = s;
}

for (std::ptrdiff_t j = n - 1; j >= 0; j--) {
auto x = --counts[keys[j]];
uninitialized_relocate(&B[x], &A[j]);
for (size_t j = n; j > 0; j--) {
auto x = --counts[keys[j-1]];
uninitialized_relocate(&B[x], &A[j-1]);
}
}

Expand Down
1 change: 1 addition & 0 deletions include/parlay/internal/counting_sort.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <algorithm>
#include <iterator>
#include <limits>
#include <type_traits>
#include <utility>

#include "sequence_ops.h"
Expand Down
2 changes: 2 additions & 0 deletions include/parlay/internal/file_map.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@

#if defined(PARLAY_NO_FILE_MAP) || defined(PARLAY_USE_FALLBACK_FILE_MAP)

#include <cassert>

#include <fstream>
#include <string>

Expand Down
18 changes: 14 additions & 4 deletions include/parlay/internal/integer_sort.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,26 @@
#ifndef PARLAY_INTEGER_SORT_H_
#define PARLAY_INTEGER_SORT_H_

#include <cmath>
#include <cstdint>
#include <cassert>
#include <cstdio>

#include <algorithm>
#include <string>
#include <tuple>
#include <type_traits>
#include <utility>

#include "counting_sort.h"
#include "sequence_ops.h"
#include "quicksort.h"
#include "uninitialized_sequence.h"
#include "get_time.h"

#include "../delayed_sequence.h"
#include "../monoid.h"
#include "../parallel.h"
#include "../range.h"
#include "../relocation.h"
#include "../sequence.h"
#include "../slice.h"
#include "../utilities.h"

Expand Down Expand Up @@ -283,8 +292,9 @@ sequence<size_t> integer_sort_r(slice<InIterator, InIterator> In,
auto b = Tmp.cut(start, end);
sequence<size_t> r;

auto new_parallelism = (parallelism * static_cast<float>(end - start)) / static_cast<float>(n + 1);
r = integer_sort_r<typename std::negation<inplace_tag>::type, uninitialized_relocate_tag>(
a, b, a, g, shift_bits, num_inner_buckets, (parallelism * (end - start)) / (n + 1));
a, b, a, g, shift_bits, num_inner_buckets, new_parallelism);

if (return_offsets) {
size_t bstart = (std::min)(i * num_inner_buckets, num_buckets);
Expand Down
2 changes: 1 addition & 1 deletion include/parlay/internal/pool_allocator.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include <utility>
#include <vector>

#include "../portability.h"
#include "../utilities.h"

#include "block_allocator.h"
Expand Down Expand Up @@ -77,7 +78,6 @@ struct pool_allocator {
}

void* a = ::operator new(alloc_size, std::align_val_t{max_alignment});
if (a == nullptr) throw std::bad_alloc();

large_allocated += n;
return a;
Expand Down
13 changes: 11 additions & 2 deletions include/parlay/internal/sample_sort.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,24 @@
#ifndef PARLAY_SAMPLE_SORT_H_
#define PARLAY_SAMPLE_SORT_H_

#include <cmath>
#include <cassert>
#include <cstdio>
#include <cstring>

#include <iterator>
#include <limits>
#include <type_traits>

#include "bucket_sort.h"
#include "quicksort.h"
#include "sequence_ops.h"
#include "transpose.h"
#include "uninitialized_sequence.h"

#include "../delayed_sequence.h"
#include "../parallel.h"
#include "../relocation.h"
#include "../sequence.h"
#include "../slice.h"
#include "../utilities.h"

namespace parlay {
Expand Down
1 change: 1 addition & 0 deletions include/parlay/internal/scheduler_plugins/sequential.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <cstddef>

#include <functional>
#include <utility>

namespace parlay {

Expand Down
11 changes: 6 additions & 5 deletions include/parlay/internal/uninitialized_sequence.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <memory>

#include "../alloc.h"
#include "../portability.h"

#include "debug_uninitialized.h"

Expand Down Expand Up @@ -156,7 +157,7 @@ class uninitialized_sequence {
std::swap(impl.data, other.impl.data);
}

size_type size() const { return impl.n; }
[[nodiscard]] size_type size() const { return impl.n; }

value_type* data() { return impl.data; }

Expand All @@ -167,8 +168,8 @@ class uninitialized_sequence {

value_type& at(size_t i) {
if (i >= size()) {
throw std::out_of_range("uninitialized_sequence access out of bounds: length = " +
std::to_string(size()) + ", index = " + std::to_string(i));
throw_exception_or_terminate<std::out_of_range>("uninitialized_sequence access out of bounds: length = " +
std::to_string(size()) + ", index = " + std::to_string(i));
}
else {
return impl.data[i];
Expand All @@ -177,8 +178,8 @@ class uninitialized_sequence {

const value_type& at(size_t i) const {
if (i >= size()) {
throw std::out_of_range("uninitialized_sequence access out of bounds: length = " +
std::to_string(size()) + ", index = " + std::to_string(i));
throw_exception_or_terminate<std::out_of_range>("uninitialized_sequence access out of bounds: length = " +
std::to_string(size()) + ", index = " + std::to_string(i));
}
else {
return impl.data[i];
Expand Down
2 changes: 1 addition & 1 deletion include/parlay/internal/work_stealing_deque.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ struct Deque {
deq[local_bot].job.store(job, std::memory_order_release); // shared store
local_bot += 1;
if (local_bot == q_size) {
std::cerr << "internal error: scheduler queue overflow" << std::endl;
std::cerr << "internal error: scheduler queue overflow\n";
std::abort();
}
bot.store(local_bot, std::memory_order_seq_cst); // shared store
Expand Down
6 changes: 2 additions & 4 deletions include/parlay/internal/work_stealing_job.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,8 @@ namespace parlay {
// and return nothing. Could be a lambda, e.g. [] () {}.

struct WorkStealingJob {
WorkStealingJob() {
done.store(false, std::memory_order_relaxed);
}
~WorkStealingJob() = default;
WorkStealingJob() : done{false} { }
virtual ~WorkStealingJob() = default;
void operator()() {
assert(done.load(std::memory_order_relaxed) == false);
execute();
Expand Down
46 changes: 46 additions & 0 deletions include/parlay/portability.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@
#endif
#endif

#include <cstdlib>

#include <iostream>

namespace parlay {

// PARLAY_INLINE: Ask the compiler politely to inline the given function.
Expand All @@ -24,6 +28,25 @@ namespace parlay {
#define PARLAY_INLINE inline
#endif

// PARLAY_NOINLINE: Ask the compiler to *not* inline the given function
#if defined(__GNUC__)
#define PARLAY_NOINLINE __attribute__((__noinline__))
#elif defined(_MSC_VER)
#define PARLAY_NOINLINE __declspec(noinline)
#else
#define PARLAY_NOINLINE
#endif

// PARLAY_COLD: Ask the compiler to place the given function far away from other code
#if defined(__GNUC__)
#define PARLAY_COLD __attribute__((__cold__))
#elif defined(_MSC_VER)
#define PARLAY_COLD
#else
#define PARLAY_COLD
#endif


// PARLAY_PACKED: Ask the compiler to pack a struct into less memory by not padding
#if defined(__GNUC__)
#define PARLAY_PACKED __attribute__((packed))
Expand Down Expand Up @@ -61,6 +84,29 @@ namespace parlay {
#define PARLAY_UNLIKELY
#endif

// Check for exceptions. The standard suggests __cpp_exceptions. Clang/GCC defined __EXCEPTIONS.
// MSVC disables them with _HAS_EXCEPTIONS=0. Might not cover obscure compilers/STLs.
//
// Exceptions can be explicitly disabled in Parlay with PARLAY_NO_EXCEPTIONS.
#if !defined(PARLAY_NO_EXCEPTIONS) && \
((defined(__cpp_exceptions) && __cpp_exceptions != 0) || \
(defined(__EXCEPTIONS)) || \
(defined(_HAS_EXCEPTIONS) && _HAS_EXCEPTIONS == 1) || \
(defined(_MSC_VER) && !defined(_HAS_EXCEPTIONS)))
#define PARLAY_EXCEPTIONS_ENABLED
#endif

template<typename Exception, typename... Args>
[[noreturn]] PARLAY_NOINLINE PARLAY_COLD void throw_exception_or_terminate(Args&&... args) {
#if defined(PARLAY_EXCEPTIONS_ENABLED)
throw Exception{std::forward<Args>(args)...};
#else
std::cerr << Exception{std::forward<Args>(args)...}.what() << "\n";
std::terminate();
#endif
}


} // namespace parlay

#endif // PARLAY_PORTABILITY_H_
1 change: 0 additions & 1 deletion include/parlay/scheduler.h
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,6 @@ class fork_join_scheduler {
execute_right();
}
else {
//sched->wait_for(right_job, conservative);
auto done = [&]() { return right_job.finished(); };
scheduler.wait_until(done, conservative);
assert(right_job.finished());
Expand Down
Loading

0 comments on commit 9e60787

Please sign in to comment.