473,404 Members | 2,195 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,404 software developers and data experts.

A simple unit test framework

nw
Hi,

I previously asked for suggestions on teaching testing in C++. Based
on some of the replies I received I decided that best way to proceed
would be to teach the students how they might write their own unit
test framework, and then in a lab session see if I can get them to
write their own. To give them an example I've created the following
UTF class (with a simple test program following). I would welcome and
suggestions on how anybody here feels this could be improved:

Thanks for your time!

class UnitTest {
private:
int tests_failed;
int tests_passed;
int total_tests_failed;
int total_tests_passed;
std::string test_set_name;
std::string current_file;
std::string current_description;

public:

UnitTest(std::string test_set_name_in) : tests_failed(0),

tests_passed(0),

total_tests_failed(0),

total_tests_passed(0),

current_file(),

current_description(),

test_set_name(test_set_name_in) {
std::cout << "*** Test set : " << test_set_name << std::endl;
}

void begin_test_set(std::string description, const char *filename) {
current_description = description;
current_file = filename;
tests_failed = 0;
tests_passed = 0;
std::cout << "****** Testing: " << current_description <<
std::endl;
}

void end_test_set() {
std::cout << "****** Test : " << current_description << "
complete, ";
std::cout << "passed " << tests_passed << ", failed " <<
tests_failed << "." << std::endl;
}

template<class _TestType>
bool test(_TestType t1,_TestType t2,int linenumber) {
bool test_result = (t1 == t2);

if(!test_result) {
std::cout << "****** FAILED : " << current_file << "," <<
linenumber;
std::cout << ": " << t1 << " is not equal to " << t2 <<
std::endl;
total_tests_failed++;
tests_failed++;
} else { tests_passed++; total_tests_passed++; }
}

void test_report() {
std::cout << "*** Test set : " << test_set_name << " complete, ";
std::cout << "passed " << total_tests_passed;
std::cout << " failed " << total_tests_failed << "." << std::endl;
if(total_tests_failed != 0) std::cout << "*** TEST FAILED!" <<
std::endl;
}
};

int main(void) {
// create a rectangle at position 0,0 with sides of length 10
UnitTest ut("Test Shapes");

// Test Class Rectangle
ut.begin_test_set("Rectangle",__FILE__);
Rectangle r(0,0,10,10);
ut.test(r.is_square(),true,__LINE__);
ut.test(r.area(),100.0,__LINE__);

Rectangle r2(0,0,1,5);
ut.test(r2.is_square(),true,__LINE__);
ut.test(r2.area(),5.0,__LINE__);
ut.end_test_set();

// Test Class Circle
ut.begin_test_set("Circle",__FILE__);
Circle c(0,0,10);
ut.test(c.area(),314.1592654,__LINE__);
ut.test(c.circumference(),62.831853080,__LINE__);

ut.end_test_set();

ut.test_report();

return 0;
}

May 3 '07
176 8153
Ian Collins wrote:
This overlooks another powerful feature of TDD - algorithm discovery.
You may not know how to solve the problem when you write the first test.
As each successive test adds functionality to the unit under test, the
algorithm will find its self. Only then do you know what the
*required* branches are.
TDD can be easily viewed as a general-purpose design generator. The
refactoring phase should be able to generate any design pattern from
scratch, if it matches the code's motivation.

However, the same process cannot be viewed as a general-purpose algorithm
generator. (Anyone who invents one of _those_ gets to be our next Overlord!)
An early refactor that follows the TDD guidelines might obscure, not
promote, the correct abstraction that your algorithm will need, later, as
you turn the corner from a solution for a narrow subset of test cases to a
solution that covers your entire input range.

This is still not a Bad Thing, because you should learn you are down a rat's
hole early, and safely, and your tests give you numerous ways to recover. If
you accidentally deployed your incorrect version, you can still restart your
algorithm from scratch, in a parallel class, and comment the old one
'deprecated'. Then swap the new one in when it can handle all the old one's
test cases.

--
Phlip
http://flea.sourceforge.net/PiglegToo_1.html
May 7 '07 #151
Branimir Maksimovic wrote:
On May 7, 6:29 am, "Phlip" <phlip...@yahoo.comwrote:
>Branimir Maksimovic wrote:
>>I think that you are missing major point here. Test driven development
(or how I imagine it) cannot work with C++ as this language has
undefined behavior programs.
Nobody said we write tests to catch every possible bug.

Next, much of C++ is simply exposed plumbing. Things like smart pointers. I
can think of a test-first for a pointer, and one to make it smart, but one
generally doesn't bother with such things. The point is to make tests that
reduce debugging and help designing.

That is the problem. Usually code that no one test, make those
problems.
For example I have yet to see thread safe reference counted object
destruction.
I don't know what you mean by "TS RC destruct".

By definition, if there are no references left (RC is 0 - i.e. no more
references), only thread that just decremented the reference count can
have a pointer (i.e. a reference). If your code violated that rule, it
isn't safe - period, threads or not.

If all you want is thread safe reference counting, there are plenty of
solutions. boost and Austria smart pointers come to mind.
May 7 '07 #152
Ian Collins wrote:
Gianni Mariani wrote:
>Ian Collins wrote:
....
>>The "architect" is the development team.

Automated acceptance tests (some call them *Customer* acceptance) tests
are a critical part of any product development process and someone has
to design and develop them. That someone can either be the customer, or
testers working as their proxy.
The customer is always ill defined.
That's why it is essential to have proxy. For a consumer product, that
should be the internal product owner.
The quality of the proxy becomes the quality of the result.

This suffers the "too many eggs in one basket" syndrome.
May 7 '07 #153
Gianni Mariani wrote:
The quality of the proxy becomes the quality of the result.

This suffers the "too many eggs in one basket" syndrome.
And you know a methodology which fixes _that_??

--
Phlip
http://flea.sourceforge.net/PiglegToo_1.html
May 7 '07 #154
Branimir Maksimovic wrote:
....
These two are sure sign of possible undefined behavior.
I can bet that Task's start function, passes context
of object into other thread while construction still works
in first one. Since call to member function is made,
no wonder that you can't do this in constructor/destructor
of base class.
Yeah - I know - technically speaking you're right - practically speaking
it's not an issue. It's platform specific for any platform that works.
So far that UB is working as expected on every platform I have tested
so I have not bothered to fix it.
This is also same example I gave for incorrect code for which test
case showing failure cannot be reliably written ;)
For which definition of reliability ? That code is reliable at least on
the platforms it compiles on - at the moment. Also, I am not alone with
this one and so it is becoming a de-fact standard anyway.
But for playing with ub in this case, I would write test case code
deriving from HardTask, implementing Work and try to play with some
member variables ;)
The *right* way to use task is to call Start in the most derived
constructor ... and Wait() in the most derived destructor. Anything
else is UB - i.e. not supported.
>
Greetings, Branimir.

