Skip to content

Commit

Permalink
wip Implement ctl::shared_ptr
Browse files Browse the repository at this point in the history
This code does not segfault and it compiles cleanly, and it took a while
to get here.
  • Loading branch information
mrdomino committed Jun 18, 2024
1 parent b99e111 commit b919b80
Show file tree
Hide file tree
Showing 3 changed files with 236 additions and 0 deletions.
94 changes: 94 additions & 0 deletions ctl/shared_ptr.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// -*- 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
107 changes: 107 additions & 0 deletions ctl/shared_ptr.h
Original file line number Diff line number Diff line change
@@ -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<typename T>
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<typename T>
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<T>::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_
35 changes: 35 additions & 0 deletions test/ctl/shared_ptr_test.cc
Original file line number Diff line number Diff line change
@@ -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<typename T>
using Ptr = ctl::shared_ptr<T>;

int
main()
{
{
Ptr<int> x;
}

{
Ptr<int> x(new int());
}
return 0;
}

0 comments on commit b919b80

Please sign in to comment.