Wednesday, December 07, 2005

Memory Management & Embedded Databases

There is an interesting article in the December, 2005 issue of Dr. Dobb's Journal about various memory management schemes in C++ written by the creators of eXtremeDB.

Tuesday, September 06, 2005

Threads Cannot be Implemented as a Library

Weiqi Gao has a post by the same name. I followed the post to the original source.

If you are a multi-threaded developer using C or C++, I think you will find this article very interesting.

Here are a few items that jumped out at me:

  • Given the following:

    thread 1 -> x = 1; r1 = y;
    thread 2 -> y = 1; r2 = x;

    Sequential consistent memory model would say that either r1 or r2 would have a value of 1 after execution. However the author of the article has this to say about sequential consistent memory, "In practice, it appears unlikely that such a restrictive memory model can be implemented with reasonable performance on conventional architectures." [Hans J. Boehm]

    It turns out that r1 == r2 == 0 is perfectly valid for the above.

  • Rewriting of adjacent data such that:

    struct { char a; char b; } x = { 'x', 'y' };

    thread 1 -> x.a = 'k';
    thread 2 -> x.b = 'h';

    It is valid for x.a == 'x' and x.b == 'y', because load and store can happen on different threads at the same time because they are in the same "memory location", where "memory location" is not defined.

  • Global data.

    Rewriting of adjacent memory even allows for adjacent global variables outside of a struct declaration. "Since linkers may, and commonly do, reorder globals, this implies that an update to any global variable may potentially read and rewrite any other global variable." [Hans J. Boehm]

    As I often say in my C++ classes, "never, ever, never use a global variable, and when you can't avoid it, still don't use one." Of course the real problem here is not global variables.


The paper goes on to discuss the importance of language support and clear specification of the memory model for correct multi-threaded programs. The author and others are working toward addressing these issues in the C++ standard similar to how they were addressed in Java.

Tuesday, July 12, 2005

Windows: How to capture stdout from a spawned child process

The below code is similar to my last post. However, it *does* work on Windows while the previous code only works on Unix.


std::string command = ...;

ACE_Process_Options opt;

// The child process to spawn.
opt.command_line(command.c_str());

// Define the two handles for the pipe,
// fd[0] will be used for stdin
// fd[1] will be used for stdout.
ACE_HANDLE fd[2];

// Create a pipe from the two file handles.
// Pipe between stdin and stdout.
// fd[0] <-> fd[1]
if (ACE_OS::pipe(fd) == -1)
ACE_DEBUG ((LM_ERROR, "%p\n", "pipe failed"));

// Set the stdin and stdout of the process to our file handles.
// fd[0] is used for stdin, fd[1] is used for stdout.
opt.set_handles(fd[0] /*stdin*/, fd[1] /*stdout*/);

// Spawn the child process.
ACE_Process proc;
if (proc.spawn(opt) == -1)
ACE_DEBUG ((LM_ERROR, "%p\n", "spawn failed"));

// Wait for the process to finish.
ACE_exitcode exitcode = 0;
proc.wait(&exitcode);

// After fork:
// fd[0] <-> fd[1] child
// fd'[0] <-> fd'[1] parent

// Need to close parent stdout or parent will be trying to read from it forever.
// With stdout closed parent will get EOF from the child so that the parent
// will know the child is done.
if (ACE_OS::close(fd[1]) == -1)
ACE_DEBUG ((LM_ERROR, "%p\n", "close stdout"));

// After close:
// fd[0] <-> fd[1] child
// fd'[0] <-> parent
// which leaves our (parent) stdin piped to child's stdout

// set_handles() dups the handles, so make sure they are released.
opt.release_handles();

// Read from parent's stdin, which is connected
// to the child's stdout.
std::string child_stdout;
const size_t BUFFSIZE = 1024;
char buf[BUFFSIZE + 1];
ssize_t n = 0;
while ((n = ACE_OS::read(fd[0], buf, BUFFSIZE)) > 0) {
buf[n] = 0;
child_stdout.append(buf, n);
}

// Close stdin.
if (ACE_OS::close(fd[0]) == -1)
ACE_DEBUG ((LM_ERROR, "%p\n", "close stdin"));

Friday, July 08, 2005

How to capture stdout from a spawned child process

A recent post to ace-users reminded me how hard it was for me to figure out how to capture stdout from a spawned child process in C++. After much research and trial and error, I came up with the following which uses ACE.