May 8 '07 #155
On May 8, 1:04 am, Gianni Mariani <gi3nos...@mariani.wswrote:
Branimir Maksimovic wrote:
For example I have yet to see thread safe reference counted object
destruction.

I don't know what you mean by "TS RC destruct".
I'll explain after next paragraph.
>
By definition, if there are no references left (RC is 0 - i.e. no more
references), only thread that just decremented the reference count can
have a pointer (i.e. a reference). If your code violated that rule, it
isn't safe - period, threads or not.
In single threaded programs maintaining reference count
is not problem because calls to addref and release functions
are always serialized.
When I'm talking about thread safety of object destruction,
I'm thinking about hypothetical situation when addref and release
are called simultaneously from two different threads and refcount
is 1. Such case will probably never happen if adding/releasing ref is
tied
to lifetime of objects such as shared_ptr.
So smart pointers help in maintaining rule you mention.

Greetings, Branimir.

May 8 '07 #156
Phlip wrote:
Gianni Mariani wrote:
>The quality of the proxy becomes the quality of the result.

This suffers the "too many eggs in one basket" syndrome.

And you know a methodology which fixes _that_??
Shotgun ?

May 8 '07 #157
Branimir Maksimovic wrote:
On May 8, 1:04 am, Gianni Mariani <gi3nos...@mariani.wswrote:
>Branimir Maksimovic wrote:
>>For example I have yet to see thread safe reference counted object
destruction.
I don't know what you mean by "TS RC destruct".

I'll explain after next paragraph.
>By definition, if there are no references left (RC is 0 - i.e. no more
references), only thread that just decremented the reference count can
have a pointer (i.e. a reference). If your code violated that rule, it
isn't safe - period, threads or not.

In single threaded programs maintaining reference count
is not problem because calls to addref and release functions
are always serialized.
When I'm talking about thread safety of object destruction,
I'm thinking about hypothetical situation when addref and release
are called simultaneously from two different threads and refcount
is 1. Such case will probably never happen if adding/releasing ref is
tied
to lifetime of objects such as shared_ptr.
So smart pointers help in maintaining rule you mention.
You're saying that when thread A owns the object and thread B is
referring to it, if A calls deletes it while B still has it this is a
bad thing - well yes, nothing specific to reference counting here.

If thread B uses the object referred to by thread A, then they need to
co-operate with the destruction. So, either use reference counting so
that the reference count is 2 and whichever thread releases it last
deletes it or use explicit co-operation between thread A and thread B
where A will wait until thread B indicates to thread A that it's safe to
delete.

Only one thread must ever call delete on an object. Hard rule. Must
never be violated.

Remember, that while the reference count may itself be atomic, the smart
pointer in which it is stored may not be thread safe. The only thread
safe smart pointer I know of is the one I wrote for Austria C++, I AM
SURE there are others, I just don't know of them. (available in the
alpha on http://netcabletv.org/public_releases/ - warning - big
download). The thread safe smart pointer in Austria C++ does not allow
threads to take the pointer without incrementing the reference count.

May 8 '07 #158
Gianni Mariani wrote:
Shotgun ?
You mean "throw mud against the wall and see what sticks"?

When writing shrink-wrap software, you simply need a marketing department
capable of collating individuals' requests into common business needs. That
is the nature of the beast.

--
Phlip
http://flea.sourceforge.net/PiglegToo_1.html
May 8 '07 #159
On May 8, 2:56 pm, "Phlip" <phlip...@yahoo.comwrote:
Gianni Mariani wrote:
Shotgun ?

You mean "throw mud against the wall and see what sticks"?
No, I mean "shotgun", Texas style :-)
>
When writing shrink-wrap software, you simply need a marketing department
capable of collating individuals' requests into common business needs. That
is the nature of the beast.
Shoot the marketing department until you have one worth keeping.
May 8 '07 #160
On May 7, 10:42 pm, Gianni Mariani <gi3nos...@mariani.wswrote:
James Kanze wrote:
On May 7, 10:55 am, Gianni Mariani <gi3nos...@mariani.wswrote:
...
std::string is not thread safe
Everything in the standard library in g++, from 3.0 on, is
supposed to be thread safe.
For some meanings of thread safe.
For the only meaning that makes sense. Do you know of another
meaning? I don't. Thread safe code is code that specifies a
set of guarantees for use in a multithreaded environment, and
conforms to those guarantees. G++ definitly does that (although
there is some question concerning what the actual guarantees are
for std::string).
None of the stl classes as far as I know support simultaneous
modification from multiple threads.
So? Is there any reason for them to?

Formally, of course, if by STL, you mean the standard library,
doesn't support threading at all. But all of the actual
implementations I know of today do, as an extension.
... There is some uncertainty, for some
classes, as to how this is defined, and I hesitated to post the
bug, because I wasn't sure that std::string was supposed to meet
the Posix requirements (although all of the other g++ containers
meet them). That is, however, a bit irrelevant to the
discussion here. Posix compliant thread safety is a reasonable
choice, the current implementation of std::string in g++ doesn't
meet it, and I cannot imagine any possible test which would
display this defect in a reliable fashion.
See attached: It finds the "supposed" bug in 24 milliseconds on my machine.
but this should work:
std::string global1( "A" );
std::string global2( global1 );
void thread1()
{
global1 += "1";
}
void thread2()
{
global2 += "A";
}
That definitly should (and as far as I know, does) work. The
problem is more along the lines of:
std::string global( "a" ) ;
void thread1()
{
std::string s1( global ) ;
}
void thread2()
{
std::string s2( global.begin(), global.end() ) ;
}
Since I'm not modifying global ...
global.begin() is pulling a modifyable pointer - all bets are off.
That's not what Posix says. If the std::string class is
supposed to give the Posix guarantees (which is not clear---but
all of the other STL classes explicitly do), then the above code
is legal.
It's a non-const call so the implementation is free to do
whatever it wants to global.
I don't modify anything, so the implementation is not free to do
whatever it wants.
In my initial implementation of the test I had taken a const reference
to the string (as a default thing I do without thinking) before I called
begin. Then I proceeded to tweak the parameters to trigger the problem,
I has one test run for ten minutes and alas, NO failures. So, I looked
again and I made it non-const and viola immediate failure.
I don't think I would call this one a bug.
It depends on whether G++ wants to support Posix guarantees or
not. As I said, it's not clear. (The next version of the C++
will probably statute one way or the other. I suspect in favor
of requiring this to work, but that suspicion is based on my
knowing the people involved, and not on any specific discussions
or vote.) At any rate, either std::string is in error, or the
code above is in error.

