[NTLUG:Discuss] Upgrade breaks things

Steve Baker sjbaker1 at airmail.net
Fri Apr 12 18:46:13 CDT 2002


brian at pongonova.net wrote:
> 
> On Fri, Apr 12, 2002 at 03:07:08PM -0500, Steve Baker wrote:
> >   3) Some libraries require a *structure* to be passed as a parameter
> >      to some library call.  If at some future time, a new variable
> >      has to be added into that structure, it's almost impossible to
> >      do it without breaking reverse compatibility.  This problem
> >      is especially acute for C++ applications that actually make
> >      use of a class-based interface.
> 
> If the developer knows what he/she is doing, this should *never* be a reason for
> backward-compatibility issues.  That's the whole point of specifying a struct or
> class pointer:  You should be able to modify the *internal* structure of the
> struct/class, along with related implementation details, without ever breaking
> older applications which depend upon the previous interface.

That depends on whether the application can 'see' the definition of the
struct/class.

In cases like the system 'FILE' structure, the application writer has no
clue as to the internals of that structure and never refers to it other
than as a pointer.  FILE structures are created in fopen and passed to
other functions like fread - without the application ever looking inside.

That's an ideal situation.

Look at something like 'stat' and 'fstat' - those both produce structures
that applications can see inside of. Changing either of those structures
is presumably a big "no-no" (although UNIX has been kludging this by adding
new entries onto the END of the structure and hopeing that this won't screw
anyone who has:

    int x = sizeof(struct stat) ;

However, consider a heavily class-laden C++ program.  If the library's
header file says something like:

  class X
  {
    public:

      virtual int abc () ;
      virtual int def () ;
  } ;

...and the application does nice object-oriented things like:

  class X *my_X = get_me_an_X_please () ;

  my_X -> def () ;


...then that program will break if a subsequent version of the
library adds:

  class X
  {
    public:

      virtual int abc () ;
      virtual int xyz () ;
      virtual int def () ;
  } ;

...because the location of 'X::def' in the virtual function table
will have changed.  Unless the application is recompiled against
the new header files, you'll be in deep trouble.

What's hard (for a library maintainer) is to avoid changing *any* aspects
of a class definition that will screw up applications in this way.

You may argue that it's simply inappropriate to use C++ class objects in
the interfaces of public libraries - but it would be hard to implement
something like my PLIB scene graph library without things like inheritance
and virtual member functions.

> What often happens is that a developer without a clue (or a clue that is just
> flat-out wrong) will pass a struct/class by value (which will obviously break older
> apps during compilation since the binary "footprint" is different), or the
> developer will modify something in the implementation which breaks
> backwards-compatibility (usually evident as a segfault or other run-time error).

I think it's more subtle than that...but then maybe I'm clueless.  In the example
above, there was no passing by value - the application only has the address and
calls a member function...but virtual functions are listed in a table in an order
that's known at COMPILE time...so things can still get screwed up if the
class is changed in any of a number of subtle ways.
 
> What I'm seeing more of lately, especially in stalwart low-level libs like glibc
> and zlib, is the *addition* of interfaces, along with the deprecation of others,
> which simply renders older code uncompilable against the new interface.  This is an
> issue of poor/inadequate planning, as a new contingent of developers
> with ideas that are different from the original lib developers seek to change the
> interfaces to better align them with their beliefs of what the interfaces should
> really look like.  I've come across this mindset in a number of GNU-based
> libs.

Well, if you want progress, sometimes things have to change.

Some of the things in the original UNIX standard C library were just *NOT*
well thought out.  Other libraries are similarly problematic - no developers
are perfect - none have 20:20 foresight - API's simply have to change
sometimes.  You'd hope that it would be done SLOWLY so that applications
become obsolete before they cease to function - but that's not always
possible either.

Sometimes you can fix that by adding new functions and NOT taking away
the old ones.  For example, when people realised that 'sleep()' was
rather coarse, they added 'usleep()' - not realising that with GigaHertz
CPU's, a millionth of a second was going to start looking like a rather
long period of time...so now we also have 'nanosleep()'...and I suspect
we'll ultimately wish we'd written 'picosleep()' instead.  Of course
these can all co-exist and there is no need to deprecate the old ones.

However, sometimes the old functions become un-tenable and have to be
deprecated and eventually removed.  That's why we have functions like
'lseek()' - which was initially in ADDITION to 'seek' - but now 'seek()'
would be unimplementable for large files - so it was first deprecated
and now no longer exists.

Then, once in a great while, some function has to *change* its meaning
with no hope of backwards compatibility. This has happened with 'signal()'
for example.

This is going to continue.

I think the biggest problem is actually the situation with the C/C++
compiler.  The fiasco with RedHat's release of 2.96 and 2.97 of GCC
right before the big changeover to GCC 3.0 meant that we have TWO
binary incompatibilities where only one was really needed.

Holding back compiler innovation in the name of compatibility is a
hard sell.
 
> > The way I deal with it in my libraries is to have applications
> > statically link to them...but that's only appropriate because
> > there are very few applications using them.  You couldn't have
> > glibc or OpenGL work like that (for example).
> 
> One of the problems here is that your statically-linked executable will be
> larger than their dynamically-linked counterparts.  But with larger drives at cheap
> prices now the norm, Linux users may want to start re-visiting the issue of static
> vs. dynamic linking. (Of course, "updating" a buggy library will require rebuilding
> all your carefully compiled executables, another problem to think about.)

Well, the library I'm talking about is for games - and it adds about a quarter megabyte
to an executable.  The disk space issue is likely to be irrelevent because 3D
games of the sort that PLIB supports mostly need many, many megabytes of texture
map images and stuff like that - so the increased size of the file on disk isn't
important.

It might be more important is that the image of the library in RAM when the program
is running can't be shared. If you have two PLIB games running - you only have one
copy of GLIBC loaded - but you'll have to have two copies of PLIB.  However, there
aren't many occasions where people would be running *MANY* 3D games at the same
time - so this is not a big issue.

Updating the library is the only significant problem - you need to re-link the
application.  Since the library is LGPL'ed, you can always (in theory) do that -
even if it's a commercial program.  However, it's quite likely (because of the
C++ class problem) that you'll need to completely recompile an application in
order to go to a new version of the library anyway.

> > The other way out is not to attempt to maintain 100% compatibility,
> > but instead to make sure that multiple versions of the same library
> > can co-exist on the same computer and that applications always pick
> > the right version.  Linux makes an attempt at that - but it's far
> > from 100% sucessful.
> 
> I have 7 different lib paths currently in use on my Linux system, and my head hurts
> from having to keep it all sorted :)

Eeekkk!

> Library versioning works, but I've seen way
> too many broken configure scripts which detect wrong library versions (or even miss
> them).  After a while, trying to keep track of several different libraries becomes
> a sizable task.

Yes.

Once in a while, a complete OS upgrade is definitely called for.  Hopefully not
*too* often though.

----------------------------- Steve Baker -------------------------------
Mail : <sjbaker1 at airmail.net>   WorkMail: <sjbaker at link.com>
URLs : http://www.sjbaker.org
       http://plib.sf.net http://tuxaqfh.sf.net http://tuxkart.sf.net
       http://prettypoly.sf.net http://freeglut.sf.net
       http://toobular.sf.net   http://lodestone.sf.net




More information about the Discuss mailing list