Skip to content

Commit

Permalink
Added a simple abtracted threading interface, made the core
Browse files Browse the repository at this point in the history
Cache API thread-safe, and ported the other bits of thread-
related code to use the new interface.  This is all very 
preliminary: please feel free to give me any feedback.
  • Loading branch information
Matthew Hancher committed Aug 23, 2007
1 parent 2d3cf37 commit be91a69
Show file tree
Hide file tree
Showing 15 changed files with 554 additions and 257 deletions.
14 changes: 9 additions & 5 deletions configure.ac
Expand Up @@ -215,6 +215,10 @@ AX_PKG_BOOST_LIB(PROGRAM_OPTIONS, [-lboost_program_options], [boost/program_opti
AX_PKG_BOOST_LIB(FILESYSTEM, [-lboost_filesystem], [boost/filesystem/path.hpp])
AX_PKG_BOOST_LIB(THREAD, [-lboost_thread], [boost/thread/thread.hpp])

# This provides an easy way for users to override the threads options
# if they are replacing Boost threads with something else for their platform.
AX_PKG(THREADS, [BOOST_THREAD], [], [])

AX_PKG_LAPACK
AM_CONDITIONAL(HAVE_PKG_LAPACK, [test "$HAVE_PKG_LAPACK" = "yes"])

Expand All @@ -236,7 +240,7 @@ AX_PKG(Z, [], [-lz], [zlib.h])
AX_PKG(PNG, [], [-lpng], [png.h])
AX_PKG(JPEG, [], [-ljpeg], [jpeglib.h])
AX_PKG(TIFF, [], [-ltiff], [tiff.h])
AX_PKG(OPENEXR, [Z PTHREADS], [-lImath -lHalf -lIex -lIlmImf], [ImfOutputFile.h], [OpenEXR])
AX_PKG(OPENEXR, [Z], [-lImath -lHalf -lIex -lIlmImf], [ImfOutputFile.h], [OpenEXR])
AX_PKG(HDF, [], [-ldf -lmfhdf], [mfhdf.h])

AX_PKG(X11, [], [-lXext -lX11 -lXmu -lXi -lXt], [])
Expand Down Expand Up @@ -270,7 +274,7 @@ AM_CONDITIONAL(HAVE_PKG_GDAL, [test "$HAVE_PKG_GDAL" = "yes"])
# module definitions
##################################################

AX_MODULE(CORE, [src/vw/Core], [libvwCore.la], yes, [M BOOST PTHREADS BOOST_THREAD])
AX_MODULE(CORE, [src/vw/Core], [libvwCore.la], yes, [M BOOST THREADS], [PTHREADS])
AX_MODULE(MATH, [src/vw/Math], [libvwMath.la], yes, [CORE], [LAPACK GMP PPL APRON QHULL])
AX_MODULE(IMAGE, [src/vw/Image], [libvwImage.la], yes, [MATH])
AX_MODULE(FILEIO, [src/vw/FileIO], [libvwFileIO.la], yes, [IMAGE], [Z PNG JPEG TIFF OPENEXR JPEG2K HDF GDAL])
Expand All @@ -280,12 +284,12 @@ if test ${MAKE_MODULE_VW} != "yes"; then
fi

AX_MODULE(CAMERA, [src/vw/Camera], [libvwCamera.la], yes, [VW BOOST_PROGRAM_OPTIONS])
AX_MODULE(MOSAIC, [src/vw/Mosaic], [libvwMosaic.la], yes, [VW PTHREADS BOOST_PROGRAM_OPTIONS BOOST_FILESYSTEM])
AX_MODULE(MOSAIC, [src/vw/Mosaic], [libvwMosaic.la], yes, [VW BOOST_PROGRAM_OPTIONS BOOST_FILESYSTEM])
AX_MODULE(INTERESTPOINT, [src/vw/InterestPoint], [libvwInterestPoint.la], yes, [VW BOOST_PROGRAM_OPTIONS])
AX_MODULE(CARTOGRAPHY, [src/vw/Cartography], [libvwCartography.la], yes, [VW PROJ4 XMLPARSER],[GDAL])
AX_MODULE(HDR, [src/vw/HDR], [libvwHDR.la], yes, [VW CAMERA BOOST_PROGRAM_OPTIONS LAPACK])
AX_MODULE(STEREO, [src/vw/Stereo], [libvwStereo.la], yes, [STEREO_SRC VW PTHREADS BOOST_THREAD])
AX_MODULE(GPU, [src/vw/GPU], [libvwGPU.la], no, [VW GL GLU GLUT GLEW], [CG PTHREADS])
AX_MODULE(STEREO, [src/vw/Stereo], [libvwStereo.la], yes, [STEREO_SRC VW])
AX_MODULE(GPU, [src/vw/GPU], [libvwGPU.la], no, [VW GL GLU GLUT GLEW], [CG])

# These are here (instead of inside the MODULE macro where they belong)
# for backwards compatability with older versions of automake.
Expand Down
10 changes: 8 additions & 2 deletions src/vw/Core/Cache.cc
Expand Up @@ -29,11 +29,16 @@
#include <vw/Core/Debugging.h>

namespace {
vw::Cache g_system_cache( 512*1024*1024 );
vw::RunOnce system_cache_once = VW_RUNONCE_INIT;
vw::Cache *system_cache_ptr = 0;
void init_system_cache() {
system_cache_ptr = new vw::Cache( 512*1024*1024 );
}
}

vw::Cache& vw::Cache::system_cache() {
return g_system_cache;
system_cache_once.run( init_system_cache );
return *system_cache_ptr;
}

void vw::Cache::allocate( size_t size ) {
Expand All @@ -49,6 +54,7 @@ void vw::Cache::allocate( size_t size ) {
}

void vw::Cache::resize( size_t size ) {
Mutex::Lock lock(m_mutex);
m_max_size = size;
while( m_size > m_max_size ) {
VW_ASSERT( m_last_valid, LogicErr() << "Cache is empty but has nonzero size!" );
Expand Down
73 changes: 57 additions & 16 deletions src/vw/Core/Cache.h
Expand Up @@ -25,6 +25,21 @@
///
/// Types and functions to assist cacheing regeneratable data.
///
/// The main public API is thread-safe:
/// Cache::insert(GeneratorT const&)
/// Cache::system_cache()
/// Cache::resize(size_t)
/// The entire Handle<GeneratorT> class
///
/// No other functions are guaranteed to be thread-safe. There are
/// two levels of synchronization: one lock per cache to protect the
/// cache data structure itself, and one lock per cache line to
/// protect the m_value pointer and synchronize the (potentially very
/// expensive) generation operation. Note also that the valid()
/// function is only useful as a heuristic: there is no guarantee that
/// the cache line won't be invalidated between when the function
/// checks the state and when you examine the result.
///
#ifndef __VW_CORE_CACHE_H__
#define __VW_CORE_CACHE_H__

Expand All @@ -36,6 +51,7 @@
#include <boost/smart_ptr.hpp>

#include <vw/Core/Exception.h>
#include <vw/Core/Thread.h>
#include <vw/Core/Stopwatch.h>

#include <iostream>
Expand All @@ -49,23 +65,23 @@ namespace vw {
// An LRU-based regeneratable-data cache
class Cache {

// CacheLineBase
// The abstract base class for all cache line objects.
class CacheLineBase {
Cache& m_cache;
CacheLineBase *m_prev, *m_next;
size_t m_size;
bool valid;
const size_t m_size;
friend class Cache;
protected:
Cache& cache() const { return m_cache; }
inline void allocate() { m_cache.allocate(m_size); }
inline void deallocate() { m_cache.deallocate(m_size); }
inline void validate() { m_cache.validate(this); valid=true; }
inline void validate() { m_cache.validate(this); }
inline void remove() { m_cache.remove( this ); }
inline void deprioritize() { m_cache.deprioritize(this); }
public:
CacheLineBase( Cache& cache, size_t size ) : m_cache(cache), m_prev(0), m_next(0), m_size(size), valid(false) { m_cache.invalidate( this ); }
virtual ~CacheLineBase() { invalidate(); m_cache.remove( this ); }
virtual inline void invalidate() { if(valid) m_cache.invalidate(this); valid=false; }
CacheLineBase( Cache& cache, size_t size ) : m_cache(cache), m_prev(0), m_next(0), m_size(size) {}
virtual inline void invalidate() { m_cache.invalidate(this); }
virtual size_t size() const { return m_size; }
void deprioritize() { m_cache.deprioritize(this); }
};
friend class CacheLineBase;

Expand All @@ -74,24 +90,32 @@ namespace vw {
class CacheLine : public CacheLineBase {
GeneratorT m_generator;
typename boost::shared_ptr<typename GeneratorT::value_type> m_value;
Mutex m_mutex; // Mutex for m_value and generation of this cache line
unsigned m_generation_count;

public:
CacheLine( Cache& cache, GeneratorT const& generator )
: CacheLineBase(cache,generator.size()), m_generator(generator), m_generation_count(0)
{
VW_CACHE_DEBUG( vw_out(VerboseDebugMessage) << "Cache creating CacheLine " << info() << std::endl; )
Mutex::Lock cache_lock(cache.m_mutex);
CacheLineBase::invalidate();
}

virtual ~CacheLine() {
if ( valid() ) this->invalidate();
invalidate();
VW_CACHE_DEBUG( vw_out(VerboseDebugMessage) << "Cache destroying CacheLine " << info() << std::endl; )
Mutex::Lock cache_lock(cache().m_mutex);
remove();
}

virtual void invalidate() {
Mutex::Lock line_lock(m_mutex);
if( ! m_value ) return;
VW_CACHE_DEBUG( vw_out(VerboseDebugMessage) << "Cache invalidating CacheLine " << info() << std::endl; )
CacheLineBase::invalidate();
m_value.reset();
CacheLineBase::deallocate();
m_value.reset();
}

std::string info() {
Expand All @@ -102,27 +126,44 @@ namespace vw {
}

typename boost::shared_ptr<typename GeneratorT::value_type> const& value() {
// VW_CACHE_DEBUG( vw_out(VerboseDebugMessage) << "Cache accessing CacheLine " << this << std::endl; )
Mutex::Lock line_lock(m_mutex);
if( !m_value ) {
m_generation_count++;
VW_CACHE_DEBUG( vw_out(VerboseDebugMessage) << "Cache generating CacheLine " << info() << std::endl );
CacheLineBase::allocate();
{
Mutex::Lock cache_lock(cache().m_mutex);
CacheLineBase::allocate();
}
ScopedWatch sw((std::string("Cache ")
+ (m_generation_count == 1 ? "generating " : "regenerating ")
+ typeid(this).name()).c_str());
m_value = m_generator.generate();
}
CacheLineBase::validate();
{
Mutex::Lock cache_lock(cache().m_mutex);
CacheLineBase::validate();
}
return m_value;
}

bool valid() const { return m_value; }
bool valid() {
Mutex::Lock line_lock(m_mutex);
return (bool)m_value;
}

void deprioritize() { if( valid() ) CacheLineBase::deprioritize(); }
void deprioritize() {
Mutex::Lock line_lock(m_mutex);
if( m_value ) {
Mutex::Lock cache_lock(cache().m_mutex);
CacheLineBase::deprioritize();
}
}
};


CacheLineBase *m_first_valid, *m_last_valid, *m_first_invalid;
size_t m_size, m_max_size;
Mutex m_mutex;

void allocate( size_t size );
void deallocate( size_t size );
Expand Down Expand Up @@ -152,8 +193,8 @@ namespace vw {
VW_ASSERT( m_line_ptr, NullPtrErr() << "Invalid cache handle!" );
return m_line_ptr->value();
}
bool valid() const { m_line_ptr->valid(); }
size_t size() const { return m_line_ptr->size(); }
bool valid() const { return m_line_ptr->valid(); }
void deprioritize() const { return m_line_ptr->deprioritize(); }
};

Expand Down
4 changes: 2 additions & 2 deletions src/vw/Core/Makefile.am
Expand Up @@ -28,8 +28,8 @@
if MAKE_MODULE_CORE

include_HEADERS = Exception.h FundamentalTypes.h TypeDeduction.h \
VarArray.h Functors.h CompoundTypes.h Debugging.h Cache.h \
ProgressCallback.h Stopwatch.h
VarArray.h Functors.h CompoundTypes.h Debugging.h Thread.h \
Cache.h ProgressCallback.h Stopwatch.h

libvwCore_la_SOURCES = Debugging.cc Exception.cc Cache.cc ProgressCallback.cc Stopwatch.cc
libvwCore_la_LDFLAGS= -version-info @LIBTOOL_VERSION@
Expand Down
6 changes: 3 additions & 3 deletions src/vw/Core/Stopwatch.cc
Expand Up @@ -48,7 +48,7 @@ namespace vw {

// StopwatchSet
string StopwatchSet::report() const {
boost::mutex::scoped_lock lock(m_mutex);
Mutex::Lock lock(m_mutex);

vector<pair<string, Stopwatch> > sorted(m_stopwatches.begin(), m_stopwatches.end());
sort(sorted.begin(), sorted.end(), pair_string_stopwatch_elapsed_gt);
Expand Down Expand Up @@ -90,8 +90,8 @@ namespace vw {
// assume that constructors in this file have already been called

StopwatchSet *global_stopwatch_set() {
static boost::once_flag flag = BOOST_ONCE_INIT;
boost::call_once(GlobalStopwatchSet::_create, flag);
static RunOnce once = VW_RUNONCE_INIT;
once.run(GlobalStopwatchSet::_create);
return GlobalStopwatchSet::_g_global_stopwatch_set;
}

Expand Down
16 changes: 8 additions & 8 deletions src/vw/Core/Stopwatch.h
Expand Up @@ -6,9 +6,9 @@
#include <string>

// BOOST includes
#include <boost/thread/mutex.hpp>
#include <boost/shared_ptr.hpp>

#include <vw/Core/Thread.h>

namespace vw {

Expand All @@ -20,7 +20,7 @@ namespace vw {
unsigned long long m_last_start; // from Stopwatch::microtime
unsigned long m_startdepth;
unsigned long m_numstops;
mutable boost::mutex m_mutex;
mutable Mutex m_mutex;
data() : m_total_elapsed(0),
m_last_start(0),
m_startdepth(0),
Expand All @@ -35,14 +35,14 @@ namespace vw {
Stopwatch() : m_data(new data()) {}

void start() {
boost::mutex::scoped_lock lock(m_data->m_mutex);
Mutex::Lock lock(m_data->m_mutex);
if (!(m_data->m_startdepth++)) {
m_data->m_last_start= microtime();
}
}

void stop() {
boost::mutex::scoped_lock lock(m_data->m_mutex);
Mutex::Lock lock(m_data->m_mutex);
if (!--(m_data->m_startdepth)) {
m_data->m_numstops++;
m_data->m_total_elapsed += microtime() - m_data->m_last_start;
Expand All @@ -54,7 +54,7 @@ namespace vw {
}

unsigned long long elapsed_microseconds() const {
boost::mutex::scoped_lock lock(m_data->m_mutex);
Mutex::Lock lock(m_data->m_mutex);
return m_data->m_total_elapsed;
}

Expand All @@ -70,7 +70,7 @@ namespace vw {

// StopwatchSet is a named set of Stopwatches
class StopwatchSet {
mutable boost::mutex m_mutex;
mutable Mutex m_mutex;
unsigned long long m_construction_time;

std::map<std::string, Stopwatch> m_stopwatches;
Expand All @@ -80,7 +80,7 @@ namespace vw {

// Find or create stopwatch named "name"
Stopwatch get(const std::string &name) {
boost::mutex::scoped_lock lock(m_mutex);
Mutex::Lock lock(m_mutex);
return m_stopwatches[name];
}

Expand All @@ -95,7 +95,7 @@ namespace vw {
// Returns a copy of the map between names and stopwatches
// (Copy is for thread safety)
std::map<std::string, Stopwatch> get_stopwatches() const {
boost::mutex::scoped_lock lock(m_mutex);
Mutex::Lock lock(m_mutex);
return m_stopwatches;
}

Expand Down

0 comments on commit be91a69

Please sign in to comment.