/* ============================================================================== This file is part of the JUCE library - "Jules' Utility Class Extensions" Copyright 2004-7 by Raw Material Software ltd. ------------------------------------------------------------------------------ JUCE can be redistributed and/or modified under the terms of the GNU General Public License, as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with JUCE; if not, visit www.gnu.org/licenses or write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ------------------------------------------------------------------------------ If you'd like to release a closed-source product which uses JUCE, commercial licenses are also available: visit www.rawmaterialsoftware.com/juce for more information. ============================================================================== */ /* This file contains posix routines that are common to both the Linux and Mac builds. It gets included directly in the cpp files for these platforms. */ //============================================================================== CriticalSection::CriticalSection() throw() { pthread_mutexattr_t atts; pthread_mutexattr_init (&atts); pthread_mutexattr_settype (&atts, PTHREAD_MUTEX_RECURSIVE); pthread_mutex_init (&internal, &atts); } CriticalSection::~CriticalSection() throw() { pthread_mutex_destroy (&internal); } void CriticalSection::enter() const throw() { pthread_mutex_lock (&internal); } bool CriticalSection::tryEnter() const throw() { return pthread_mutex_trylock (&internal) == 0; } void CriticalSection::exit() const throw() { pthread_mutex_unlock (&internal); } //============================================================================== struct EventStruct { pthread_cond_t condition; pthread_mutex_t mutex; bool triggered; }; WaitableEvent::WaitableEvent() throw() { EventStruct* const es = new EventStruct(); es->triggered = false; pthread_cond_init (&es->condition, 0); pthread_mutex_init (&es->mutex, 0); internal = es; } WaitableEvent::~WaitableEvent() throw() { EventStruct* const es = (EventStruct*) internal; pthread_cond_destroy (&es->condition); pthread_mutex_destroy (&es->mutex); delete es; } bool WaitableEvent::wait (const int timeOutMillisecs) const throw() { EventStruct* const es = (EventStruct*) internal; bool ok = true; pthread_mutex_lock (&es->mutex); if (timeOutMillisecs < 0) { while (! es->triggered) pthread_cond_wait (&es->condition, &es->mutex); } else { while (! es->triggered) { struct timeval t; gettimeofday (&t, 0); struct timespec time; time.tv_sec = t.tv_sec + (timeOutMillisecs / 1000); time.tv_nsec = (t.tv_usec + ((timeOutMillisecs % 1000) * 1000)) * 1000; if (time.tv_nsec >= 1000000000) { time.tv_nsec -= 1000000000; time.tv_sec++; } if (pthread_cond_timedwait (&es->condition, &es->mutex, &time) == ETIMEDOUT) { ok = false; break; } } } es->triggered = false; pthread_mutex_unlock (&es->mutex); return ok; } void WaitableEvent::signal() const throw() { EventStruct* const es = (EventStruct*) internal; pthread_mutex_lock (&es->mutex); es->triggered = true; pthread_cond_broadcast (&es->condition); pthread_mutex_unlock (&es->mutex); } void WaitableEvent::reset() const throw() { EventStruct* const es = (EventStruct*) internal; pthread_mutex_lock (&es->mutex); es->triggered = false; pthread_mutex_unlock (&es->mutex); } //============================================================================== void JUCE_CALLTYPE Thread::sleep (int millisecs) throw() { struct timespec time; time.tv_sec = millisecs / 1000; time.tv_nsec = (millisecs % 1000) * 1000000; nanosleep (&time, 0); } //============================================================================== const tchar File::separator = T('/'); const tchar* File::separatorString = T("/"); //============================================================================== bool juce_copyFile (const String& s, const String& d) throw(); static bool juce_stat (const String& fileName, struct stat& info) throw() { return fileName.isNotEmpty() && (stat (fileName.toUTF8(), &info) == 0); } bool juce_isDirectory (const String& fileName) throw() { struct stat info; return fileName.isEmpty() || (juce_stat (fileName, info) && ((info.st_mode & S_IFDIR) != 0)); } bool juce_fileExists (const String& fileName, const bool dontCountDirectories) throw() { if (fileName.isEmpty()) return false; const char* const fileNameUTF8 = fileName.toUTF8(); bool exists = access (fileNameUTF8, F_OK) == 0; if (exists && dontCountDirectories) { struct stat info; const int res = stat (fileNameUTF8, &info); if (res == 0 && (info.st_mode & S_IFDIR) != 0) exists = false; } return exists; } int64 juce_getFileSize (const String& fileName) throw() { struct stat info; return juce_stat (fileName, info) ? info.st_size : 0; } //============================================================================== bool juce_canWriteToFile (const String& fileName) throw() { return access (fileName.toUTF8(), W_OK) == 0; } bool juce_deleteFile (const String& fileName) throw() { if (juce_isDirectory (fileName)) return rmdir ((const char*) fileName.toUTF8()) == 0; else return remove ((const char*) fileName.toUTF8()) == 0; } bool juce_moveFile (const String& source, const String& dest) throw() { if (rename (source.toUTF8(), dest.toUTF8()) == 0) return true; if (juce_canWriteToFile (source) && juce_copyFile (source, dest)) { if (juce_deleteFile (source)) return true; juce_deleteFile (dest); } return false; } void juce_createDirectory (const String& fileName) throw() { mkdir (fileName.toUTF8(), 0777); } void* juce_fileOpen (const String& fileName, bool forWriting) throw() { int flags = O_RDONLY; if (forWriting) { if (juce_fileExists (fileName, false)) { const int f = open ((const char*) fileName.toUTF8(), O_RDWR, 00644); if (f != -1) lseek (f, 0, SEEK_END); return (void*) f; } else { flags = O_RDWR + O_CREAT; } } return (void*) open ((const char*) fileName.toUTF8(), flags, 00644); } void juce_fileClose (void* handle) throw() { if (handle != 0) close ((int) (pointer_sized_int) handle); } int juce_fileRead (void* handle, void* buffer, int size) throw() { if (handle != 0) return read ((int) (pointer_sized_int) handle, buffer, size); return 0; } int juce_fileWrite (void* handle, const void* buffer, int size) throw() { if (handle != 0) return write ((int) (pointer_sized_int) handle, buffer, size); return 0; } int64 juce_fileSetPosition (void* handle, int64 pos) throw() { if (handle != 0 && lseek ((int) (pointer_sized_int) handle, pos, SEEK_SET) == pos) return pos; return -1; } int64 juce_fileGetPosition (void* handle) throw() { if (handle != 0) return lseek ((int) (pointer_sized_int) handle, 0, SEEK_CUR); else return -1; } void juce_fileFlush (void* handle) throw() { if (handle != 0) fsync ((int) (pointer_sized_int) handle); } const File juce_getExecutableFile() { Dl_info exeInfo; dladdr ((const void*) juce_getExecutableFile, &exeInfo); return File (exeInfo.dli_fname); } //============================================================================== // if this file doesn't exist, find a parent of it that does.. static bool doStatFS (const File* file, struct statfs& result) throw() { File f (*file); for (int i = 5; --i >= 0;) { if (f.exists()) break; f = f.getParentDirectory(); } return statfs (f.getFullPathName().toUTF8(), &result) == 0; } int64 File::getBytesFreeOnVolume() const throw() { struct statfs buf; if (doStatFS (this, buf)) return (int64) buf.f_bsize * (int64) buf.f_bavail; // Note: this returns space available to non-super user return 0; } int64 File::getVolumeTotalSize() const throw() { struct statfs buf; if (doStatFS (this, buf)) return (int64) buf.f_bsize * (int64) buf.f_blocks; return 0; } const String juce_getVolumeLabel (const String& filenameOnVolume, int& volumeSerialNumber) throw() { volumeSerialNumber = 0; #if JUCE_MAC struct VolAttrBuf { u_int32_t length; attrreference_t mountPointRef; char mountPointSpace [MAXPATHLEN]; } attrBuf; struct attrlist attrList; zerostruct (attrList); attrList.bitmapcount = ATTR_BIT_MAP_COUNT; attrList.volattr = ATTR_VOL_INFO | ATTR_VOL_NAME; File f (filenameOnVolume); for (;;) { if (getattrlist ((const char*) f.getFullPathName().toUTF8(), &attrList, &attrBuf, sizeof(attrBuf), 0) == 0) { return String::fromUTF8 (((const uint8*) &attrBuf.mountPointRef) + attrBuf.mountPointRef.attr_dataoffset, (int) attrBuf.mountPointRef.attr_length); } const File parent (f.getParentDirectory()); if (f == parent) break; f = parent; } #endif return String::empty; } //============================================================================== void juce_runSystemCommand (const String& command) { int result = system ((const char*) command.toUTF8()); (void) result; } const String juce_getOutputFromCommand (const String& command) { // slight bodge here, as we just pipe the output into a temp file and read it... const File tempFile (File::getSpecialLocation (File::tempDirectory) .getNonexistentChildFile (String::toHexString (Random::getSystemRandom().nextInt()), ".tmp", false)); juce_runSystemCommand (command + " > " + tempFile.getFullPathName()); String result (tempFile.loadFileAsString()); tempFile.deleteFile(); return result; } //============================================================================== #if JUCE_64BIT #define filedesc ((long long) internal) #else #define filedesc ((int) internal) #endif InterProcessLock::InterProcessLock (const String& name_) throw() : internal (0), name (name_), reentrancyLevel (0) { #if JUCE_MAC // (don't use getSpecialLocation() to avoid the temp folder being different for each app) const File temp (File (T("~/Library/Caches/Juce")).getChildFile (name)); #else const File temp (File::getSpecialLocation (File::tempDirectory).getChildFile (name)); #endif temp.create(); internal = (void*) open (temp.getFullPathName().toUTF8(), O_RDWR); } InterProcessLock::~InterProcessLock() throw() { while (reentrancyLevel > 0) this->exit(); close (filedesc); } bool InterProcessLock::enter (const int timeOutMillisecs) throw() { if (internal == 0) return false; if (reentrancyLevel != 0) return true; const int64 endTime = Time::currentTimeMillis() + timeOutMillisecs; struct flock fl; zerostruct (fl); fl.l_whence = SEEK_SET; fl.l_type = F_WRLCK; for (;;) { const int result = fcntl (filedesc, F_SETLK, &fl); if (result >= 0) { ++reentrancyLevel; return true; } if (errno != EINTR) { if (timeOutMillisecs == 0 || (timeOutMillisecs > 0 && Time::currentTimeMillis() >= endTime)) break; Thread::sleep (10); } } return false; } void InterProcessLock::exit() throw() { if (reentrancyLevel > 0 && internal != 0) { --reentrancyLevel; struct flock fl; zerostruct (fl); fl.l_whence = SEEK_SET; fl.l_type = F_UNLCK; for (;;) { const int result = fcntl (filedesc, F_SETLKW, &fl); if (result >= 0 || errno != EINTR) break; } } }