diff --git a/ctl/shared_ptr.cc b/ctl/shared_ptr.cc new file mode 100644 index 000000000..ae8e4e75d --- /dev/null +++ b/ctl/shared_ptr.cc @@ -0,0 +1,97 @@ +// -*- 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 "shared_ptr.h" + +#include "libc/intrin/atomic.h" + +namespace { + +inline void +incref(_Atomic(size_t)* r) +{ + size_t r2 = atomic_fetch_add_explicit(r, 1, memory_order_relaxed); + if (r2 > ((size_t)-1) >> 1) + __builtin_trap(); +} + +inline int +decref(_Atomic(size_t)* r) +{ + if (!atomic_fetch_sub_explicit(r, 1, memory_order_release)) { + atomic_thread_fence(memory_order_acquire); + return 1; + } + return 0; +} + +inline size_t +getref(const _Atomic(size_t)* r) +{ + return atomic_load_explicit(r, memory_order_relaxed); +} + +} // namespace + +namespace ctl { + +namespace __ { + +void +shared_control::add_shared() noexcept +{ + incref(&shared); +} + +void +shared_control::release_shared() noexcept +{ + if (decref(&shared)) { + on_zero_shared(); + release_weak(); + } +} + +void +shared_control::add_weak() noexcept +{ + incref(&weak); +} + +void +shared_control::release_weak() noexcept +{ + if (decref(&weak)) + on_zero_weak(); +} + +size_t +shared_control::use_count() const noexcept +{ + return 1 + getref(&shared); +} + +size_t +shared_control::weak_count() const noexcept +{ + return getref(&weak); +} + +} // namespace __ + +} // namespace ctl diff --git a/ctl/shared_ptr.h b/ctl/shared_ptr.h new file mode 100644 index 000000000..619c3d7a6 --- /dev/null +++ b/ctl/shared_ptr.h @@ -0,0 +1,107 @@ +// -*-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_SHARED_PTR_H_ +#define COSMOPOLITAN_CTL_SHARED_PTR_H_ +#include "unique_ptr.h" + +namespace ctl { + +// TODO(mrdomino): move +struct exception +{ + virtual const char* what() const noexcept + { + return "exception"; + } +}; + +namespace __ { + +struct shared_control +{ + _Atomic(size_t) shared; + _Atomic(size_t) weak; + + constexpr shared_control() noexcept : shared(0), weak(0) + { + } + virtual ~shared_control() + { + } + void add_shared() noexcept; + void release_shared() noexcept; + void add_weak() noexcept; + void release_weak() noexcept; + size_t use_count() const noexcept; + size_t weak_count() const noexcept; + + private: + virtual void on_zero_shared() noexcept = 0; + virtual void on_zero_weak() noexcept = 0; +}; + +template +struct shared_pointer : shared_control +{ + T* p; + + static shared_pointer* make(T* p) + { + return new shared_pointer(p); + } + + private: + shared_pointer(T* p) noexcept : p(p) + { + } + + void on_zero_shared() noexcept override + { + delete p; + } + + void on_zero_weak() noexcept override + { + delete this; + } +}; + +} // namespace __ + +struct bad_weak_ptr : ctl::exception +{ + const char* what() const noexcept override + { + return "bad_weak_ptr"; + } +}; + +template +class shared_ptr +{ + public: + constexpr shared_ptr(nullptr_t = nullptr) noexcept + : p(nullptr), ctl(nullptr) + { + } + shared_ptr(T* const p2) + { + auto hold = ctl::unique_ptr(p2); + ctl = __::shared_pointer::make(p2); + p = hold.release(); + } + + ~shared_ptr() + { + if (ctl) + ctl->release_shared(); + } + + private: + T* p; + __::shared_control* ctl; +}; + +} // namespace ctl + +#endif // COSMOPOLITAN_CTL_SHARED_PTR_H_ diff --git a/test/ctl/shared_ptr_test.cc b/test/ctl/shared_ptr_test.cc new file mode 100644 index 000000000..c00eab891 --- /dev/null +++ b/test/ctl/shared_ptr_test.cc @@ -0,0 +1,35 @@ +// -*- 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/shared_ptr.h" + +template +using Ptr = ctl::shared_ptr; + +int +main() +{ + { + Ptr x; + } + + { + Ptr x(new int()); + } + return 0; +}