Update 7/12/2005: The below code does not work on Windows because Windows does not allow redirection of stdout/stderr to sockets which is what ACE_Pipe uses. See my next post for code that does work on Windows.


std::string command = ...;

ACE_Process_Options opt;

// The child process to spawn.
opt.command_line(command.c_str());

// Define the two handles for the pipe,
// fd[0] will be used for stdin
// fd[1] will be used for stdout.
ACE_HANDLE fd[2];

// Create a pipe from the two file handles.
// Pipe between stdin and stdout.
// fd[0] <-> fd[1]
ACE_Pipe pipe(fd);

// Set the stdin and stdout of the process to our file handles.
// fd[0] is used for stdin, fd[1] is used for stdout.
opt.set_handles(fd[0] /*stdin*/, fd[1] /*stdout*/);

// Spawn the child process.
ACE_Process proc;
if (proc.spawn(opt) == -1) ACE_DEBUG ((LM_ERROR, "%p\n", "spawn failed"));

// After fork:
// fd[0] <-> fd[1] child
// fd'[0] <-> fd'[1] parent

// Need to close parent stdout or parent will be trying to read from it forever.
// With stdout closed parent will get EOF from the child so that the parent
// will know the child is done.
if (ACE_OS::close(fd[1]) == -1) ACE_DEBUG ((LM_ERROR, "%p\n", "close stdout"));

// After close:
// fd[0] <-> fd[1] child
// fd'[0] <-> parent
// which leaves our (parent) stdin piped to child's stdout

// set_handles() dups the handles, so make sure they are released.
opt.release_handles();

// Read from parent's stdin, which is connected
// to the child's stdout.
std::string child_stdout;
const size_t BUFFSIZE = 1024;
char buf[BUFFSIZE + 1];
ssize_t n = 0;
while ((n = ACE_OS::read(fd[0], buf, BUFFSIZE)) > 0) {
buf[n] = 0;
child_stdout.append(buf, n);
}

ACE_exitcode exitcode = 0;
proc.wait(&exitcode);

// Close stdin.
if (ACE_OS::close(fd[0]) == -1) ACE_DEBUG ((LM_ERROR, "%p\n", "close stdin"));

// Close the pipe, pipe destructor does not close the pipe.
pipe.close();

Tuesday, June 07, 2005

C++ reference to an array

A number of years ago I remember learning how to pass a reference to an array of fixed size to a function in C++. The C++ Primer by Stan Lippman and Josee Lajoie discuss how to do this in section 7.3 (Functions - Argument Passing). It looks like this:

void foo(int (&refArr)[10])
{
size_t size = sizeof(refArr); // returns 10*sizeof(int)
}


Today Jonathan asked how to return a reference to an array of fixed size. I took a quick look at the spec and saw that returning a reference to an array is allowed, but I couldn't find how.

Well, it turns out that is looks like this:

const int (&getArray() const)[10]
{
return array_;
}


Fun!

Friday, May 20, 2005

Top ranking on google for "c++ spec"

I was looking at traffic to my blog and noticed that a google search for "c++ spec" has my blog as the top site, specifically this article: "c++ spec".

Wednesday, May 18, 2005

ComboTimer from Java to C++

A few weeks back I started work of a port from Java to C++ of the ObjectRepository. The ObjectRepository (OR) is a load-balancing NameService like CORBA service that OCI developed for a client a couple of years ago. The client open-sourced it and I decided to create a C++ version of it. One of the first classes that I ported was the ComboTimer. Justin wrote the original Java version, so the design is his. He also made some good suggestions for improvements that I incorporated into the C++ version below.

I welcome comments about the implementation, specifically about any threading issues.

I will attempt to post more about the ObjectRepository in the future.

== File ComboTimer.java ==

/*
* Created on Nov 21, 2003
* Copyright 2003 Object Computing, Inc.
*/
package ObjectRepository;

import java.util.Timer;
import java.util.TimerTask;