(An interesting point if the code above is in error: you
definitely cannot write a test to detect the error using VC++ or
Sun CC, because the code works with those compilers. Obviously,
testing can't reveal dependencies on the compilers you're
currently using.)
...
Yes. Better tested code is easier to develop with.
The issue hasn't been raised to date, but...
Good code is easy to understand. Code that isn't easy to
understand fails code review. How does testing verify this?
(In some ways, easy to understand is more important than an
absence of errors.)
Ya ... I thought we were talking about UNIT TESTING ! Do you like to
digress all the time ?
The claim is that writing the tests before writing the code
improves quality. I'm just showing that this is a specious
claim.
What are you smoking ? Sure you can find some obvious bugs in
a code review, but I would have already run the unit tests
before I do the code review.
That's doing things the hard way, and is hardly cost effective.
Having the computer do the hard work is far better than for me doing the
hard work. My B.S. meter just pegged again.
The problem is that the computer doesn't do the hard work for
you. It does the easy part, saying that there is an error
somewhere. Afterwards, you have the hard part: finding where.

In a code review, this is automatic.
In code review, you are told that you forgot to initialize
variable i in line 1234 of abc.cc. The unit test tells you that
tests 25 through 30 all fail. Which information makes it easier
to find and fix the error?
And a well run code review doesn't find just the obvious bugs.
It also finds those which no test will find. (I found the bug
in the g++ implementation of std::string by reviewing the code.)
I don't consider this one a bug. It's expected to fail IMHO.
As I said, it depends on the guarantee. Posix says it should
work. It works with char[]; if the goal of std::string is to
replace char[], it has to work. I suspect that the next version
of the standard will require it to work.
...
I couldn't get this to compile on my machine; I'm missing some
of the necessary headers. I'll have to download them first. If
I've understood it correctly, the basic idea does seem
interesting. I think it still depends somewhat on the
scheduling algorithm being used by the system, but I'll try to
find time to evaluate it thoroughly. (It obviously depends
somewhat on the scheduling algorithm, because the code is thread
safe if non-preemptive threading is used. But that's not the
default on most modern platforms.)

[...]
// Should this be const or not - I think it should
// James Kanze possibly believes otherwise.
Just a nit: in real code, if I were accessing through a
reference, I would use a const reference unless I planned on
modification (in which case, a lock is required). My problem is
when the code accesses the object directly, without an
intervening reference. (I presume, here, however, that the
reference is an artifact of the test suite, and is used to
simulate accessing the object directly.)

Of course, the question here isn't how the user should write
code, but whether the specific construction should be guaranteed
to work or not. Posix bases its guarantees on whether
modification takes place, not on const-ness, which is the basis
of my argument.

