Compare commits

...

10 commits

Author SHA1 Message Date
Pagwin
74a843720e
got test(s) to compile and run even though I fail them 2024-12-18 00:31:46 -05:00
Pagwin
7dc09fd541
commented out lines in test to allow compilation 2024-12-17 22:53:52 -05:00
Pagwin
099905897e
message for grading TA 2024-12-13 22:48:02 -05:00
Pagwin
12d0c45322
I'm to tired to figure out how to get the base and derived class configured to work properly much less the pointer casts 2024-12-13 22:46:36 -05:00
Pagwin
d90f19f897
more inheritence fuckery 2024-12-13 22:23:04 -05:00
Pagwin
4a9fe59bb2
inheritence stuff is annoying to fix 2024-12-13 20:55:06 -05:00
Pagwin
45a0b765a4
remaining issues are private/protected destructors and the pointer casts 2024-12-09 16:07:33 -05:00
Pagwin
cacc07d01c
initial commit 2024-12-09 00:32:18 -05:00
github-classroom[bot]
416b35961e
add deadline 2024-12-04 18:22:40 +00:00
github-classroom[bot]
7b22a191c9
Setting up GitHub Classroom Feedback 2024-12-04 18:22:37 +00:00
3 changed files with 1008 additions and 0 deletions

1
README.md Normal file
View file