/**
* This timer class allows scheduling of two related timeouts, one short and
* one long. The second timeout is used as a maximum. The first call to
* reset() schedules both timeouts, and subsequent calls to reset() will
* cancel the short timeout and reschedule it.
*
* If either timeout is <= 0, it is disabled.
* The max timeout must be >= the short timeout.
* Example:
* ComboTimer ct = new ComboTimer(cb, 50, 5000);
* This creates a timer that will callback 50ms after the first call to
* reset(). If, for example, reset() is called every 40ms, the timer will
* callback 5s after the first call to reset().
* It's expected that this timer will be used to allow files to be written
* during a period of inactivity, but with a maximum age to prevent much
* data loss in the event of a crash.
*
* Any call to save() or cancel() will stop both timers.
*
* Internally a separate thread is used for the timers.
*/
public final class ComboTimer {

/**
* Implement this interface to handle the timer event. There is no way to
* distinguish which timeout occurred.
*/
public interface Callback {
public void run();
}

private final class Timeout extends TimerTask {
public void run() {
ComboTimer.this.cancel();
if (cb_ != null) {
cb_.run();
}
}
}

// Setting false in the Timer constructor means that
// the timer thread will prevent the main application from
// exiting. This is typically desired, but can be a problem
// if the application using the combotimer doesn't remember
// to cancel() it before exiting.
private Timer tmr_ = new Timer(true);
private final Callback cb_;
private Timeout to_ = new Timeout();
private Timeout max_ = new Timeout();
private int ms_ = 0;
private int max_ms_ = 0;
private boolean is_set_ = false;

/**
* Schedule the Callback in ms milliseconds, with a maximum timeout
* of max_ms milliseconds.
* Specify 0 for either timer to disable that timer.
*/
public ComboTimer(Callback cb, int ms, int max_ms) {
if (cb == null) {
throw new IllegalArgumentException("Callback == null is not allowed.");
}
cb_ = cb;
ms_ = ms;
max_ms_ = max_ms;
is_set_ = false;
if (ms_ < 0) {
ms_ = 0;
}
if (max_ms_ > 0 && max_ms_ < ms_) {
max_ms_ = ms_;
ms_ = 0;
}
}
/**
* The first time this method is called, both timeouts (if > 0) are
* scheduled. Each subsequent call resets the short timeout.
* Once a timeout event occurs the state of the ComboTimer resets, and
* the next call to this method will act as the first.
*/
public synchronized void reset() {
if (tmr_ == null) {
return;
}
if (ms_ > 0) {
to_.cancel();
to_ = new Timeout();
tmr_.schedule(to_, ms_);
}
if (!is_set_) {
is_set_ = true;
if (max_ms_ > 0) {
max_.cancel();
max_ = new Timeout();
tmr_.schedule(max_, max_ms_);
}
}
}
/**
* Cancel both timeouts, resetting the ComboTimer to its initial state.
*/
public synchronized void cancel() {
if (tmr_ == null) {
return;
}
is_set_ = false;
to_.cancel();
max_.cancel();
}
/**
* Cancel both timeouts, and kill the timer thread. Once this method
* is called, the ComboTimer is no longer useable.
*/
public synchronized void close() {
if (tmr_ == null) {
return;
}
tmr_.cancel();
tmr_ = null;
}
/**
* Check to see if the timeout(s) have been scheduled.
*/
public synchronized boolean isSet() {
return is_set_;
}
}



== File ComboTimer.h ==



// Copyright (c) 2005 by Kevin Heifner. heifner at ociweb dot com.
// Permission is granted to use this code without restriction
// so long as this copyright notice appears in all source files.

#ifndef OR_COMBOTIMER_H
#define OR_COMBOTIMER_H

#include "OR_Export.h"
#include "ace/Timer_Queue.h"
#include "ace/Reactor.h"
#include "ace/Thread_Manager.h"
#include "boost/function.hpp"