[...]
enum { s_count = 43 }; // prime number
Just curious: why is it important that s_count be prime?
static const unsigned s_length = 3; // tweak for maximum effect
std::string m_strings[ s_count ];
virtual void test2( int l_val, std::string & o_tval )
{
t_local_type & l_str = m_strings[ l_val ];
t_iter_type beg = l_str.begin();
// at::OSTraitsBase::SchedulerYield();
o_tval.assign( beg, l_str.end() );
Just curious: is there some reason behind using o_tval.assign,
and not simply:

std::string localCopy( l_str.begin(), l_str.end() ) ;

followed by the AT_TCAssert?

[...]
class HardTask
: public String_TaskBase< 2 >
{
public:
static at::AtomicCount s_task_counter;
static int s_xcount;
// each test thread calls this with a unique thread number
virtual void TestWork( int l_thrnum )
{
unsigned pnum = g_primes[ l_thrnum % at::CountElements( g_primes ) ];
unsigned count = l_thrnum;
unsigned done = 0;
std::string l_teststr;
for ( int i = 0; i < m_iters; ++i )
{
int choice = ( i * pnum ) % g_test->s_count;
switch ( l_thrnum & 1 ? i % 8 : (7 - (i % 8) ) )
{
case 0 : case 2 :
g_test->test1( choice, l_teststr );
break;
case 1 : case 3 :
g_test->test2( choice, l_teststr );
break;
case 4 :
g_test->test3( choice, l_teststr );
break;
case 5 :
g_test->test4( choice, l_teststr );
break;
}
// after every 1<<5 iterations - rebuild the test object
// with brand new strings
if ( true && !( i % (1<<5) ) )
{
// stuff is done here
at::Lock<at::ConditionalMutex l_lock( m_mutex );
int l_num = ++ s_xcount;
if ( m_thr_count == l_num )
{
s_xcount = 0;
g_test = new STDStringTest();
l_lock.PostAll();
}
else
{
l_lock.Wait();
}
}
I'm not sure I understand this. How is it synchronized with the
previous loop? If some of the threads are still in the previous
loop when a thread gets here, then it is undefined behavior.
Even using const_iterators.

I've saved a copy of your code locally, and will try to find
time to download your test suite in order to evaluate it on my
machines. If you really can trigger threading errors with any
degree of reliability, I'm very interested.

--
James Kanze (Gabi Software) email: ja*********@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

May 8 '07 #161
On May 8, 12:13 am, Branimir Maksimovic <b...@hotmail.comwrote:
On May 7, 10:42 pm, Gianni Mariani <gi3nos...@mariani.wswrote:
James Kanze wrote:
On May 7, 10:55 am, Gianni Mariani <gi3nos...@mariani.wswrote:
...
>std::string is not thread safe
........
And a well run code review doesn't find just the obvious bugs.
It also finds those which no test will find. (I found the bug
in the g++ implementation of std::string by reviewing the code.)
I don't consider this one a bug. It's expected to fail IMHO.
All the time I want to point out that code with undefined
behavior is expected to do anything. This is first what I have
learned on this newsgroup.
That's actually an important point. Gianni's first example:

void thread1()
{
globalString += 'A' ;
}

void thread2()
{
globalString += 'B' ;
}

entails undefined behavior with STLport, g++ and (I think) VC++,
but is defined using Sun CC (with the Rogue Wave library).
Logically, I would expect it to be undefined behavior, and
consider the behavior of Sun CC an extension (defining undefined
behavior)---I'm fairly sure that this will be the situation in
the next version of the standard.

Now, suppose that you are developping on Sun CC, but the
requirements are that your code be portable. How do you test
this?
Seems that your test code also employs undefined behavior:
I'm not sure myself. I don't know enough about his framework to
say for sure. But there are supicious points.
[tst_string.cpp]/**
* Test std::string.
*
*/
.........
template <int N, int ThreadCount = 3 >
class String_TaskBase
: public at::Task
{
..........
void Work();
};

...........
class HardTask
: public String_TaskBase< 2 >
{
}
..........
HardTask()
{
Start();
}
........
~HardTask()
{
Wait();
}
};
These two are sure sign of possible undefined behavior.
I can bet that Task's start function, passes context
of object into other thread while construction still works
in first one.
I think that HardTask is the most derived class. While
formally, it is true that you cannot use a pointer to the class
until having finished construction, in practice, it's safe to
say that once all subobjects have been constructed and
initialized, as the last thing in the constructor of the most
derived class, it should be safe. (Provided, of course, that
Start() ensures proper synchronization everywhere.)
Since call to member function is made,
no wonder that you can't do this in constructor/destructor
of base class.
Did I miss something? I didn't see any derivation from HardTask.

--
James Kanze (Gabi Software) email: ja*********@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

May 8 '07 #162
On May 8, 3:21 am, Gianni Mariani <gi3nos...@mariani.wswrote:
Branimir Maksimovic wrote:
...
These two are sure sign of possible undefined behavior.
I can bet that Task's start function, passes context
of object into other thread while construction still works
in first one. Since call to member function is made,
no wonder that you can't do this in constructor/destructor
of base class.
Yeah - I know - technically speaking you're right - practically speaking
it's not an issue. It's platform specific for any platform that works.
So far that UB is working as expected on every platform I have tested
so I have not bothered to fix it.
Supposing, of course, that HardTask is the most derived class.
(which I'm pretty sure is the case).
This is also same example I gave for incorrect code for which test
case showing failure cannot be reliably written ;)
For which definition of reliability ? That code is reliable at least on
the platforms it compiles on - at the moment. Also, I am not alone with
this one and so it is becoming a de-fact standard anyway.
There is an impedence mismatch here with regards to what can be
specified clearly in the standard, and what actually works.
Practically speaking, provided you have ensured proper
synchronization in Start, etc., an implementation where your
specific code won't work is impossible. Try writing a
specification, however, that defines its behavior, but doesn't
allow cases where you don't actually initialize something until
after the call to Start.
But for playing with ub in this case, I would write test case code
deriving from HardTask, implementing Work and try to play with some
member variables ;)
The *right* way to use task is to call Start in the most derived
constructor ... and Wait() in the most derived destructor. Anything
else is UB - i.e. not supported.
And of course, Start() must be *after* all essential
initializations in the constructor, and Wait() before you start
tearing things down in the destructor.

--
James Kanze (Gabi Software) email: ja*********@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

May 8 '07 #163
On May 7, 9:43 pm, Ian Collins <ian-n...@hotmail.comwrote:
James Kanze wrote:
On May 7, 11:31 am, Ian Collins <ian-n...@hotmail.comwrote:
James Kanze wrote:
>Comprehensive unit tests are most important for maintenance.
New code should always be reviewed. But a review is not without
costs, and reviewing an entire module because someone has
changed just a couple of characters in just one line isn't
really cost effective. Comprehensive unit tests, on the other
hand, are very effective at catching slips of the finger in the
editor.
Again, that's where paring and continuous integration come in, the code
is under constant scrutiny from many pairs of eyes.
It's not a question of numbers. It's important that the
reviewer have a fresh view of the code.
Say Fred and Jim work on a story for a couple of hours, then Fred moves
off and Bill joins Fred to carry on, doesn't Bill have a fresh view of
Fred and Jim's code?
Certainly, for that part which is already written. Does Bill
carefully reread all of the code that was already written, to
ensure that it is correct and conform to the local coding
standards? Is there a mechanism which ensures that yet someone
else intervenes once Bill and Fred have written something?

In practice, well run code reviews are necessary, and once they
are taking place, adding pair programming adds very, very little
value, while effectively doubling the cost. In general---I can
imagine that in some specific circumstances, it is worth while.

--
James Kanze (Gabi Software) email: ja*********@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

May 8 '07 #164
On May 7, 10:54 pm, Gianni Mariani <gi3nos...@mariani.wswrote:
James Kanze wrote:
Gianni Mariani wrote:
...
Example ?
Bug #21334 in the std::string implementation in g++.
Not a bug in my opinion. See my other post for a unit test case that
finds the supposed bug in less than 25 milliseconds.
I'll try it. (I'm not 100% sure that there isn't a bug in your
code, however.)
Most of the problems in DCL.
They are surprisingly easy to find too.
Really. Consider the following implementation of DCL:

pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER ;
pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER ;
Singleton* theOneAndOnly = NULL ;

static Singleton*
Singleton::instance()
{
if ( theOneAndOnly == NULL ) {
Locker lock( mutex1 ) ;
if ( theOneAndOnly == NULL ) {
Singleton* tmp ;
{
Locker lock2( mutex2 ) ;
tmp = new Singleton ;
}
theOneAndOnly = tmp ;
}
}
return theOneAndOnly ;
}

It doesn't work, but can you write a test which will reveal the
error? (For starters, you certainly cannot on most Intel 32
bits processors, because they do guarantee that it works, even
if it violates Posix, and fails in practice on some other
architectures.)

--
James Kanze (Gabi Software) email: ja*********@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

May 8 '07 #165
On May 8, 12:03 pm, James Kanze <james.ka...@gmail.comwrote:
On May 8, 12:13 am, Branimir Maksimovic <b...@hotmail.comwrote:
[tst_string.cpp]/**
* Test std::string.
*
*/
.........
template <int N, int ThreadCount = 3 >
class String_TaskBase
: public at::Task
{
..........
void Work();
};
...........
class HardTask
: public String_TaskBase< 2 >
{
}
..........
HardTask()
{
Start();
}
........
~HardTask()
{
Wait();
}
};
These two are sure sign of possible undefined behavior.
I can bet that Task's start function, passes context
of object into other thread while construction still works
in first one.

I think that HardTask is the most derived class. While
formally, it is true that you cannot use a pointer to the class
until having finished construction, in practice, it's safe to
say that once all subobjects have been constructed and
initialized, as the last thing in the constructor of the most
derived class, it should be safe. (Provided, of course, that
Start() ensures proper synchronization everywhere.)
Yes. But I have to comment that proper practice is not
to share objects between threads that are not yet constructed
and not to destruct them while other threads are still using them.
This code violates both rules.
>
Since call to member function is made,
no wonder that you can't do this in constructor/destructor
of base class.