@ -0,0 +1 @@
[![Review Assignment Due Date](https://classroom.github.com/assets/deadline-readme-button-22041afd0340ce965d47ae6ef1cefeee28c7c493a6346c4f15d667ab976d596c.svg)](https://classroom.github.com/a/nYZgPGGq)

147
SharedPtr.hpp Normal file
View file

@ -0,0 +1,147 @@
#include <atomic>
#ifndef _POWELLCS440
#include <cassert>
#include <iostream>
#define todo(msg) \
std::cerr << msg << std::endl; \
assert(false);
namespace cs440 {
using counter = std::atomic<std::size_t>;
template <typename T> struct Base {
template <typename U> friend struct Base;
T *ptr;
Base(T *ptr) : ptr{ptr} {}
template <typename U> Base(Base<U> &ptr) : ptr{ptr.ptr} {}
template <typename U> Base(Base<U> &&ptr) : ptr{ptr.ptr} {
ptr.ptr = nullptr;
}
public:
virtual T &operator*() { return *ptr; }
virtual T *operator->() { return ptr; }
virtual ~Base() = default;
};
template <typename T, typename U> class MyDerived : public Base<T> {
U *ptr;
public:
MyDerived(U *ptr) : Base<T>{ptr} { this->ptr = ptr; }
MyDerived(Base<U> &ptr) : Base<T>{ptr.ptr} { this->ptr = ptr.ptr; }
U &operator*() { return *ptr; }
U *operator->() { return ptr; }
virtual ~MyDerived() { delete this->ptr; }
};
template <typename T> class SharedPtr {
public:
Base<T> *ptr;
counter *count;
SharedPtr() : ptr{nullptr}, count{new std::atomic<std::size_t>{0}} {}
template <typename U>
explicit SharedPtr(U *ptr)
: ptr{static_cast<Base<T> *>(new MyDerived<T, U>{ptr})},
count{new std::atomic<std::size_t>{
static_cast<std::size_t>(ptr ? 1 : 0)}} {}
SharedPtr(const SharedPtr &p) : ptr{p.ptr}, count{p.count} {}
template <typename U>
SharedPtr(const SharedPtr<U> &p)
: ptr(MyDerived<U, T>(const_cast<cs440::Base<U> &>(*p.ptr))),
count{p.count} {
(*count)++;
}
SharedPtr(SharedPtr &&p) : ptr{p.ptr} { p.ptr = nullptr; }
template <typename U> SharedPtr(SharedPtr<U> &&p) : ptr{p.ptr} {
p.ptr = nullptr;
this->count = p.count;
p->count = nullptr;
}
SharedPtr &operator=(const SharedPtr &ptr) {
this->ptr = ptr.ptr;
(*count)++;
return *this;
}
template <typename U> SharedPtr<T> &operator=(const SharedPtr<U> &ptr) {
this->ptr = ptr.ptr;
(*count)++;
return *this;
}
SharedPtr &operator=(SharedPtr &&p) {
ptr = p.ptr;
p.ptr = nullptr;
this->count = p.count;
p->count = nullptr;
return *this;
}
template <typename U> SharedPtr &operator=(SharedPtr<U> &&p) {
ptr = p.ptr;
p.ptr = nullptr;
}
~SharedPtr() {
if (this->count == nullptr) {
if (this->ptr != nullptr) {
delete this->ptr;
this->ptr = nullptr;
}
return;
}
if (--(*this->count) == 0) {
delete this->count;
this->count = nullptr;
delete this->ptr;
this->ptr = nullptr;
}
}
void reset() {
this->ptr = nullptr;
--(*this->count);
}
template <typename U> void reset(U *p) {
this->ptr = new MyDerived<T, U>{p};
--(*this->count);
this->count = new counter{0};
}
T *get() const { return this->ptr ? this->ptr->ptr : nullptr; }
T &operator*() const { return this->ptr->operator*(); }
T *operator->() const { return this->ptr->operator->(); }
explicit operator bool() const {
return this->ptr != nullptr && this->ptr->ptr != nullptr;
}
};
// template <typename T, typename U>
// SharedPtr<T> static_pointer_cast(const SharedPtr<U> &sp) {
// todo("");
// }
// template <typename T, typename U>
// SharedPtr<T> dynamic_pointer_cast(const SharedPtr<U> &sp) {
// todo("");
// }
template <typename T1, typename T2>
bool operator==(const SharedPtr<T1> &lhs, const SharedPtr<T2> &rhs) {
return lhs.get() == rhs.get();
}
template <typename T>
bool operator==(const SharedPtr<T> &lhs, std::nullptr_t rhs) {
return lhs.get() == rhs;
}
template <typename T>
bool operator==(std::nullptr_t lhs, const SharedPtr<T> &rhs) {
return lhs == rhs.get();
}
template <typename T1, typename T2>
bool operator!=(const SharedPtr<T1> &lhs, const SharedPtr<T2> &rhs) {
return !(lhs == rhs);
}
template <typename T>
bool operator!=(const SharedPtr<T> &lhs, std::nullptr_t rhs) {
return !(lhs == rhs);
}
template <typename T>
bool operator!=(std::nullptr_t lhs, const SharedPtr<T> &rhs) {
return !(lhs == rhs);
}
} // namespace cs440
#endif

860
SharedPtr_test.cpp Normal file
View file

@ -0,0 +1,860 @@
/*
* Usage: -t secs
* where secs is how long to run the thread test for. Defaults
* to 15 seconds.
*/
// NOTE compile with -pthread
#include "SharedPtr.hpp"
#include <algorithm>
#include <assert.h>
#include <errno.h>
#include <iostream>
#include <new>
#include <pthread.h>
#include <random>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
using namespace std;
using namespace cs440;
class Random {
public:
Random(unsigned int s = 0);
// Generate random int in range [l,h].
int operator()(int l, int h) {
return std::uniform_int_distribution<int>(l, h)(gen);
}
Random(const Random &) = delete;
Random &operator=(const Random &) = delete;
private:
std::default_random_engine gen;
};
Random::Random(unsigned int seed) : gen(seed) {}
void basic_tests_1();
void basic_tests_2();
// RunSecs needs to be here so that it can be set via command-line arg.
int RunSecs = 15;
void threaded_test();
size_t AllocatedSpace;
void usage() {
fprintf(stderr, "Bad args, usage: ./a.out [ -t secs ]\n");
exit(1);
}
int main(int argc, char *argv[]) {
int c;
setlinebuf(stdout);
{
// Force initial iostreams allocation so that memory-leak detecting
// will work right. Tabs and pointer are to get locale stuff
// allocated.
int *p = (int *)0x1234;
cout << "\tForce initial allocation on cout: " << p << endl;
cerr << "\tForce initial allocation on cerr: " << p << endl;
clog << "\tForce initial allocation on clog: " << p << endl;
}
while ((c = getopt(argc, argv, "t:")) != -1) {
switch (c) {
case 't':
RunSecs = atoi(optarg);
assert(1 <= RunSecs && RunSecs <= 10000);
break;
case '?':
usage();
break;
default:
assert(false);
abort();
}
}
// Catch any command-line args without -.
if (optind < argc) {
usage();
}
// Initial printf(), to force any internal allocation.
printf("Tests below:\n");
basic_tests_1();
basic_tests_2();
threaded_test();
}
void *operator new(size_t sz) {
char *p = (char *)malloc(sz + 8);
*((size_t *)p) = sz;
__sync_add_and_fetch(&AllocatedSpace, sz);
return p + 8;
}
void operator delete(void *vp) noexcept {
if (vp == 0) {
return;
}
char *p = (char *)vp;
size_t sz = *((size_t *)(p - 8));
__sync_sub_and_fetch(&AllocatedSpace, sz);
// Zero out memory to help catch bugs.
memset(p - 8, 0xff, sz + 8);
free(p - 8);
}
/* Basic Tests 1
* ================================================================================
*/
struct Base1 {
Base1() : derived_destructor_called(false) { printf("Base1::Base1()\n"); }
Base1(const Base1 &); // Disallow.
Base1 &operator=(const Base1 &); // Disallow.
~Base1() {
printf("Base1::~Base1()\n");
assert(derived_destructor_called);
}
protected:
bool derived_destructor_called;
};
class Derived : public Base1 {
friend void basic_tests_1();
private:
Derived() {}
Derived(const Derived &); // Disallow.
Derived &operator=(const Derived &); // Disallow.
public:
~Derived() {
printf("Derived::~Derived()\n");
derived_destructor_called = true;
}
int value;
};
struct Base_polymorphic {
Base_polymorphic() { printf("Base_polymorphic::Base_polymorphic()\n"); }
Base_polymorphic(const Base_polymorphic &); // Disallow.
Base_polymorphic &operator=(const Base_polymorphic &); // Disallow.
virtual ~Base_polymorphic() {
printf("Base_polymorphic::~Base_polymorphic()\n");
}
};
class Derived_polymorphic : public Base_polymorphic {
friend void basic_tests_1();
private:
Derived_polymorphic() {}
Derived_polymorphic(const Derived_polymorphic &); // Disallow.
Derived_polymorphic &operator=(const Derived_polymorphic &); // Disallow.
};
class Derived2_polymorphic : public Base_polymorphic {
private:
Derived2_polymorphic() {}
Derived2_polymorphic(const Derived2_polymorphic &); // Disallow.
Derived2_polymorphic &operator=(const Derived2_polymorphic &); // Disallow.
};
struct Base2 {
Base2() : derived_destructor_called(false) { printf("Base2::Base2()\n"); }
Base2(const Base2 &); // Disallow.
Base2 &operator=(const Base2 &); // Disallow.
~Base2() {
printf("Base2::~Base2()\n");
assert(derived_destructor_called);
}
bool derived_destructor_called;
};
class Derived_mi : public Base1, public Base2 {
friend void basic_tests_1();
private:
Derived_mi() {}
Derived_mi(const Derived_mi &); // Disallow.
Derived_mi &operator=(const Derived_mi &); // Disallow.
public:
~Derived_mi() {
printf("Derived_mi::~Derived_mi()\n");
Base1::derived_destructor_called = true;
Base2::derived_destructor_called = true;
}
int value;
};
struct Base1_vi {
Base1_vi() : derived_destructor_called(false) {
printf("Base1_vi::Base1_vi()\n");
}
Base1_vi(const Base1_vi &); // Disallow.
Base1_vi &operator=(const Base1_vi &); // Disallow.
~Base1_vi() {
printf("Base1_vi::~Base1_vi()\n");
assert(derived_destructor_called);
}
bool derived_destructor_called;
};
struct Base2_vi : public virtual Base1_vi {
Base2_vi() { printf("Base2_vi::Base2_vi()\n"); }
Base2_vi(const Base2_vi &); // Disallow.
Base2_vi &operator=(const Base2_vi &); // Disallow.
~Base2_vi() {
printf("Base2_vi::~Base2_vi()\n");
assert(derived_destructor_called);
}
};
class Derived_vi : public virtual Base1_vi, public Base2_vi {
friend void basic_tests_1();
private:
Derived_vi() {}
Derived_vi(const Derived_vi &); // Disallow.
Derived_vi &operator=(const Derived_vi &); // Disallow.
public:
~Derived_vi() {
printf("Derived_vi::~Derived_vi()\n");
derived_destructor_called = true;
}
int value;
};
void basic_tests_1() {
size_t base = AllocatedSpace;
// Test deleting through original class.
{
// Base1 created directly with Derived *.
{
SharedPtr<Base1> sp(new Derived);
{
// Test copy constructor.
SharedPtr<Base1> sp2(sp);
}
}
// Base1 assigned from SharedPtr<Derived>.
{
SharedPtr<Base1> sp2;
{
SharedPtr<Derived> sp(new Derived);
// Test template copy constructor.
// SharedPtr<Base1> sp3(sp);
// sp2 = sp;
// sp2 = sp2;
}
}
}
// Test copy constructor and template copy constructor.
{
{
SharedPtr<Derived> sp(new Derived);
SharedPtr<Derived> sp2(sp);
}
{
SharedPtr<Derived> sp(new Derived);
// SharedPtr<Base1> sp2(sp);
}
}
// Test assignment operator and template assignment operator.
{
{
SharedPtr<Derived> sp(new Derived);
SharedPtr<Derived> sp2;
sp2 = sp;
sp = sp; // Self assignment.
}
{
SharedPtr<Derived> sp(new Derived);
SharedPtr<Base1> sp2;
// sp2 = sp;
sp2 = sp2; // Self assignment.
// sp2 = sp;
sp = sp;
}
}
// Test reset.
{
{
SharedPtr<Derived> sp(new Derived);
SharedPtr<Base1> sp2;
// sp2 = sp;
sp2 = sp2;
sp.reset();
sp.reset(new Derived);
SharedPtr<Base1> sp3(sp2);
}
// Need to make sure that it's the reset that
// is forcing the object to be deleted, and
// not the smart pointer destructor.
// Commented out for now, since I think this
// test causes more trouble than it's worth.
// Need to rewrite it so that I can test instead
// by monitoring side-effect from a test class
// dtor.
/*
{
SharedPtr<Derived> sp(new Derived);
char *buf = (char *) ::operator new(sizeof(SharedPtr<Base1>));
SharedPtr<Base1> &sp2 = *(new (buf) SharedPtr<Base1>());
sp2 = sp;
sp2 = sp2;
sp.reset();
sp2.reset();
::operator delete(buf);
}
*/
}
// Test *.
{
SharedPtr<Derived> sp(new Derived);
(*sp).value = 1234;
// SharedPtr<const Derived> sp2(sp);
// int i = (*sp2).value;
// assert(i == 1234);
// (*sp2).value = 567; // Should give a syntax error if uncommented.
}
// Test ->.
{
SharedPtr<Derived> sp(new Derived);
sp->value = 1234;
// SharedPtr<const Derived> sp2(sp);
// int i = sp2->value;
// assert(i == 1234);
// sp2->value = 567; // Should give a syntax error if uncommented.
}
// Test get().
{
SharedPtr<Derived> sp(new Derived);
sp.get()->value = 1234;
// SharedPtr<const Derived> sp2(sp);
// int i = sp2.get()->value;
// assert(i == 1234);
}
// Test bool.
{
SharedPtr<Derived> sp(new Derived);
assert(sp);
sp.reset();
assert(!sp);
SharedPtr<Derived> sp2;
// int i = sp; // Must give syntax error.
// delete sp; // Must give syntax error.
}
// Test ==.
{
SharedPtr<Derived> sp(new Derived);
SharedPtr<Derived> sp2(sp);
SharedPtr<Derived> sp3;
assert(sp2 == sp);
assert(!(sp2 == sp3));
sp3.reset(new Derived);
assert(!(sp2 == sp3));
}
// Test static_pointer_cast.
{
SharedPtr<Derived> sp(new Derived);
// SharedPtr<Base1> sp2(sp);
// SharedPtr<Derived> sp3(sp2); // Should give a syntax error.
// SharedPtr<Derived> sp3(static_pointer_cast<Derived>(sp2));
// SharedPtr<Derived> sp4(dynamic_pointer_cast<Derived>(sp2)); // Should
// give syntax error about polymorphism.
}
// Test dynamic_pointer_cast.
{
SharedPtr<Derived_polymorphic> sp(new Derived_polymorphic);
// SharedPtr<Base_polymorphic> sp2(sp);
// SharedPtr<Derived_polymorphic> sp3(sp2); // Should give a syntax error.
// SharedPtr<Derived_polymorphic> sp3(
// dynamic_pointer_cast<Derived_polymorphic>(sp2));
// SharedPtr<Derived_polymorphic> sp4(
// static_pointer_cast<Derived_polymorphic>(sp2));
// SharedPtr<Derived2_polymorphic> sp5(
// dynamic_pointer_cast<Derived2_polymorphic>(sp2));
// assert(!sp5);
}
// Test to make sure works with MI.
{
SharedPtr<Base2> sp2;
{
SharedPtr<Base1> sp1;
{
// SharedPtr<Derived_mi> sp(new Derived_mi);
// sp1 = sp;
// sp2 =
// static_pointer_cast<Base2>(static_pointer_cast<Derived_mi>(sp1));
} // Destructor for sp called.
} // Destructor for sp1 called.
} // Destructor for sp2 called.
// Test with MI to make sure not doing invalid casts elsewhere.
{
{
SharedPtr<Base1> sp1;
{
SharedPtr<Derived_mi> sp(new Derived_mi);
// sp1 =
// static_pointer_cast<Base1>(static_pointer_cast<Derived_mi>(sp));
} // Destructor for sp called.
} // Destructor for sp1 called.
{
SharedPtr<Base2> sp2;
{
SharedPtr<Derived_mi> sp(new Derived_mi);
// sp2 =
// static_pointer_cast<Base2>(static_pointer_cast<Derived_mi>(sp));
} // Destructor for sp called.
} // Destructor for sp2 called.
}
// Should add test here to make sure not doing cast like from Helper<T> * to
// Helper<U> *.
// Test to make sure works with virtual inheritance.
{
SharedPtr<Base2_vi> sp2;
{
SharedPtr<Base1_vi> sp1;
{
SharedPtr<Derived_vi> sp(new Derived_vi);
// sp1 = sp;
// sp2 = sp;
} // Destructor for sp called.
} // Destructor for sp1 called.
} // Destructor for sp2 called.
// Test const assignment.
{
SharedPtr<const Derived> sp_const(new Derived);
SharedPtr<Derived> sp(new Derived);
// sp_const = sp;
// sp = sp_const; // Syntax error if uncommented.
}
if (base != AllocatedSpace) {
printf("Leaked %zu bytes in basic tests 1.\n", AllocatedSpace - base);
// abort();
}
printf("Basic tests 1 passed.\n");
}
/* Basic Tests 2
* ================================================================================
*/
class A {
public:
virtual ~A() {
// printf("%p deleted\n", (Obj *) this);
}
};
class B : public A {
public:
virtual ~B() {}
};
class C : public A {
public:
virtual ~C() {}
};
// These tests overlap a lot with the ones in basic tests 1.
void basic_tests_2() {
size_t base = AllocatedSpace;
{
// Test default constructor.
{
SharedPtr<A> np;
assert(!np);
}
// Test construction from object.
{
A *ap = new A;
SharedPtr<A> a(ap);
assert(a.get() == ap);
}
// Test construction from another SharedPtr of the same type.
{
SharedPtr<A> a(new A);
SharedPtr<A> a2(a);
}
// Test construction from another SharedPtr of a derived type.
{
SharedPtr<B> b(new B);
// SharedPtr<A> a(b);
}
// Test assignment operator.
{
// Same type.
SharedPtr<A> a1, a2(new A);
a1 = a2;
// Derived to base.
SharedPtr<B> b(new B);
// a1 = b;
// Object ptr.
a1.reset(new A);
// To Null.
a1.reset();
}
// More misc tests.
{
SharedPtr<B> b(new B);
SharedPtr<C> c(new C);
/*
printf("new B: %p\n", static_cast<Body *>(b.ptr()));
printf("new C: %p\n", static_cast<Body *>(c.ptr()));
printf("b: %p\n", &b);
printf("c: %p\n", &c);
*/
// Initialization from base should not require cast.
// SharedPtr<A> a_base(b);
// Default initialization and cast to base.
// SharedPtr<A> a_dc(b);
// Note that this will use the templated constructor to do a conversion.
// if a templated assignment does not exist.
// a_dc = b;
// Static cast to base.
// SharedPtr<A> a_b = b;
// SharedPtr<A> a_c = c;
/*
printf("a_b: %p\n", &a_b);
printf("a_c: %p\n", &a_c);
*/
// Dynamic downcast.
// SharedPtr<B> b2 = dynamic_pointer_cast<B>(a_b);
// If the below is uncommented, we should get an error in the templated
// assignment operator. This will verify that that is being used rather
// than templated constructors, etc.
// b2 = a_b;
// assert(b2);
// SharedPtr<C> c2 = dynamic_pointer_cast<C>(a_b);
// assert(!c2);
/*
printf("b2: %p\n", &b2);
printf("c2: %p\n", &c2);
*/
// Dynamic downcast.
// SharedPtr<B> b3 = dynamic_pointer_cast<B>(a_c);
// assert(!b3);
// SharedPtr<C> c3 = dynamic_pointer_cast<C>(a_c);
// assert(c3);
/*
printf("b3: %p\n", &b3);
printf("c3: %p\n", &c3);
*/
}
}
if (base != AllocatedSpace) {
printf("Leaked %zu bytes in basic tests 2.\n", AllocatedSpace - base);
// abort();
}
printf("Basic tests 2 passed.\n");
}
/* Threaded Test *
* ==============================================================================
*/
// These need to be global so the threads can access it.
time_t StartTime;
const int TABLE_SIZE = 100;
class TestObj {
public:
TestObj() {}
TestObj(int i) : a(i) {}
int a;
};
struct TableEntry {
TableEntry();
~TableEntry();
pthread_mutex_t lock;
SharedPtr<TestObj> *ptr;
} *Table;
TableEntry::TableEntry() : ptr(0) {
int ec;
ec = pthread_mutex_init(&lock, 0);
assert(ec == 0);
}
TableEntry::~TableEntry() {
int ec;
ec = pthread_mutex_destroy(&lock);
assert(ec == 0);
if (ptr != 0) {
delete ptr;
ptr = 0;
}
}
void *run(void *vp) {
Random rand((unsigned int)(unsigned long)vp);
pthread_t tid = pthread_self();
int ec;
struct Counters {
unsigned long assignment_new, assignment, reset, new_default, new_copy,
delet;
} counters = {0, 0, 0, 0, 0, 0};
while (time(NULL) < StartTime + RunSecs) {
int act = rand(0, 4);
switch (act) {
case 0:
// Assign ptr new TestObj;
{
int i = rand(0, TABLE_SIZE - 1);
// printf("%d: new %d start\n", (int)tid, i);
ec = pthread_mutex_lock(&Table[i].lock);
assert(ec == 0);
if (Table[i].ptr != 0) {
Table[i].ptr->reset(new TestObj); // fix
counters.assignment_new++;
}
ec = pthread_mutex_unlock(&Table[i].lock);
assert(ec == 0);
// printf("%d: new %d done\n", (int) tid, i);
}
break;
case 1:
// Set ptr to NULL
{
int i = rand(0, TABLE_SIZE - 1);
// printf("%d: clear %d start\n", (int) tid, i);
ec = pthread_mutex_lock(&Table[i].lock);
assert(ec == 0);
if (Table[i].ptr != 0) {
Table[i].ptr->reset();
counters.reset++;
}
ec = pthread_mutex_unlock(&Table[i].lock);
assert(ec == 0);
// printf("%d: clear %d done\n", (int) tid, i);
}
break;
case 2:
// Assign ptr from another ptr
{
int i = rand(0, TABLE_SIZE - 1);
int j = rand(0, TABLE_SIZE - 1);
// printf("%d: assign %d=%d start\n", (int) tid, i, j);
// Order to avoid deadlock.
if (i <= j) {
ec = pthread_mutex_lock(&Table[i].lock);
assert(ec == 0);
if (i != j) {
ec = pthread_mutex_lock(&Table[j].lock);
assert(ec == 0);
}
} else {
ec = pthread_mutex_lock(&Table[j].lock);
assert(ec == 0);
ec = pthread_mutex_lock(&Table[i].lock);
assert(ec == 0);
}
if (Table[i].ptr && Table[j].ptr) {
*Table[i].ptr = *Table[j].ptr;
counters.assignment++;
}
ec = pthread_mutex_unlock(&Table[i].lock);
assert(ec == 0);
if (i != j) {
ec = pthread_mutex_unlock(&Table[j].lock);
assert(ec == 0);
}
// printf("%d: assign %d=%d done\n", (int) tid, i, j);
}
break;
case 3:
// Create new sptr.
{
// Create with default.
if (rand(0, 1) == 0) {
int i = rand(0, TABLE_SIZE - 1);
ec = pthread_mutex_lock(&Table[i].lock);
assert(ec == 0);
if (!Table[i].ptr) {
Table[i].ptr = new SharedPtr<TestObj>;
counters.new_default++;
}
ec = pthread_mutex_unlock(&Table[i].lock);
assert(ec == 0);
// Create with copy constructor.
} else {
int i = rand(0, TABLE_SIZE - 1), j;
do {
j = rand(0, TABLE_SIZE - 1);
} while (i == j);
// Order to avoid deadlock.
if (i < j) {
ec = pthread_mutex_lock(&Table[i].lock);
assert(ec == 0);
ec = pthread_mutex_lock(&Table[j].lock);
assert(ec == 0);
} else {
ec = pthread_mutex_lock(&Table[j].lock);
assert(ec == 0);
ec = pthread_mutex_lock(&Table[i].lock);
assert(ec == 0);
}
if (!Table[i].ptr && Table[j].ptr) {
Table[i].ptr = new SharedPtr<TestObj>(*Table[j].ptr);
counters.new_copy++;
}
ec = pthread_mutex_unlock(&Table[i].lock);
assert(ec == 0);
ec = pthread_mutex_unlock(&Table[j].lock);
assert(ec == 0);
// printf("%d: assign %d=%d done\n", (int) tid, i, j);
}
}
break;
case 4:
// Delete
{
int i = rand(0, TABLE_SIZE - 1);
ec = pthread_mutex_lock(&Table[i].lock);
assert(ec == 0);
if (Table[i].ptr) {
delete Table[i].ptr;
Table[i].ptr = 0;
counters.delet++;
}
ec = pthread_mutex_unlock(&Table[i].lock);
assert(ec == 0);
}
break;
default:
fprintf(stderr, "Bad case: %d\n", act);
abort();
break;
}
}
double rs = RunSecs;
printf("%10lu: assignment_new=%lu ops, assignment=%lu ops, reset=%lu ops, "
"new_default=%lu ops, new_copy=%lu ops, delete=%lu ops\n"
" rates: assignment_new=%.0f ops/sec, assignment=%.0f ops/sec, "
"reset=%.0f ops/sec, new_default=%.0f ops/sec, new_copy=%.0f ops/sec, "
"delete=%.0f ops/sec\n",
(unsigned long)tid, counters.assignment_new, counters.assignment,
counters.reset, counters.new_default, counters.new_copy,
counters.delet, double(counters.assignment_new) / rs,
double(counters.assignment) / rs, double(counters.reset) / rs,
double(counters.new_default) / rs, double(counters.new_copy) / rs,
double(counters.delet) / rs);
return NULL;
}
void threaded_test() {
int ec;
size_t base = AllocatedSpace;
Table = new TableEntry[TABLE_SIZE];
printf("Running threaded test for %d seconds.\n", RunSecs);
StartTime = time(NULL);
pthread_t tid1, tid2, tid3, tid4;
ec = pthread_create(&tid1, 0, run, (void *)1);
assert(ec == 0);
ec = pthread_create(&tid2, 0, run, (void *)2);
assert(ec == 0);
ec = pthread_create(&tid3, 0, run, (void *)3);
assert(ec == 0);
ec = pthread_create(&tid4, 0, run, (void *)3);
assert(ec == 0);
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
pthread_join(tid3, NULL);
pthread_join(tid4, NULL);
delete[] Table;
if (base != AllocatedSpace) {
printf("Leaked %zu bytes in threaded test.\n", AllocatedSpace - base);
abort();
}
}
/* Local Variables: */
/* c-basic-offset: 4 */
/* End: */
/* vim: set filetype=cpp tabstop=8 shiftwidth=4 softtabstop=4: */