namespace OR {

/**
* This timer class allows scheduling of two related timeouts, one short and
* one long. The second timeout is used as a maximum. The first call to
* reset() schedules both timeouts, and subsequent calls to reset() will
* cancel the short timeout and reschedule it.
*
* If either timeout is == 0, it is disabled.
* The max timeout must be >= the short timeout.
* Example:
* ComboTimer ct(cb, ACE_Time_Value(10), ACE_Time_Value(60));
* This creates a timer that will callback 10sec after the first call to
* reset(). If, for example, reset() is called every 5secs, the timer will
* callback 60secs after the first call to reset().
*
* cancel() will stop both timers.
*
* Provide a Callback function object. There is no way to
* distinguish which timeout occurred, although that could be easily
* added.
*
* Internally a separate thread is used for the timers.
*/
class OR_Export ComboTimer {
public:

// pointer to function of form: void foo();
typedef boost::function Callback_t;

/**
* Schedule the Callback.
* Specify 0 for either timer to disable that timer.
* Spawns separate thread, but timer does not start until
* reset() is called.
* @throw std::invalid_argument if maxTime < shortTime.
*/
ComboTimer(
Callback_t callback,
const ACE_Time_Value& shortTime,
const ACE_Time_Value& maxTime = ACE_Time_Value(0)
);

/**
* Calls fini()
*/
~ComboTimer();

/**
* The first time this method is called, both timeouts (if > 0) are
* scheduled. Each subsequent call resets the short timeout.
* Once a timeout event occurs the state of the ComboTimer resets, and
* the next call to this method will act as the first.
*/
void reset();

/**
* Cancel both timeouts, resetting the ComboTimer to its initial state.
*/
void cancel();

/**
* Cancel both timeouts, and kill the timer thread. Once this method
* is called, the ComboTimer is no longer useable.
*/
void fini();

/**
* Check to see if the timeout(s) have been scheduled.
*/
bool scheduled() const;

private:
// Calls run()
static ACE_THR_FUNC_RETURN thr_func(void* arg);
// The worker thread's main loop.
void run();

friend class TimerHandler;
class TimerHandler : public ACE_Event_Handler {
public:
explicit TimerHandler(ComboTimer& ct)
: ct_(ct)
{}
// Method which is called back by the Reactor when timeout occurs.
virtual int handle_timeout(const ACE_Time_Value& tv, const void* arg);
private:
ComboTimer& ct_;
};

typedef ACE_Guard TGuard;

private:
ACE_Reactor reactor_;
Callback_t cb_;
const ACE_Time_Value shortTime_;
const ACE_Time_Value maxTime_;
long shortTimerId_;
long maxTimerId_;
int threadGroupId_;
mutable ACE_Thread_Mutex mtx_;
// reactor_ has to live longer than timerHandler_
// because the timerHandler_ destructor calls back
// on the reactor_.
TimerHandler timerHandler_;

};

} // namespace OR

#endif // OR_COMBOTIMER_H



== File ComboTimer.cpp ==



// Copyright (c) 2005 by Kevin Heifner. heifner at ociweb dot com.
// Permission is granted to use this code without restriction
// so long as this copyright notice appears in all source files.

#include "ObjectRepository_pch.h"

#include "ComboTimer.h"
#include


OR::ComboTimer::ComboTimer(
Callback_t cb,
const ACE_Time_Value& shortTime,
const ACE_Time_Value& maxTime
)
: reactor_()
, cb_(cb)
, shortTime_(shortTime)
, maxTime_(maxTime)
, shortTimerId_(-1)
, maxTimerId_(-1)
, threadGroupId_(-1)
, mtx_()
, timerHandler_(*this)
{
if (maxTime_.msec() > 0 && maxTime_ < shortTime_) {
throw std::invalid_argument("maxTime < shortTime");
}

TGuard g(mtx_);
threadGroupId_ = ACE_Thread_Manager::instance()->spawn(thr_func, this);
}


OR::ComboTimer::~ComboTimer()
{
fini();
}


void
OR::ComboTimer::fini()
{
mtx_.acquire();
long shortTimerId = shortTimerId_;
long maxTimerId = maxTimerId_;
int threadGroupId = threadGroupId_;
mtx_.release();

reactor_.cancel_timer(shortTimerId);
reactor_.cancel_timer(maxTimerId);
// This should not be necessary because the reactor_
// is destroyed after timerHandler_.
timerHandler_.reactor(0);
// If this is called before run_reactor_event_loop(), which
// is possible if you create and then quickly destroy, then
// end_reactor_event_loop() deactivates the reactor so that
// run_reactor_event_loop() just returns, which is what we want.
reactor_.end_reactor_event_loop();
// Wait for the worker thread to exit.
// Using wait_grp because it does not interact with ACE_Object_Manager.
// We want to wait even if we are at program shutdown.
ACE_Thread_Manager::instance()->wait_grp(threadGroupId);

mtx_.acquire();
maxTimerId_ = shortTimerId_ = threadGroupId_ = -1;
mtx_.release();
}


ACE_THR_FUNC_RETURN
OR::ComboTimer::thr_func(void* arg)
{
try {
OR::ComboTimer& worker = *static_cast(arg);
worker.run();
} catch (std::exception&) {
// todo: log exception
}
return 0;
}


