diff --git a/ctl/equal.h b/ctl/equal.h new file mode 100644 index 000000000..464e09c7c --- /dev/null +++ b/ctl/equal.h @@ -0,0 +1,64 @@ +// -*-mode:c++;indent-tabs-mode:nil;c-basic-offset:4;tab-width:8;coding:utf-8-*- +// vi: set et ft=cpp ts=4 sts=4 sw=4 fenc=utf-8 :vi +#ifndef CTL_EQUAL_H_ +#define CTL_EQUAL_H_ + +namespace ctl { + +template +bool +equal(InputIt1 first1, InputIt1 last1, InputIt2 first2) +{ + for (; first1 != last1; ++first1, ++first2) { + if (!(*first1 == *first2)) { + return false; + } + } + return true; +} + +// Overload that takes a predicate +template +bool +equal(InputIt1 first1, InputIt1 last1, InputIt2 first2, BinaryPredicate pred) +{ + for (; first1 != last1; ++first1, ++first2) { + if (!pred(*first1, *first2)) { + return false; + } + } + return true; +} + +// Overloads that take two ranges (C++14 and later) +template +bool +equal(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2) +{ + for (; first1 != last1 && first2 != last2; ++first1, ++first2) { + if (!(*first1 == *first2)) { + return false; + } + } + return first1 == last1 && first2 == last2; +} + +template +bool +equal(InputIt1 first1, + InputIt1 last1, + InputIt2 first2, + InputIt2 last2, + BinaryPredicate pred) +{ + for (; first1 != last1 && first2 != last2; ++first1, ++first2) { + if (!pred(*first1, *first2)) { + return false; + } + } + return first1 == last1 && first2 == last2; +} + +} // namespace ctl + +#endif /* CTL_EQUAL_H_ */ diff --git a/ctl/initializer_list.h b/ctl/initializer_list.h new file mode 100644 index 000000000..8c9241ab8 --- /dev/null +++ b/ctl/initializer_list.h @@ -0,0 +1,64 @@ +// -*-mode:c++;indent-tabs-mode:nil;c-basic-offset:4;tab-width:8;coding:utf-8-*- +// vi: set et ft=cpp ts=4 sts=4 sw=4 fenc=utf-8 :vi +#ifndef _LIBCPP_INITIALIZER_LIST +#define _LIBCPP_INITIALIZER_LIST + +namespace std { + +template +class initializer_list +{ + constexpr initializer_list(const T* b, size_t s) noexcept + : begin_(b), size_(s) + { + } + + public: + typedef T value_type; + typedef const T& reference; + typedef const T& const_reference; + typedef size_t size_type; + typedef const T* iterator; + typedef const T* const_iterator; + + constexpr initializer_list() noexcept : begin_(nullptr), size_(0) + { + } + + constexpr size_t size() const noexcept + { + return size_; + } + + constexpr const T* begin() const noexcept + { + return begin_; + } + + constexpr const T* end() const noexcept + { + return begin_ + size_; + } + + private: + const T* begin_; + size_t size_; +}; + +template +inline constexpr const T* +begin(initializer_list i) noexcept +{ + return i.begin(); +} + +template +inline constexpr const T* +end(initializer_list i) noexcept +{ + return i.end(); +} + +} // namespace std + +#endif // _LIBCPP_INITIALIZER_LIST diff --git a/ctl/less.h b/ctl/less.h new file mode 100644 index 000000000..12ec3c47e --- /dev/null +++ b/ctl/less.h @@ -0,0 +1,38 @@ +// -*-mode:c++;indent-tabs-mode:nil;c-basic-offset:4;tab-width:8;coding:utf-8-*- +// vi: set et ft=cpp ts=4 sts=4 sw=4 fenc=utf-8 :vi +#ifndef CTL_LESS_H_ +#define CTL_LESS_H_ +#include "utility.h" + +namespace ctl { + +template +struct less +{ + constexpr bool operator()(const T& lhs, const T& rhs) const + { + return lhs < rhs; + } + + typedef T first_argument_type; + typedef T second_argument_type; + typedef bool result_type; +}; + +template<> +struct less +{ + template + constexpr auto operator()(T&& lhs, + U&& rhs) const -> decltype(ctl::forward(lhs) < + ctl::forward(rhs)) + { + return ctl::forward(lhs) < ctl::forward(rhs); + } + + typedef void is_transparent; +}; + +} // namespace ctl + +#endif /* CTL_LESS_H_ */ diff --git a/ctl/map.h b/ctl/map.h new file mode 100644 index 000000000..ce564793c --- /dev/null +++ b/ctl/map.h @@ -0,0 +1,352 @@ +// -*-mode:c++;indent-tabs-mode:nil;c-basic-offset:4;tab-width:8;coding:utf-8-*- +// vi: set et ft=cpp ts=4 sts=4 sw=4 fenc=utf-8 :vi +#ifndef CTL_MAP_H_ +#define CTL_MAP_H_ +#include "set.h" + +namespace ctl { + +template> +class map +{ + class EntryCompare + { + public: + explicit EntryCompare(Compare comp = Compare()) : comp_(comp) + { + } + + bool operator()(const ctl::pair& lhs, + const ctl::pair& rhs) const + { + return comp_(lhs.first, rhs.first); + } + + private: + Compare comp_; + }; + + ctl::set, EntryCompare> data_; + + public: + using key_type = Key; + using mapped_type = Value; + using value_type = ctl::pair; + using size_type = typename ctl::set::size_type; + using difference_type = + typename ctl::set::difference_type; + using key_compare = Compare; + using value_compare = EntryCompare; + using iterator = typename ctl::set::iterator; + using const_iterator = + typename ctl::set::const_iterator; + using reverse_iterator = + typename ctl::set::reverse_iterator; + using const_reverse_iterator = + typename ctl::set::const_reverse_iterator; + + map() : data_(EntryCompare()) + { + } + + explicit map(const Compare& comp) : data_(EntryCompare(comp)) + { + } + + map(const map& other) = default; + map(map&& other) noexcept = default; + map(std::initializer_list init, const Compare& comp = Compare()) + : data_(init, EntryCompare(comp)) + { + } + + template + map(InputIt first, InputIt last, const Compare& comp = Compare()) + : data_(first, last, EntryCompare(comp)) + { + } + + map& operator=(const map& other) = default; + map& operator=(map&& other) noexcept = default; + map& operator=(std::initializer_list ilist) + { + data_ = ilist; + return *this; + } + + iterator begin() noexcept + { + return data_.begin(); + } + + const_iterator begin() const noexcept + { + return data_.begin(); + } + + const_iterator cbegin() const noexcept + { + return data_.cbegin(); + } + + iterator end() noexcept + { + return data_.end(); + } + + const_iterator end() const noexcept + { + return data_.end(); + } + + const_iterator cend() const noexcept + { + return data_.cend(); + } + + reverse_iterator rbegin() noexcept + { + return data_.rbegin(); + } + + const_reverse_iterator rbegin() const noexcept + { + return data_.rbegin(); + } + + const_reverse_iterator crbegin() const noexcept + { + return data_.crbegin(); + } + + reverse_iterator rend() noexcept + { + return data_.rend(); + } + + const_reverse_iterator rend() const noexcept + { + return data_.rend(); + } + + const_reverse_iterator crend() const noexcept + { + return data_.crend(); + } + + bool empty() const noexcept + { + return data_.empty(); + } + + size_type size() const noexcept + { + return data_.size(); + } + + size_type max_size() const noexcept + { + return data_.max_size(); + } + + Value& operator[](const Key& key) + { + return ((data_.insert(make_pair(key, Value()))).first)->second; + } + + Value& operator[](Key&& key) + { + return ((data_.insert(make_pair(ctl::move(key), Value()))).first) + ->second; + } + + Value& at(const Key& key) + { + auto it = find(key); + if (it == end()) + __builtin_trap(); + return it->second; + } + + const Value& at(const Key& key) const + { + auto it = find(key); + if (it == end()) + __builtin_trap(); + return it->second; + } + + ctl::pair insert(const value_type& value) + { + return data_.insert(value); + } + + ctl::pair insert(value_type&& value) + { + return data_.insert(ctl::move(value)); + } + + template + ctl::pair insert(P&& value) + { + return data_.insert(value_type(ctl::forward

