This is the mail archive of the cygwin-developers mailing list for the Cygwin project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: LoadLibrary error 487 (was Re: Please test latest developer snapshot)


On Mar  1 21:42, Corinna Vinschen wrote:
> Or we implement timeGetTime ourselves.
> [...]

Here's my complete replacement implementation for the time functions
from winmm.  I debugged the winmm time functions in Win XP as well as
in Windows 7, so I'm pretty sure this reflects the actual mechanism.

There are a couple of places which could be optimized.  For instance,
since the OS works in 100ns units anyway, there's no reason to use ms
values.  This would allow to get rid of a couple of multiplications and
divisions by 10000.  However, that's not critical for the functionality
so I skipped this step for now.


This patch also reverts the remaining waveFoo functions so that they
don't call api_fatal if they can't be loaded.  As a first measure we
have the LoadLibraryEx call, but if even that one fails, do we really
want the Cygwin DLL to exit just because fhandler_dev_dsp has a bit of
trouble?

I guess we should not put this into 1.7.9, but it sounds like a good
idea to me at least for 1.7.10.


Corinna


	* autoload.cc (winmm): Rremove time functions.  Don't treat
	unloadable wave functions as fatal.
	* hires.h (hires_ms::timeGetTime_ns): New private method.
	(hires_ms::dmsecs): Call timeGetTime_ns here.
	* ntdll.h (struct _KSYSTEM_TIME): Define.
	(KUSER_SHARED_DATA): Redefine to allow access to InterruptTime.
	(SharedUserData): Define here.
	(NtQueryTimerResolution): Declare.
	(NtSetTimerResolution): Declare.
	* path.cc (SharedUserData): Move to ntdll.h.
	* times.cc (hires_ms::timeGetTime_ns): New private method.
	Use throughout instead of timeGetTime.
	(hires_ms::resolution): Try a call to NtQueryTimerResolution
	to fetch current period.  Fall back to heuristic if that fails.
	(clock_setres): Align period to possible values per a call to
	NtQueryTimerResolution.  Explain why.  Replace calls to
	timeBeginPeriod and timeEndPeriod with underlying call to
	NtSetTimerResolution.