void
OR::ComboTimer::run()
{
// The reactor checks to see that it is the owner thread when calling
// the handle_timeout so we must tell it we are the owning thread.
reactor_.owner(ACE_Thread::self());
// Don't call reactor_.reset_reactor_event_loop() because
// if end_reactor_event_looop is called first then we
// don't want to run.
reactor_.run_reactor_event_loop();
}


int
OR::ComboTimer::TimerHandler::handle_timeout(const ACE_Time_Value& /*tv*/, const void* /*arg*/)
{
// Make sure user exception does not cause us to die.
try {
ct_.cancel();
ct_.cb_();
} catch (std::exception&) {
// todo: need to log, or somehow handle exception
}
return 0;
}


void
OR::ComboTimer::reset()
{
mtx_.acquire();
long shortTimerId = shortTimerId_;
long maxTimerId = maxTimerId_;
mtx_.release();

bool scheduled = maxTimerId != -1;

if (shortTime_.msec() > 0) {
reactor_.cancel_timer(shortTimerId);
shortTimerId =
reactor_.schedule_timer(
&timerHandler_, // ACE_Event_Handler
0, // argument sent to handle_timeout()
shortTime_ // set timer to go off with delay
);
}
if (!scheduled) {
if (maxTime_.msec() > 0) {
reactor_.cancel_timer(maxTimerId);
maxTimerId =
reactor_.schedule_timer(
&timerHandler_, // ACE_Event_Handler
0, // argument sent to handle_timeout()
maxTime_ // set timer to go off with delay
);
}
}

mtx_.acquire();
shortTimerId_ = shortTimerId;
maxTimerId_ = maxTimerId;
mtx_.release();
}


void
OR::ComboTimer::cancel()
{
mtx_.acquire();
long shortTimerId = shortTimerId_;
long maxTimerId = maxTimerId_;
mtx_.release();

reactor_.cancel_timer(shortTimerId);
reactor_.cancel_timer(maxTimerId);

mtx_.acquire();
maxTimerId_ = shortTimerId_ = -1;
mtx_.release();
}


bool
OR::ComboTimer::scheduled() const
{
TGuard g(mtx_);
return maxTimerId_ != -1;
}



== Unit test: ComboTimer_UT.cpp ==


#include "ut_pch.h"

#include "unittester.h"
#include "ObjectRepository/ComboTimer.h"
#include "boost/bind.hpp"
#include

namespace {

int callbackCalled = 0;

struct Callback {
void operator()() {
++callbackCalled;
}
};

struct CTCallback {
CTCallback() : called(0) {}
void operator()() {
++called;
}
int called;
};
}

