WIP: performance improvement: copy-on-write for flat edge pairs

This commit is contained in:
Matthias Koefferlein 2020-12-27 18:45:10 +01:00
parent 4ec00fb129
commit cc58d7d8ee
7 changed files with 547 additions and 29 deletions

View File

@ -32,7 +32,7 @@ namespace db
// FlatEdgePairs implementation // FlatEdgePairs implementation
FlatEdgePairs::FlatEdgePairs () FlatEdgePairs::FlatEdgePairs ()
: AsIfFlatEdgePairs (), m_edge_pairs (false) : AsIfFlatEdgePairs (), mp_edge_pairs (new db::Shapes (false))
{ {
// .. nothing yet .. // .. nothing yet ..
} }
@ -43,13 +43,13 @@ FlatEdgePairs::~FlatEdgePairs ()
} }
FlatEdgePairs::FlatEdgePairs (const FlatEdgePairs &other) FlatEdgePairs::FlatEdgePairs (const FlatEdgePairs &other)
: AsIfFlatEdgePairs (other), m_edge_pairs (false) : AsIfFlatEdgePairs (other), mp_edge_pairs (other.mp_edge_pairs)
{ {
m_edge_pairs = other.m_edge_pairs; // .. nothing yet ..
} }
FlatEdgePairs::FlatEdgePairs (const db::Shapes &edge_pairs) FlatEdgePairs::FlatEdgePairs (const db::Shapes &edge_pairs)
: AsIfFlatEdgePairs (), m_edge_pairs (edge_pairs) : AsIfFlatEdgePairs (), mp_edge_pairs (new db::Shapes (edge_pairs))
{ {
// .. nothing yet .. // .. nothing yet ..
} }
@ -61,56 +61,58 @@ void FlatEdgePairs::invalidate_cache ()
void FlatEdgePairs::reserve (size_t n) void FlatEdgePairs::reserve (size_t n)
{ {
m_edge_pairs.reserve (db::EdgePair::tag (), n); mp_edge_pairs->reserve (db::EdgePair::tag (), n);
} }
EdgePairsIteratorDelegate *FlatEdgePairs::begin () const EdgePairsIteratorDelegate *FlatEdgePairs::begin () const
{ {
return new FlatEdgePairsIterator (&m_edge_pairs); return new FlatEdgePairsIterator (mp_edge_pairs.get_const ());
} }
std::pair<db::RecursiveShapeIterator, db::ICplxTrans> FlatEdgePairs::begin_iter () const std::pair<db::RecursiveShapeIterator, db::ICplxTrans> FlatEdgePairs::begin_iter () const
{ {
return std::make_pair (db::RecursiveShapeIterator (m_edge_pairs), db::ICplxTrans ()); return std::make_pair (db::RecursiveShapeIterator (*mp_edge_pairs), db::ICplxTrans ());
} }
bool FlatEdgePairs::empty () const bool FlatEdgePairs::empty () const
{ {
return m_edge_pairs.empty (); return mp_edge_pairs->empty ();
} }
size_t FlatEdgePairs::count () const size_t FlatEdgePairs::count () const
{ {
return m_edge_pairs.size (); return mp_edge_pairs->size ();
} }
size_t FlatEdgePairs::hier_count () const size_t FlatEdgePairs::hier_count () const
{ {
return m_edge_pairs.size (); return mp_edge_pairs->size ();
} }
Box FlatEdgePairs::compute_bbox () const Box FlatEdgePairs::compute_bbox () const
{ {
m_edge_pairs.update_bbox (); mp_edge_pairs->update_bbox ();
return m_edge_pairs.bbox (); return mp_edge_pairs->bbox ();
} }
EdgePairsDelegate * EdgePairsDelegate *
FlatEdgePairs::filter_in_place (const EdgePairFilterBase &filter) FlatEdgePairs::filter_in_place (const EdgePairFilterBase &filter)
{ {
edge_pair_iterator_type pw = m_edge_pairs.get_layer<db::EdgePair, db::unstable_layer_tag> ().begin (); db::Shapes &ep = *mp_edge_pairs;
edge_pair_iterator_type pw = ep.get_layer<db::EdgePair, db::unstable_layer_tag> ().begin ();
for (EdgePairsIterator p (begin ()); ! p.at_end (); ++p) { for (EdgePairsIterator p (begin ()); ! p.at_end (); ++p) {
if (filter.selected (*p)) { if (filter.selected (*p)) {
if (pw == m_edge_pairs.get_layer<db::EdgePair, db::unstable_layer_tag> ().end ()) { if (pw == ep.get_layer<db::EdgePair, db::unstable_layer_tag> ().end ()) {
m_edge_pairs.get_layer<db::EdgePair, db::unstable_layer_tag> ().insert (*p); ep.get_layer<db::EdgePair, db::unstable_layer_tag> ().insert (*p);
pw = m_edge_pairs.get_layer<db::EdgePair, db::unstable_layer_tag> ().end (); pw = ep.get_layer<db::EdgePair, db::unstable_layer_tag> ().end ();
} else { } else {
m_edge_pairs.get_layer<db::EdgePair, db::unstable_layer_tag> ().replace (pw++, *p); ep.get_layer<db::EdgePair, db::unstable_layer_tag> ().replace (pw++, *p);
} }
} }
} }
m_edge_pairs.get_layer<db::EdgePair, db::unstable_layer_tag> ().erase (pw, m_edge_pairs.get_layer<db::EdgePair, db::unstable_layer_tag> ().end ()); ep.get_layer<db::EdgePair, db::unstable_layer_tag> ().erase (pw, ep.get_layer<db::EdgePair, db::unstable_layer_tag> ().end ());
return this; return this;
} }
@ -147,22 +149,24 @@ EdgePairsDelegate *FlatEdgePairs::add_in_place (const EdgePairs &other)
{ {
invalidate_cache (); invalidate_cache ();
db::Shapes &ep = *mp_edge_pairs;
FlatEdgePairs *other_flat = dynamic_cast<FlatEdgePairs *> (other.delegate ()); FlatEdgePairs *other_flat = dynamic_cast<FlatEdgePairs *> (other.delegate ());
if (other_flat) { if (other_flat) {
m_edge_pairs.insert (other_flat->raw_edge_pairs ().get_layer<db::EdgePair, db::unstable_layer_tag> ().begin (), other_flat->raw_edge_pairs ().get_layer<db::EdgePair, db::unstable_layer_tag> ().end ()); ep.insert (other_flat->raw_edge_pairs ().get_layer<db::EdgePair, db::unstable_layer_tag> ().begin (), other_flat->raw_edge_pairs ().get_layer<db::EdgePair, db::unstable_layer_tag> ().end ());
} else { } else {
size_t n = m_edge_pairs.size (); size_t n = ep.size ();
for (EdgePairsIterator p (other.begin ()); ! p.at_end (); ++p) { for (EdgePairsIterator p (other.begin ()); ! p.at_end (); ++p) {
++n; ++n;
} }
m_edge_pairs.reserve (db::EdgePair::tag (), n); ep.reserve (db::EdgePair::tag (), n);
for (EdgePairsIterator p (other.begin ()); ! p.at_end (); ++p) { for (EdgePairsIterator p (other.begin ()); ! p.at_end (); ++p) {
m_edge_pairs.insert (*p); ep.insert (*p);
} }
} }
@ -172,7 +176,7 @@ EdgePairsDelegate *FlatEdgePairs::add_in_place (const EdgePairs &other)
const db::EdgePair *FlatEdgePairs::nth (size_t n) const const db::EdgePair *FlatEdgePairs::nth (size_t n) const
{ {
return n < m_edge_pairs.size () ? &m_edge_pairs.get_layer<db::EdgePair, db::unstable_layer_tag> ().begin () [n] : 0; return n < mp_edge_pairs->size () ? &mp_edge_pairs->get_layer<db::EdgePair, db::unstable_layer_tag> ().begin () [n] : 0;
} }
bool FlatEdgePairs::has_valid_edge_pairs () const bool FlatEdgePairs::has_valid_edge_pairs () const
@ -197,13 +201,13 @@ FlatEdgePairs::insert_into_as_polygons (Layout *layout, db::cell_index_type into
void void
FlatEdgePairs::insert_into (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer) const FlatEdgePairs::insert_into (Layout *layout, db::cell_index_type into_cell, unsigned int into_layer) const
{ {
layout->cell (into_cell).shapes (into_layer).insert (m_edge_pairs); layout->cell (into_cell).shapes (into_layer).insert (*mp_edge_pairs);
} }
void void
FlatEdgePairs::insert (const db::EdgePair &ep) FlatEdgePairs::insert (const db::EdgePair &ep)
{ {
m_edge_pairs.insert (ep); mp_edge_pairs->insert (ep);
invalidate_cache (); invalidate_cache ();
} }

View File

@ -29,6 +29,7 @@
#include "dbAsIfFlatEdgePairs.h" #include "dbAsIfFlatEdgePairs.h"
#include "dbShapes.h" #include "dbShapes.h"
#include "dbGenericShapeIterator.h" #include "dbGenericShapeIterator.h"
#include "tlCopyOnWrite.h"
namespace db { namespace db {
@ -109,14 +110,16 @@ public:
void transform (const Trans &trans) void transform (const Trans &trans)
{ {
if (! trans.is_unity ()) { if (! trans.is_unity ()) {
for (edge_pair_iterator_type p = m_edge_pairs.template get_layer<db::EdgePair, db::unstable_layer_tag> ().begin (); p != m_edge_pairs.template get_layer<db::EdgePair, db::unstable_layer_tag> ().end (); ++p) { db::Shapes &ep = *mp_edge_pairs;
m_edge_pairs.get_layer<db::EdgePair, db::unstable_layer_tag> ().replace (p, p->transformed (trans)); for (edge_pair_iterator_type p = ep.template get_layer<db::EdgePair, db::unstable_layer_tag> ().begin (); p != ep.template get_layer<db::EdgePair, db::unstable_layer_tag> ().end (); ++p) {
ep.get_layer<db::EdgePair, db::unstable_layer_tag> ().replace (p, p->transformed (trans));
} }
invalidate_cache (); invalidate_cache ();
} }
} }
db::Shapes &raw_edge_pairs () { return m_edge_pairs; } db::Shapes &raw_edge_pairs () { return *mp_edge_pairs; }
const db::Shapes &raw_edge_pairs () const { return *mp_edge_pairs; }
protected: protected:
virtual Box compute_bbox () const; virtual Box compute_bbox () const;
@ -127,7 +130,7 @@ private:
FlatEdgePairs &operator= (const FlatEdgePairs &other); FlatEdgePairs &operator= (const FlatEdgePairs &other);
mutable db::Shapes m_edge_pairs; mutable tl::copy_on_write_ptr<db::Shapes> mp_edge_pairs;
}; };
} }

View File

@ -11,6 +11,7 @@ FORMS =
SOURCES = \ SOURCES = \
tlAssert.cc \ tlAssert.cc \
tlClassRegistry.cc \ tlClassRegistry.cc \
tlCopyOnWrite.cc \
tlDataMapping.cc \ tlDataMapping.cc \
tlDeflate.cc \ tlDeflate.cc \
tlException.cc \ tlException.cc \
@ -54,6 +55,7 @@ HEADERS = \
tlAlgorithm.h \ tlAlgorithm.h \
tlAssert.h \ tlAssert.h \
tlClassRegistry.h \ tlClassRegistry.h \
tlCopyOnWrite.h \
tlDataMapping.h \ tlDataMapping.h \
tlDeflate.h \ tlDeflate.h \
tlException.h \ tlException.h \

View File

@ -0,0 +1,31 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2020 Matthias Koefferlein
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "tlCopyOnWrite.h"
namespace tl
{
tl::Mutex tl::CopyOnWritePtrBase::ms_lock;
}

264
src/tl/tl/tlCopyOnWrite.h Normal file
View File

@ -0,0 +1,264 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2020 Matthias Koefferlein
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef HDR_tlCopyOnWrite
#define HDR_tlCopyOnWrite
#include "tlCommon.h"
#include "tlThreads.h"
namespace tl
{
/**
* @brief A copy duplicator (see copy_on_write_ptr below)
*/
template <class X>
struct copy_duplicator
{
X *operator() (const X &o)
{
return new X (o);
}
};
/**
* @brief A clone duplicator (see copy_on_write_ptr below)
*/
template <class X>
struct clone_duplicator
{
X *operator() (const X &o)
{
return o.clone ();
}
};
/**
* @brief A base class for the copy-on-write shared pointers
*/
class TL_PUBLIC CopyOnWritePtrBase
{
protected:
static tl::Mutex ms_lock;
};
/**
* @brief The holder object: keeps the actual reference of the object
*/
template <class X>
class copy_on_write_holder
{
public:
copy_on_write_holder (X *x)
: m_ref_count (1), mp_x (x)
{ }
~copy_on_write_holder ()
{
delete mp_x;
mp_x = 0;
}
X *x ()
{
return mp_x;
}
int ref_count () const { return m_ref_count; };
int dec_ref () { return --m_ref_count; }
void inc_ref () { ++m_ref_count; }
private:
int m_ref_count;
X *mp_x;
};
/**
* @brief Supplies a copy-on-write shared pointer scheme
*
* The idea is to provide a smart, "unique" pointer providing a copy constructor and assignment, but
* sharing the object as long as the object is not written.
*
* Write access is assumed as soon as the non-const pointer is retrieved.
*
* In order to duplicate the object, a "duplicator" needs to be defined. By default, the
* object is duplicated using the copy constructor ("copy_duplicator"). An alternative implementation
* is provided through the "clone_duplicator", which assumes a "clone" method to supply the duplicated
* object.
*/
template <class X, class Dup = copy_duplicator<X> >
class copy_on_write_ptr
: public CopyOnWritePtrBase
{
public:
typedef X value_type;
copy_on_write_ptr ()
: mp_holder (0)
{ }
copy_on_write_ptr (X *x)
: mp_holder (x ? new copy_on_write_holder<X> (x) : 0)
{ }
explicit copy_on_write_ptr (const copy_on_write_ptr<X, Dup> &other)
: mp_holder (other.mp_holder)
{
aquire ();
}
copy_on_write_ptr &operator= (const copy_on_write_ptr<X, Dup> &other)
{
if (this != &other) {
release ();
mp_holder = other.mp_holder;
aquire ();
}
return *this;
}
~copy_on_write_ptr ()
{
release ();
}
/**
* @brief Gets a writable object
* This is when we will create a new copy if the object is shared.
*/
X *get_non_const ()
{
if (mp_holder) {
tl::MutexLocker locker (&ms_lock);
if (mp_holder->ref_count () > 1) {
X *x = mp_holder->x ();
mp_holder->dec_ref ();
mp_holder = new copy_on_write_holder<X> (Dup () (*x));
}
return mp_holder->x ();
} else {
return 0;
}
}
/**
* @brief Gets the const pointer
* No copy will be created.
*/
const X *get_const () const
{
if (mp_holder) {
return mp_holder->x ();
} else {
return 0;
}
}
/**
* @brief Sets the pointer
*/
void reset (X *x)
{
release ();
if (x) {
mp_holder = new copy_on_write_holder<X> (x);
}
}
/**
* @brief Gets the non-const pointer
* This is when we will create a new copy if the object is shared.
*/
X *operator-> ()
{
return get_non_const ();
}
/**
* @brief Gets the const pointer
* No copy will be created.
*/
const X *operator-> () const
{
return get_const ();
}
/**
* @brief Gets the non-const reference
* This is when we will create a new copy if the object is shared.
*/
X &operator* ()
{
return *get_non_const ();
}
/**
* @brief Gets the const reference
* No copy will be created.
*/
const X &operator* () const
{
return *get_const ();
}
/**
* @brief Debugging and testing: gets the reference count
*/
int ref_count () const
{
int rc = 0;
if (mp_holder) {
tl::MutexLocker locker (&ms_lock);
rc = mp_holder->ref_count ();
}
return rc;
}
private:
X *mp_x;
copy_on_write_holder<X> *mp_holder;
void release ()
{
if (mp_holder) {
tl::MutexLocker locker (&ms_lock);
if (mp_holder->dec_ref () <= 0) {
delete mp_holder;
}
mp_holder = 0;
}
}
void aquire ()
{
if (mp_holder) {
tl::MutexLocker locker (&ms_lock);
mp_holder->inc_ref ();
}
}
};
}
#endif

View File

@ -0,0 +1,213 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2020 Matthias Koefferlein
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "tlCopyOnWrite.h"
#include "tlUnitTest.h"
// This namespace separates the test structs from other objects
namespace copy_on_write_tests
{
static int x_instances = 0;
class X
{
public:
X () { ++x_instances; }
virtual ~X () { --x_instances; }
virtual const char *name () const = 0;
virtual X *clone () const = 0;
};
class Y : public X
{
public:
virtual const char *name () const { return "Y"; }
virtual X *clone () const { return new Y (); }
};
class Z : public X
{
public:
virtual const char *name () const { return "Z"; }
virtual X *clone () const { return new Z (); }
};
static int a_instances = 0;
class A
{
public:
A () { ++a_instances; }
A (const A &) { ++a_instances; }
~A () { --a_instances; }
const char *name () const { return "A"; }
};
}
TEST(1)
{
tl::copy_on_write_ptr<copy_on_write_tests::A> ptr1, ptr2;
EXPECT_EQ (copy_on_write_tests::a_instances, 0);
ptr1.reset (new copy_on_write_tests::A ());
EXPECT_EQ (copy_on_write_tests::a_instances, 1);
EXPECT_EQ (ptr1.ref_count (), 1);
EXPECT_EQ (ptr1.get_const ()->name (), "A");
EXPECT_EQ (ptr1.get_non_const ()->name (), "A");
EXPECT_EQ (ptr1.ref_count (), 1);
ptr2 = ptr1;
EXPECT_EQ (ptr1.ref_count (), 2);
EXPECT_EQ (ptr2.ref_count (), 2);
EXPECT_EQ (ptr1.get_const () == ptr2.get_const (), true);
EXPECT_EQ (ptr1.get_const ()->name (), "A");
EXPECT_EQ (ptr1.get_non_const ()->name (), "A");
EXPECT_EQ (copy_on_write_tests::a_instances, 2);
EXPECT_EQ (ptr1.ref_count (), 1);
EXPECT_EQ (ptr2.ref_count (), 1);
EXPECT_EQ (ptr1.get_const () == ptr2.get_const (), false);
EXPECT_EQ (ptr1.get_const ()->name (), "A");
EXPECT_EQ (ptr2.get_const ()->name (), "A");
ptr1.reset (0);
EXPECT_EQ (copy_on_write_tests::a_instances, 1);
ptr2.reset (0);
EXPECT_EQ (copy_on_write_tests::a_instances, 0);
}
TEST(2)
{
tl::copy_on_write_ptr<copy_on_write_tests::A> ptr1;
EXPECT_EQ (copy_on_write_tests::a_instances, 0);
ptr1.reset (new copy_on_write_tests::A ());
EXPECT_EQ (copy_on_write_tests::a_instances, 1);
EXPECT_EQ (ptr1.ref_count (), 1);
EXPECT_EQ (ptr1.get_const ()->name (), "A");
EXPECT_EQ (ptr1.get_non_const ()->name (), "A");
EXPECT_EQ (ptr1.ref_count (), 1);
tl::copy_on_write_ptr<copy_on_write_tests::A> ptr2 (ptr1);
EXPECT_EQ (ptr1.ref_count (), 2);
EXPECT_EQ (ptr2.ref_count (), 2);
EXPECT_EQ (ptr1.get_const () == ptr2.get_const (), true);
EXPECT_EQ (ptr1.get_const ()->name (), "A");
EXPECT_EQ (ptr1.get_non_const ()->name (), "A");
EXPECT_EQ (ptr1.ref_count (), 1);
EXPECT_EQ (ptr2.ref_count (), 1);
EXPECT_EQ (ptr1.get_const () == ptr2.get_const (), false);
EXPECT_EQ (ptr1.get_const ()->name (), "A");
EXPECT_EQ (ptr2.get_const ()->name (), "A");
}
TEST(3)
{
tl::copy_on_write_ptr<copy_on_write_tests::A> ptr1;
EXPECT_EQ (copy_on_write_tests::a_instances, 0);
ptr1.reset (new copy_on_write_tests::A ());
EXPECT_EQ (copy_on_write_tests::a_instances, 1);
EXPECT_EQ (ptr1.ref_count (), 1);
EXPECT_EQ (ptr1.get_const ()->name (), "A");
EXPECT_EQ (ptr1.get_non_const ()->name (), "A");
EXPECT_EQ (ptr1.ref_count (), 1);
tl::copy_on_write_ptr<copy_on_write_tests::A> ptr2 (ptr1);
EXPECT_EQ (ptr1.ref_count (), 2);
EXPECT_EQ (ptr2.ref_count (), 2);
EXPECT_EQ (ptr1.get_const () == ptr2.get_const (), true);
EXPECT_EQ (ptr1.get_const ()->name (), "A");
ptr2.reset (0);
EXPECT_EQ (copy_on_write_tests::a_instances, 1);
EXPECT_EQ (ptr1.get_non_const ()->name (), "A");
EXPECT_EQ (ptr1.ref_count (), 1);
EXPECT_EQ (ptr2.ref_count (), 0);
EXPECT_EQ (ptr1.get_const () == 0, false);
EXPECT_EQ (ptr2.get_const () == 0, true);
EXPECT_EQ (ptr1.get_const () == ptr2.get_const (), false);
ptr1.reset (0);
EXPECT_EQ (ptr1.ref_count (), 0);
EXPECT_EQ (copy_on_write_tests::a_instances, 0);
}
TEST(4)
{
tl::copy_on_write_ptr<copy_on_write_tests::X, tl::clone_duplicator<copy_on_write_tests::X> > ptr1, ptr2;
EXPECT_EQ (copy_on_write_tests::x_instances, 0);
ptr1.reset (new copy_on_write_tests::Y ());
EXPECT_EQ (copy_on_write_tests::x_instances, 1);
EXPECT_EQ (ptr1.ref_count (), 1);
EXPECT_EQ (ptr1.get_const ()->name (), "Y");
EXPECT_EQ (ptr1.get_non_const ()->name (), "Y");
EXPECT_EQ (ptr1.ref_count (), 1);
ptr2 = ptr1;
EXPECT_EQ (ptr1.ref_count (), 2);
EXPECT_EQ (ptr2.ref_count (), 2);
EXPECT_EQ (ptr1.get_const () == ptr2.get_const (), true);
EXPECT_EQ (ptr1.get_const ()->name (), "Y");
EXPECT_EQ (ptr1.get_non_const ()->name (), "Y");
EXPECT_EQ (copy_on_write_tests::x_instances, 2);
EXPECT_EQ (ptr1.ref_count (), 1);
EXPECT_EQ (ptr2.ref_count (), 1);
EXPECT_EQ (ptr1.get_const () == ptr2.get_const (), false);
EXPECT_EQ (ptr1.get_const ()->name (), "Y");
EXPECT_EQ (ptr2.get_const ()->name (), "Y");
ptr1.reset (0);
EXPECT_EQ (copy_on_write_tests::x_instances, 1);
ptr2.reset (new copy_on_write_tests::Z ());
EXPECT_EQ (copy_on_write_tests::x_instances, 1);
EXPECT_EQ (ptr2.get_const ()->name (), "Z");
ptr2.reset (0);
EXPECT_EQ (copy_on_write_tests::x_instances, 0);
}

View File

@ -10,6 +10,7 @@ SOURCES = \
tlAlgorithm.cc \ tlAlgorithm.cc \
tlClassRegistry.cc \ tlClassRegistry.cc \
tlCommandLineParser.cc \ tlCommandLineParser.cc \
tlCopyOnWriteTests.cc \
tlDataMapping.cc \ tlDataMapping.cc \
tlDeflate.cc \ tlDeflate.cc \
tlEvents.cc \ tlEvents.cc \