Index: autoload.cc
===================================================================
RCS file: /cvs/src/src/winsup/cygwin/autoload.cc,v
retrieving revision 1.189
diff -u -p -r1.189 autoload.cc
--- autoload.cc	1 Mar 2011 22:36:19 -0000	1.189
+++ autoload.cc	2 Mar 2011 11:05:46 -0000
@@ -455,27 +455,23 @@ LoadDLLfunc (SetProcessWindowStation, 4,
 LoadDLLfunc (SetThreadDesktop, 4, user32)
 LoadDLLfunc (ShowWindowAsync, 8, user32)
 
-LoadDLLfuncEx3 (timeBeginPeriod, 4, winmm, 0, 0, 1)
-LoadDLLfuncEx3 (timeEndPeriod, 4, winmm, 0, 0, 1)
-LoadDLLfuncEx3 (timeGetDevCaps, 8, winmm, 0, 0, 1)
-LoadDLLfuncEx3 (timeGetTime, 0, winmm, 0, 0, 1)
-LoadDLLfuncEx3 (waveInAddBuffer, 12, winmm, 0, 0, 1)
-LoadDLLfuncEx3 (waveInClose, 4, winmm, 0, 0, 1)
-LoadDLLfuncEx3 (waveInGetNumDevs, 0, winmm, 0, 0, 1)
-LoadDLLfuncEx3 (waveInOpen, 24, winmm, 0, 0, 1)
-LoadDLLfuncEx3 (waveInPrepareHeader, 12, winmm, 0, 0, 1)
-LoadDLLfuncEx3 (waveInReset, 4, winmm, 0, 0, 1)
-LoadDLLfuncEx3 (waveInStart, 4, winmm, 0, 0, 1)
-LoadDLLfuncEx3 (waveInUnprepareHeader, 12, winmm, 0, 0, 1)
-LoadDLLfuncEx3 (waveOutClose, 4, winmm, 0, 0, 1)
-LoadDLLfuncEx3 (waveOutGetNumDevs, 0, winmm, 0, 0, 1)
-LoadDLLfuncEx3 (waveOutGetVolume, 8, winmm, 0, 0, 1)
-LoadDLLfuncEx3 (waveOutOpen, 24, winmm, 0, 0, 1)
-LoadDLLfuncEx3 (waveOutPrepareHeader, 12, winmm, 0, 0, 1)
-LoadDLLfuncEx3 (waveOutReset, 4, winmm, 0, 0, 1)
-LoadDLLfuncEx3 (waveOutSetVolume, 8, winmm, 0, 0, 1)
-LoadDLLfuncEx3 (waveOutUnprepareHeader, 12, winmm, 0, 0, 1)
-LoadDLLfuncEx3 (waveOutWrite, 12, winmm, 0, 0, 1)
+LoadDLLfuncEx3 (waveInAddBuffer, 12, winmm, 1, 0, 1)
+LoadDLLfuncEx3 (waveInClose, 4, winmm, 1, 0, 1)
+LoadDLLfuncEx3 (waveInGetNumDevs, 0, winmm, 1, 0, 1)
+LoadDLLfuncEx3 (waveInOpen, 24, winmm, 1, 0, 1)
+LoadDLLfuncEx3 (waveInPrepareHeader, 12, winmm, 1, 0, 1)
+LoadDLLfuncEx3 (waveInReset, 4, winmm, 1, 0, 1)
+LoadDLLfuncEx3 (waveInStart, 4, winmm, 1, 0, 1)
+LoadDLLfuncEx3 (waveInUnprepareHeader, 12, winmm, 1, 0, 1)
+LoadDLLfuncEx3 (waveOutClose, 4, winmm, 1, 0, 1)
+LoadDLLfuncEx3 (waveOutGetNumDevs, 0, winmm, 1, 0, 1)
+LoadDLLfuncEx3 (waveOutGetVolume, 8, winmm, 1, 0, 1)
+LoadDLLfuncEx3 (waveOutOpen, 24, winmm, 1, 0, 1)
+LoadDLLfuncEx3 (waveOutPrepareHeader, 12, winmm, 1, 0, 1)
+LoadDLLfuncEx3 (waveOutReset, 4, winmm, 1, 0, 1)
+LoadDLLfuncEx3 (waveOutSetVolume, 8, winmm, 1, 0, 1)
+LoadDLLfuncEx3 (waveOutUnprepareHeader, 12, winmm, 1, 0, 1)
+LoadDLLfuncEx3 (waveOutWrite, 12, winmm, 1, 0, 1)
 
 LoadDLLfunc (accept, 12, ws2_32)
 LoadDLLfunc (bind, 12, ws2_32)
Index: hires.h
===================================================================
RCS file: /cvs/src/src/winsup/cygwin/hires.h,v
retrieving revision 1.16
diff -u -p -r1.16 hires.h
--- hires.h	9 Aug 2010 16:47:46 -0000	1.16
+++ hires.h	2 Mar 2011 11:05:46 -0000
@@ -1,6 +1,6 @@
 /* hires.h: Definitions for hires clock calculations
 
-   Copyright 2002, 2003, 2004, 2005, 2009, 2010 Red Hat, Inc.
+   Copyright 2002, 2003, 2004, 2005, 2009, 2010, 2011 Red Hat, Inc.
 
 This file is part of Cygwin.
 
@@ -43,12 +43,13 @@ class hires_ns : public hires_base
 class hires_ms : public hires_base
 {
   LONGLONG initime_ns;
+  LONGLONG timeGetTime_ns ();
   void prime ();
  public:
   LONGLONG nsecs ();
   LONGLONG usecs () {return nsecs () / 10LL;}
   LONGLONG msecs () {return nsecs () / 10000LL;}
-  UINT dmsecs () { return timeGetTime (); }
+  UINT dmsecs () { return timeGetTime_ns () / 10000L; }
   UINT resolution ();
   LONGLONG uptime () {return (nsecs () - initime_ns) / 10000LL;}
 };
Index: ntdll.h
===================================================================
RCS file: /cvs/src/src/winsup/cygwin/ntdll.h,v
retrieving revision 1.106
diff -u -p -r1.106 ntdll.h
--- ntdll.h	9 Oct 2010 10:54:12 -0000	1.106
+++ ntdll.h	2 Mar 2011 11:05:46 -0000
@@ -599,9 +599,18 @@ typedef struct _TEB
   /* A lot more follows... */
 } TEB, *PTEB;
 
+typedef struct _KSYSTEM_TIME
+{
+  ULONG LowPart;
+  LONG High1Time;
+  LONG High2Time;
+} KSYSTEM_TIME, *PKSYSTEM_TIME;
+
 typedef struct _KUSER_SHARED_DATA
 {
-  BYTE Reserved1[0x2dc];
+  BYTE Reserved1[0x08];
+  KSYSTEM_TIME InterruptTime;
+  BYTE Reserved2[0x2c8];
   ULONG DismountCount;
   /* A lot more follows... */
 } KUSER_SHARED_DATA, *PKUSER_SHARED_DATA;