namespace UnitTest {


class ComboTimer_UT : public cppunit::Test {
private:
static bool register_tests() {
add(&ComboTimer_UT::testStartStop, "ComboTimer_UT::testStartStop");
add(&ComboTimer_UT::testShortTimer, "ComboTimer_UT::testShortTimer");
add(&ComboTimer_UT::testMaxTimer, "ComboTimer_UT::testMaxTimer");
add(&ComboTimer_UT::testCancel, "ComboTimer_UT::testCancel");
add(&ComboTimer_UT::testException, "ComboTimer_UT::testException");
add(&ComboTimer_UT::testCallback, "ComboTimer_UT::testCallback");
return true;
}
static bool tests_registered;

public:

virtual void setup() {
callbackCalled = 0;
}
virtual void teardown() {}

void testStartStop() {
Callback cb;
{
OR::ComboTimer ct(cb, ACE_Time_Value(0,1), ACE_Time_Value(0,2));
assertEqual(callbackCalled, 0);
}
{
OR::ComboTimer ct(cb, ACE_Time_Value(0,1), ACE_Time_Value(0,2));
ct.fini();
assertEqual(callbackCalled, 0);
}
{
OR::ComboTimer ct(cb, ACE_Time_Value(0,1));
assertEqual(callbackCalled, 0);
}
{
OR::ComboTimer ct(cb, ACE_Time_Value(0,0));
assertEqual(callbackCalled, 0);
}
{
OR::ComboTimer ct(cb, ACE_Time_Value(0,1), ACE_Time_Value(0,2));
ct.fini();
assertEqual(callbackCalled, 0);
}
{
OR::ComboTimer ct(cb, ACE_Time_Value(2), ACE_Time_Value(20));
ct.reset();
assertEqual(callbackCalled, 0);
}
{
OR::ComboTimer ct(cb, ACE_Time_Value(2), ACE_Time_Value(20));
ct.reset();
ct.reset();
assertEqual(callbackCalled, 0);
}
{
OR::ComboTimer ct(cb, ACE_Time_Value(2), ACE_Time_Value(20));
ct.cancel();
ct.reset();
assertEqual(callbackCalled, 0);
}
{
OR::ComboTimer ct(cb, ACE_Time_Value(2), ACE_Time_Value(20));
ct.reset();
ct.cancel();
ct.fini();
ct.fini();
assertEqual(callbackCalled, 0);
}
}

void testShortTimer() {
Callback cb;
{
OR::ComboTimer ct(cb, ACE_Time_Value(0,1000), ACE_Time_Value(20));
ct.reset();
bool reset = false;
for (int i = 0; i < 1000; ++i) {
if (callbackCalled == 1 && !reset) {
ct.reset();
reset = true;
}
if (callbackCalled == 2) break;
ACE_OS::thr_yield();
ACE_OS::sleep(ACE_Time_Value(0,1000));
}
assertEqual(callbackCalled, 2);
}
}

void testMaxTimer() {
Callback cb;
{
OR::ComboTimer ct(cb, ACE_Time_Value(0,3000), ACE_Time_Value(0,5000));
for (int i = 0; i < 1000; ++i) {
ct.reset();
ACE_OS::sleep(ACE_Time_Value(0,1000));
ct.reset();
if (callbackCalled > 0) break;
}
assertTrue(callbackCalled > 0);
}
}

void testCancel() {
Callback cb;
{
OR::ComboTimer ct(cb, ACE_Time_Value(0,1000), ACE_Time_Value(0,2000));
ct.reset();
ct.cancel();
ACE_OS::thr_yield();
ACE_OS::sleep(ACE_Time_Value(0,5000));
assertEqual(callbackCalled, 0);

ct.reset();
for (int i = 0; i < 1000; ++i) {
ACE_OS::thr_yield();
ACE_OS::sleep(ACE_Time_Value(0,1000));
if (callbackCalled > 0) break;
}
assertTrue(callbackCalled > 0);
}
}

void testException() {
Callback cb;
try {
OR::ComboTimer ct(cb, ACE_Time_Value(3), ACE_Time_Value(2));
failTest("expected exception");
} catch (std::invalid_argument&) {
}
}

void testCallback() {
CTCallback cb;
OR::ComboTimer ct(boost::ref(cb), ACE_Time_Value(0,1000), ACE_Time_Value(0,2000));
ct.reset();
for (int i = 0; i < 1000; ++i) {
ACE_OS::thr_yield();
ACE_OS::sleep(ACE_Time_Value(0,1000));
if (cb.called > 0) break;
}
assertTrue(cb.called > 0);
}

};

bool ComboTimer_UT::tests_registered = ComboTimer_UT::register_tests();

} // namespace UnitTest

Thursday, May 05, 2005

Problems moving hard drive

I just finished what I thought would be a 15 minute task. Turns out that I spent a couple of hours trying to move a hard drive from one dead machine to another. As I have mentioned before, I try to keep the computers running at Providence Christian Academy in St. Louis MO.

Yesterday the machine (Windows 2000 Pro) we use for attendance and Peachtree started turning itself off after a few minutes of run time. I figure there is something wrong with the power supply or the motherboard. I had an extra computer and figured I would just grab the hard drive from the dying machine and move it to the extra computer. I assumed it would just be a matter of swapping the hard drive and restarting the machine. Since these are old Dell machines, swapping the hard drive is really easy. I really like the Dell cases, they are easy to get into and the parts are easy to change.

Anyway, swapping the hard drives was easy but when I tried to boot with the hard drive from the dead machine I got a blue screen of death:

***STOP: 0x0000007B
INACCESSIBLE_BOOT_DEVICE

The hard drive worked in the old machine, so I wondered what could be the problem. I tried a number of things from running chkdsk to doing a Windows 2000 rescue. Nothing worked. I did find a Microsoft article about what to do when this happens, but it didn't help. Finally, I found the following article, followed the directions, and it solved the problem.

Move an IDE Drive w/Windows 2000 or XP to A New System

Yeah!

Saturday, April 02, 2005

I want to plug my Prius in.

