Freitag, 25. April 2008

memcpy an object

Some days ago we stumbled upon a problem which first seemed unaccountable: After adding a virtual function to a class, another of our programs which (at first sight) should not have been affected by this change, simply: crashed.
After some analysing and hard thinking we realised what the problem was: This program used an array of objects of this class to which the virtual function was added.
That of course was not the problem, but at one place another object of this class was created and then the data of this object copied into the array.
That was the idea. And it worked ok as long as the class did not contain any virtual function.
A class without virtual functions can be treated like a class, and its data can be copied from one object to another with memcpy without any problem (as long as the class does not contain any pointers, but that's another story).
By adding a virtual function a 'virtual function table' is added to the each object which is used to determine the function address to use when the function is called. Using memcpy on such objects copies the data and the virtual function table from one object to another. This may even work as long as both objects exist, but when the source object is deleted, the object where the data was copied to contains pointers in the virtual function table which point to anything but not valid function adresses anymore. Trying to call a virtual function then leads to a crash.

Freitag, 29. Februar 2008

Better than usleep ...

What do you do when a socket read or write returns EAGAIN/EWOULDBLOCK?

Tests showed that an immediate attempt to read again also returns the same error code.
So, what I did was to implement a usleep() for 10 milliseconds (shorter times are not possible on Solaris systems) and then try again. This works.
But nowadays 10 milliseconds is far too much: A process that can handle 5000 msgs/s would get through 50 messages in this time...

Now I learned about the function yield() (sched_yield() on Linux). This function tells the scheduler to look for another process to execute (in simple words).
I replaced the usleep() call with a call to yield(), of course hoping that the calling process would be activated by the scheduler earlier than when usleep() was called.
And test showed that it works: A process that handled 2'500-3'000 msgs/s before managed more than 7'500 messages afterwards!

So, if you have a process that is blocked somehow and should wait for a change of state, specially when this change of state is caused by another process, call yield()!

Montag, 25. Februar 2008

Socket read

A few days ago one of our customers complained that the connection loss notification did not work. To cut a long story short, the read() function still returned 0 and 0 bytes read, even though the socket was closed long ago.
Wouldn't you expect the read() function to return some kind of error after some time?
Well, luckily both client and server were running on the same system, and the pid of the server is stored in the shared memory, so the client can check if the server is still running. With this change the client detects connection losses within a matter of microseconds.

Ok, the problem is solved, but still I'd like the read() function to return an error code if the socket was closed, especially if the client and server are running on the same system?!?

Mittwoch, 20. Februar 2008

Virtual Function Table

Every now and then you read in books or magasins that C++ gets slow when virtual functions are used, because it must be determined at run-time which function must be executed.
Well, I wrote a set of classes and a test program to measure the performance of the following different implementations:
  • Class without any virtual function.
  • Class with a virtual destructor.
  • Class where the function that's called is defined virtual.
  • Class with a virtual destructor and the function defined as virtual.
  • Class derived from a base class (interface-like) which defined the function as pure virtual, and the implementation in the derived class.
Since the test function itself didn't do much I had to call it several million times to get a run-time in seconds for every test. And the result was: No difference at all, every type of implementation showed the same run-time behaviour, the difference between the different measured times was less than 1 percent.

So, use base classes and virtual functions and all the fancy features that C++ offers!