@@ -889,6 +898,11 @@ typedef enum _EVENT_INFORMATION_CLASS
 #define NtCurrentProcess() ((HANDLE) 0xffffffff)
 #define NtCurrentThread()  ((HANDLE) 0xfffffffe)
 
+/* This is the mapping of the KUSER_SHARED_DATA structure into the 32 bit
+   user address space.  We need it here to access the current DismountCount. */
+static KUSER_SHARED_DATA &SharedUserData
+			 = *(volatile PKUSER_SHARED_DATA) 0x7ffe0000;
+
 extern "C"
 {
   NTSTATUS NTAPI NtAdjustPrivilegesToken (HANDLE, BOOLEAN, PTOKEN_PRIVILEGES,
@@ -970,6 +984,7 @@ extern "C"
   NTSTATUS NTAPI NtQuerySecurityObject (HANDLE, SECURITY_INFORMATION,
 					PSECURITY_DESCRIPTOR, ULONG, PULONG);
   NTSTATUS NTAPI NtQuerySymbolicLinkObject (HANDLE, PUNICODE_STRING, PULONG);
+  NTSTATUS NTAPI NtQueryTimerResolution (PULONG, PULONG, PULONG);
   NTSTATUS NTAPI NtQueryVirtualMemory (HANDLE, PVOID, MEMORY_INFORMATION_CLASS,
 				       PVOID, ULONG, PULONG);
   NTSTATUS NTAPI NtQueryVolumeInformationFile (HANDLE, IO_STATUS_BLOCK *,
@@ -984,6 +999,7 @@ extern "C"
 				       FILE_INFORMATION_CLASS);
   NTSTATUS NTAPI NtSetSecurityObject (HANDLE, SECURITY_INFORMATION,
 				      PSECURITY_DESCRIPTOR);
+  NTSTATUS NTAPI NtSetTimerResolution (ULONG, BOOLEAN, PULONG);
   NTSTATUS NTAPI NtUnlockVirtualMemory (HANDLE, PVOID *, ULONG *, ULONG);
   NTSTATUS NTAPI NtUnmapViewOfSection (HANDLE, PVOID);
   NTSTATUS NTAPI NtWriteFile (HANDLE, HANDLE, PIO_APC_ROUTINE, PVOID,
Index: path.cc
===================================================================
RCS file: /cvs/src/src/winsup/cygwin/path.cc,v
retrieving revision 1.625
diff -u -p -r1.625 path.cc
--- path.cc	15 Feb 2011 15:25:59 -0000	1.625
+++ path.cc	2 Mar 2011 11:05:47 -0000
@@ -3453,11 +3453,6 @@ static PFAST_CWD *fast_cwd_ptr
 static int fast_cwd_version
   __attribute__((section (".cygwin_dll_common"), shared)) = 1;
 
-/* This is the mapping of the KUSER_SHARED_DATA structure into the 32 bit
-   user address space.  We need it here to access the current DismountCount. */
-static KUSER_SHARED_DATA &SharedUserData
-			 = *(volatile PKUSER_SHARED_DATA) 0x7ffe0000;
-
 #define peek32(x)	(*(uint32_t *)(x))
 
 /* This function scans the code in ntdll.dll to find the address of the
Index: times.cc
===================================================================
RCS file: /cvs/src/src/winsup/cygwin/times.cc,v
retrieving revision 1.103
diff -u -p -r1.103 times.cc
--- times.cc	9 Aug 2010 16:47:47 -0000	1.103
+++ times.cc	2 Mar 2011 11:05:47 -0000
@@ -1,7 +1,7 @@
 /* times.cc
 
    Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
-   2005, 2006, 2007, 2008, 2009, 2010 Red Hat, Inc.
+   2005, 2006, 2007, 2008, 2009, 2010, 2011 Red Hat, Inc.
 
 This file is part of Cygwin.
 
@@ -666,6 +666,27 @@ hires_ns::nsecs ()
   return now.QuadPart;
 }
 
+LONGLONG
+hires_ms::timeGetTime_ns ()
+{
+  LARGE_INTEGER t;
+
+  /* This is how timeGetTime is implemented in winmm.dll.
+     The real timeGetTime subtracts and adds some values which are constant
+     over the lifetime of the process.  Since we don't need absolute accuracy
+     of the value returned by timeGetTime, only relative accuracy, we can skip
+     this step. */
+  do
+    {
+      t.HighPart = SharedUserData.InterruptTime.High1Time;
+      t.LowPart = SharedUserData.InterruptTime.LowPart;
+    }
+  while (t.HighPart != SharedUserData.InterruptTime.High2Time);
+  /* We use the value in full 100ns resolution in the calling functions
+     anyway, so we can skip dividing by 10000 here. */
+  return t.QuadPart;
+}
+
 void
 hires_ms::prime ()
 {
@@ -673,7 +694,7 @@ hires_ms::prime ()
     {
       int priority = GetThreadPriority (GetCurrentThread ());
       SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_TIME_CRITICAL);
-      initime_ns = systime_ns () - (((LONGLONG) timeGetTime ()) * 10000LL);
+      initime_ns = systime_ns () - timeGetTime_ns ();
       inited = true;
       SetThreadPriority (GetCurrentThread (), priority);
     }
@@ -687,12 +708,12 @@ hires_ms::nsecs ()
     prime ();
 
   LONGLONG t = systime_ns ();
-  LONGLONG res = initime_ns + (((LONGLONG) timeGetTime ()) * 10000LL);
+  LONGLONG res = initime_ns + timeGetTime_ns ();
   if (res < (t - 40 * 10000LL))
     {
       inited = false;
       prime ();
-      res = initime_ns + (((LONGLONG) timeGetTime ()) * 10000LL);
+      res = initime_ns + timeGetTime_ns ();
     }
   return res;
 }
@@ -752,24 +773,33 @@ hires_ms::resolution ()
 {
   if (!minperiod)
     {
-      /* Try to empirically determine current timer resolution */
-      int priority = GetThreadPriority (GetCurrentThread ());
-      SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_TIME_CRITICAL);
-      DWORD period = 0;
-      for (int i = 0; i < 4; i++)
+      NTSTATUS status;
+      ULONG coarsest, finest, actual;
+
+      status = NtQueryTimerResolution (&coarsest, &finest, &actual);
+      if (NT_SUCCESS (status))
+	minperiod = (UINT) actual;
+      else
 	{
-	  DWORD now;
-	  DWORD then = timeGetTime ();
-	  while ((now = timeGetTime ()) == then)
-	    continue;
-	  then = now;
-	  while ((now = timeGetTime ()) == then)
-	    continue;
-	  period += now - then;
+	  /* Try to empirically determine current timer resolution */
+	  int priority = GetThreadPriority (GetCurrentThread ());
+	  SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_TIME_CRITICAL);
+	  LONGLONG period = 0;
+	  for (int i = 0; i < 4; i++)
+	    {
+	      LONGLONG now;
+	      LONGLONG then = timeGetTime_ns ();
+	      while ((now = timeGetTime_ns ()) == then)
+		continue;
+	      then = now;
+	      while ((now = timeGetTime_ns ()) == then)
+		continue;
+	      period += now - then;
+	    }
+	  SetThreadPriority (GetCurrentThread (), priority);
+	  period /= 40000;
+	  minperiod = (UINT) period;
 	}
-      SetThreadPriority (GetCurrentThread (), priority);
-      period /= 4;
-      minperiod = period;
     }
   return minperiod;
 }
