470 lines
13 KiB
C++
470 lines
13 KiB
C++
/*
|
|
*
|
|
* @APPLE_LICENSE_HEADER_START@
|
|
*
|
|
* Copyright (c) 1999-2008 Apple Inc. All Rights Reserved.
|
|
*
|
|
* This file contains Original Code and/or Modifications of Original Code
|
|
* as defined in and that are subject to the Apple Public Source License
|
|
* Version 2.0 (the 'License'). You may not use this file except in
|
|
* compliance with the License. Please obtain a copy of the License at
|
|
* http://www.opensource.apple.com/apsl/ and read it before using this
|
|
* file.
|
|
*
|
|
* The Original Code and all software distributed under the License are
|
|
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
|
|
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
|
|
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
|
|
* Please see the License for the specific language governing rights and
|
|
* limitations under the License.
|
|
*
|
|
* @APPLE_LICENSE_HEADER_END@
|
|
*
|
|
*/
|
|
/*
|
|
File: OS.cpp
|
|
|
|
Contains: OS utility functions
|
|
|
|
|
|
|
|
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include "SafeStdLib.h"
|
|
#include <string.h>
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <errno.h>
|
|
|
|
#include <math.h>
|
|
|
|
#ifndef __Win32__
|
|
#include <sys/time.h>
|
|
#endif
|
|
|
|
#ifdef __sgi__
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
#include "OS.h"
|
|
#include "OSThread.h"
|
|
#include "MyAssert.h"
|
|
#include "OSFileSource.h"
|
|
|
|
#if __MacOSX__
|
|
|
|
#ifndef __COREFOUNDATION__
|
|
#include <CoreFoundation/CoreFoundation.h>
|
|
//extern "C" { void Microseconds (UnsignedWide *microTickCount); }
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
#if (__FreeBSD__ || __MacOSX__)
|
|
#include <sys/sysctl.h>
|
|
#endif
|
|
|
|
#if (__solaris__ || __linux__ || __linuxppc__)
|
|
#include "StringParser.h"
|
|
#endif
|
|
|
|
#if __sgi__
|
|
#include <sys/systeminfo.h>
|
|
#endif
|
|
|
|
|
|
double OS::sDivisor = 0;
|
|
double OS::sMicroDivisor = 0;
|
|
SInt64 OS::sMsecSince1970 = 0;
|
|
SInt64 OS::sMsecSince1900 = 0;
|
|
SInt64 OS::sInitialMsec = 0;
|
|
SInt64 OS::sWrapTime = 0;
|
|
SInt64 OS::sCompareWrap = 0;
|
|
SInt64 OS::sLastTimeMilli = 0;
|
|
OSMutex OS::sStdLibOSMutex;
|
|
|
|
#if DEBUG || __Win32__
|
|
#include "OSMutex.h"
|
|
#include "OSMemory.h"
|
|
static OSMutex* sLastMillisMutex = NULL;
|
|
#endif
|
|
|
|
void OS::Initialize()
|
|
{
|
|
Assert (sInitialMsec == 0); // do only once
|
|
if (sInitialMsec != 0) return;
|
|
::tzset();
|
|
|
|
//setup t0 value for msec since 1900
|
|
|
|
//t.tv_sec is number of seconds since Jan 1, 1970. Convert to seconds since 1900
|
|
SInt64 the1900Sec = (SInt64) (24 * 60 * 60) * (SInt64) ((70 * 365) + 17) ;
|
|
sMsecSince1900 = the1900Sec * 1000;
|
|
|
|
sWrapTime = (SInt64) 0x00000001 << 32;
|
|
sCompareWrap = (SInt64) 0xffffffff << 32;
|
|
sLastTimeMilli = 0;
|
|
|
|
sInitialMsec = OS::Milliseconds(); //Milliseconds uses sInitialMsec so this assignment is valid only once.
|
|
|
|
sMsecSince1970 = ::time(NULL); // POSIX time always returns seconds since 1970
|
|
sMsecSince1970 *= 1000; // Convert to msec
|
|
|
|
|
|
#if DEBUG || __Win32__
|
|
sLastMillisMutex = NEW OSMutex();
|
|
#endif
|
|
}
|
|
|
|
SInt64 OS::Milliseconds()
|
|
{
|
|
/*
|
|
#if __MacOSX__
|
|
|
|
#if DEBUG
|
|
OSMutexLocker locker(sLastMillisMutex);
|
|
#endif
|
|
|
|
UnsignedWide theMicros;
|
|
::Microseconds(&theMicros);
|
|
SInt64 scalarMicros = theMicros.hi;
|
|
scalarMicros <<= 32;
|
|
scalarMicros += theMicros.lo;
|
|
scalarMicros = ((scalarMicros / 1000) - sInitialMsec) + sMsecSince1970; // convert to msec
|
|
|
|
#if DEBUG
|
|
static SInt64 sLastMillis = 0;
|
|
//Assert(scalarMicros >= sLastMillis); // currently this fails on dual processor machines
|
|
sLastMillis = scalarMicros;
|
|
#endif
|
|
return scalarMicros;
|
|
*/
|
|
#if __Win32__
|
|
OSMutexLocker locker(sLastMillisMutex);
|
|
// curTimeMilli = timeGetTime() + ((sLastTimeMilli/ 2^32) * 2^32)
|
|
// using binary & to reduce it to one operation from two
|
|
// sCompareWrap and sWrapTime are constants that are never changed
|
|
// sLastTimeMilli is updated with the curTimeMilli after each call to this function
|
|
SInt64 curTimeMilli = (UInt32) ::timeGetTime() + (sLastTimeMilli & sCompareWrap);
|
|
if((curTimeMilli - sLastTimeMilli) < 0)
|
|
{
|
|
curTimeMilli += sWrapTime;
|
|
}
|
|
sLastTimeMilli = curTimeMilli;
|
|
|
|
// For debugging purposes
|
|
//SInt64 tempCurMsec = (curTimeMilli - sInitialMsec) + sMsecSince1970;
|
|
//SInt32 tempCurSec = tempCurMsec / 1000;
|
|
//char buffer[kTimeStrSize];
|
|
//qtss_printf("OS::MilliSeconds current time = %s\n", qtss_ctime(&tempCurSec, buffer, sizeof(buffer)));
|
|
|
|
return (curTimeMilli - sInitialMsec) + sMsecSince1970; // convert to application time
|
|
#else
|
|
struct timeval t;
|
|
int theErr = ::gettimeofday(&t, NULL);
|
|
Assert(theErr == 0);
|
|
|
|
SInt64 curTime;
|
|
curTime = t.tv_sec;
|
|
curTime *= 1000; // sec -> msec
|
|
curTime += t.tv_usec / 1000; // usec -> msec
|
|
|
|
return (curTime - sInitialMsec) + sMsecSince1970;
|
|
#endif
|
|
|
|
}
|
|
|
|
SInt64 OS::Microseconds()
|
|
{
|
|
/*
|
|
#if __MacOSX__
|
|
UnsignedWide theMicros;
|
|
::Microseconds(&theMicros);
|
|
SInt64 theMillis = theMicros.hi;
|
|
theMillis <<= 32;
|
|
theMillis += theMicros.lo;
|
|
return theMillis;
|
|
*/
|
|
#if __Win32__
|
|
SInt64 curTime = (SInt64) ::timeGetTime(); // system time in milliseconds
|
|
curTime -= sInitialMsec; // convert to application time
|
|
curTime *= 1000; // convert to microseconds
|
|
return curTime;
|
|
#else
|
|
struct timeval t;
|
|
int theErr = ::gettimeofday(&t, NULL);
|
|
Assert(theErr == 0);
|
|
|
|
SInt64 curTime;
|
|
curTime = t.tv_sec;
|
|
curTime *= 1000000; // sec -> usec
|
|
curTime += t.tv_usec;
|
|
|
|
return curTime - (sInitialMsec * 1000);
|
|
#endif
|
|
}
|
|
|
|
SInt32 OS::GetGMTOffset()
|
|
{
|
|
#ifdef __Win32__
|
|
TIME_ZONE_INFORMATION tzInfo;
|
|
DWORD theErr = ::GetTimeZoneInformation(&tzInfo);
|
|
if (theErr == TIME_ZONE_ID_INVALID)
|
|
return 0;
|
|
|
|
return ((tzInfo.Bias / 60) * -1);
|
|
#else
|
|
|
|
time_t clock;
|
|
struct tm *tmptr= localtime(&clock);
|
|
if (tmptr == NULL)
|
|
return 0;
|
|
|
|
return tmptr->tm_gmtoff / 3600;//convert seconds to hours before or after GMT
|
|
#endif
|
|
}
|
|
|
|
|
|
SInt64 OS::HostToNetworkSInt64(SInt64 hostOrdered)
|
|
{
|
|
#if BIGENDIAN
|
|
return hostOrdered;
|
|
#else
|
|
return (SInt64) ( (UInt64) (hostOrdered << 56) | (UInt64) (((UInt64) 0x00ff0000 << 32) & (hostOrdered << 40))
|
|
| (UInt64) ( ((UInt64) 0x0000ff00 << 32) & (hostOrdered << 24)) | (UInt64) (((UInt64) 0x000000ff << 32) & (hostOrdered << 8))
|
|
| (UInt64) ( ((UInt64) 0x00ff0000 << 8) & (hostOrdered >> 8)) | (UInt64) ((UInt64) 0x00ff0000 & (hostOrdered >> 24))
|
|
| (UInt64) ( (UInt64) 0x0000ff00 & (hostOrdered >> 40)) | (UInt64) ((UInt64) 0x00ff & (hostOrdered >> 56)) );
|
|
#endif
|
|
}
|
|
|
|
SInt64 OS::NetworkToHostSInt64(SInt64 networkOrdered)
|
|
{
|
|
#if BIGENDIAN
|
|
return networkOrdered;
|
|
#else
|
|
return (SInt64) ( (UInt64) (networkOrdered << 56) | (UInt64) (((UInt64) 0x00ff0000 << 32) & (networkOrdered << 40))
|
|
| (UInt64) ( ((UInt64) 0x0000ff00 << 32) & (networkOrdered << 24)) | (UInt64) (((UInt64) 0x000000ff << 32) & (networkOrdered << 8))
|
|
| (UInt64) ( ((UInt64) 0x00ff0000 << 8) & (networkOrdered >> 8)) | (UInt64) ((UInt64) 0x00ff0000 & (networkOrdered >> 24))
|
|
| (UInt64) ( (UInt64) 0x0000ff00 & (networkOrdered >> 40)) | (UInt64) ((UInt64) 0x00ff & (networkOrdered >> 56)) );
|
|
#endif
|
|
}
|
|
|
|
|
|
OS_Error OS::MakeDir(char *inPath)
|
|
{
|
|
struct stat theStatBuffer;
|
|
if (::stat(inPath, &theStatBuffer) == -1)
|
|
{
|
|
//this directory doesn't exist, so let's try to create it
|
|
#ifdef __Win32__
|
|
if (::mkdir(inPath) == -1)
|
|
#else
|
|
if (::mkdir(inPath, S_IRWXU) == -1)
|
|
#endif
|
|
return (OS_Error)OSThread::GetErrno();
|
|
}
|
|
#ifdef __Win32__
|
|
else if (!(theStatBuffer.st_mode & _S_IFDIR)) // MSVC++ doesn't define the S_ISDIR macro
|
|
return EEXIST; // there is a file at this point in the path!
|
|
#else
|
|
else if (!S_ISDIR(theStatBuffer.st_mode))
|
|
return EEXIST;//there is a file at this point in the path!
|
|
#endif
|
|
|
|
//directory exists
|
|
return OS_NoErr;
|
|
}
|
|
|
|
OS_Error OS::RecursiveMakeDir(char *inPath)
|
|
{
|
|
Assert(inPath != NULL);
|
|
|
|
//iterate through the path, replacing '/' with '\0' as we go
|
|
char *thePathTraverser = inPath;
|
|
|
|
//skip over the first / in the path.
|
|
if (*thePathTraverser == kPathDelimiterChar)
|
|
thePathTraverser++;
|
|
|
|
while (*thePathTraverser != '\0')
|
|
{
|
|
if (*thePathTraverser == kPathDelimiterChar)
|
|
{
|
|
//we've found a filename divider. Now that we have a complete
|
|
//filename, see if this partial path exists.
|
|
|
|
//make the partial path into a C string
|
|
*thePathTraverser = '\0';
|
|
OS_Error theErr = MakeDir(inPath);
|
|
//there is a directory here. Just continue in our traversal
|
|
*thePathTraverser = kPathDelimiterChar;
|
|
|
|
if (theErr != OS_NoErr)
|
|
return theErr;
|
|
}
|
|
thePathTraverser++;
|
|
}
|
|
|
|
//need to create the last directory in the path
|
|
return MakeDir(inPath);
|
|
}
|
|
|
|
Bool16 OS::ThreadSafe()
|
|
{
|
|
|
|
#if (__MacOSX__) // check for version 7 or greater for thread safe stdlib
|
|
char releaseStr[32] = "";
|
|
size_t strLen = sizeof(releaseStr);
|
|
int mib[2];
|
|
mib[0] = CTL_KERN;
|
|
mib[1] = KERN_OSRELEASE;
|
|
|
|
UInt32 majorVers = 0;
|
|
int err = sysctl(mib,2, releaseStr, &strLen, NULL,0);
|
|
if (err == 0)
|
|
{
|
|
StrPtrLen rStr(releaseStr,strLen);
|
|
char* endMajor = rStr.FindString(".");
|
|
if (endMajor != NULL) // truncate to an int value.
|
|
*endMajor = 0;
|
|
|
|
if (::strlen(releaseStr) > 0) //convert to an int
|
|
::sscanf(releaseStr, "%"_U32BITARG_"", &majorVers);
|
|
}
|
|
if (majorVers < 7) // less than OS X Panther 10.3
|
|
return false; // force 1 worker thread because < 10.3 means std c lib is not thread safe.
|
|
|
|
#endif
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
UInt32 OS::GetNumProcessors()
|
|
{
|
|
#if (__Win32__)
|
|
SYSTEM_INFO theSystemInfo;
|
|
::GetSystemInfo(&theSystemInfo);
|
|
|
|
return (UInt32)theSystemInfo.dwNumberOfProcessors;
|
|
#endif
|
|
|
|
#if (__MacOSX__ || __FreeBSD__)
|
|
int numCPUs = 1;
|
|
size_t len = sizeof(numCPUs);
|
|
int mib[2];
|
|
mib[0] = CTL_HW;
|
|
mib[1] = HW_NCPU;
|
|
(void) ::sysctl(mib,2,&numCPUs,&len,NULL,0);
|
|
if (numCPUs < 1)
|
|
numCPUs = 1;
|
|
return (UInt32) numCPUs;
|
|
#endif
|
|
|
|
#if(__linux__ || __linuxppc__)
|
|
|
|
char cpuBuffer[8192] = "";
|
|
StrPtrLen cpuInfoBuf(cpuBuffer, sizeof(cpuBuffer));
|
|
FILE *cpuFile = ::fopen( "/proc/cpuinfo", "r" );
|
|
if (cpuFile)
|
|
{ cpuInfoBuf.Len = ::fread(cpuInfoBuf.Ptr, sizeof(char), cpuInfoBuf.Len, cpuFile);
|
|
::fclose(cpuFile);
|
|
}
|
|
|
|
StringParser cpuInfoFileParser(&cpuInfoBuf);
|
|
StrPtrLen line;
|
|
StrPtrLen word;
|
|
UInt32 numCPUs = 0;
|
|
|
|
while( cpuInfoFileParser.GetDataRemaining() != 0 )
|
|
{
|
|
cpuInfoFileParser.GetThruEOL(&line); // Read each line
|
|
StringParser lineParser(&line);
|
|
lineParser.ConsumeWhitespace(); //skip over leading whitespace
|
|
|
|
if (lineParser.GetDataRemaining() == 0) // must be an empty line
|
|
continue;
|
|
|
|
lineParser.ConsumeUntilWhitespace(&word);
|
|
|
|
if ( word.Equal("processor") ) // found a processor as first word in line
|
|
{ numCPUs ++;
|
|
}
|
|
}
|
|
|
|
if (numCPUs == 0)
|
|
numCPUs = 1;
|
|
|
|
return numCPUs;
|
|
#endif
|
|
|
|
#if(__solaris__)
|
|
{
|
|
UInt32 numCPUs = 0;
|
|
char linebuff[512] = "";
|
|
StrPtrLen line(linebuff, sizeof(linebuff));
|
|
StrPtrLen word;
|
|
|
|
FILE *p = ::popen("uname -X","r");
|
|
while((::fgets(linebuff, sizeof(linebuff -1), p)) > 0)
|
|
{
|
|
StringParser lineParser(&line);
|
|
lineParser.ConsumeWhitespace(); //skip over leading whitespace
|
|
|
|
if (lineParser.GetDataRemaining() == 0) // must be an empty line
|
|
continue;
|
|
|
|
lineParser.ConsumeUntilWhitespace(&word);
|
|
|
|
if ( word.Equal("NumCPU")) // found a tag as first word in line
|
|
{
|
|
lineParser.GetThru(NULL,'=');
|
|
lineParser.ConsumeWhitespace(); //skip over leading whitespace
|
|
lineParser.ConsumeUntilWhitespace(&word); //read the number of cpus
|
|
if (word.Len > 0)
|
|
::sscanf(word.Ptr, "%"_U32BITARG_"", &numCPUs);
|
|
|
|
break;
|
|
}
|
|
}
|
|
if (numCPUs == 0)
|
|
numCPUs = 1;
|
|
|
|
::pclose(p);
|
|
|
|
return numCPUs;
|
|
}
|
|
#endif
|
|
|
|
#if(__sgi__)
|
|
UInt32 numCPUs = 0;
|
|
|
|
numCPUs = sysconf(_SC_NPROC_ONLN);
|
|
|
|
return numCPUs;
|
|
#endif
|
|
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
//CISCO provided fix for integer + fractional fixed64.
|
|
SInt64 OS::TimeMilli_To_Fixed64Secs(SInt64 inMilliseconds)
|
|
{
|
|
SInt64 result = inMilliseconds / 1000; // The result is in lower bits.
|
|
result <<= 32; // shift it to higher 32 bits
|
|
// Take the remainder (rem = inMilliseconds%1000) and multiply by
|
|
// 2**32, divide by 1000, effectively this gives (rem/1000) as a
|
|
// binary fraction.
|
|
double p = ldexp((double)(inMilliseconds%1000), +32) / 1000.;
|
|
UInt32 frac = (UInt32)p;
|
|
result |= frac;
|
|
return result;
|
|
}
|