Updated with README and stuff.

This commit is contained in:
Elf M. Sternberg 2016-12-30 10:47:42 -08:00
parent 27d16e33bd
commit 9635a253f5
5 changed files with 97 additions and 7 deletions

View File

@ -1,7 +1,6 @@
project("Collector")
list(APPEND CMAKE_C_FLAGS "-std=c99 ${CMAKE_C_FLAGS} -g -ftest-coverage -fprofile-arcs")
list(APPEND CMAKE_CXX_FLAGS "${CXXMAKE_C_FLAGS} -std=c++1y -I../src/include/ -g")
add_executable(collector
src/collector.c)
add_executable(collector src/collector.cpp)

65
README.md Normal file
View File

@ -0,0 +1,65 @@
---
**WHAT:**
This is an example of Bob Nystrom's
"[Baby's First Garbage Collector](http://journal.stuffwithstuff.com/2013/12/08/babys-first-garbage-collector/),"
which I've been wanting to implement for a while in order to understand
it better. To make the problem harder (as I always do), I decided to
write it in C++, and to make it even more fun, I've implemented it using
the new Variant container from C++17.
**WHY:**
I've never written a garbage collector before. Now I know what it is
and how it works.
**DETAILS:**
The collector Nystrom wrote is a simple bi-color mark-and-sweep
collector for a singly threaded process with distinct pauses based upon
a straightforward memory usage heuristic. That heuristic is simply, "Is
the amount of memory currently in use twice as much as the last time we
garbage collected?" If the answer is yes, the collector runs and sweeps
up the stack.
Nystrom's code is highly readable, and I hope mine is as well. Because
I used Variant, my Object class has an internal Pair class, and then the
Variant is just \<int, Pair\>, where "Pair" is a pair of pointers to
other objects. The entirety of the VM is basically a stack of
singly-linked lists which either represents integers or collections of
integers in a Lisp-like structure.
The allocator creates two kinds of objects, then: pairs, and lists. A
pair is created by pushing two other objects onto the stack, then
calling `push()`, which pops them off the stack and replaces them with a
Pair object. The VM class has two methods, both named `push()`, one of
which pushes an integer, the other a pair. Since a pair is built from
objects on the stack, the Pair version takes no arguments, and since
C++14 and beyond have move semantics that Variant honors,
Variant\<Pair\> only constructs a single pair. Pretty nice. I was also
able to use both lambda-style and constructor-style visitors in my
Variant, which was a fun little bonus.
**NOTE:**
I have included the header files for the
[Mapbox version of Variant](https://github.com/mapbox/variant) since the
C++17 committee's standards haven't quite reached the general public and
the Variant implementation is still a subject of some debate. This
implementation looks straightforward enough and is a header-only
release. It works with both GCC 4.8.5 and Clang 3.8, and that's good
enough for me.
The Mapbox variant is BSD licensed, and a copy of the license is
included in the Include directory.
**BUILDING:**
From the base directory of the project:
mkdir build
cd build
cmake ..
make
And you should be able to run the basic tests. It's just one file.

View File

@ -37,6 +37,7 @@ public:
unsigned char marked;
Object *next;
Object(int v): marked(0), value(v) {}
// Variant<Pair> uses move semantics; this doesn't result in Pair being built twice.
Object(Object* head, Object* tail): marked(0), value(Pair(head, tail)) {}
class Pair {
@ -113,10 +114,10 @@ public:
/* The saddest fact: I went with using NULL as our end-of-stack
discriminator rather than something higher-level, like an
Optional or Either-variant, because to use those I'd have to
user recursion to sweep the interpreter's stack, which means
I'm at the mercy of the C stack, complete with the cost of the
unwind at the end. Bummer. */
Optional or Either-variant, because to use those I'd have to use
recursion to sweep the interpreter's stack, which means I'm at
the mercy of the C stack, complete with the cost of the unwind at
the end. Bummer. */
/* I look at this and ask, WWHSD? What Would Herb Sutter Do? */

View File

@ -0,0 +1,25 @@
Copyright (c) MapBox
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
- Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
- Neither the name "MapBox" nor the names of its contributors may be
used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File