Did I miss something? I didn't see any derivation from HardTask.
Obviously, I can't express myself in English good enough.
I meant if calls to Start and Wait are moved to base class Task
constructor/destructor,then, not necessarily, but probably,
code will fail.

Greetings, Branimir.

May 8 '07 #166
On May 7, 11:12 pm, Ian Collins <ian-n...@hotmail.comwrote:
James Kanze wrote:
In other words, you have to write the code first, to know what
the branches are.
This overlooks another powerful feature of TDD - algorithm discovery.
You may not know how to solve the problem when you write the first test.
As each successive test adds functionality to the unit under test, the
algorithm will find its self. Only then do you know what the
*required* branches are.
But suppose you never add a test which triggers a required
branch? You'll admit that it isn't reasonable to test every
possible double value. So how do you choose the test values in
such a way as to ensure that they trigger all of the required
branches? And that they trigger them at the right place?

Take a simple example: a function that requires special handling
for very small values. Obviously, you'll end up testing some
very small values, find that the results are wrong, and modify
the code to handle them. But having done that, how does
"testing" help to determine the cut-off point? (Once you know
the cut-off point, of course, you will add the two bounding
values to the test suite. But you should use numeric analysis
to find the optimal cut-off point.)

And what about threading problems? Gianni has claimed that his
test find the error I found in g++'s std::string. Maybe...
I've not yet been able to evaluate it. But would it have
occured to anyone to write such a test unless the error case
were known in advance? There is an almost infinite number of
combinations of doing A in one thread, and B in another; I doubt
you can cover them all. And of course, the actual problem only
occurs very rarely once you've got the right operations.

Also: how do you know that the tests are correct. At first
glance, I think there was a problem in Gianni's test. I could
be wrong, because I'm not familiar with the surrounding
environment, but basically, how do you know that the test tests
what you want it to test, and not something else? How do you
know you've covered all of the special cases?

--
James Kanze (Gabi Software) email: ja*********@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

May 8 '07 #167
James Kanze wrote:
Certainly, for that part which is already written. Does Bill carefully
reread all of the code that was already written, to ensure that it is
correct and conform to the local coding standards?
In my situation, yes; Bill asks to be shown all the changes, and he reads
them to himself with limited explanation. Any comments are expected to be
transferable to self-documenting code as the project progresses.

In Ian's situation, no. XP does not "require" code review, so a new pair
might just dive in and start adding the next feature. Ian's point was that
code review will still happen, serendipitously. Suppose Bill formally
reviewed but Alice is the next pair. She gets her say, too.

And "community agreements" like clear and enforcable coding standards,
covering both esthetic and technical details, is a primary XP practice. And,
no, we don't claim to have invented it. You should not be able to tell who
wrote what code.
Is there a mechanism which ensures that yet someone else intervenes once
Bill and Fred have written something?
Again, in my situation the mechanism is overt and in Ian's it is covert. It
still happens. Either the modules are an active site of progress, and they
get more review, via pairing, or they are relatively "finished", and we
don't look at them again.
In practice, well run code reviews are necessary, and once they are taking
place, adding pair programming adds very, very little value, while
effectively doubling the cost. In general---I can imagine that in some
specific circumstances, it is worth while.
You just said, "because it costs more and adds no value, it costs more and
adds no value".

I look forward to pairing with you some day.

--
Phlip
http://flea.sourceforge.net/PiglegToo_1.html
May 8 '07 #168
James Kanze wrote:
But suppose you never add a test which triggers a required branch?
Then the branch never exists in the code.
You'll admit that it isn't reasonable to test every possible double value.
Those aren't branches. They are (typically) continuous relations with
(typically known) discontinuities. So you test-first the edge cases because
you know there will be branches there.
So how do you choose the test values in such a way as to ensure that they
trigger all of the required branches? And that they trigger them at the
right place?
Take a simple example: a function that requires special handling for very
small values. Obviously, you'll end up testing some very small values,
find that the results are wrong, and modify the code to handle them. But
having done that, how does "testing" help to determine the cut-off point?
(Once you know the cut-off point, of course, you will add the two bounding
values to the test suite. But you should use numeric analysis to find the
optimal cut-off point.)
You are applying your understanding of soak testing to TDD. Imagine if you
knew nothing about soak testing, and start again.

TDD is a good way to emerge algorithms, but I'd rather use it for command
and control code. If I need to TDD a subtle math thing, I look up its
algorithm, and this tells me where the edge cases are. If there's no
algorithm to look up, I can't use TDD to _start_ the feature. I can
certainly use it to _finish_, after developing the algorithm.

Game programmers do this all the time, for the physics and geometry stuff.
And what about threading problems?
Once again, from the top, read my lips: TDD is not soak testing, and is not
expected to find every possible problem. TDD is a system to match each line
of code to a line of test, which will fail if you ERASE the line. NOT if you
put a tiny bug in it.

That's why TDD helps you go fast and avoid punitive debugging.
Also: how do you know that the tests are correct.
Asked and answered; by reviewing them. That's typically easy, because they
are typically 3~5 lines long. Even in C++.

--
Phlip
http://flea.sourceforge.net/PiglegToo_1.html
May 8 '07 #169
James Kanze wrote:
On May 7, 10:42 pm, Gianni Mariani <gi3nos...@mariani.wswrote:
....
>
>For some meanings of thread safe.

For the only meaning that makes sense.
Are stl objects thread safe ? i.e. can I call string::assign from
multiple threads simultaneously ? I know of no STL implementation that
allows that. All recent implementations that I have used though do
allow different stl objects to be called by multiple threads
simultanteously.

... Do you know of another
meaning? I don't.
Chris Thomasson on comp.pragramming.threads is proposing calling it
"strong thread safe" and "normal thread safe". Ya, don't start me.
... Thread safe code is code that specifies a
set of guarantees for use in a multithreaded environment, and
conforms to those guarantees. G++ definitly does that (although
there is some question concerning what the actual guarantees are
for std::string).
>None of the stl classes as far as I know support simultaneous
modification from multiple threads.

So? Is there any reason for them to?
Yes and no, that's the point. Sometimes it would be nice, but most of
the time no.
>
Formally, of course, if by STL, you mean the standard library,
doesn't support threading at all. But all of the actual
implementations I know of today do, as an extension.
>>>
....
>It's a non-const call so the implementation is free to do
whatever it wants to global.

I don't modify anything, so the implementation is not free to do
whatever it wants.
Well, you call a non const method (begin()) and it does some
modification for you, whether you like it or not in this implementation.
It's kind of a given for COW when you pull a pointer. Otherwise,
every iterator gets really complicated. IIRC, the standard wanted to
specifically allow COW implementations of basic_string, maybe this is
where the rubber meets the road on this requirement.