Looks like I'm not the only one that wants to plug his Prius in at night. I really like my Prius, I just wish it would get better gas mileage. For all the complicated technology it seems like I should get better than 40-45 mpg. I'm hopeful that when I have to replace my batteries I will be able to replace them with better versions that will allow for more stealth driving and better mpg. I'm also hopeful that the replacement of the batteries will be many years from now.

Hybrid-Car Tinkerers Scoff at No-Plug-In Rule
By DANNY HAKIM

The idea of making hybrid cars that can be plugged in to wall outlets is supported by a diverse group of interests, from neoconservatives to utilities.

Wednesday, March 30, 2005

Lessons from economics

Katie sent me an email a few months back that she thought I would enjoy. It is titled The Entrepreneur As American Hero.

Professor Walter E. Williams discusses how free market forces benefit the average consumer and finishes up with what he calls Williams' Law.
Whenever the profit incentive is missing, the probability that people’s wants can be safely ignored is the greatest. If a poll were taken asking people which services they are most satisfied with and which they are most dissatisfied with, for-profit organizations (supermarkets, computer companies and video stores) would dominate the first list while non-profit organizations (schools, offices of motor vehicle registration) would dominate the latter. In a free economy, the pursuit of profits and serving people are one and the same. No one argues that the free enterprise system is perfect, but it’s the closest we’ will come here on Earth.

I completely agree. It is time we start eliminating government run schools.

Saturday, March 26, 2005

Free Boxes from USPS

I'm visiting my folks and my mother just received a shipment of boxes that she ordered from the USPS. She has been selling items on eBay and needed some boxes to ship the items. Turns out she was able to order the boxes from the USPS for free with free delivery. The same boxes that you pay for at the post office you can get for free. Here is the link.

Update: Just noticed that the link does not work. To find the items just search for supplies.

Friday, March 18, 2005

if wallet || purse stolen then ...

I received an email from my Mortgage broker (Accutira) today. It had some good advice that I would like to share.

A number of years ago Misty was a victim of identity theft. Some lady in North County decided to use Misty's identity to buy a Sprint phone, go on a shopping spree at Home Depot (or maybe it was HQ, don't remember and it doesn't matter), and others. We found out about it when a collection agency called. It was a nightmare.

Anyway here is the advice...

We've all heard horror stories of fraud committed when a name, credit card, address, and/or social security number is stolen. Here's the information you need should it happen to you or someone you know.

First... photo copy everything you carry in your wallet. When the time comes and you do have to cancel your cards, you'll have the account numbers and phone numbers to contact immediately. Keep this in a secure place you can get access to easily.

Second... file a police report immediately in the jurisdiction where it was stolen. This proves to credit providers that you were diligent and is a first step toward any investigation if there is one.

Third (and most important)...Call the three national credit reporting agencies immediately and place a fraud alert on your name and social security number. The alert means that any company that checks your credit knows your information was stolen and they have to contact you by phone to authorize new credit.

The numbers to report fraud to the big 3 are:

Equifax 800.525.6285
Experian 888.397.3742
Trans Union 800.680.7289

The fraud line for the Social Security Administration is 800-269-0271.

Ansi C++ Spec Quote

I was looking up a question for Dan about explicit template specialization in the ANSI C++ Spec and noticed the following:

When writing a specialization, be careful about its location; or to make it compile will be such a trial as to kindle its self-immolation.

Tuesday, March 08, 2005

C++ Test Coverage

I've been working on additional functional tests this week for my current project. Dan was working with me this morning when he asked about C++ test coverage tools. I have never used one for C++, so we did the obvious thing and googled for it.

Turns out that gcc comes with gcov.

1) Add the following option to the environment.

export CPPFLAGS="-fprofile-arcs -ftest-coverage"

2) Compile as normal
3) Run your tests.
4) gcov -l -c FileName.cpp
5) vi FileName.cpp.gcov and search for "######"

"######" indicate lines that were never executed.

It is simple and easy to use.

Here is an example of the output of gcov:

11: 9: for (i = 0; i < 10; i++)
10: 10: total += i;
-: 11:
1: 12: if (total != 45)
#####: 13: printf ("Failure\n");
-: 14: else
1: 15: printf ("Success\n");

Line #13 above was never executed. Line #9 was executed 11 times. You can also have it output percentages.

Thursday, February 24, 2005

SQLite

I just came accross SQLite. Looks like a nice simple to use relational database. Has anyone used it before?

Saturday, January 22, 2005

How to re-create a website in 10 hours