@@ -807,26 +837,38 @@ extern "C" int
 clock_setres (clockid_t clk_id, struct timespec *tp)
 {
   static NO_COPY bool period_set;
+
   if (clk_id != CLOCK_REALTIME)
     {
       set_errno (EINVAL);
       return -1;
     }
 
-  if (period_set)
-    timeEndPeriod (minperiod);
-
   DWORD period = (tp->tv_sec * 1000) + ((tp->tv_nsec) / 1000000);
 
-  if (timeBeginPeriod (period))
+  /* clock_setres is non-POSIX/non-Linux.  On QNX, the function always
+     rounds the incoming value to the nearest supported value. */
+  ULONG coarsest, finest, actual;
+  if (NT_SUCCESS (NtQueryTimerResolution (&coarsest, &finest, &actual)))
+    {
+      if (period > coarsest / 10000L)
+	period = coarsest / 10000L;
+      else if (finest / 10000L > period)
+	period = finest / 10000L;
+    }
+
+  if (period_set)
+    NtSetTimerResolution (minperiod * 10000L, FALSE, &actual);
+
+  if (NT_SUCCESS (NtSetTimerResolution (period * 10000L, TRUE, &actual)))
     {
-      minperiod = period;
+      minperiod = actual / 10000L;
       period_set = true;
     }
   else
     {
       __seterrno ();
-      timeBeginPeriod (minperiod);
+      NtSetTimerResolution (minperiod * 10000L, TRUE, &actual);
       return -1;
     }
 

-- 
Corinna Vinschen                  Please, send mails regarding Cygwin to
Cygwin Project Co-Leader          cygwin AT cygwin DOT com
Red Hat


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]