Subject: Time skew in MS-Windows and MS-DOS version of GNU Make (fwd)

From: Eli Zaretskii <[email-masked]>
Date: Mon, 4 May 1998 11:19:57 +0300 (IDT)

Hi, guys.

  I'm writing to you about this problem because I need all the help I can 
get from people who know about Win32 systems.  The message below was sent 
to the GNU Make maintainer and to Rob Tulloh who maintains the Win32 port 
of Make as well.

What I would like to ask you is maybe you can shed some light on this 
strange phenomenon.  Why should a filesystem set the file time to be 2 
seconds in the future?

I'm hoping that if I understand the reasons behind this madness, I might 
be able to devise a good solution, or maybe even to avoid hitting the 
problem in the first place.

Thanks in advance for any help.

---------- Forwarded message ----------
Date: Tue, 28 Apr 1998 14:13:03 +0300 (IDT)
From: Eli Zaretskii <[email-masked]>
To: "Paul D. Smith" <[email-masked]>
Cc: Rob Tulloh <[email-masked]>
Subject: Time skew in MS-Windows and MS-DOS version of GNU Make

I'm writing to you because I need advice on solving a grave problem
with Make on Windows 9X and Windows NT.  The data below was gathered
using programs compiled with DJGPP, and therefore pertains to the
DJGPP version of Make.  However, I believe that the Win32 version
should have similar problems, at least with FAT volumes.  (Part of the
motivation for this message is to hear from you, Rob, how do things
work--or, rather, how do they break--in the Win32 version.)

It goes without saying that I would like very much to solve this
problem in the next version of Make.

Here's the deal: the test in remake.c for file times in the future
produces false alarms on Windows NT and the latest versions of Windows
95.  Originally, I thought that this is due to the fact that DOS and
its FAT filesystem only support 2-sec granularity of the file time
stamps, and that some newer versions of Windows round the current time
instead of truncating it, thereby producing future times for some
files.

However, testing reveals that the problem is much more serious.  (In
fact, I'm at a loss trying to even understand how in the world can a
reasonable OS behave like that; but then I've stopped second-guessing
Microsoft a long time ago.)  It appears that on NT and newer versions
of Windows 95 (certainly true for version 4.00.1111), the file times
are *always* in the future.  The difference between the file time and
the system clock is between 0 to 3 seconds(!), even if the system
clock is sampled *after* accessing a file and calling `stat' on it.  

I attach below a short program that can be used to test this.  It
compiles with DJGPP, but might need some adjustment for Win32.  Rob, I
would be very interested to hear the results from your testing.  From
the Win32-specific code in remake.c I understand that the problem
exists, but I'd like to see the numbers.

Assuming that the same 0-3sec timeskew exists in both the DJGPP and
the Win32 versions, the next question is what do we do with it?  Is it
reasonable to allow up to 3 sec of skew before a message is printed?
If not, what, if any, are other options?

There's also a more general and more grave problem of comparing file
times when some of them reside on different types of filesystems, like
some on FAT volumes and some on NTFS or mounted via NFS.  It seems
that there's a potential of Make working erroneously in some of these
cases due to this problem.  The only way out of this mess would be to
maintain the time skew for each directory or each file Make records in
its data structures and correct them before comparison.  This time
skew can be computed once for each directory by craeting a file and
looking at its time stamp.  Is it reasonable to do this?

Here's the test program.  (Rob, `kbhit' is a DOS-specific function
which returns non-zero if any key is pressed.)

Thanks in advance for any help.
-------------------------------------------------------------------
#include <fcntl.h>
#include <pc.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <time.h>

#ifdef PENTIMER

/* Define only for Intel Pentium and Pentium II processors. The RDTSC
   instruction, however, seems to be reliable even under Windows. */

inline unsigned long long pentimer(int mode) {
   static unsigned long long tref;
   unsigned long long time;

   if (mode == 0) {
      asm ("rdtsc"
         : "=d" (((unsigned long *) &tref)[1]),
           "=a" (((unsigned long *) &tref)[0])
         :
          );
      return 0.;
   } else {
      asm ("rdtsc
            subl  %2, %%eax
            sbbl  %3, %%edx
           "
         : "=d" (((unsigned long *) &time)[1]),
           "=a" (((unsigned long *) &time)[0])
         : "m"  (tref),
           "m"  (((unsigned long *) &tref)[1])
          );
      return time;
   }
}
#endif

int main(void) {
   FILE *file;
   struct stat sbuf;
   char fname[L_tmpnam], fsystype[40];

#ifdef PENTIMER
   pentimer(0);
#endif
   tmpnam(fname);
   while (!kbhit()) {
      file = fopen(fname, "wt");
      fflush (file);
      fsync (fileno (file));
      fstat(fileno(file), &sbuf);
#ifdef PENTIMER
      printf("%10ld %10ld %10.6f %14lld\n",
         (unsigned long) time(0),
         (unsigned long) sbuf.st_mtime, (double) uclock() / UCLOCKS_PER_SEC
         , pentimer(1)
         );
#else
      printf("%10ld %10ld %10.6f\n",
         (unsigned long) time(0),
         (unsigned long) sbuf.st_mtime, (double) uclock() / UCLOCKS_PER_SEC
         );
#endif
      fclose(file);
   }
   remove(fname);
   printf("file = %s\n", fname);
   _get_volume_info(NULL, NULL, NULL, fsystype);
   printf("file system type = %s\n", fsystype);
   return 0;
}

---

From: Eli Zaretskii <[email-masked]>
Date: Thu, 7 May 1998 10:36:39 +0300 (IDT)

On Wed, 6 May 1998, Geoff Voelker wrote:

> I haven't heard of this behavior before, so I can't immediately be of
> much help.  I will look through the Win32 documentation and
> knowledgebase libraries when I get a chance, though.

Thanks a lot.

Somebody suggested that Windows attempts to estimate the time that file's 
data will be actually written to disk when the disk cache flushes its 
buffers.  (If that's the reason, I will have to go and re-learn 
everything I thought I knew about how disk caches are supposed to work. ;-)  
Maybe this could supply a clue as to where to look in the docs.