(value))); + } + + iterator insert(const_iterator hint, const value_type& value) + { + return data_.insert(hint, value); + } + + iterator insert(const_iterator hint, value_type&& value) + { + return data_.insert(hint, ctl::move(value)); + } + + template + iterator insert(const_iterator hint, P&& value) + { + return data_.insert(hint, value_type(ctl::forward

(value))); + } + + template + void insert(InputIt first, InputIt last) + { + data_.insert(first, last); + } + + void insert(std::initializer_list ilist) + { + data_.insert(ilist); + } + + template + ctl::pair emplace(Args&&... args) + { + return data_.emplace(ctl::forward(args)...); + } + + template + iterator emplace_hint(const_iterator hint, Args&&... args) + { + return data_.emplace_hint(hint, ctl::forward(args)...); + } + + iterator erase(const_iterator pos) + { + return data_.erase(pos); + } + + iterator erase(const_iterator first, const_iterator last) + { + return data_.erase(first, last); + } + + size_type erase(const Key& key) + { + return data_.erase(make_pair(key, Value())); + } + + void swap(map& other) noexcept + { + data_.swap(other.data_); + } + + void clear() noexcept + { + data_.clear(); + } + + iterator find(const Key& key) + { + return data_.find(make_pair(key, Value())); + } + + const_iterator find(const Key& key) const + { + return data_.find(make_pair(key, Value())); + } + + size_type count(const Key& key) const + { + return data_.count(make_pair(key, Value())); + } + + iterator lower_bound(const Key& key) + { + return data_.lower_bound(make_pair(key, Value())); + } + + const_iterator lower_bound(const Key& key) const + { + return data_.lower_bound(make_pair(key, Value())); + } + + iterator upper_bound(const Key& key) + { + return data_.upper_bound(make_pair(key, Value())); + } + + const_iterator upper_bound(const Key& key) const + { + return data_.upper_bound(make_pair(key, Value())); + } + + ctl::pair equal_range(const Key& key) + { + return data_.equal_range(make_pair(key, Value())); + } + + ctl::pair equal_range(const Key& key) const + { + return data_.equal_range(make_pair(key, Value())); + } + + key_compare key_comp() const + { + return data_.value_comp().comp; + } + + value_compare value_comp() const + { + return data_.value_comp(); + } + + friend bool operator==(const map& lhs, const map& rhs) + { + return lhs.data_ == rhs.data_; + } + + friend bool operator!=(const map& lhs, const map& rhs) + { + return !(lhs == rhs); + } + + friend bool operator<(const map& lhs, const map& rhs) + { + return lhs.data_ < rhs.data_; + } + + friend bool operator<=(const map& lhs, const map& rhs) + { + return !(rhs < lhs); + } + + friend bool operator>(const map& lhs, const map& rhs) + { + return rhs < lhs; + } + + friend bool operator>=(const map& lhs, const map& rhs) + { + return !(lhs < rhs); + } + + friend void swap(map& lhs, map& rhs) noexcept + { + lhs.swap(rhs); + } +}; + +} // namespace ctl + +#endif // CTL_MAP_H_ diff --git a/ctl/new.cc b/ctl/new.cc index 5df2d54ff..638f820a7 100644 --- a/ctl/new.cc +++ b/ctl/new.cc @@ -34,7 +34,10 @@ _ctl_alloc(size_t n, size_t a) static void* _ctl_alloc1(size_t n) { - return _ctl_alloc(n, 1); + void* p; + if (!(p = malloc(n))) + __builtin_trap(); + return p; } static void* @@ -68,10 +71,21 @@ COSMOPOLITAN_C_END_ now. If you have any brain cells left after reading this comment then go look at the eight operator delete weak references to free in the below. */ -__weak_reference(void* operator new(size_t, ctl::align_val_t), _ctl_alloc); -__weak_reference(void* operator new[](size_t, ctl::align_val_t), _ctl_alloc); -__weak_reference(void* operator new(size_t), _ctl_alloc1); -__weak_reference(void* operator new[](size_t), _ctl_alloc1); +__weak_reference(void* + operator new(size_t, ctl::align_val_t), + _ctl_alloc); + +__weak_reference(void* + operator new[](size_t, ctl::align_val_t), + _ctl_alloc); + +__weak_reference(void* + operator new(size_t), + _ctl_alloc1); + +__weak_reference(void* + operator new[](size_t), + _ctl_alloc1); // XXX clang-format currently mutilates these for some reason. // clang-format off diff --git a/ctl/new.h b/ctl/new.h index 97fe85ef8..9993a02f8 100644 --- a/ctl/new.h +++ b/ctl/new.h @@ -3,6 +3,10 @@ #ifndef COSMOPOLITAN_CTL_NEW_H_ #define COSMOPOLITAN_CTL_NEW_H_ +#ifndef TINY +__static_yoink("__demangle"); +#endif + namespace ctl { enum class align_val_t : size_t diff --git a/ctl/pair.h b/ctl/pair.h new file mode 100644 index 000000000..1bc045046 --- /dev/null +++ b/ctl/pair.h @@ -0,0 +1,150 @@ +// -*-mode:c++;indent-tabs-mode:nil;c-basic-offset:4;tab-width:8;coding:utf-8-*- +// vi: set et ft=cpp ts=4 sts=4 sw=4 fenc=utf-8 :vi +#ifndef CTL_PAIR_H_ +#define CTL_PAIR_H_ +#include "utility.h" + +namespace ctl { + +template +struct pair +{ + using first_type = T1; + using second_type = T2; + + T1 first; + T2 second; + + constexpr pair() : first(), second() + { + } + + constexpr pair(const T1& x, const T2& y) : first(x), second(y) + { + } + + constexpr pair(const pair& p) : first(p.first), second(p.second) + { + } + + template + constexpr pair(U1&& x, U2&& y) + : first(ctl::forward(x)), second(ctl::forward(y)) + { + } + + template + constexpr pair(const pair& p) : first(p.first), second(p.second) + { + } + + constexpr pair(pair&& p) + : first(ctl::move(p.first)), second(ctl::move(p.second)) + { + } + + template + constexpr pair(pair&& p) + : first(ctl::move(p.first)), second(ctl::move(p.second)) + { + } + + pair& operator=(const pair& other) + { + first = other.first; + second = other.second; + return *this; + } + + pair& operator=(pair&& other) noexcept + { + first = ctl::move(other.first); + second = ctl::move(other.second); + return *this; + } + + template + pair& operator=(const pair& other) + { + first = other.first; + second = other.second; + return *this; + } + + template + pair& operator=(pair&& other) + { + first = ctl::move(other.first); + second = ctl::move(other.second); + return *this; + } + + void swap(pair& other) noexcept + { + using ctl::swap; + swap(first, other.first); + swap(second, other.second); + } +}; + +template +constexpr pair +make_pair(T1&& t, T2&& u) +{ + return pair(ctl::forward(t), ctl::forward(u)); +} + +// Comparison operators +template +constexpr bool +operator==(const pair& lhs, const pair& rhs) +{ + return lhs.first == rhs.first && lhs.second == rhs.second; +} + +template +constexpr bool +operator!=(const pair& lhs, const pair& rhs) +{ + return !(lhs == rhs); +} + +template +constexpr bool +operator<(const pair& lhs, const pair& rhs) +{ + return lhs.first < rhs.first || + (!(rhs.first < lhs.first) && lhs.second < rhs.second); +} + +template +constexpr bool +operator<=(const pair& lhs, const pair& rhs) +{ + return !(rhs < lhs); +} + +template +constexpr bool +operator>(const pair& lhs, const pair& rhs) +{ + return rhs < lhs; +} + +template +constexpr bool +operator>=(const pair& lhs, const pair& rhs) +{ + return !(lhs < rhs); +} + +template +void +swap(pair& lhs, pair& rhs) noexcept(noexcept(lhs.swap(rhs))) +{ + lhs.swap(rhs); +} + +} // namespace ctl + +#endif // CTL_PAIR_H_ diff --git a/ctl/set.h b/ctl/set.h new file mode 100644 index 000000000..94644d267 --- /dev/null +++ b/ctl/set.h @@ -0,0 +1,945 @@ +// -*-mode:c++;indent-tabs-mode:nil;c-basic-offset:4;tab-width:8;coding:utf-8-*- +// vi: set et ft=cpp ts=4 sts=4 sw=4 fenc=utf-8 :vi +#ifndef CTL_SET_H_ +#define CTL_SET_H_ +#include "initializer_list.h" +#include "less.h" +#include "pair.h" + +namespace ctl { + +template> +class set +{ + struct rbtree + { + rbtree* left; + rbtree* right; + rbtree* parent; + bool is_red; + Key value; + + rbtree(const Key& val) + : left(nullptr) + , right(nullptr) + , parent(nullptr) + , is_red(true) + , value(val) + { + } + }; + + public: + using key_type = Key; + using value_type = Key; + using size_type = size_t; + using node_type = rbtree; + using key_compare = Compare; + using value_compare = Compare; + using difference_type = ptrdiff_t; + using reference = value_type&; + using pointer = value_type*; + using const_pointer = const value_type*; + using const_reference = const value_type&; + + class iterator + { + public: + using value_type = Key; + using difference_type = ptrdiff_t; + using pointer = Key*; + using reference = Key&; + + iterator() : node_(nullptr) + { + } + + reference operator*() const + { + return node_->value; + } + + pointer operator->() const + { + return &(node_->value); + } + + iterator& operator++() + { + if (node_ == nullptr) + return *this; + if (node_->right) { + node_ = leftmost(node_->right); + } else { + node_type* parent = node_->parent; + while (parent && node_ == parent->right) { + node_ = parent; + parent = parent->parent; + } + node_ = parent; + } + return *this; + } + + iterator& operator--() + { + if (node_ == nullptr) + __builtin_trap(); + if (node_->left) { + node_ = rightmost(node_->left); + } else { + node_type* parent = node_->parent; + for (;;) { + if (parent == nullptr) + break; + if (node_ == parent->left) { + node_ = parent; + parent = parent->parent; + } else { + break; + } + } + node_ = parent; + } + return *this; + } + + iterator operator++(int) + { + iterator tmp = *this; + ++(*this); + return tmp; + } + + iterator operator--(int) + { + iterator tmp = *this; + --(*this); + return tmp; + } + + bool operator==(const iterator& other) const + { + return node_ == other.node_; + } + + bool operator!=(const iterator& other) const + { + return !(*this == other); + } + + private: + friend class set; + node_type* node_; + + explicit iterator(node_type* node) : node_(node) + { + } + }; + + class reverse_iterator + { + public: + using value_type = Key; + using difference_type = ptrdiff_t; + using pointer = Key*; + using reference = Key&; + + reverse_iterator() : node_(nullptr) + { + } + + reference operator*() const + { + return node_->value; + } + + pointer operator->() const + { + return &(node_->value); + } + + reverse_iterator& operator++() + { + if (node_->left) { + node_ = rightmost(node_->left); + } else { + node_type* parent = node_->parent; + while (parent && node_ == parent->left) { + node_ = parent; + parent = parent->parent; + } + node_ = parent; + } + return *this; + } + + reverse_iterator operator++(int) + { + reverse_iterator tmp = *this; + ++(*this); + return tmp; + } + + reverse_iterator& operator--() + { + if (node_->right) { + node_ = leftmost(node_->right); + } else { + node_type* parent = node_->parent; + while (parent && node_ == parent->right) { + node_ = parent; + parent = parent->parent; + } + node_ = parent; + } + return *this; + } + + reverse_iterator operator--(int) + { + reverse_iterator tmp = *this; + --(*this); + return tmp; + } + + bool operator==(const reverse_iterator& other) const + { + return node_ == other.node_; + } + + bool operator!=(const reverse_iterator& other) const + { + return !(*this == other); + } + + iterator base() const + { + if (node_ == nullptr) + return iterator(leftmost(root_)); + reverse_iterator tmp = *this; + ++tmp; + return iterator(tmp.node_); + } + + private: + friend class set; + node_type* node_; + + explicit reverse_iterator(node_type* node) : node_(node) + { + } + }; + + using const_iterator = iterator; + using const_reverse_iterator = reverse_iterator; + + set() : root_(nullptr), size_(0), comp_(Compare()) + { + } + + explicit set(const Compare& comp) : root_(nullptr), size_(0), comp_(comp) + { + } + + template + set(InputIt first, InputIt last, const Compare& comp = Compare()) + : root_(nullptr), size_(0), comp_(comp) + { + for (; first != last; ++first) + insert(*first); + } + + set(const set& other) : root_(nullptr), size_(0) + { + if (other.root_) { + root_ = copier(other.root_); + size_ = other.size_; + } + } + + set(set&& other) noexcept : root_(other.root_), size_(other.size_) + { + other.root_ = nullptr; + other.size_ = 0; + } + + set(std::initializer_list init, const Compare& comp = Compare()) + : root_(nullptr), size_(0), comp_(comp) + { + for (const auto& value : init) + insert(value); + } + + ~set() + { + clear(); + } + + set& operator=(const set& other) + { + if (this != &other) { + clear(); + if (other.root_) { + root_ = copier(other.root_); + size_ = other.size_; + } + } + return *this; + } + + set& operator=(set&& other) noexcept + { + if (this != &other) { + clear(); + root_ = other.root_; + size_ = other.size_; + other.root_ = nullptr; + other.size_ = 0; + } + return *this; + } + + bool empty() const noexcept + { + return size_ == 0; + } + + size_type size() const noexcept + { + return size_; + } + + iterator begin() noexcept + { + if (root_) + return iterator(leftmost(root_)); + return iterator(nullptr); + } + + const_iterator begin() const noexcept + { + if (root_) + return const_iterator(leftmost(root_)); + return const_iterator(nullptr); + } + + const_iterator cbegin() const noexcept + { + return begin(); + } + + reverse_iterator rbegin() + { + return reverse_iterator(rightmost(root_)); + } + + const_reverse_iterator rbegin() const + { + return const_reverse_iterator(rightmost(root_)); + } + + const_reverse_iterator crbegin() const + { + return const_reverse_iterator(rightmost(root_)); + } + + iterator end() noexcept + { + return iterator(nullptr); + } + + const_iterator end() const noexcept + { + return const_iterator(nullptr); + } + + const_iterator cend() const noexcept + { + return end(); + } + + reverse_iterator rend() + { + return reverse_iterator(nullptr); + } + + const_reverse_iterator rend() const + { + return const_reverse_iterator(nullptr); + } + + const_reverse_iterator crend() const + { + return const_reverse_iterator(nullptr); + } + + void clear() noexcept + { + clearer(root_); + root_ = nullptr; + size_ = 0; + } + + pair insert(value_type&& value) + { + return insert_node(new node_type(ctl::move(value))); + } + + pair insert(const value_type& value) + { + return insert_node(new node_type(value)); + } + + iterator insert(const_iterator hint, const value_type& value) + { + return insert(value).first; + } + + iterator insert(const_iterator hint, value_type&& value) + { + return insert(ctl::move(value)).first; + } + + template + void insert(InputIt first, InputIt last) + { + for (; first != last; ++first) + insert(*first); + } + + void insert(std::initializer_list ilist) + { + for (const auto& value : ilist) + insert(value); + } + + template + pair emplace(Args&&... args) + { + value_type value(ctl::forward(args)...); + return insert(ctl::move(value)); + } + + iterator erase(iterator pos) + { + node_type* node = pos.node_; + iterator res = ++pos; + erase_node(node); + return res; + } + + size_type erase(const key_type& key) + { + node_type* node = get_element(key); + if (node == nullptr) + return 0; + erase_node(node); + return 1; + } + + iterator erase(const_iterator first, const_iterator last) + { + while (first != last) + first = erase(first); + return iterator(const_cast(last.node_)); + } + + void swap(set& other) noexcept + { + ctl::swap(root_, other.root_); + ctl::swap(size_, other.size_); + } + + pair equal_range(const key_type& key) + { + return { iterator(get_floor(key)), iterator(get_ceiling(key)) }; + } + + pair equal_range(const key_type& key) const + { + return { const_iterator(get_floor(key)), + const_iterator(get_ceiling(key)) }; + } + + iterator lower_bound(const key_type& key) + { + return iterator(get_floor(key)); + } + + const_iterator lower_bound(const key_type& key) const + { + return const_iterator(get_floor(key)); + } + + iterator upper_bound(const key_type& key) + { + return iterator(get_ceiling(key)); + } + + const_iterator upper_bound(const key_type& key) const + { + return const_iterator(get_ceiling(key)); + } + + size_type count(const key_type& key) const + { + return !!get_element(key); + } + + iterator find(const key_type& key) + { + return iterator(get_element(key)); + } + + const_iterator find(const key_type& key) const + { + return const_iterator(get_element(key)); + } + + template + iterator emplace_hint(const_iterator hint, Args&&... args) + { + return emplace(ctl::forward(args)...).first; + } + + void check() const + { + size_type count = 0; + if (root_ != nullptr) { + if (root_->is_red) + // ILLEGAL TREE: root node must be black + __builtin_trap(); + int black_height = -1; + checker(root_, nullptr, 0, black_height); + count = tally(root_); + } + if (count != size_) + // ILLEGAL TREE: unexpected number of nodes + __builtin_trap(); + } + + private: + static node_type* leftmost(node_type* node) noexcept + { + while (node && node->left) + node = node->left; + return node; + } + + static node_type* rightmost(node_type* node) noexcept + { + while (node && node->right) + node = node->right; + return node; + } + + static void clearer(node_type* node) noexcept + { + node_type* right; + for (; node; node = right) { + right = node->right; + clearer(node->left); + delete node; + } + } + + static node_type* copier(const node_type* node) + { + if (node == nullptr) + return nullptr; + node_type* new_node = new node_type(node->value); + new_node->left = copier(node->left); + new_node->right = copier(node->right); + if (new_node->left) + new_node->left->parent = new_node; + if (new_node->right) + new_node->right->parent = new_node; + return new_node; + } + + static size_type tally(const node_type* node) + { + if (node == nullptr) + return 0; + return 1 + tally(node->left) + tally(node->right); + } + + template + node_type* get_element(const K& key) const + { + node_type* current = root_; + while (current != nullptr) { + if (comp_(key, current->value)) { + current = current->left; + } else if (comp_(current->value, key)) { + current = current->right; + } else { + return current; + } + } + return nullptr; + } + + template + node_type* get_floor(const K& key) const + { + node_type* result = nullptr; + node_type* current = root_; + while (current != nullptr) { + if (!comp_(current->value, key)) { + result = current; + current = current->left; + } else { + current = current->right; + } + } + return result; + } + + template + node_type* get_ceiling(const K& key) const + { + node_type* result = nullptr; + node_type* current = root_; + while (current != nullptr) { + if (comp_(key, current->value)) { + result = current; + current = current->left; + } else { + current = current->right; + } + } + return result; + } + + pair insert_node(node_type* node) + { + if (root_ == nullptr) { + root_ = node; + root_->is_red = false; + size_++; + return { iterator(root_), true }; + } + node_type* current = root_; + node_type* parent = nullptr; + while (current != nullptr) { + parent = current; + if (comp_(node->value, current->value)) { + current = current->left; + } else if (comp_(current->value, node->value)) { + current = current->right; + } else { + delete node; // already exists + return { iterator(current), false }; + } + } + if (comp_(node->value, parent->value)) { + parent->left = node; + } else { + parent->right = node; + } + node->parent = parent; + size_++; + rebalance_after_insert(node); + return { iterator(node), true }; + } + + void erase_node(node_type* node) + { + node_type* y = node; + node_type* x = nullptr; + node_type* x_parent = nullptr; + bool y_original_color = y->is_red; + if (node->left == nullptr) { + x = node->right; + transplant(node, node->right); + x_parent = node->parent; + } else if (node->right == nullptr) { + x = node->left; + transplant(node, node->left); + x_parent = node->parent; + } else { + y = leftmost(node->right); + y_original_color = y->is_red; + x = y->right; + if (y->parent == node) { + if (x) + x->parent = y; + x_parent = y; + } else { + transplant(y, y->right); + y->right = node->right; + y->right->parent = y; + x_parent = y->parent; + } + transplant(node, y); + y->left = node->left; + y->left->parent = y; + y->is_red = node->is_red; + } + if (!y_original_color) + rebalance_after_erase(x, x_parent); + delete node; + --size_; + } + + void left_rotate(node_type* x) + { + node_type* y = x->right; + x->right = y->left; + if (y->left != nullptr) + y->left->parent = x; + y->parent = x->parent; + if (x->parent == nullptr) { + root_ = y; + } else if (x == x->parent->left) { + x->parent->left = y; + } else { + x->parent->right = y; + } + y->left = x; + x->parent = y; + } + + void right_rotate(node_type* y) + { + node_type* x = y->left; + y->left = x->right; + if (x->right != nullptr) + x->right->parent = y; + x->parent = y->parent; + if (y->parent == nullptr) { + root_ = x; + } else if (y == y->parent->right) { + y->parent->right = x; + } else { + y->parent->left = x; + } + x->right = y; + y->parent = x; + } + + void transplant(node_type* u, node_type* v) + { + if (u->parent == nullptr) { + root_ = v; + } else if (u == u->parent->left) { + u->parent->left = v; + } else { + u->parent->right = v; + } + if (v != nullptr) + v->parent = u->parent; + } + + void checker(const node_type* node, + const node_type* parent, + int black_count, + int& black_height) const + { + if (node == nullptr) { + // Leaf nodes are considered black + if (black_height == -1) { + black_height = black_count; + } else if (black_count != black_height) { + // ILLEGAL TREE: Black height mismatch + __builtin_trap(); + } + return; + } + if (node->parent != parent) + // ILLEGAL TREE: Parent link is incorrect + __builtin_trap(); + if (parent) { + if (parent->left == node && !comp_(node->value, parent->value)) + // ILLEGAL TREE: Binary search property violated on left child + __builtin_trap(); + if (parent->right == node && !comp_(parent->value, node->value)) + // ILLEGAL TREE: Binary search property violated on right child + __builtin_trap(); + } + if (!node->is_red) { + black_count++; + } else if (parent != nullptr && parent->is_red) { + // ILLEGAL TREE: Red node has red child + __builtin_trap(); + } + checker(node->left, node, black_count, black_height); + checker(node->right, node, black_count, black_height); + } + + void rebalance_after_insert(node_type* node) + { + node->is_red = true; + while (node != root_ && node->parent->is_red) { + if (node->parent == node->parent->parent->left) { + node_type* uncle = node->parent->parent->right; + if (uncle && uncle->is_red) { + node->parent->is_red = false; + uncle->is_red = false; + node->parent->parent->is_red = true; + node = node->parent->parent; + } else { + if (node == node->parent->right) { + node = node->parent; + left_rotate(node); + } + node->parent->is_red = false; + node->parent->parent->is_red = true; + right_rotate(node->parent->parent); + } + } else { + node_type* uncle = node->parent->parent->left; + if (uncle && uncle->is_red) { + node->parent->is_red = false; + uncle->is_red = false; + node->parent->parent->is_red = true; + node = node->parent->parent; + } else { + if (node == node->parent->left) { + node = node->parent; + right_rotate(node); + } + node->parent->is_red = false; + node->parent->parent->is_red = true; + left_rotate(node->parent->parent); + } + } + } + root_->is_red = false; + } + + void rebalance_after_erase(node_type* node, node_type* parent) + { + while (node != root_ && (node == nullptr || !node->is_red)) { + if (node == parent->left) { + node_type* sibling = parent->right; + if (sibling->is_red) { + sibling->is_red = false; + parent->is_red = true; + left_rotate(parent); + sibling = parent->right; + } + if ((sibling->left == nullptr || !sibling->left->is_red) && + (sibling->right == nullptr || !sibling->right->is_red)) { + sibling->is_red = true; + node = parent; + parent = node->parent; + } else { + if (sibling->right == nullptr || !sibling->right->is_red) { + sibling->left->is_red = false; + sibling->is_red = true; + right_rotate(sibling); + sibling = parent->right; + } + sibling->is_red = parent->is_red; + parent->is_red = false; + sibling->right->is_red = false; + left_rotate(parent); + node = root_; + break; + } + } else { + node_type* sibling = parent->left; + if (sibling->is_red) { + sibling->is_red = false; + parent->is_red = true; + right_rotate(parent); + sibling = parent->left; + } + if ((sibling->right == nullptr || !sibling->right->is_red) && + (sibling->left == nullptr || !sibling->left->is_red)) { + sibling->is_red = true; + node = parent; + parent = node->parent; + } else { + if (sibling->left == nullptr || !sibling->left->is_red) { + sibling->right->is_red = false; + sibling->is_red = true; + left_rotate(sibling); + sibling = parent->left; + } + sibling->is_red = parent->is_red; + parent->is_red = false; + sibling->left->is_red = false; + right_rotate(parent); + node = root_; + break; + } + } + } + if (node != nullptr) + node->is_red = false; + } + + node_type* root_; + size_type size_; + Compare comp_; +}; + +template> +bool +operator==(const set& lhs, const set& rhs) +{ + if (lhs.size() != rhs.size()) + return false; + auto i = lhs.cbegin(); + auto j = rhs.cbegin(); + for (; i != lhs.end(); ++i, ++j) { + if (!(*i == *j)) + return false; + } + return true; +} + +template> +bool +operator<(const set& lhs, const set& rhs) +{ + auto i = lhs.cbegin(); + auto j = rhs.cbegin(); + for (; i != lhs.end() && j != rhs.end(); ++i, ++j) { + if (Compare()(*i, *j)) + return true; + if (Compare()(*j, *i)) + return false; + } + return i == lhs.end() && j != rhs.end(); +} + +template> +bool +operator!=(const set& lhs, const set& rhs) +{ + return !(lhs == rhs); +} + +template> +bool +operator<=(const set& lhs, const set& rhs) +{ + return !(rhs < lhs); +} + +template> +bool +operator>(const set& lhs, const set& rhs) +{ + return rhs < lhs; +} + +template> +bool +operator>=(const set& lhs, const set& rhs) +{ + return !(lhs < rhs); +} + +template> +void +swap(set& lhs, set& rhs) noexcept; + +} // namespace ctl + +#endif // CTL_SET_H_ diff --git a/ctl/string.h b/ctl/string.h index 84aac204b..6b6935424 100644 --- a/ctl/string.h +++ b/ctl/string.h @@ -1,7 +1,7 @@ // -*-mode:c++;indent-tabs-mode:nil;c-basic-offset:4;tab-width:8;coding:utf-8-*- // vi: set et ft=cpp ts=4 sts=4 sw=4 fenc=utf-8 :vi -#ifndef COSMOPOLITAN_CTL_STRING_H_ -#define COSMOPOLITAN_CTL_STRING_H_ +#ifndef CTL_STRING_H_ +#define CTL_STRING_H_ #include "string_view.h" namespace ctl { @@ -397,4 +397,4 @@ operator"" s(const char* s, size_t n) } #pragma GCC diagnostic pop -#endif // COSMOPOLITAN_CTL_STRING_H_ +#endif // CTL_STRING_H_ diff --git a/examples/greenbean.c b/examples/greenbean.c index 7bace793c..969051e82 100644 --- a/examples/greenbean.c +++ b/examples/greenbean.c @@ -23,6 +23,7 @@ #include #include #include +#include "libc/mem/leaks.h" /** * @fileoverview greenbean lightweight threaded web server @@ -411,7 +412,5 @@ int main(int argc, char *argv[]) { unassert(!pthread_mutex_destroy(&statuslock)); // quality assurance - if (IsModeDbg()) { - CheckForMemoryLeaks(); - } + CheckForMemoryLeaks(); } diff --git a/libc/calls/fcntl-nt.c b/libc/calls/fcntl-nt.c index 830a50830..73584e48d 100644 --- a/libc/calls/fcntl-nt.c +++ b/libc/calls/fcntl-nt.c @@ -28,11 +28,11 @@ #include "libc/calls/wincrash.internal.h" #include "libc/errno.h" #include "libc/intrin/kprintf.h" -#include "libc/intrin/leaky.internal.h" #include "libc/intrin/weaken.h" #include "libc/limits.h" #include "libc/log/backtrace.internal.h" #include "libc/macros.internal.h" +#include "libc/mem/leaks.h" #include "libc/mem/mem.h" #include "libc/nt/createfile.h" #include "libc/nt/enum/fileflagandattributes.h" @@ -75,7 +75,7 @@ static textwindows struct FileLock *NewFileLock(void) { fl = g_locks.free; g_locks.free = fl->next; } else { - unassert((fl = _weaken(malloc)(sizeof(*fl)))); + unassert((fl = may_leak(_weaken(malloc)(sizeof(*fl))))); } bzero(fl, sizeof(*fl)); fl->next = g_locks.list; @@ -83,8 +83,6 @@ static textwindows struct FileLock *NewFileLock(void) { return fl; } -IGNORE_LEAKS(NewFileLock) - static textwindows void FreeFileLock(struct FileLock *fl) { fl->next = g_locks.free; g_locks.free = fl; diff --git a/libc/intrin/leaky.internal.h b/libc/intrin/leaky.internal.h deleted file mode 100644 index 59a463d42..000000000 --- a/libc/intrin/leaky.internal.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef COSMOPOLITAN_LIBC_INTRIN_LEAKY_INTERNAL_H_ -#define COSMOPOLITAN_LIBC_INTRIN_LEAKY_INTERNAL_H_ -#include "libc/dce.h" -COSMOPOLITAN_C_START_ - -#define IGNORE_LEAKS(FUNC) - -extern intptr_t _leaky_end[] __attribute__((__weak__)); -extern intptr_t _leaky_start[] __attribute__((__weak__)); - -COSMOPOLITAN_C_END_ -#endif /* COSMOPOLITAN_LIBC_INTRIN_LEAKY_INTERNAL_H_ */ diff --git a/libc/thread/pthread_orphan_np.c b/libc/intrin/pthread_orphan_np.c similarity index 100% rename from libc/thread/pthread_orphan_np.c rename to libc/intrin/pthread_orphan_np.c diff --git a/libc/isystem/cosmo.h b/libc/isystem/cosmo.h index 4bda8eb58..e8f15be72 100644 --- a/libc/isystem/cosmo.h +++ b/libc/isystem/cosmo.h @@ -43,6 +43,7 @@ #include "libc/intrin/weaken.h" #include "libc/mem/critbit0.h" #include "libc/mem/gc.h" +#include "libc/mem/leaks.h" #include "libc/nexgen32e/rdtsc.h" #include "libc/nexgen32e/stackframe.h" #include "libc/nexgen32e/x86feature.h" diff --git a/libc/log/showcrashreports.c b/libc/log/showcrashreports.c index da63e3136..bd68fe1b5 100644 --- a/libc/log/showcrashreports.c +++ b/libc/log/showcrashreports.c @@ -20,7 +20,6 @@ #include "libc/calls/struct/sigaction.h" #include "libc/calls/struct/sigaltstack.h" #include "libc/calls/struct/sigset.h" -#include "libc/intrin/leaky.internal.h" #include "libc/log/internal.h" #include "libc/mem/mem.h" #include "libc/runtime/runtime.h" @@ -93,5 +92,3 @@ void ShowCrashReports(void) { InstallCrashHandler(SIGABRT, 0); InstallCrashHandler(SIGSEGV, SA_ONSTACK); } - -IGNORE_LEAKS(ShowCrashReports) diff --git a/libc/log/leaks.c b/libc/mem/leaks.c similarity index 50% rename from libc/log/leaks.c rename to libc/mem/leaks.c index 5978dbf5e..bb70e05d6 100644 --- a/libc/log/leaks.c +++ b/libc/mem/leaks.c @@ -16,10 +16,79 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/cxxabi.h" +#include "libc/intrin/cxaatexit.internal.h" +#include "libc/intrin/dll.h" +#include "libc/intrin/kprintf.h" +#include "libc/intrin/weaken.h" +#include "libc/mem/mem.h" +#include "libc/nt/typedef/imagetlscallback.h" +#include "libc/runtime/runtime.h" +#include "libc/thread/posixthread.internal.h" +#include "libc/thread/thread.h" + +#define LEAK_CONTAINER(e) DLL_CONTAINER(struct Leak, elem, e) + +struct Leak { + void *alloc; + struct Dll elem; +}; + +static int leak_count; +static struct Dll *leaks; +static struct Dll *freaks; +static pthread_mutex_t lock; + +void __may_leak(void *alloc) { + if (!alloc) + return; + pthread_mutex_lock(&lock); + if (dll_is_empty(freaks)) { + int g = __granularity(); + struct Leak *p = _mapanon(g); + int n = g / sizeof(struct Leak); + for (int i = 0; i < n; ++i) { + dll_init(&p[i].elem); + dll_make_first(&freaks, &p[i].elem); + } + } + struct Dll *e = dll_first(freaks); + LEAK_CONTAINER(e)->alloc = alloc; + dll_remove(&freaks, e); + dll_make_first(&leaks, e); + pthread_mutex_unlock(&lock); +} + +static void visitor(void *start, void *end, size_t used_bytes, void *arg) { + if (!used_bytes) + return; + for (struct Dll *e = dll_first(leaks); e; e = dll_next(leaks, e)) + if (start == LEAK_CONTAINER(e)->alloc) + return; + kprintf("error: leaked %'zu byte allocation at %p\n", used_bytes, start); + ++leak_count; +} /** * Tests for memory leaks. */ void CheckForMemoryLeaks(void) { - // TODO(jart): give me a new lease on life + + // validate usage of this api + if (_weaken(_pthread_decimate)) + _weaken(_pthread_decimate)(); + if (!pthread_orphan_np()) + kprintf("warning: called CheckForMemoryLeaks() from non-orphaned thread\n"); + + // call dynamic global destructors + __cxa_thread_finalize(); + __cxa_finalize(0); + + // check for leaks + malloc_inspect_all(visitor, 0); + if (leak_count) { + kprintf("loser: you forgot to call free %'d time%s\n", leak_count, + leak_count == 1 ? "" : "s"); + _exit(73); + } } diff --git a/libc/mem/leaks.h b/libc/mem/leaks.h new file mode 100644 index 000000000..dcf2ad464 --- /dev/null +++ b/libc/mem/leaks.h @@ -0,0 +1,22 @@ +#ifndef COSMOPOLITAN_LIBC_MEM_LEAKS_H_ +#define COSMOPOLITAN_LIBC_MEM_LEAKS_H_ +#include "libc/intrin/weaken.h" +COSMOPOLITAN_C_START_ + +void CheckForMemoryLeaks(void) libcesque; + +/** + * Declares that allocation needn't be freed. + * + * This function does nothing if CheckForMemoryLeaks() hasn't been + * linked into the binary. + */ +forceinline void *may_leak(void *__p) { + void __may_leak(void *) libcesque; + if (_weaken(__may_leak)) + _weaken(__may_leak)(__p); + return __p; +} + +COSMOPOLITAN_C_END_ +#endif /* COSMOPOLITAN_LIBC_MEM_LEAKS_H_ */ diff --git a/libc/mem/putenv.c b/libc/mem/putenv.c index d135a3f9a..3482f8b99 100644 --- a/libc/mem/putenv.c +++ b/libc/mem/putenv.c @@ -17,10 +17,10 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/intrin/getenv.internal.h" -#include "libc/intrin/leaky.internal.h" #include "libc/intrin/strace.internal.h" #include "libc/macros.internal.h" #include "libc/mem/internal.h" +#include "libc/mem/leaks.h" #include "libc/mem/mem.h" #include "libc/runtime/runtime.h" #include "libc/sysv/errfuns.h" @@ -42,7 +42,7 @@ static char **__growenv(char **a) { a = environ; n = a ? __lenenv(a) : 0; c = MAX(8ul, n) << 1; - if ((b = malloc(c * sizeof(char *)))) { + if ((b = may_leak(malloc(c * sizeof(char *))))) { if (a) { for (p = b; *a;) { *p++ = *a++; @@ -59,8 +59,6 @@ static char **__growenv(char **a) { } } -IGNORE_LEAKS(__growenv) - int __putenv(char *s, bool overwrite) { char **p; struct Env e; diff --git a/libc/mem/setenv.c b/libc/mem/setenv.c index 64c7620ab..10135428b 100644 --- a/libc/mem/setenv.c +++ b/libc/mem/setenv.c @@ -16,9 +16,9 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/intrin/leaky.internal.h" #include "libc/intrin/strace.internal.h" #include "libc/mem/internal.h" +#include "libc/mem/leaks.h" #include "libc/mem/mem.h" #include "libc/runtime/runtime.h" #include "libc/str/str.h" @@ -39,7 +39,8 @@ int setenv(const char *name, const char *value, int overwrite) { size_t n, m; if (!name || !*name || !value || strchr(name, '=')) return einval(); - if ((s = malloc((n = strlen(name)) + 1 + (m = strlen(value)) + 1))) { + if ((s = may_leak( + malloc((n = strlen(name)) + 1 + (m = strlen(value)) + 1)))) { memcpy(mempcpy(mempcpy(s, name, n), "=", 1), value, m + 1); rc = __putenv(s, overwrite); } else { @@ -48,5 +49,3 @@ int setenv(const char *name, const char *value, int overwrite) { STRACE("setenv(%#s, %#s, %hhhd) → %d% m", name, value, overwrite, rc); return rc; } - -IGNORE_LEAKS(setenv) diff --git a/libc/proc/proc.c b/libc/proc/proc.c index 837957482..e43262ce8 100644 --- a/libc/proc/proc.c +++ b/libc/proc/proc.c @@ -27,9 +27,9 @@ #include "libc/errno.h" #include "libc/fmt/wintime.internal.h" #include "libc/intrin/dll.h" -#include "libc/intrin/leaky.internal.h" #include "libc/intrin/strace.internal.h" #include "libc/intrin/weaken.h" +#include "libc/mem/leaks.h" #include "libc/mem/mem.h" #include "libc/nt/accounting.h" #include "libc/nt/enum/processaccess.h" @@ -283,7 +283,7 @@ textwindows struct Proc *__proc_new(void) { } if (!proc) { if (_weaken(malloc)) { - proc = _weaken(malloc)(sizeof(struct Proc)); + proc = may_leak(_weaken(malloc)(sizeof(struct Proc))); } else { enomem(); return 0; @@ -297,8 +297,6 @@ textwindows struct Proc *__proc_new(void) { return proc; } -IGNORE_LEAKS(__proc_new) - /** * Adds process to active list. * diff --git a/libc/thread/__cxa_thread_atexit_impl.c b/libc/runtime/cxa_thread_atexit.c similarity index 65% rename from libc/thread/__cxa_thread_atexit_impl.c rename to libc/runtime/cxa_thread_atexit.c index 3c0326cfb..ced061833 100644 --- a/libc/thread/__cxa_thread_atexit_impl.c +++ b/libc/runtime/cxa_thread_atexit.c @@ -17,7 +17,12 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/intrin/cxaatexit.internal.h" +#include "libc/intrin/weaken.h" #include "libc/mem/mem.h" +#include "libc/nexgen32e/gc.internal.h" +#include "libc/thread/posixthread.internal.h" +#include "libc/thread/tls.h" +#include "third_party/nsync/wait_s.internal.h" struct Dtor { void *fun; @@ -27,18 +32,58 @@ struct Dtor { static _Thread_local struct Dtor *__cxa_thread_atexit_list; +static void __cxa_thread_unkey(struct CosmoTib *tib) { + void *val; + int i, j, gotsome; + pthread_key_dtor dtor; + for (j = 0; j < PTHREAD_DESTRUCTOR_ITERATIONS; ++j) { + for (gotsome = i = 0; i < PTHREAD_KEYS_MAX; ++i) { + if ((val = tib->tib_keys[i]) && + (dtor = atomic_load_explicit(_pthread_key_dtor + i, + memory_order_relaxed)) && + dtor != (pthread_key_dtor)-1) { + gotsome = 1; + tib->tib_keys[i] = 0; + dtor(val); + } + } + if (!gotsome) { + break; + } + } +} + +static void _pthread_ungarbage(struct CosmoTib *tib) { + struct Garbages *g; + while ((g = tib->tib_garbages)) { + tib->tib_garbages = 0; + while (g->i--) { + ((void (*)(intptr_t))g->p[g->i].fn)(g->p[g->i].arg); + } + _weaken(free)(g->p); + _weaken(free)(g); + } +} + void __cxa_thread_finalize(void) { struct Dtor *dtor; while ((dtor = __cxa_thread_atexit_list)) { __cxa_thread_atexit_list = dtor->next; ((void (*)(void *))dtor->fun)(dtor->arg); - free(dtor); + _weaken(free)(dtor); } + struct CosmoTib *tib = __get_tls(); + __cxa_thread_unkey(tib); + if (tib->tib_nsync) + _weaken(nsync_waiter_destroy)(tib->tib_nsync); + _pthread_ungarbage(tib); } int __cxa_thread_atexit_impl(void *fun, void *arg, void *dso_symbol) { struct Dtor *dtor; - if (!(dtor = malloc(sizeof(struct Dtor)))) + if (!_weaken(malloc)) + return -1; + if (!(dtor = _weaken(malloc)(sizeof(struct Dtor)))) return -1; dtor->fun = fun; dtor->arg = arg; diff --git a/libc/runtime/exit.c b/libc/runtime/exit.c index 4a299f05b..33456c7fe 100644 --- a/libc/runtime/exit.c +++ b/libc/runtime/exit.c @@ -40,14 +40,10 @@ wontreturn void exit(int exitcode) { STRACE("exit(%d)", exitcode); // call thread local c++ object destructors - if (_weaken(__cxa_thread_finalize)) { - _weaken(__cxa_thread_finalize)(); - } + __cxa_thread_finalize(); // call atexit() and __cxa_atexit() destructors - if (_weaken(__cxa_finalize)) { - _weaken(__cxa_finalize)(NULL); - } + __cxa_finalize(NULL); // call __destructor__ and finiarray destructors const uintptr_t *p; diff --git a/libc/thread/pthread_keys.c b/libc/runtime/pthread_keys.c similarity index 100% rename from libc/thread/pthread_keys.c rename to libc/runtime/pthread_keys.c diff --git a/libc/runtime/runtime.h b/libc/runtime/runtime.h index a4c62ffda..9450bf435 100644 --- a/libc/runtime/runtime.h +++ b/libc/runtime/runtime.h @@ -109,7 +109,6 @@ void __paginate_file(int, const char *) libcesque; void _weakfree(void *) libcesque; void *_mapanon(size_t) attributeallocsize((1)) mallocesque libcesque; void *_mapshared(size_t) attributeallocsize((1)) mallocesque libcesque; -void CheckForMemoryLeaks(void) libcesque; void CheckForFileLeaks(void) libcesque; void __enable_threads(void) libcesque; void __oom_hook(size_t) libcesque; diff --git a/libc/testlib/testmain.c b/libc/testlib/testmain.c index 438663b91..78cacd2a4 100644 --- a/libc/testlib/testmain.c +++ b/libc/testlib/testmain.c @@ -33,6 +33,7 @@ #include "libc/intrin/weaken.h" #include "libc/log/log.h" #include "libc/macros.internal.h" +#include "libc/mem/leaks.h" #include "libc/mem/mem.h" #include "libc/nexgen32e/nexgen32e.h" #include "libc/runtime/runtime.h" diff --git a/libc/thread/posixthread.internal.h b/libc/thread/posixthread.internal.h index b0d56e94c..319646e0e 100644 --- a/libc/thread/posixthread.internal.h +++ b/libc/thread/posixthread.internal.h @@ -110,8 +110,6 @@ void _pthread_lock(void) libcesque; void _pthread_onfork_child(void) libcesque; void _pthread_onfork_parent(void) libcesque; void _pthread_onfork_prepare(void) libcesque; -void _pthread_ungarbage(void) libcesque; -void _pthread_unkey(struct CosmoTib *) libcesque; void _pthread_unlock(void) libcesque; void _pthread_unref(struct PosixThread *) libcesque; void _pthread_unwind(struct PosixThread *) libcesque; diff --git a/libc/thread/pthread_atfork.c b/libc/thread/pthread_atfork.c index a16657713..251d9c81f 100644 --- a/libc/thread/pthread_atfork.c +++ b/libc/thread/pthread_atfork.c @@ -23,7 +23,6 @@ #include "libc/errno.h" #include "libc/intrin/atomic.h" #include "libc/intrin/dll.h" -#include "libc/intrin/leaky.internal.h" #include "libc/intrin/strace.internal.h" #include "libc/macros.internal.h" #include "libc/proc/proc.internal.h" @@ -100,5 +99,3 @@ int _pthread_atfork(atfork_f prepare, atfork_f parent, atfork_f child) { rc = 0; return rc; } - -IGNORE_LEAKS(_pthread_atfork) diff --git a/libc/thread/pthread_exit.c b/libc/thread/pthread_exit.c index bf163980e..f6558670d 100644 --- a/libc/thread/pthread_exit.c +++ b/libc/thread/pthread_exit.c @@ -44,27 +44,6 @@ void _pthread_unwind(struct PosixThread *pt) { } } -void _pthread_unkey(struct CosmoTib *tib) { - void *val; - int i, j, gotsome; - pthread_key_dtor dtor; - for (j = 0; j < PTHREAD_DESTRUCTOR_ITERATIONS; ++j) { - for (gotsome = i = 0; i < PTHREAD_KEYS_MAX; ++i) { - if ((val = tib->tib_keys[i]) && - (dtor = atomic_load_explicit(_pthread_key_dtor + i, - memory_order_relaxed)) && - dtor != (pthread_key_dtor)-1) { - gotsome = 1; - tib->tib_keys[i] = 0; - dtor(val); - } - } - if (!gotsome) { - break; - } - } -} - /** * Terminates current POSIX thread. * @@ -111,14 +90,7 @@ wontreturn void pthread_exit(void *rc) { // free resources _pthread_unwind(pt); - if (_weaken(__cxa_thread_finalize)) { - _weaken(__cxa_thread_finalize)(); - } - _pthread_unkey(tib); - if (tib->tib_nsync) { - nsync_waiter_destroy(tib->tib_nsync); - } - _pthread_ungarbage(); + __cxa_thread_finalize(); _pthread_decimate(); // run atexit handlers if orphaned thread diff --git a/libc/thread/pthread_ungarbage.c b/libc/thread/pthread_ungarbage.c deleted file mode 100644 index 67582c57e..000000000 --- a/libc/thread/pthread_ungarbage.c +++ /dev/null @@ -1,35 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2022 Justine Alexandra Roberts Tunney │ -│ │ -│ Permission to use, copy, modify, and/or distribute this software for │ -│ any purpose with or without fee is hereby granted, provided that the │ -│ above copyright notice and this permission notice appear in all copies. │ -│ │ -│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ -│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ -│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ -│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ -│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ -│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ -│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ -│ PERFORMANCE OF THIS SOFTWARE. │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/mem/mem.h" -#include "libc/nexgen32e/gc.internal.h" -#include "libc/thread/tls.h" - -void _pthread_ungarbage(void) { - struct Garbages *g; - struct CosmoTib *tib; - tib = __get_tls(); - while ((g = tib->tib_garbages)) { - tib->tib_garbages = 0; - while (g->i--) { - ((void (*)(intptr_t))g->p[g->i].fn)(g->p[g->i].arg); - } - free(g->p); - free(g); - } -} diff --git a/test/ctl/BUILD.mk b/test/ctl/BUILD.mk index 9b2b59c13..0a57a98cf 100644 --- a/test/ctl/BUILD.mk +++ b/test/ctl/BUILD.mk @@ -14,9 +14,7 @@ TEST_CTL_TESTS = $(TEST_CTL_COMS:%=%.ok) TEST_CTL_DIRECTDEPS = \ CTL \ LIBC_INTRIN \ - LIBC_LOG \ LIBC_MEM \ - THIRD_PARTY_LIBCXX \ TEST_CTL_DEPS := \ $(call uniq,$(foreach x,$(TEST_CTL_DIRECTDEPS),$($(x)))) diff --git a/test/ctl/map_test.cc b/test/ctl/map_test.cc new file mode 100644 index 000000000..4f4f57a40 --- /dev/null +++ b/test/ctl/map_test.cc @@ -0,0 +1,223 @@ +// -*- mode:c++; indent-tabs-mode:nil; c-basic-offset:4; coding:utf-8 -*- +// vi: set et ft=cpp ts=4 sts=4 sw=4 fenc=utf-8 :vi +// +// Copyright 2024 Justine Alexandra Roberts Tunney +// +// Permission to use, copy, modify, and/or distribute this software for +// any purpose with or without fee is hereby granted, provided that the +// above copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +// AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL +// DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +// PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +// TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +#include "ctl/map.h" +#include "ctl/string.h" +#include "libc/mem/leaks.h" + +// #include +// #include +// #define ctl std + +int +main() +{ + + { + ctl::map s; + if (!s.empty()) + return 1; + if (s.size()) + return 2; + s[1] = 10; + s[2] = 20; + s[3] = 3.14; + if (s[1] != 10) + return 3; + if (s[2] != 20) + return 4; + if (s[3] != 3.14) + return 5; + } + + { + ctl::map m; + if (!m.empty()) + return 1; + if (m.size() != 0) + return 2; + + m[1] = 10.5; + m[2] = 20.7; + m[3] = 3.14; + + if (m.empty()) + return 3; + if (m.size() != 3) + return 4; + if (m[1] != 10.5) + return 5; + if (m[2] != 20.7) + return 6; + if (m[3] != 3.14) + return 7; + } + + { + ctl::map m; + m["one"] = 1; + m["two"] = 2; + m["three"] = 3; + + int sum = 0; + for (const auto& pair : m) { + sum += pair.second; + } + if (sum != 6) + return 8; + + auto it = m.find("two"); + if (it == m.end()) + return 9; + if (it->second != 2) + return 10; + + it = m.find("four"); + if (it != m.end()) + return 11; + } + + { + ctl::map m = { { 1, "one" }, + { 2, "two" }, + { 3, "three" } }; + if (m.size() != 3) + return 12; + if (m[2] != "two") + return 13; + + auto result = m.insert({ 4, "four" }); + if (!result.second) + return 14; + if (result.first->second != "four") + return 15; + + result = m.insert({ 2, "deux" }); + if (result.second) + return 16; + if (result.first->second != "two") + return 17; + } + + { + ctl::map m1 = { { 1, 1.1 }, { 2, 2.2 }, { 3, 3.3 } }; + ctl::map m2 = { { 1, 1.1 }, { 2, 2.2 }, { 3, 3.3 } }; + ctl::map m3 = { { 1, 1.1 }, { 2, 2.2 }, { 4, 4.4 } }; + + if (!(m1 == m2)) + return 18; + if (m1 != m2) + return 19; + if (m1 == m3) + return 20; + if (!(m1 != m3)) + return 21; + } + + { + ctl::map m; + m[1] = "one"; + m[2] = "two"; + m[3] = "three"; + + if (m.count(2) != 1) + return 22; + if (m.count(4) != 0) + return 23; + + m.erase(2); + if (m.size() != 2) + return 24; + if (m.count(2) != 0) + return 25; + } + + { + ctl::map m = { { 1, 1.1 }, { 3, 3.3 }, { 5, 5.5 } }; + + auto lower = m.lower_bound(2); + if (lower->first != 3) + return 26; + + auto upper = m.upper_bound(3); + if (upper->first != 5) + return 27; + + auto range = m.equal_range(3); + if (range.first->first != 3 || range.second->first != 5) + return 28; + } + + { + ctl::map m = { + { 1, "one" }, { 2, "two" }, { 3, "three" }, { 4, "four" } + }; + + // Test reverse iteration + auto rit = m.rbegin(); + if (rit->first != 4 || rit->second != "four") + return 29; + + ++rit; + if (rit->first != 3 || rit->second != "three") + return 30; + + // Test reverse iteration with const_reverse_iterator + auto crit = m.crbegin(); + if (crit->first != 4 || crit->second != "four") + return 31; + + ++crit; + ++crit; + if (crit->first != 2 || crit->second != "two") + return 32; + + // Test rend() + rit = m.rbegin(); + int count = 0; + while (rit != m.rend()) { + ++rit; + ++count; + } + if (count != 4) + return 33; + + // Test reverse iteration with range-based for loop + ctl::string result; + for (auto it = m.rbegin(); it != m.rend(); ++it) { + result += it->second; + } + if (result != "fourthreetwoone") + return 34; + + // Test modification through reverse iterator + rit = m.rbegin(); + rit->second = "quatre"; + if (m[4] != "quatre") + return 35; + + // Test comparison of reverse iterators + auto rit1 = m.rbegin(); + auto rit2 = m.rbegin(); + ++rit2; + if (!(*rit1 > *rit2)) + return 36; + } + + CheckForMemoryLeaks(); +} diff --git a/test/ctl/optional_test.cc b/test/ctl/optional_test.cc index a7d3a9b14..b53415f2b 100644 --- a/test/ctl/optional_test.cc +++ b/test/ctl/optional_test.cc @@ -17,11 +17,10 @@ // PERFORMANCE OF THIS SOFTWARE. #include "ctl/optional.h" +#include "libc/mem/leaks.h" #include "ctl/string.h" -#include "libc/runtime/runtime.h" - // #include // #include // #define ctl std @@ -131,5 +130,4 @@ main() } CheckForMemoryLeaks(); - return 0; } diff --git a/test/ctl/set_test.cc b/test/ctl/set_test.cc new file mode 100644 index 000000000..5eecca5e2 --- /dev/null +++ b/test/ctl/set_test.cc @@ -0,0 +1,306 @@ +// -*- mode:c++; indent-tabs-mode:nil; c-basic-offset:4; coding:utf-8 -*- +// vi: set et ft=cpp ts=4 sts=4 sw=4 fenc=utf-8 :vi +// +// Copyright 2024 Justine Alexandra Roberts Tunney +// +// Permission to use, copy, modify, and/or distribute this software for +// any purpose with or without fee is hereby granted, provided that the +// above copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +// AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL +// DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +// PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +// TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +#include "ctl/set.h" +#include "libc/mem/leaks.h" + +// #include +// #define ctl std +// #define check() size() + +int +rand32(void) +{ + /* Knuth, D.E., "The Art of Computer Programming," Vol 2, + Seminumerical Algorithms, Third Edition, Addison-Wesley, 1998, + p. 106 (line 26) & p. 108 */ + static unsigned long long lcg = 1; + lcg *= 6364136223846793005; + lcg += 1442695040888963407; + return lcg >> 32; +} + +int +main() +{ + + { + // Test construction and basic operations + ctl::set s; + if (!s.empty()) + return 1; + if (s.size() != 0) + return 2; + + s.insert(1); + s.insert(2); + s.insert(3); + if (s.size() != 3) + return 3; + if (s.count(2) != 1) + return 4; + if (s.count(4) != 0) + return 5; + } + + { + // Test insertion and find + ctl::set s; + auto result = s.insert(5); + if (!result.second || *result.first != 5) + return 6; + + result = s.insert(5); // Duplicate insertion + if (result.second || *result.first != 5) + return 7; + + auto it = s.find(5); + if (it == s.end() || *it != 5) + return 8; + + it = s.find(6); + if (it != s.end()) + return 9; + s.check(); + } + + { + // Test erase + ctl::set s; + s.insert(1); + s.insert(2); + s.insert(3); + s.insert(4); + s.insert(5); + s.erase(3); + if (s.size() != 4 || s.count(3) != 0) + return 10; + + auto it = s.find(2); + s.erase(it); + if (s.size() != 3 || s.count(2) != 0) + return 11; + } + + { + // Test clear + ctl::set s; + s.insert(1); + s.insert(2); + s.insert(3); + s.insert(4); + s.insert(5); + s.clear(); + if (!s.empty() || s.size() != 0) + return 12; + } + + { + // Test comparison operators + ctl::set s1{ 1, 2, 3 }; + ctl::set s2{ 1, 2, 3 }; + ctl::set s3{ 1, 2, 3, 4 }; + + if (!(s1 == s2)) + return 13; + if (s1 != s2) + return 14; + if (!(s1 < s3)) + return 15; + if (s3 <= s1) + return 16; + } + + { + // Test iterator functionality + ctl::set s{ 5, 3, 1, 4, 2 }; + int prev = 0; + for (const auto& value : s) { + if (value <= prev) + return 17; + prev = value; + s.check(); + } + } + + { + // Test lower_bound and upper_bound + ctl::set s{ 1, 3, 5, 7, 9 }; + auto lower = s.lower_bound(4); + auto upper = s.upper_bound(4); + if (*lower != 5 || *upper != 5) + return 18; + s.check(); + } + + { + // Test emplace + ctl::set> s; + auto result = s.emplace(1, 2); + if (!result.second || result.first->first != 1 || + result.first->second != 2) + return 19; + s.check(); + } + + { + // Test insertion and size + ctl::set s; + for (int i = 0; i < 1000; ++i) + s.insert(i); + if (s.size() != 1000) + return 20; + s.check(); + } + + { + // Test duplicate insertions + ctl::set s; + for (int i = 0; i < 100; ++i) { + s.insert(i); + s.insert(i); // Duplicate + } + if (s.size() != 100) + return 21; + s.check(); + } + + { + // Test deletion + ctl::set s; + for (int i = 0; i < 100; ++i) + s.insert(i); + for (int i = 0; i < 50; ++i) + s.erase(i); + if (s.size() != 50 || s.find(0) != s.end() || s.find(99) == s.end()) + return 22; + s.check(); + } + + { + // Test balance after multiple insertions + ctl::set s; + for (int i = 0; i < 1000; ++i) { + s.insert(i); + s.check(); + } + } + + { + // Test balance after multiple insertions and deletions + ctl::set s; + for (int i = 0; i < 20000; ++i) { + if (s.empty() || rand32() % 2 == 0) { + s.insert(rand32() % 500); + } else { + s.erase(rand32() % s.size()); + } + s.check(); + } + } + + { + // Test iterator functionality + ctl::set s; + for (int i = 0; i < 100; ++i) + s.insert(i); + int prev = -1; + for (const auto& val : s) { + if (val <= prev) + return 25; + prev = val; + } + } + + { + // Test clear() function + ctl::set s; + for (int i = 0; i < 100; ++i) + s.insert(i); + s.clear(); + s.check(); + if (!s.empty() || s.size() != 0) + return 26; + } + + { + // Test extreme case: insert in descending order + ctl::set s; + for (int i = 1000; i >= 0; --i) + s.insert(i); + if (s.size() != 1001 || *s.begin() != 0 || *s.rbegin() != 1000) + return 27; + } + + { + // Test extreme case: insert in ascending order + ctl::set s; + for (int i = 0; i <= 1000; ++i) + s.insert(i); + if (s.size() != 1001 || *s.begin() != 0 || *s.rbegin() != 1000) + return 28; + } + + { + // Test lower_bound and upper_bound more extensively + ctl::set s{ 10, 20, 30, 40, 50 }; + if (s.lower_bound(25) != s.find(30) || s.upper_bound(25) != s.find(30)) + return 29; + if (s.lower_bound(30) != s.find(30) || s.upper_bound(30) != s.find(40)) + return 30; + if (s.lower_bound(60) != s.end() || s.upper_bound(60) != s.end()) + return 31; + } + + { + // Test emplace with more complex types + struct TestStruct + { + int a; + double b; + + TestStruct(int a, double b) : a(a), b(b) + { + } + + bool operator<(const TestStruct& other) const + { + return a < other.a; + } + }; + + ctl::set s; + auto result = s.emplace(5, 3.14); + if (!result.second || result.first->a != 5 || result.first->b != 3.14) + return 32; + s.check(); + } + + { + ctl::set s; + if (s.count(6) != 0) + return 33; + s.insert(6); + s.insert(6); + s.insert(6); + if (s.count(6) != 1) + return 34; + } + + CheckForMemoryLeaks(); +} diff --git a/test/ctl/string_test.cc b/test/ctl/string_test.cc index 67fcf625e..7e7cb3f32 100644 --- a/test/ctl/string_test.cc +++ b/test/ctl/string_test.cc @@ -17,10 +17,10 @@ // PERFORMANCE OF THIS SOFTWARE. #include "ctl/string.h" +#include "libc/mem/leaks.h" #include <__type_traits/is_same.h> -#include "libc/runtime/runtime.h" #include "libc/str/str.h" // #include diff --git a/test/ctl/string_view_test.cc b/test/ctl/string_view_test.cc index 89da0f3a7..8ca1f8bee 100644 --- a/test/ctl/string_view_test.cc +++ b/test/ctl/string_view_test.cc @@ -17,8 +17,7 @@ // PERFORMANCE OF THIS SOFTWARE. #include "ctl/string_view.h" - -#include "libc/runtime/runtime.h" +#include "libc/mem/leaks.h" #include "libc/str/str.h" // #include diff --git a/test/ctl/unique_ptr_test.cc b/test/ctl/unique_ptr_test.cc index 4b207fd75..4f83de945 100644 --- a/test/ctl/unique_ptr_test.cc +++ b/test/ctl/unique_ptr_test.cc @@ -20,8 +20,6 @@ #include <__type_traits/is_same.h> -#include "libc/runtime/runtime.h" - // #include // #define ctl std diff --git a/test/ctl/vector_test.cc b/test/ctl/vector_test.cc index bee9c33b0..aa70cb21d 100644 --- a/test/ctl/vector_test.cc +++ b/test/ctl/vector_test.cc @@ -17,6 +17,7 @@ // PERFORMANCE OF THIS SOFTWARE. #include "ctl/vector.h" +#include "libc/mem/leaks.h" #include diff --git a/test/libc/log/backtrace.c b/test/libc/log/backtrace.c index 0f4cd3fb0..0d553b1ed 100644 --- a/test/libc/log/backtrace.c +++ b/test/libc/log/backtrace.c @@ -19,6 +19,7 @@ #include "libc/fmt/conv.h" #include "libc/limits.h" #include "libc/log/log.h" +#include "libc/mem/leaks.h" #include "libc/mem/mem.h" #include "libc/runtime/internal.h" #include "libc/runtime/symbols.internal.h" diff --git a/test/libcxx/cexception_test.cc b/test/libcxx/cexception_test.cc index 358dda421..0e09c014b 100644 --- a/test/libcxx/cexception_test.cc +++ b/test/libcxx/cexception_test.cc @@ -17,6 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/mem/alg.h" +#include "libc/mem/leaks.h" #include "libc/mem/mem.h" #include "libc/runtime/runtime.h" diff --git a/third_party/dlmalloc/README.cosmo b/third_party/dlmalloc/README.cosmo index ed4acf7e2..0db6ea937 100644 --- a/third_party/dlmalloc/README.cosmo +++ b/third_party/dlmalloc/README.cosmo @@ -9,6 +9,7 @@ LICENSE LOCAL CHANGES + - Fix bug in dlmalloc_inspect_all() - Define dlmalloc_requires_more_vespene_gas() - Make dlmalloc scalable using sched_getcpu() - Use faster two power roundup for memalign() diff --git a/third_party/dlmalloc/dlmalloc.c b/third_party/dlmalloc/dlmalloc.c index 2dd38035e..4ec0983d3 100644 --- a/third_party/dlmalloc/dlmalloc.c +++ b/third_party/dlmalloc/dlmalloc.c @@ -25,6 +25,7 @@ #include "third_party/dlmalloc/vespene.internal.h" #include "libc/thread/tls.h" #include "libc/intrin/kprintf.h" +#include "libc/intrin/kprintf.h" #include "third_party/nsync/mu.h" #if !IsTiny() @@ -1221,7 +1222,8 @@ static void internal_inspect_all(mstate m, } } if (start < (void*)next) /* skip if all space is bookkeeping */ - handler(start, next, used, arg); + if (start != s) /* [jart] fix phantom alloc bug w/ mspace+mmap */ + handler(start, next, used, arg); if (q == top) break; q = next; diff --git a/third_party/dlmalloc/threaded.inc b/third_party/dlmalloc/threaded.inc index 904201603..9b87934e8 100644 --- a/third_party/dlmalloc/threaded.inc +++ b/third_party/dlmalloc/threaded.inc @@ -25,6 +25,8 @@ #include "libc/nexgen32e/x86feature.h" #include "libc/runtime/runtime.h" #include "libc/thread/thread.h" +#include "libc/runtime/runtime.h" +#include "libc/intrin/weaken.h" #include "third_party/dlmalloc/dlmalloc.h" #if !FOOTERS || !MSPACES @@ -64,11 +66,28 @@ size_t dlbulk_free(void *array[], size_t nelem) { return 0; } +struct ThreadedMallocVisitor { + mstate heap; + void (*handler)(void *start, void *end, + size_t used_bytes, void *arg); + void *arg; +}; + +static void threaded_malloc_visitor(void *start, void *end, + size_t used_bytes, void *arg) { + struct ThreadedMallocVisitor *tmv = arg; + if (start == tmv->heap) + return; + tmv->handler(start, end, used_bytes, tmv->arg); +} + void dlmalloc_inspect_all(void handler(void *start, void *end, - size_t used_bytes, void *callback_arg), + size_t used_bytes, void *arg), void *arg) { - for (unsigned i = 0; i < g_heapslen; ++i) - mspace_inspect_all(g_heaps[i], handler, arg); + for (unsigned i = 0; i < g_heapslen; ++i) { + struct ThreadedMallocVisitor tmv = {g_heaps[i], handler, arg}; + mspace_inspect_all(g_heaps[i], threaded_malloc_visitor, &tmv); + } } forceinline mstate get_arena(void) { diff --git a/third_party/lua/lua.main.c b/third_party/lua/lua.main.c index dcc10ff4d..18c450639 100644 --- a/third_party/lua/lua.main.c +++ b/third_party/lua/lua.main.c @@ -50,6 +50,7 @@ #include "third_party/lua/lrepl.h" #include "third_party/lua/lualib.h" #include "third_party/lua/lunix.h" +#include "libc/mem/leaks.h" #include "tool/args/args.h" __static_yoink("lua_notice"); diff --git a/tool/build/runitd.c b/tool/build/runitd.c index 14b1a7e7e..d1b0f375f 100644 --- a/tool/build/runitd.c +++ b/tool/build/runitd.c @@ -35,6 +35,7 @@ #include "libc/log/check.h" #include "libc/macros.internal.h" #include "libc/mem/gc.h" +#include "libc/mem/leaks.h" #include "libc/mem/mem.h" #include "libc/nexgen32e/crc32.h" #include "libc/proc/posix_spawn.h" diff --git a/tool/net/redbean.c b/tool/net/redbean.c index a4edaecd3..2ef31064b 100644 --- a/tool/net/redbean.c +++ b/tool/net/redbean.c @@ -49,6 +49,7 @@ #include "libc/math.h" #include "libc/mem/alloca.h" #include "libc/mem/gc.h" +#include "libc/mem/leaks.h" #include "libc/mem/mem.h" #include "libc/nexgen32e/crc32.h" #include "libc/nexgen32e/rdtsc.h"