BTW - I added this code below to your GCC bug.
(http://gcc.gnu.org/bugzilla/show_bug.cgi?id=21334) It turns out you
don't need to have multiple threads to expose the bug. See this:

#include <string>
#include <cassert>

std::string str( 1<<20, 'a' );

void foo()
{

std::string str2( str );

const std::string & rstr2 = str2;

// str2.begin(); // uncomment line and all works well

std::string::const_iterator bc = rstr2.begin();
std::string::iterator bnc = str2.begin();

std::string::const_iterator ec = rstr2.end();
std::string::iterator enc = str2.end();

std::string r1( bc, ec );
std::string r2( bnc, enc );

assert( r1 == r2 );

assert( r1 == str );
}

int main()
{
foo();
}

I'm not a C++ lawyer so I'm not sure if the above code is malformed but
I would advocate that it does not for purely selfish reasons. Since no
threads are involved here, I'd say that it's a standard
interpretation/clarification thing.
>
>In my initial implementation of the test I had taken a const reference
to the string (as a default thing I do without thinking) before I called
begin. Then I proceeded to tweak the parameters to trigger the problem,
I has one test run for ten minutes and alas, NO failures. So, I looked
again and I made it non-const and viola immediate failure.
>I don't think I would call this one a bug.

It depends on whether G++ wants to support Posix guarantees or
not. As I said, it's not clear. (The next version of the C++
will probably statute one way or the other. I suspect in favor
of requiring this to work, but that suspicion is based on my
knowing the people involved, and not on any specific discussions
or vote.) At any rate, either std::string is in error, or the
code above is in error.
Yup.
>
(An interesting point if the code above is in error: you
definitely cannot write a test to detect the error using VC++ or
Sun CC, because the code works with those compilers. Obviously,
testing can't reveal dependencies on the compilers you're
currently using.)
? I'm not sure what the point is here ? You can't detect a problem
that's not there ? An astonishing revelation ?
>
....
>Having the computer do the hard work is far better than for me doing the
hard work. My B.S. meter just pegged again.

The problem is that the computer doesn't do the hard work for
you. It does the easy part, saying that there is an error
somewhere. Afterwards, you have the hard part: finding where.
Oh, that's the easy part, give me an evidence trail and I'll find the
culprit. Give me a 100 million lines of code and I'll fall asleep in 20
minutes.
>
In a code review, this is automatic.
The human is too fallible. Yes, a humans can pick the obvious things
pretty quickly. Things like "are there loops in your resource
acquisition" can at times be hard to tell by reading the code and some
times resource acquisition might be non-obvious, i.e. not just mutexes.
These things can be easily found in a MC stress test.
....
>
I couldn't get this to compile on my machine; I'm missing some
of the necessary headers.
You will need the latest Austria C++ alpha -
http://netcabletv.org/public_releases/ - warning large download - needs
cygwin on win32 to compile. Some of the code in this alpha is not pretty.
... I'll have to download them first. If
I've understood it correctly, the basic idea does seem
interesting. I think it still depends somewhat on the
scheduling algorithm being used by the system, but I'll try to
find time to evaluate it thoroughly.
... (It obviously depends
somewhat on the scheduling algorithm, because the code is thread
safe if non-preemptive threading is used. But that's not the
default on most modern platforms.)

Most OS's on machines with multiple processors will do the same kind of
thing and yes, it does depend on pre-emptive multitasking but all modern
OS's (NT,W2K,XP,OSX,Solaris etc) are.

Machines with multiple cores or hyperthreads are pretty much the norm.
>
[...]
> // Should this be const or not - I think it should
// James Kanze possibly believes otherwise.

Just a nit: in real code, if I were accessing through a
reference, I would use a const reference unless I planned on
modification (in which case, a lock is required). My problem is
when the code accesses the object directly, without an
intervening reference. (I presume, here, however, that the
reference is an artifact of the test suite, and is used to
simulate accessing the object directly.)
Well yes, in theory it is an artifact, however my first cut at the code
had me taking a const reference as a default thing.
>
Of course, the question here isn't how the user should write
code, but whether the specific construction should be guaranteed
to work or not. Posix bases its guarantees on whether
modification takes place, not on const-ness, which is the basis
of my argument.

[...]
> enum { s_count = 43 }; // prime number

Just curious: why is it important that s_count be prime?
Because of this:

( i * pnum ) % g_test->s_count;

