cosmopolitan/test/ctl/shared_ptr_test.cc
2024-09-15 16:32:13 -07:00

284 lines
6.4 KiB
C++

// -*- 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"
#include "ctl/vector.h"
#include "libc/mem/leaks.h"
// #include <memory>
// #include <vector>
// #define ctl std
using ctl::bad_weak_ptr;
using ctl::enable_shared_from_this;
using ctl::make_shared;
using ctl::move;
using ctl::shared_ptr;
using ctl::unique_ptr;
using ctl::vector;
using ctl::weak_ptr;
#undef ctl
static int g = 0;
struct ConstructG
{
ConstructG()
{
++g;
}
};
struct DestructG
{
~DestructG()
{
++g;
}
};
struct CallG
{
void operator()(auto*) const noexcept
{
++g;
}
};
struct Base
{};
struct Derived : Base
{};
class SharedThis : public enable_shared_from_this<SharedThis>
{
struct Private
{
explicit Private() = default;
};
public:
SharedThis(Private)
{
}
static shared_ptr<SharedThis> create()
{
return make_shared<SharedThis>(Private());
}
};
class CanShareThis : public enable_shared_from_this<CanShareThis>
{};
int
main()
{
int a, b;
{
// Shouldn't cause memory leaks.
shared_ptr<int> x(new int(5));
}
{
// Objects get destroyed when the last shared_ptr is reset.
shared_ptr<int> x(&a, CallG());
shared_ptr<int> y(x);
x.reset();
if (g)
return 1;
y.reset();
if (g != 1)
return 2;
}
{
g = 0;
// Weak pointers don't prevent object destruction.
shared_ptr<int> x(&a, CallG());
weak_ptr<int> y(x);
x.reset();
if (g != 1)
return 3;
}
{
g = 0;
// Weak pointers can be promoted to shared pointers.
shared_ptr<int> x(&a, CallG());
weak_ptr<int> y(x);
auto z = y.lock();
x.reset();
if (g)
return 4;
y.reset();
if (g)
return 5;
z.reset();
if (g != 1)
return 6;
}
{
// Shared null pointers are falsey.
shared_ptr<int> x;
if (x)
return 7;
x.reset(new int);
if (!x)
return 8;
}
{
// You can cast a shared pointer validly.
shared_ptr<Derived> x(new Derived);
shared_ptr<Base> y(x);
// But not invalidly:
// shared_ptr<Base> x(new Derived);
// shared_ptr<Derived> y(x);
}
{
// You can cast a shared pointer to void to retain a reference.
shared_ptr<int> x(new int);
shared_ptr<void> y(x);
}
{
// You can also create a shared pointer to void in the first place.
shared_ptr<void> x(new int);
}
{
// You can take a shared pointer to a subobject, and it will free the
// base object.
shared_ptr<vector<int>> x(new vector<int>);
x->push_back(5);
shared_ptr<int> y(x, &x->at(0));
x.reset();
if (*y != 5)
return 9;
}
{
g = 0;
// You can create a shared_ptr from a unique_ptr.
unique_ptr<int, CallG> x(&a, CallG());
shared_ptr<int> y(move(x));
if (x)
return 10;
y.reset();
if (g != 1)
return 11;
}
{
g = 0;
// You can reassign shared_ptrs.
shared_ptr<int> x(&a, CallG());
shared_ptr<int> y;
y = x;
x.reset();
if (g)
return 12;
y.reset();
if (g != 1)
return 13;
}
{
// owner_before shows equivalence only for equivalent objects.
shared_ptr<int> x(&a, CallG());
shared_ptr<int> y(&b, CallG());
shared_ptr<void> z(x, &b);
if (z.owner_before(x) || x.owner_before(z))
return 14;
if (!z.owner_before(y) && !y.owner_before(z))
return 15;
}
{
// Use counts work like you'd expect
shared_ptr<int> x(new int);
if (x.use_count() != 1)
return 16;
shared_ptr<int> y(x);
if (x.use_count() != 2 || y.use_count() != 2)
return 17;
x.reset();
if (x.use_count() != 0 || y.use_count() != 1)
return 18;
}
{
// There is a make_shared that will allocate an object for you safely.
auto x = make_shared<int>(5);
if (!x)
return 19;
if (*x != 5)
return 20;
}
{
// Expired weak pointers lock to nullptr, and throw when promoted to
// shared pointer by constructor.
auto x = make_shared<int>();
weak_ptr<int> y(x);
x.reset();
if (y.lock())
return 21;
int caught = 0;
try {
shared_ptr<int> z(y);
} catch (bad_weak_ptr& e) {
caught = 1;
}
if (!caught)
return 22;
}
{
// nullptr is always expired.
shared_ptr<int> x(nullptr);
weak_ptr<int> y(x);
if (!y.expired())
return 23;
}
{
// enable_shared_from_this allows shared pointers to self.
auto x = SharedThis::create();
auto y = x->shared_from_this();
if (x.use_count() != 2 || x.get() != y.get())
return 24;
auto z = new CanShareThis();
auto w = shared_ptr<CanShareThis>(z);
auto v = w->shared_from_this();
if (w.use_count() != 2 || w.get() != v.get())
return 25;
}
// TODO(mrdomino): exercise threads / races. The reference count should be
// atomically maintained.
CheckForMemoryLeaks();
return 0;
}