I decided it was time that our Classical Christian School, Providence Christian Academy, had a new website design. I knew very little about html and nothing about css style sheets when I started. I'm quite pleased with the result. I still want to add some pictures to the site, but other than that I think it looks pretty good. Please let me know what you think. I would love to hear any suggestions for improvements.

I began the re-design by downloading CityDesk, grabbing one of the example templates, and playing with the included style sheet. It took two nights, about 10 hours, to redesign the site even while learning css.

I was talking to Weiqi about this over lunch a few days back and he was asking about CityDesk. I'm using the free edition which allows you to manage up to 50 files. CityDesk does not have a great html editor, what it does have is the ability to build a template that can be applied to all of your pages so that they all look alike and can be easily changed. It also makes it easy to publish the website. What I wish it had was CVS like history for your pages. If I hit the 50 page max for the free edition, I'll probably just write a perl script to do what the template in CityDesk does for me and use CVS. As a matter of fact, I'm tempted to do that now just so I can have the ability to keep history information. Then it would just be a matter of getting a ftp client that can publish the site easily (I'm sure there are plenty to pick from.)

Saturday, January 15, 2005

The Top 100 Things I'd Do If I Ever Became An Evil Overlord

I think I read this years ago, but Rob pointed it out to me again on Friday.

Monday, January 10, 2005

Concurrency Revolution

Herb Sutter has a very good article in C/C++ User's Journal on the Concurrency Revolution. As he sees it, multithreaded development is the next big 'thing' since OO development. He talks about how chip manufacturers are looking at other ways to speed up processing since increasing clock speed has suddenly hit a wall.

Friday, January 07, 2005

chkdsk often on NTFS

chkdsk is not something I ever do unless there is a problem. However, I ran chkdsk on one of my machines at work on accident.

[You might ask how that is possible. Well for my last post I wanted to make sure I spelled the command correctly (remember I don't run the command very often) and hit return after I did the command completion.]

Anyway, I was surprised to see that it found problems that needed to be corrected. So I ran it on my laptop for fun, and it also needed to fix some problems. I also ran it on my home machine and it also needed some problems fixed.

So, it appears that chkdsk should be ran fairly often to find and fix problems.

Go ahead and run it yourself and let me know if it finds anything on your machine that needs to be fixed.

Thursday, January 06, 2005

Rescue contents of a sick hard drive

How to rescue the contents of a sick hard drive without spending any money. Or why you need a copy of Knoppix laying around. Knoppix is a bootable linux CD that runs completely off the CD.
Rob's daughter's laptop would only boot part way into Windows XP and then halt. Windows recovery couldn't fix the problem and it looked like the hard drive was dieing (chkdsk was failing, etc.) So the next thing we tried was booting Linux using Knoppix. Once Knoppix booted up we did the following:

1. Attempted to mount the drive... that failed with something about bad drive type.

2. Attempted to use partimage to backup the drive... that failed with similar error.

3. Ran badblocks on the drive and got intermittent indications of bad blocks.
(Then Jonathan stepped in and did the following.)
4. He used raw dd to copy the contents of the drive over the network to another machine running Knoppix.
   for i in 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18; do

dd if=/dev/hda of=/mnt/other/rob_dd.img.$i skip=`echo $i * 2097152 bc` count=2097152;
done


(2097152 is 1GB divided by 512bytes - the sector size)

5. He then concatenated the files together.
   for i in 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18; do

cat /mnt/other/rob_dd.img.$i >> /mnt/other/rob_dd.img;
rm /mnt/other/rob_dd.img.$i;
done


6. Next, mount the file as a drive using Linux's loopback device. The gory details of using Linux's loopback device are explained by this link. Since the drive only had one partition which started at sector 63, the offset was 63 * 512 bytes => 32256 bytes.
   mount -o loop,offset=32256 -t ntfs  rob_dd.img /mnt/rob_dd


7. cd /mnt/rob_dd

8. Copy all the data you want from the "drive".

Wednesday, January 05, 2005

McObject's eXtremeDB

I'm quoted in a case study of McObject's eXtremeDB. Some of the quotes are even mine :)

Tuesday, January 04, 2005

Opening winmail.dat

I just received an attachment as winmail.dat. This free util was able to open it. Just drag the winmail.dat onto the wmparser icon and it presents a simple GUI for viewing the contents. In my case I was able to extract the embedded Microsoft Word document.