I want every thread to be able to access every element. If it's not
prime (relatively prime) then some threads won't access all the strings.
>
> static const unsigned s_length = 3; // tweak for maximum effect
> std::string m_strings[ s_count ];
> virtual void test2( int l_val, std::string & o_tval )
{
t_local_type & l_str = m_strings[ l_val ];
t_iter_type beg = l_str.begin();
// at::OSTraitsBase::SchedulerYield();
o_tval.assign( beg, l_str.end() );

Just curious: is there some reason behind using o_tval.assign,
and not simply:

std::string localCopy( l_str.begin(), l_str.end() ) ;
A hunch - mabe a false one. I'm trying to keep the loop from doing too
many other things that allocate memory and produce contention - avoid
the threads from synchronizing if possible. Keeping a pre-constructed
string in the thread's stack might avoid (maybe not) a memory
allocation. In this case, it probably will.
>
followed by the AT_TCAssert?
TCAssert is "Test Case" Assert. It's an assert for the test case, it
normally throws on failure (aborting the rest of the test) but it can
also abort and dump core (which the Makefiles do when running automated
tests).
>
[...]
>class HardTask
: public String_TaskBase< 2 >
{
public:
static at::AtomicCount s_task_counter;
static int s_xcount;
> // each test thread calls this with a unique thread number
virtual void TestWork( int l_thrnum )
{
unsigned pnum = g_primes[ l_thrnum % at::CountElements( g_primes ) ];
unsigned count = l_thrnum;
unsigned done = 0;
> std::string l_teststr;
> for ( int i = 0; i < m_iters; ++i )
{
int choice = ( i * pnum ) % g_test->s_count;
> switch ( l_thrnum & 1 ? i % 8 : (7 - (i % 8) ) )
{
case 0 : case 2 :
g_test->test1( choice, l_teststr );
break;
case 1 : case 3 :
g_test->test2( choice, l_teststr );
break;
case 4 :
g_test->test3( choice, l_teststr );
break;
case 5 :
g_test->test4( choice, l_teststr );
break;
}
> // after every 1<<5 iterations - rebuild the test object
// with brand new strings
if ( true && !( i % (1<<5) ) )
{
// stuff is done here
> at::Lock<at::ConditionalMutex l_lock( m_mutex );
> int l_num = ++ s_xcount;
> if ( m_thr_count == l_num )
{
s_xcount = 0;
g_test = new STDStringTest();
l_lock.PostAll();
}
else
{
l_lock.Wait();
}
}

I'm not sure I understand this. How is it synchronized with the
previous loop? If some of the threads are still in the previous
loop when a thread gets here, then it is undefined behavior.
Even using const_iterators.
It's a standard barrier, all the threads except one drop into the
condition variable Wait. The last thread resets a few things and pulls
all the threads out of the wait. Bad code - I really need to implement
a standard barrier class and fix it a little bit. Threads can drop out
of lock.Wait() spuriously according to pthread_cond_wait but I have
never witnessed it in a unit test.
>
I've saved a copy of your code locally, and will try to find
time to download your test suite in order to evaluate it on my
machines. If you really can trigger threading errors with any
degree of reliability, I'm very interested.
Well, writing test cases that really push the interface still needs to
be done, and no it's not a design test (As in TDD). It has however
uncovered some really contorted bugs that I would never have imagined
way before code release. For example, in the Austria C++ timer code, a
very contorted shutdown sequence could happen that needed a "take the
lock but give up if X happens".

This is the comment:

* On shutdown, the client thread locks *m_mutex, sets the
* m_shutdown flag, and waits for this thread to die (while
* still holding *m_mutex). If we try to lock *m_mutex
* normally here, it will be a deadlock. So we spin until
* either we get the lock, or m_shutdown is set.

TryLock< Mutex l_trylock( *m_mutex, m_shutdown );

This one was found using the timer MC test. This is the kind of bug
that usually gets through the human only process. I'm pretty confident
that the Austria C++ timer code will handle almost any situation because
it's pushed very hard on every test run with random new requests every
time it is run. Sure, there is a more regular test as well and it found
bugs that the MC test didn't.

May 8 '07 #170
Branimir Maksimovic wrote:
....
>
Yes. But I have to comment that proper practice is not
to share objects between threads that are not yet constructed
and not to destruct them while other threads are still using them.
This code violates both rules.
The constructor may not have returned, but when Start is called, it
should be, for all intents and purposes, fully constructed. If it's
not, it's broken, no question.
>
>>Since call to member function is made,
no wonder that you can't do this in constructor/destructor
of base class.
Did I miss something? I didn't see any derivation from HardTask.

Obviously, I can't express myself in English good enough.
I meant if calls to Start and Wait are moved to base class Task
constructor/destructor,then, not necessarily, but probably,
code will fail.
It will fail very quickly on a call to a pure virtual function since the
thread will not call Work (the most derived destructor's work) but work
in the base class which is undefined (i.e. pure virtual).
May 8 '07 #171
James Kanze wrote:
On May 7, 10:54 pm, Gianni Mariani <gi3nos...@mariani.wswrote:
>James Kanze wrote:
>>Gianni Mariani wrote:
...
>>>Example ?
>>Bug #21334 in the std::string implementation in g++.
>Not a bug in my opinion. See my other post for a unit test case that
finds the supposed bug in less than 25 milliseconds.

I'll try it. (I'm not 100% sure that there isn't a bug in your
code, however.)
>>Most of the problems in DCL.
>They are surprisingly easy to find too.

Really. Consider the following implementation of DCL:

pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER ;
pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER ;
Singleton* theOneAndOnly = NULL ;

static Singleton*
Singleton::instance()
{
if ( theOneAndOnly == NULL ) {
Locker lock( mutex1 ) ;
if ( theOneAndOnly == NULL ) {
Singleton* tmp ;
{
Locker lock2( mutex2 ) ;
tmp = new Singleton ;
}
theOneAndOnly = tmp ;
}
}
return theOneAndOnly ;
}

It doesn't work, but can you write a test which will reveal the
error? (For starters, you certainly cannot on most Intel 32
bits processors, because they do guarantee that it works, even
if it violates Posix, and fails in practice on some other
architectures.)
What is that mutex2 doinf other than an attempt to make mods visible ?

GCC supports it like:
static Singleton* Singleton::instance()
{
static Singleton* s_x = new Singleton;
return s_x;
}
or even:
static Singleton* Singleton::instance()
{
static Singleton s_x;
return & s_x;
}

OK - DCL on singletons, I'd implement it using thread local data. Yes,
a hard one for memory systems with visibility issues. This is one of
the cases where you have to re-start the whole executable and introduce
randomization on start up and you need to determine how the errors are
found. i.e. two threads calling Singleton::instance() returning
different values.

I must admit, I have not had much experience on recent archs that need
acquire and release semantics.
May 8 '07 #172
James Kanze wrote:
Ya ... I thought we were talking aboutUNITTESTING ! Do you like to
digress all the time ?

The claim is that writing the tests before writing the code
improves quality. I'm just showing that this is a specious
claim.
The claim is that "developer tests" written first improve velocity and
productivity. A "unit test" is a QA concept. Applying all the known
guidelines for QA tests to developer tests will naturally slow you
down. So, yes, such a claim would be specious. Nobody is making it
(except you, responding to a Straw Person argument).
The problem is that the computer doesn't do the hard work for
you. It does the easy part, saying that there is an error
somewhere. Afterwards, you have the hard part: finding where.
If you use TDD, and if any test fails unexpectedly, you have the
option to revert to the last state where all code worked. That means
you have the option to /not/ find where something broke. Most of the
time it will be obvious, but you could even revert then anyway.

So the point of TDD is not to somehow make every conceivable soak test
before writing the code. The point is to increase your options while
coding.

--
Phlip

May 8 '07 #173
On May 8, 4:03 am, James Kanze <james.ka...@gmail.comwrote:
[...]
entails undefined behavior with STLport, g++ and (I think) VC++,
but is defined using Sun CC (with theRogue Wavelibrary).
Logically, I would expect it to be undefined behavior, and
consider the behavior of Sun CC an extension (defining undefined
behavior)---I'm fairly sure that this will be the situation in
the next version of the standard.
FYI, Sun ships a modified version of the Rogue Wave
implementation. The Sun string is thread safe only when
the _RWSTD_STRING_MT_SAFETY_LEVEL_2 macro
is defined. Otherwise it's up to the user to synchronize
accesses to the same string object from multiple threads.
That is also the behavior of the Rogue Wave C++ Standard
library (including the derived Apache stdcxx), and I suspect
most other implementations out there. Here's some more
detail on thread safety in the Sun implementation:
http://developers.sun.com/solaris/articles/stl-new.html

May 8 '07 #174
On May 8, 2:50 pm, Gianni Mariani <gi3nos...@mariani.wswrote:
James Kanze wrote:
On May 7, 10:54 pm, Gianni Mariani <gi3nos...@mariani.wswrote:
James Kanze wrote:
[...]
>Most of the problems in DCL.
They are surprisingly easy to find too.
Really. Consider the following implementation of DCL:
pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER ;
pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER ;
Singleton* theOneAndOnly = NULL ;
static Singleton*
Singleton::instance()
{
if ( theOneAndOnly == NULL ) {
Locker lock( mutex1 ) ;
if ( theOneAndOnly == NULL ) {
Singleton* tmp ;
{
Locker lock2( mutex2 ) ;
tmp = new Singleton ;
}
theOneAndOnly = tmp ;
}
}
return theOneAndOnly ;
}
It doesn't work, but can you write a test which will reveal the
error? (For starters, you certainly cannot on most Intel 32
bits processors, because they do guarantee that it works, even
if it violates Posix, and fails in practice on some other
architectures.)
What is that mutex2 doinf other than an attempt to make mods visible ?
It's making the modifications in the constructor of Singleton
visible before allowing the modification of theOneAndOnly.
Without it, the compler or the write scheduler in the CPU can
reorder the writes, so that the write of the non-null value to
Singleton precedes the writes in the constructor of the
Singleton.

Obviously, you don't need a mutex. Introducing some platform
specific membar or fence instructions can achieve the same ends.
But I felt that using Posix was going far enough; I didn't want
to throw in inline ASM.
GCC supports it like:
static Singleton* Singleton::instance()
{
static Singleton* s_x = new Singleton;
return s_x;
}
or even:

static Singleton* Singleton::instance()
{
static Singleton s_x;
return & s_x;
}
On some platforms. The generated code doesn't work on a Sparc,
or at least, it doesn't meet the guarantees it is claimed to.
In 32 bit mode, on a Sparc, it can even result in an endless
loop; I consistently use the option to turn it off. (In 64 bit
mode, it's missing a critical membar. Which means that on most
machines, it will work anyway, because most Sparc processors
don't use the fully relaxed memory model. Which is a real trap,
because obviously, until you do use the relaxed memory model,
the code works. A hardware upgrade, and it starts crashing.
Only rarely, and of course, nothing that you can reproduce.)
OK - DCL on singletons, I'd implement it using thread local data. Yes,
a hard one for memory systems with visibility issues. This is one of
the cases where you have to re-start the whole executable and introduce
randomization on start up and you need to determine how the errors are
found. i.e. two threads calling Singleton::instance() returning
different values.
I must admit, I have not had much experience on recent archs that need
acquire and release semantics.
They do make life rather difficult. Not just for testing.

--
James Kanze (Gabi Software) email: ja*********@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

May 8 '07 #175
James Kanze wrote:
>
In practice, well run code reviews are necessary, and once they
are taking place, adding pair programming adds very, very little
value, while effectively doubling the cost. In general---I can
imagine that in some specific circumstances, it is worth while.
In practice, pair programming and continuous integration are necessary,
and once they are taking place, adding code reviews adds little value,
while increasing the cost.

Remember, pair programming adds more than just continuous review.

--
Ian Collins.
May 8 '07 #176
James Kanze wrote:
On May 8, 2:50 pm, Gianni Mariani <gi3nos...@mariani.wswrote:
>James Kanze wrote:
....
>>It doesn't work, but can you write a test which will reveal the
error? (For starters, you certainly cannot on most Intel 32
bits processors, because they do guarantee that it works, even
if it violates Posix, and fails in practice on some other
architectures.)
>What is that mutex2 doinf other than an attempt to make mods visible ?

It's making the modifications in the constructor of Singleton
visible before allowing the modification of theOneAndOnly.
Without it, the compler or the write scheduler in the CPU can
reorder the writes, so that the write of the non-null value to
Singleton precedes the writes in the constructor of the
Singleton.

Obviously, you don't need a mutex. Introducing some platform
specific membar or fence instructions can achieve the same ends.
But I felt that using Posix was going far enough; I didn't want
to throw in inline ASM.
OK, I would declare this code too hard to test. It should use a
standard mechanism (like a pthread once or something like that) and that
can be tested in an MC test.
>
>GCC supports it like:
> static Singleton* Singleton::instance()
{
static Singleton* s_x = new Singleton;
return s_x;
}
>or even:

static Singleton* Singleton::instance()
{
static Singleton s_x;
return & s_x;
}

On some platforms. The generated code doesn't work on a Sparc,
or at least, it doesn't meet the guarantees it is claimed to.
In 32 bit mode, on a Sparc, it can even result in an endless
loop; I consistently use the option to turn it off.
Do you have a GCC bug open on this one ?
... (In 64 bit
mode, it's missing a critical membar. Which means that on most
machines, it will work anyway, because most Sparc processors
don't use the fully relaxed memory model. Which is a real trap,
because obviously, until you do use the relaxed memory model,
the code works. A hardware upgrade, and it starts crashing.
Only rarely, and of course, nothing that you can reproduce.)
>OK - DCL on singletons, I'd implement it using thread local data. Yes,
a hard one for memory systems with visibility issues. This is one of
the cases where you have to re-start the whole executable and introduce
randomization on start up and you need to determine how the errors are
found. i.e. two threads calling Singleton::instance() returning
different values.
>I must admit, I have not had much experience on recent archs that need
acquire and release semantics.

They do make life rather difficult. Not just for testing.
May 8 '07 #177

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

14
by: | last post by:
Hi! I'm looking for unit-testing tools for .NET. Somthing like Java has --> http://www.junit.org regards, gicio
16
by: Greg Roberts | last post by:
Hi I want to place the tests needed in the code using attributes. There seems to be enough code snippets around for me to cover this. e.g. // Test cases, run these here on the function and...
4
by: sylcheung | last post by:
Hi, Does anyone has any suggestion for Unit Test Framework for c++ program? Is there any framework which let me use some script languages (e.g. python) to write the test cases to test c++...
5
by: VvanN | last post by:
hi, fellows I'd like to intruduce a new unit test framework for C++ freely available at: http://unit--.sourceforge.net/ It does not need bothering test registration, here is an example ...
6
by: Michael Bray | last post by:
I've just inherited a fairly large project with multiple classes. The developer also wrote a huge number of unit tests (using NUnit) to validate that the classes work correctly. However, I don't...
10
by: Brendan Miller | last post by:
What would heavy python unit testers say is the best framework? I've seen a few mentions that maybe the built in unittest framework isn't that great. I've heard a couple of good things about...
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...
0
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each...
0
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing,...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.