From: VMware, Inc <> Date: Mon, 28 Sep 2009 20:30:51 +0000 (-0700) Subject: Use the pthread implementation of sync mutex in open-vm-tools. X-Git-Tag: p4-sync-929606~1 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=6f43bd1ac7b13500c81f03bddd77becfa5b8f7f1;p=thirdparty%2Fopen-vm-tools.git Use the pthread implementation of sync mutex in open-vm-tools. This avoids having to deal with the eventfd library used by the syncWaitQ code. For some background of "issues this change avoids", see sourceforge.net bug 2865804. Signed-off-by: Marcelo Vanzin --- diff --git a/open-vm-tools/configure.ac b/open-vm-tools/configure.ac index c9ad095e3..0b2221346 100644 --- a/open-vm-tools/configure.ac +++ b/open-vm-tools/configure.ac @@ -743,11 +743,6 @@ AC_PATH_PROG( ### Headers ### -AC_CHECK_DECL([SYS_eventfd], - [has_sys_eventfd="yes"], - [has_sys_eventfd="no"], - [ [#include ] ]) - AC_CHECK_HEADERS([crypt.h]) AC_CHECK_HEADERS([inttypes.h]) AC_CHECK_HEADERS([stdint.h]) @@ -1056,7 +1051,6 @@ AM_CONDITIONAL(HAVE_FUSE, test "$have_fuse" = "yes") AM_CONDITIONAL(HAVE_GNU_LD, test "$with_gnu_ld" = "yes") AM_CONDITIONAL(HAVE_GTKMM, test "$have_x" = "yes" -a "$with_gtkmm" = "yes") AM_CONDITIONAL(HAVE_PAM, test "$with_pam" = "yes") -AM_CONDITIONAL(NEED_EVENTFD, test "$has_sys_eventfd" = "no") AM_CONDITIONAL(USE_SLASH_PROC, test "os" = "linux" -a "$have_glib_2_14" = "yes") ### Feature-specific flags / actions diff --git a/open-vm-tools/lib/include/eventfd.h b/open-vm-tools/lib/include/eventfd.h deleted file mode 100644 index 1e0b8e21f..000000000 --- a/open-vm-tools/lib/include/eventfd.h +++ /dev/null @@ -1,75 +0,0 @@ -/********************************************************* - * Copyright (C) 2009 VMware, Inc. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation version 2.1 and no later version. - * - * This program 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 Lesser GNU General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - *********************************************************/ - -/* - * eventfd.h -- - * - * eventfd interface. Use only if platform does not have - * its own. - */ - -#ifndef EVENTFD_H -#define EVENTFD_H - -#define INCLUDE_ALLOW_USERLEVEL -#include "includeCheck.h" - -#include "vm_basic_types.h" - -#include -#include - -typedef uint64 eventfd_t; - -#define EFD_CLOEXEC O_CLOEXEC -#define EFD_NONBLOCK O_NONBLOCK - -/* Only Linux eventfd implementation is available. */ -#if defined(__linux__) && !defined(N_PLAT_NLM) -# define VMWARE_EVENTFD_REAL - -int eventfd(int count, int flags); -int eventfd_read(int fd, eventfd_t *value); -int eventfd_write(int fd, eventfd_t value); - -#else - -static INLINE int -eventfd(int count, int flags) -{ - errno = ENOSYS; - return -1; -} - -static INLINE int -eventfd_read(int fd, eventfd_t *value) -{ - errno = ENOSYS; - return -1; -} - -static INLINE int -eventfd_write(int fd, eventfd_t value) -{ - errno = ENOSYS; - return -1; -} - -#endif - -#endif /* EVENTFD_H */ diff --git a/open-vm-tools/lib/misc/Makefile.am b/open-vm-tools/lib/misc/Makefile.am index 3f111301e..ff2ef3f94 100644 --- a/open-vm-tools/lib/misc/Makefile.am +++ b/open-vm-tools/lib/misc/Makefile.am @@ -25,9 +25,6 @@ libMisc_la_SOURCES += dbllnklst.c libMisc_la_SOURCES += dynarray.c libMisc_la_SOURCES += dynbuf.c libMisc_la_SOURCES += escape.c -if NEED_EVENTFD -libMisc_la_SOURCES += eventfd.c -endif libMisc_la_SOURCES += hashTable.c libMisc_la_SOURCES += hostinfoPosix.c libMisc_la_SOURCES += hostname.c diff --git a/open-vm-tools/lib/misc/eventfd.c b/open-vm-tools/lib/misc/eventfd.c deleted file mode 100644 index 05c26081f..000000000 --- a/open-vm-tools/lib/misc/eventfd.c +++ /dev/null @@ -1,222 +0,0 @@ -/********************************************************* - * Copyright (C) 2009 VMware, Inc. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation version 2.1 and no later version. - * - * This program 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 Lesser GNU General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - *********************************************************/ - -/* - * eventfd.c -- - * - * Implements eventfd syscall interface. - */ - -#ifndef _GNU_SOURCE -#define _GNU_SOURCE -#endif -#include "eventfd.h" - -#ifdef VMWARE_EVENTFD_REAL -#include -#include - - -#ifdef SYS_eventfd2 -/* - * If this error fires, either start using instead of - * "eventfd.h", or rename our eventfd/eventfd_read/eventfd_write to some - * private names and modify all users to use them. As you can guess, - * switching to glibc's eventfd.h is preferred choice. - */ - -# error "You have real SYS_eventfd2. You should stop using this one." -#endif -#ifdef VM_X86_64 -# define SYS_eventfd 284 -# define SYS_eventfd2 290 -#else -# define SYS_eventfd 323 -# define SYS_eventfd2 328 -#endif - - -/* - *----------------------------------------------------------------------------- - * - * eventfd -- - * - * Create eventfd descriptor. - * - * Results: - * -1 on failure, errno set - * >= 0 on success, file descriptor to use - * - * Side effects: - * None. - * - *----------------------------------------------------------------------------- - */ - -int -eventfd(int count, // IN: Initial count - int flags) // IN: Initial flags -{ - static enum { - EVENTFD_UNKNOWN = 0, - EVENTFD_UNKNOWN_NOT2, - EVENTFD_EVENTFD2, - EVENTFD_EVENTFD, - EVENTFD_NONE, - } eventfdStyle; - int ret; - - switch (eventfdStyle) { - case EVENTFD_EVENTFD2: /* ESX and linux 2.6.27+ */ - return syscall(SYS_eventfd2, count, flags); - case EVENTFD_EVENTFD: /* linux 2.6.22 - 2.6.27 */ - if (flags) { - errno = EINVAL; - return -1; - } - return syscall(SYS_eventfd, count); - case EVENTFD_UNKNOWN: /* here we start */ - ret = syscall(SYS_eventfd2, count, flags); - if (ret != -1 || errno != ENOSYS) { - /* - * We can get back SYS_eventfd2 instead of -1/ENOSYS on some - * broken systems - see bug 460859. This tries to figure out - * whether returned value is legitimate fd 328, or just - * masqueraded ENOSYS. - */ - if (ret != SYS_eventfd2) { - eventfdStyle = EVENTFD_EVENTFD2; - return ret; - } - - /* - * Allocate another eventfd. If that one is 328 too, it is - * clear that kernel is broken, as we already got such fd - * above. - * - * Otherwise if it is not -1, or if errno is not ENOSYS, - * then eventfd2 works. Close newly allocated fd, and - * return 328 (we return 328 and not new one so code path - * is same for both success & failure). - * - * If new call failed with ENOSYS, then it means that someone - * attached strace between first and second eventfd2 syscalls - - * it means that 328 we got was bogus, and system does not - * support eventfd2. - */ - ret = syscall(SYS_eventfd2, count, flags); - if (ret != SYS_eventfd2) { - if (ret != -1 || errno != ENOSYS) { - eventfdStyle = EVENTFD_EVENTFD2; - if (ret >= 0) { - close(ret); - } - return SYS_eventfd2; - } - } - } - eventfdStyle = EVENTFD_UNKNOWN_NOT2; - /* FALLTHRU */ - case EVENTFD_UNKNOWN_NOT2: /* not eventfd2, but caller needed flags */ - if (flags) { - errno = EINVAL; - return -1; - } - ret = syscall(SYS_eventfd, count); - if (ret != -1 || errno != ENOSYS) { - eventfdStyle = EVENTFD_EVENTFD; - return ret; - } - eventfdStyle = EVENTFD_NONE; - /* FALLTHRU */ - default: /* none - 2.6.21 and older */ - break; - } - errno = ENOSYS; - return -1; -} - - -/* - *----------------------------------------------------------------------------- - * - * eventfd_read -- - * - * Read count from the descriptor. - * - * Results: - * -1 on failure, errno set - * 0 on success, number of pending events retrieved - * - * Side effects: - * None. - * - *----------------------------------------------------------------------------- - */ - -int -eventfd_read(int fd, // IN: event fd - eventfd_t *value) // OUT: value -{ - ssize_t ret; - - ret = read(fd, value, sizeof *value); - if (ret == sizeof *value) { - return 0; - } - if (ret != -1) { - errno = EINVAL; - } - return -1; -} - - -/* - *----------------------------------------------------------------------------- - * - * eventfd_write -- - * - * Write (increase) count on the descriptor. - * - * Results: - * -1 on failure, errno set - * 0 on success, number of pending events increased - * - * Side effects: - * None. - * - *----------------------------------------------------------------------------- - */ - -int -eventfd_write(int fd, // IN: event fd - eventfd_t value) // OUT: value -{ - ssize_t ret; - - ret = write(fd, &value, sizeof value); - if (ret == sizeof value) { - return 0; - } - if (ret != -1) { - errno = EINVAL; - } - return -1; -} - -#endif /* VMWARE_EVENTFD_REAL */ diff --git a/open-vm-tools/lib/sync/Makefile.am b/open-vm-tools/lib/sync/Makefile.am index ccb7495ea..cc0cb11c2 100644 --- a/open-vm-tools/lib/sync/Makefile.am +++ b/open-vm-tools/lib/sync/Makefile.am @@ -18,6 +18,5 @@ noinst_LTLIBRARIES = libSync.la libSync_la_SOURCES = -libSync_la_SOURCES += syncMutex.c -libSync_la_SOURCES += syncWaitQPosix.c +libSync_la_SOURCES += syncMutexPthread.c diff --git a/open-vm-tools/lib/sync/syncMutex.c b/open-vm-tools/lib/sync/syncMutexPthread.c similarity index 65% rename from open-vm-tools/lib/sync/syncMutex.c rename to open-vm-tools/lib/sync/syncMutexPthread.c index e0a84ffd5..c212fd3d6 100644 --- a/open-vm-tools/lib/sync/syncMutex.c +++ b/open-vm-tools/lib/sync/syncMutexPthread.c @@ -17,22 +17,14 @@ *********************************************************/ /* - * syncMutex.c -- + * syncMutexPthread.c -- * - * Implements a non-recursive mutex in a platform independent way. + * Implements a non-recursive mutex using pthreads */ -#if defined(_WIN32) -#include -#elif defined(N_PLAT_NLM) -#include -#include -#include -#include -#else -#include +#include #include -#endif +#include #include "vm_assert.h" #include "syncMutex.h" @@ -57,22 +49,14 @@ Bool SyncMutex_Init(SyncMutex *that, // OUT char const *path) // IN { - ASSERT(that); - -#if defined(N_PLAT_NLM) - that->semaphoreHandle = OpenLocalSemaphore(0); -#else - if (!SyncWaitQ_Init(&that->wq, path)) { + int error; + error = pthread_mutex_init(&that->_mutex, NULL); + if (error != 0) { return FALSE; } - - Atomic_Write(&that->unlocked, TRUE); -#endif - return TRUE; } - /* *---------------------------------------------------------------------- * @@ -87,17 +71,15 @@ SyncMutex_Init(SyncMutex *that, // OUT void SyncMutex_Destroy(SyncMutex *that) // IN { - ASSERT(that); - -#if defined(N_PLAT_NLM) - WaitOnLocalSemaphore(that->semaphoreHandle); - CloseLocalSemaphore(that->semaphoreHandle); -#else - SyncWaitQ_Destroy(&that->wq); +#ifdef VMX86_DEBUG + int error = +#endif + pthread_mutex_destroy(&that->_mutex); +#ifdef VMX86_DEBUG + ASSERT(error != EBUSY); #endif } - /* *---------------------------------------------------------------------- * @@ -114,77 +96,14 @@ SyncMutex_Destroy(SyncMutex *that) // IN Bool SyncMutex_Lock(SyncMutex *that) // IN { -#if defined(N_PLAT_NLM) - ASSERT(that); - - WaitOnLocalSemaphore(that->semaphoreHandle); -#else -#if defined(VMX86_DEVEL) -#define RETRY_TIMEOUT_MS (-1) // Infinite time out to catch bug #23716 (devel) -#else -#define RETRY_TIMEOUT_MS 5000 // Workaround for bug #23716 -#endif - - PollDevHandle handle; - - ASSERT(that); - - if (Atomic_ReadIfEqualWrite(&that->unlocked, TRUE, FALSE)) { - return TRUE; - } - - for (;;) { - int status; - - handle = SyncWaitQ_Add(&that->wq); - if (handle < 0) { - return FALSE; - } - - if (Atomic_ReadIfEqualWrite(&that->unlocked, TRUE, FALSE)) { - if (!SyncWaitQ_Remove(&that->wq, handle)) { - return FALSE; - } - - break; - } - -#if defined(_WIN32) - status = WaitForSingleObject((HANDLE) handle, RETRY_TIMEOUT_MS); - ASSERT(status != WAIT_FAILED); -#else // #ifdef _WIN32 - { - struct pollfd p; - p.events = POLLIN; - p.fd = handle; - - for (;;) { - status = poll(&p, 1, RETRY_TIMEOUT_MS); - if (status == 1 || status == 0) { - break; - } - - ASSERT(status < 0); - if (errno != EINTR) { - SyncWaitQ_Remove(&that->wq, handle); - return FALSE; - } - - /* We were interrupted by a signal, retry --hpreg */ - } - } -#endif // #ifdef _WIN32 - - if (!SyncWaitQ_Remove(&that->wq, handle)) { - return FALSE; - } + int error = pthread_mutex_lock(&that->_mutex); + ASSERT(error != EINVAL); + if (error != 0) { + return FALSE; } -#endif - return TRUE; } - /* *---------------------------------------------------------------------- * @@ -201,17 +120,12 @@ SyncMutex_Lock(SyncMutex *that) // IN Bool SyncMutex_Unlock(SyncMutex *that) // IN { - ASSERT(that); - -#if defined(N_PLAT_NLM) - SignalLocalSemaphore(that->semaphoreHandle); - + int error = pthread_mutex_unlock(&that->_mutex); + ASSERT(error != EINVAL); + if (error != 0) { + return FALSE; + } return TRUE; -#else - Atomic_Write(&that->unlocked, TRUE); - - return SyncWaitQ_WakeUp(&that->wq); -#endif } @@ -265,7 +179,6 @@ SyncMutex_CreateSingleton(Atomic_Ptr *lckStorage) // IN } -#if !defined(N_PLAT_NLM) /* *----------------------------------------------------------------------------- * @@ -286,8 +199,10 @@ Bool SyncMutex_Trylock(SyncMutex *that) { ASSERT(that); - - return Atomic_ReadIfEqualWrite(&that->unlocked, TRUE, FALSE); -} +#ifdef VMX86_SERVER + NOT_IMPLEMENTED(); + return FALSE; +#else + return (0 == pthread_mutex_trylock(&that->_mutex)); #endif - +} diff --git a/open-vm-tools/lib/sync/syncWaitQPosix.c b/open-vm-tools/lib/sync/syncWaitQPosix.c deleted file mode 100644 index 755f15c11..000000000 --- a/open-vm-tools/lib/sync/syncWaitQPosix.c +++ /dev/null @@ -1,1069 +0,0 @@ -/********************************************************* - * Copyright (C) 1998 VMware, Inc. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation version 2.1 and no later version. - * - * This program 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 Lesser GNU General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - *********************************************************/ - -#ifdef _WIN32 -#error "This file should not be compiled under Win32" -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef __APPLE__ -#include -#endif - -#include "vm_assert.h" -#include "str.h" -#include "util.h" -#include "syncWaitQ.h" -#include "posix.h" -#include "eventfd.h" - -#define LOGLEVEL_MODULE syncWaitQ -#include "loglevel_user.h" - -#define LGPFX "SyncWaitQ: " - - -/* - * syncWaitQPosix.c -- - * - * Kernel (2.4, it changed a bit in 2.5) wait-queue semantics in - * userland, yeah baby! -- hpreg - * - * The semantics of this implementation are as follows: - * - * o Client threads can add themselves to a waitqueue object and - * receive a pollable handle via a call to SyncWaitQ_Add - * - * o When the waitqueue is woken up, each handle that was - * previously obtained via a call to SyncWaitQ_Add becomes - * signalled and remains so until it is removed via a call to - * SyncWaitQ_Remove. Any calls to SyncWaitQ_Add, after the queue - * has been woken up, will return fresh, unsignalled handles. - * - */ - -/* - * TODO: - * Run this test code on 2.0 and 2.2 kernels - * - * kernel user - * ------ ---- - * wait queue FIFO - * - * state=INT, add to wait queue open FIFO in read mode - * check sleep condition check sleep condition - * schedule poll - * remove from wait queue close - * - * wake_up wait queue open FIFO in write mode + close - * - * I put a lot of thought into this, we may want to patent it just - * for fun. I don't know of any prior art --hpreg - */ - -/* - * How to use this to avoid races: - * - * Some process initializes the wait queue in shared mem with - * SyncWaitQ_Init(&wq) - * - * P0 does: - * - * for (;;) { - * fd = SyncWaitQ_Add(&wq); - * if (some condition) { - * SyncWaitQ_Remove(&wq, fd); - * break; - * } - * poll(fd, ...); - * SyncWaitQ_Remove(&wq, fd); - * } - * - * P1 does: - * - * some condition = TRUE; - * SyncWaitQ_WakeUp(&wq); - * - * Note2: if there is activity on a wait-queue fd, it basically means - * that the fd becomes unusable after that (it keeps returning - * POLLHUP), and you need a new one. Actually I take this back, we can - * reopen and then dup2, so we keep the same fd number and there is no - * need for the client code to re-register the new fd with its poll - * loop. I knew this trick would be useful one day :) - * - */ - -typedef union { - uint32 fd[2]; - uint64 i64; -} HandlesAsI64; - - -static INLINE Bool SyncWaitQWakeUpNamed(SyncWaitQ *that); -static INLINE Bool SyncWaitQWakeUpAnon(SyncWaitQ *that); - - -#if __APPLE__ -/* - * See VMware bug 116441: we workaround Apple bug 4751096 (calling close and - * dup simultaneously on the same fd makes the Mac OS kernel panic when the - * application exits) by serializing these calls. - * - * The bug is fixed in Leopard GA (build 9A581). - */ - -enum { - WORKAROUND_UNKNOWN, - WORKAROUND_NO, - WORKAROUND_YES, -}; -static Atomic_Int workaround = { WORKAROUND_UNKNOWN, }; - - -/* - *----------------------------------------------------------------------------- - * - * SyncWaitQInit -- - * - * If the workaround is needed, initialize the wait queue's mutex. - * - * Results: - * On success: 0. - * On failure: errno. - * - * Side effects: - * None - * - *---------------------------------------------------------------------------- - */ - -static int -SyncWaitQInit(SyncWaitQ *that) // IN: -{ - if (UNLIKELY(Atomic_ReadInt(&workaround) == WORKAROUND_UNKNOWN)) { - struct utsname u; - unsigned int major; - - /* - * We purposedly do not use Hostinfo_OSVersion() to avoid introducing a - * library dependency just for a workaround. - */ - - Atomic_ReadIfEqualWriteInt(&workaround, WORKAROUND_UNKNOWN, - ( uname(&u) == -1 - || sscanf(u.release, "%u.", &major) != 1 - || major < 9) ? WORKAROUND_YES : WORKAROUND_NO); - LOG(1, (LGPFX "dup() crash workaround %s\n", - Atomic_ReadInt(&workaround) == WORKAROUND_YES ? "activated" - : "not necessary")); - } - - ASSERT(Atomic_ReadInt(&workaround) != WORKAROUND_UNKNOWN); - - return Atomic_ReadInt(&workaround) == WORKAROUND_YES ? - pthread_mutex_init(&that->mutex, NULL) : 0; -} - - -/* - *----------------------------------------------------------------------------- - * - * SyncWaitQDestroy -- - * - * If the workaround is needed, destroy the wait queue's mutex. - * - * Results: - * None - * - * Side effects: - * None - * - *---------------------------------------------------------------------------- - */ - -static void -SyncWaitQDestroy(SyncWaitQ *that) // IN -{ - ASSERT(Atomic_ReadInt(&workaround) != WORKAROUND_UNKNOWN); - if (Atomic_ReadInt(&workaround) == WORKAROUND_YES) { - int result; - - result = pthread_mutex_destroy(&that->mutex); - ASSERT(!result); - } -} - - -/* - *----------------------------------------------------------------------------- - * - * SyncWaitQLock -- - * - * If the workaround is needed, grab the wait queue's mutex. - * - * Results: - * None - * - * Side effects: - * None - * - *---------------------------------------------------------------------------- - */ - -static void -SyncWaitQLock(SyncWaitQ *that) // IN -{ - ASSERT(Atomic_ReadInt(&workaround) != WORKAROUND_UNKNOWN); - if (Atomic_ReadInt(&workaround) == WORKAROUND_YES) { - int result; - - result = pthread_mutex_lock(&that->mutex); - ASSERT(!result); - } -} - - -/* - *----------------------------------------------------------------------------- - * - * SyncWaitQUnlock -- - * - * If the workaround is needed, release the wait queue's mutex. - * - * Results: - * None - * - * Side effects: - * None - * - *---------------------------------------------------------------------------- - */ - -static void -SyncWaitQUnlock(SyncWaitQ *that) // IN: -{ - ASSERT(Atomic_ReadInt(&workaround) != WORKAROUND_UNKNOWN); - - if (Atomic_ReadInt(&workaround) == WORKAROUND_YES) { - int result; - - result = pthread_mutex_unlock(&that->mutex); - ASSERT(!result); - } -} - -#else - - -/* - *----------------------------------------------------------------------------- - * - * SyncWaitQ{Init,Destroy,Lock,Unlock} -- - * - * Non-OS X version of dup() crash workaround. - * - * Results: - * Init: 0, success - * Lock,Unlock: None. - * - * Side effects: - * None - * - *---------------------------------------------------------------------------- - */ - -static INLINE int SyncWaitQInit(SyncWaitQ *dummy) { return 0; } -static INLINE void SyncWaitQDestroy(SyncWaitQ *dummy) { } -static INLINE void SyncWaitQLock(SyncWaitQ *dummy) { } -static INLINE void SyncWaitQUnlock(SyncWaitQ *dummy) { } - -#endif - - -/* - *----------------------------------------------------------------------------- - * - * SyncWaitQPanicOnFdLimit -- - * - * Panic if 'error' corresponds to an fd limit being reached. See bug - * 72108. Just like an out-of-memory condition, an out-of-fd condition is - * pretty much unrecoverable. The only thing we can do is help our users - * diagnose the problem. - * - * Results: - * None - * - * Side effects: - * None - * - *---------------------------------------------------------------------------- - */ - -static void -SyncWaitQPanicOnFdLimit(int error) // IN: -{ - switch (error) { - case EMFILE: - Panic("%s: Too many file descriptors are in use by the process.\n", - __FUNCTION__); - break; - - case ENFILE: - Panic("%s: The system limit on the total number of open files has " - "been reached.\n", __FUNCTION__); - break; - - default: - break; - } -} - - -/* - *----------------------------------------------------------------------------- - * - * SyncWaitQCreateNonBlockingPipe -- - * - * Creates non-blocking pipe. - * - * Results: - * FALSE on sucess. - * TRUE on failure. - * - * Side effects: - * Panics if EMFILE or ENFILE are hit. - * - *---------------------------------------------------------------------------- - */ - -static Bool -SyncWaitQCreateNonBlockingPipe(int fd[2]) // OUT -{ - if (pipe(fd) < 0) { - int error = errno; - - LOG(0, (LGPFX "Could not create pipe pair: %s (%d)\n", - strerror(error), error)); - SyncWaitQPanicOnFdLimit(error); - return TRUE; - } - - if (fcntl(fd[0], F_SETFL, O_RDONLY | O_NONBLOCK) < 0 || - fcntl(fd[1], F_SETFL, O_WRONLY | O_NONBLOCK) < 0) { - int error = errno; - - Warning("%s: fcntl failed, %s (%d)\n", __FUNCTION__, strerror(error), error); - close(fd[1]); - close(fd[0]); - return TRUE; - } - return FALSE; -} - - -/* - *---------------------------------------------------------------------- - * - * SyncWaitQ_Init -- - * - * Initializes a waitqueue structure. - * - * On Win32 the 'path' is the name of the waitqueue in the kernel - * namespace (actually a Win32 event). - * - * On Posix the 'path' is the file path to a fifo. The fifo - * object itself does not have to exist, but its parent directory - * must exist. - * - * If 'path' is NULL, then the waitqueue object is anonymous - * - * It is illegal for the 'path' argument to be empty string. - * - * Results: - * TRUE on success and FALSE otherwise. - * - * Side effects: - * On Posix, when named creates a fifo. - * - *---------------------------------------------------------------------- - */ - -Bool -SyncWaitQ_Init(SyncWaitQ *that, // OUT: - char const *path) // IN/OPT: -{ - ASSERT(that); - ASSERT(!path || path[0]); - - memset(that, 0, sizeof(SyncWaitQ)); - - if (!path) { - /* - * Anonymous - */ - - int fd = eventfd(0, EFD_NONBLOCK); - - if (fd >= 0) { - LOG(3, (LGPFX "Queue %p uses event fd %d\n", that, fd)); - that->usesEventFd = TRUE; - if (SyncWaitQInit(that)) { - close(fd); - return FALSE; - } - Atomic_Write32(&that->u.eventHandle, fd); - } else if (errno != ENOSYS && errno != EINVAL) { - int error = errno; - - LOG(2, (LGPFX "Could not allocate event fd for %p: %s (%d)\n", - that, strerror(error), error)); - SyncWaitQPanicOnFdLimit(error); - return FALSE; - } else { - HandlesAsI64 rwHandles; - - if (SyncWaitQCreateNonBlockingPipe(rwHandles.fd)) { - return FALSE; - } - - LOG(3, (LGPFX "Queue %p uses pair of pipes, %d & %d\n", that, - rwHandles.fd[0], rwHandles.fd[1])); - - if (SyncWaitQInit(that)) { - close(rwHandles.fd[0]); - close(rwHandles.fd[1]); - return FALSE; - } - - Atomic_Write64(&that->u.pipeHandles64, rwHandles.i64); - } - } else { - /* - * Named - */ - - LOG(3, (LGPFX "Queue %p uses base name %s\n", that, path)); - that->pathName = Util_SafeStrdup(path); - } - - that->initialized = TRUE; - return TRUE; -} - - -/* - *---------------------------------------------------------------------- - * - * SyncWaitQMakeName -- - * - * Computes the name of the named system object based on the - * path of the wait queue and a sequence number - * - *---------------------------------------------------------------------- - */ - -static char * -SyncWaitQMakeName(const char *path, // IN: - uint64 seq) // IN: -{ - return Str_SafeAsprintf(NULL, "%s.%"FMT64"x", path, seq); -} - - -/* - *---------------------------------------------------------------------- - * - * SyncWaitQ_Destroy -- - * - * Destroys the system resources associated with the specified - * waitqueue. The waitqueue structure itself is not freed. - * - * Side effects: - * Closes handles. On Posix, also unlinks the fifo associated - * with this waitqueue. - * - *---------------------------------------------------------------------- - */ - -void -SyncWaitQ_Destroy(SyncWaitQ *that) // IN: -{ - if (!that->initialized) { - LOG(0, (LGPFX "Uninitialized queue %p is being destroyed!\n", that)); - return; - } - - if (that->pathName == NULL) { - /* - * Anonymous - */ - - if (that->usesEventFd) { - int fd; - int err; - - fd = Atomic_Read32(&that->u.eventHandle); - LOG(3, (LGPFX "Destroying queue %p with event fd %d\n", that, fd)); - err = close(fd); - ASSERT(err == 0); - } else { - HandlesAsI64 rwHandles; - - rwHandles.i64 = Atomic_Read64(&that->u.pipeHandles64); - LOG(3, (LGPFX "Destroying queue %p with pipe pair %d, %d\n", - that, rwHandles.fd[0], rwHandles.fd[1])); - close(rwHandles.fd[0]); - close(rwHandles.fd[1]); - } - SyncWaitQDestroy(that); - } else { - /* - * Named - */ - - uint64 seq; - char *name; - - seq = Atomic_Read64(&that->seq); - name = SyncWaitQMakeName(that->pathName, seq); - LOG(3, (LGPFX "Destroying queue %p with name %s\n", that, name)); - Posix_Unlink(name); - free(name); - free(that->pathName); - that->pathName = NULL; - } - - that->initialized = FALSE; -} - - -/* - *----------------------------------------------------------------------------- - * - * SyncWaitQ_Add -- - * - * Add a waiter to the waitqueue. - * - * Results: - * On success, a pollable handle (fd on Posix and a HANDLE on Win32) - * that can be used by the caller to determine when the queue has been - * woken up. On failure, -1. - * - *----------------------------------------------------------------------------- - */ - -PollDevHandle -SyncWaitQ_Add(SyncWaitQ *that) // IN: -{ - uint64 seq; - int ret = -1; - char *name = NULL; - - ASSERT(that); - ASSERT(that->initialized); - - /* Hint that we are about to wait */ - Atomic_Write(&that->waiters, TRUE); - - /* - * The following statement is the demarcation line for Add. Any - * wakeup that happens after this line should wake up this waiter - * -- Ticho - */ - - seq = Atomic_Read64(&that->seq); - - /* - * It is OK to fail in the following 2 paths, because if the sequence - * number has changed, we manufacture our own fd, so any error is harmless. - */ - - if (that->pathName == NULL) { - /* - * Anonymous - */ - - int fd; - - /* - * XXX? - * - * There is an extremely small chance for a side effect to - * unrelated code caused by a race condition here. Please refer - * to the corresponding comment in syncWaitQWin32.c for more - * information - * - * -- Ticho - */ - - if (that->usesEventFd) { - fd = Atomic_Read32(&that->u.eventHandle); - LOG(5, (LGPFX "Adding waiter for queue %p. " - "Current event fd is %d, sequence %"FMT64"u\n", - that, fd, seq)); - } else { - fd = Atomic_Read32(&that->u.pipeHandles[0]); - LOG(5, (LGPFX "Adding waiter for queue %p. " - "Current read pipe fd is %d, sequence %"FMT64"u\n", - that, fd, seq)); - } - SyncWaitQLock(that); - ret = dup(fd); - SyncWaitQUnlock(that); - if (ret < 0) { - int error = errno; - - LOG(5, (LGPFX "Could not duplicate file descriptor %d: %s (%d)\n", - fd, strerror(error), error)); - SyncWaitQPanicOnFdLimit(error); - } else { - LOG(5, (LGPFX "Created private read descriptor %d\n", ret)); - } - } else { - /* - * Named - */ - - name = SyncWaitQMakeName(that->pathName, seq); - - /* - * Create fifo object with the generated name - * - * XXX? Note that if the object already exists on the file - * system, we will assume that this is a fifo created by another - * waiter for this wait queue. Of course, this cannot be - * guaranteed 100%, but in practice this will be the case since - * the names of the waitqueues are fairly unique and since in - * practice they will be created in a specially designated - * directory. -- Ticho. - */ - - LOG(5, (LGPFX "Adding waiter for queue %p. " - "Current event name is %s, sequence %"FMT64"u\n", - that, name, seq)); - - ret = Posix_Mkfifo(name, S_IRUSR | S_IWUSR); - if (ret >= 0 || errno == EEXIST) { - /* - * We open in non-blocking mode so that we won't block if nobody - * has opened in write mode. We prefer to block in poll, because - * we can block on several wait queues, and we can have a - * timeout without using signals --hpreg - */ - - /* - * It is possible that from the time we create the fifo to the - * time it is opened, the wait queue was woken up and the fifo - * was unlinked from the file system. That is OK because we - * detect this case and will not use the fd returned from open() - * -- Ticho. - */ - - ret = Posix_Open(name, O_RDONLY | O_NONBLOCK); - if (ret < 0) { - int error = errno; - - LOG(5, (LGPFX "Could not open named pipe: %s (%d)\n", - strerror(error), error)); - SyncWaitQPanicOnFdLimit(error); - } else { - LOG(5, (LGPFX "Created private fd %d for named pipe\n", - ret)); - } - } else { - LOG_ONLY(int error = errno;) - - LOG(5, (LGPFX "Problem when creating named pipe: %s (%d)\n", - strerror(error), error)); - } - } - - /* - * Check to see whether someone didn't wake us up while we were - * adding ourselves to the queue - */ - - if (seq != Atomic_Read64(&that->seq)) { - /* - * Someone woke up the queue while we were adding ourselves to it, so - * just pretend that we were woken up too by returning a conjured up, - * woken up handle - */ - - LOG(5, (LGPFX "Sequence number changed on queue %p\n", that)); - - if (ret >= 0) { - close(ret); - if (that->pathName != NULL) { - Posix_Unlink(name); - } - } - - /* Create event with initial value of 1. Any non-zero value is good. */ - ret = eventfd(1, EFD_NONBLOCK); - if (ret >= 0) { - /* We are done... */ - LOG(5, (LGPFX "Created dummy event fd %d\n", ret)); - } else if (errno != ENOSYS && errno != EINVAL) { - int error = errno; - - LOG(0, (LGPFX "Could not create dummy event fd: %s (%d)\n", - strerror(error), error)); - SyncWaitQPanicOnFdLimit(error); - ASSERT(ret == -1); - } else { - int fd[2]; - - if (SyncWaitQCreateNonBlockingPipe(fd)) { - free(name); - return -1; - } - - LOG(5, (LGPFX "Created dummy pipe pair %d, %d\n", fd[0], fd[1])); - - if (write(fd[1], "X", 1) == 1) { - /* - * fd[0] now should be in a perpetual woken up state. It will be - * closed when the client code calls SyncWaitQ_Remove() on it. - */ - - ret = fd[0]; - } else { - close(fd[0]); - ret = -1; - } - - close(fd[1]); - } - } else { - if (ret < 0) { - free(name); - return -1; - } - - /* - * It is possible that another thread woke up the queue and set - * waiters=FALSE, even if we didn't detect that the sequence - * number has changed. This can happen like so - * - * (T1=this thread, T2=wakeup thread): - * - * T1: Set waiters=TRUE - * T2: Set waiters=FALSE - * T2: Create and save new wakeup handle - * T1: Read seq - * T1: Dup new wakeup handle - * T1: compare seq (they match, so the thread won't wake itself up) - * - * At this point T1 has a new event handle that hasn't been - * woken up, but 'waiters' is FALSE. To prevent that case, we - * set waiters=TRUE once again here. - * - * Note that there is no harm, as far as correctness is - * concerned in setting waiters=TRUE anywhere - * - * -- Ticho 09/11/2003 - * - */ - - Atomic_Write(&that->waiters, TRUE); - } - - free(name); - - LOG(4, (LGPFX "New waiter on queue %p: %d\n", that, ret)); - - return ret; -} - - -/* - *---------------------------------------------------------------------- - * - * SyncWaitQ_Remove -- - * - * Removes the caller from the waitqueue. The caller must provide - * the handle which it originally obtained via a call to - * SyncWaitQ_Add - * - * Results: - * TRUE on success and FALSE otherwise - * - * Side effects: - * The caller supplied handle is closed - * - *---------------------------------------------------------------------- - */ - -Bool -SyncWaitQ_Remove(SyncWaitQ *that, // Unused - PollDevHandle handle) // IN: -{ - ASSERT(that); - if (!that->initialized) { - LOG(0, (LGPFX "Poll handle %d on queue %p is released after queue is destroyed\n", - handle, that)); - return FALSE; - } - - /* - * The anonymous and named case are the same. -- Ticho - */ - - LOG(4, (LGPFX "Destroying waiter %d on queue %p\n", handle, that)); - - return close(handle) >= 0; -} - - -/* - *----------------------------------------------------------------------------- - * - * SyncWaitQ_WakeUp -- - * - * Wakes up all waiters (if any) by making their pollable handles - * signalled. - * - * Note that there are no provisions for the handles previously - * given to the waiters to be made unsignalled and used again for - * waiting. If an ex-waiter wants to wait again, it will have to - * first call SyncWaitQ_Remove and then SyncWaitQ_Add again. The - * latter will give it a fresh pollable handle to wait on. - * - * Results: - * TRUE on success - * FALSE on failure - * - * Side effects: - * None - * - *---------------------------------------------------------------------------- - */ - -Bool -SyncWaitQ_WakeUp(SyncWaitQ *that) // IN: -{ - ASSERT(that); - ASSERT(that->initialized); - - if (!Atomic_Read(&that->waiters)) { - /* Fast path --hpreg */ - LOG(4, (LGPFX "Waking up queue %p: no waiters\n", that)); - return TRUE; - } - - Atomic_Write(&that->waiters, FALSE); - - /* - * Slow path --hpreg - */ - - return (that->pathName == NULL) ? SyncWaitQWakeUpAnon(that) : - SyncWaitQWakeUpNamed(that); -} - - -/* - *----------------------------------------------------------------------------- - * - * SyncWaitQWakeUpAnon -- - * - * Anonymous version of SyncWaitQ_WakeUp - * - * Results: - * TRUE on success - * FALSE on failure - * - * Side effects: - * None - * - *---------------------------------------------------------------------------- - */ - -static Bool -SyncWaitQWakeUpAnon(SyncWaitQ *that) // IN: -{ - if (that->usesEventFd) { - int fd; - int ret; - int error; - - fd = eventfd(0, EFD_NONBLOCK); - if (fd < 0) { - int error = errno; - - LOG(1, (LGPFX "Could not create event fd while waking up queue %p: %s (%d)\n", - that, strerror(error), error)); - ASSERT(error != ENOSYS && errno != EINVAL); - SyncWaitQPanicOnFdLimit(error); - return FALSE; - } - LOG(4, (LGPFX "Queue %p woken up. New event fd is %d\n", that, fd)); - fd = Atomic_ReadWrite32(&that->u.eventHandle, fd); - Atomic_Inc64(&that->seq); - ret = eventfd_write(fd, 1); - error = errno; - SyncWaitQLock(that); - close(fd); - SyncWaitQUnlock(that); - if (ret != 0) { - Warning("%s: eventfd_write failed, %s (%d)\n", - __FUNCTION__, strerror(ret), error); - return FALSE; - } - } else { - HandlesAsI64 rwHandles, wakeupHandles; - ssize_t ret; - int error; - - /* Create the new anonymous handle. */ - if (SyncWaitQCreateNonBlockingPipe(rwHandles.fd)) { - return FALSE; - } - - /* - * The following statement is the demarcation line for wakeup - * - * There is a possibility for suprious wakeups if the WaitQ_Add - * began executing after this line but before the inc of the sequence. - * - * We assume that spurious wakups are OK. - */ - - LOG(4, (LGPFX "Queue %p woken up. New pipe pair is %d, %d\n", - that, rwHandles.fd[0], rwHandles.fd[1])); - wakeupHandles.i64 = Atomic_ReadWrite64(&that->u.pipeHandles64, rwHandles.i64); - Atomic_Inc64(&that->seq); - - ret = write(wakeupHandles.fd[1], "X", 1); - error = errno; - close(wakeupHandles.fd[1]); - SyncWaitQLock(that); - close(wakeupHandles.fd[0]); - SyncWaitQUnlock(that); - if (ret != 1) { - if (ret < 0) { - Warning("%s: write to pipe failed, %s (%d)\n", - __FUNCTION__, strerror(error), error); - } else { - Warning("%s: write to pipe failed, %"FMTSZ"d bytes written\n", - __FUNCTION__, ret); - } - return FALSE; - } - } - return TRUE; -} - - -/* - *----------------------------------------------------------------------------- - * - * SyncWaitQWakeUpNamed -- - * - * Named version of SyncWaitQ_WakeUp - * - * Results: - * TRUE on success - * FALSE on failure - * - * Side effects: - * None - * - *---------------------------------------------------------------------------- - */ - -static Bool -SyncWaitQWakeUpNamed(SyncWaitQ *that) // IN: -{ - uint64 seq; - char *name; - int wakeupHandle = -1; - ssize_t ret; - int error; - - /* The following statement is the demarcation line for wakeup */ - seq = Atomic_FetchAndInc64(&that->seq); - name = SyncWaitQMakeName(that->pathName, seq); - - /* - * We open in non-blocking mode so that we won't block if there is - * no reader, instead we will get ENXIO. Another way would be to - * open in non-blocking read-write mode, but it adds an extra close - * system call. - */ - - wakeupHandle = Posix_Open(name, O_WRONLY | O_NONBLOCK); - error = errno; - Posix_Unlink(name); - free(name); - - if (wakeupHandle < 0) { - SyncWaitQPanicOnFdLimit(error); - - /* - * If error == ENXIO or ENOENT, then there are no waiters, so - * the wakeup is considered successful. - */ - - if (error == ENXIO || error == ENOENT) { - LOG(4, (LGPFX "No waiters while waking up queue %p " - "using name %s and sequence %"FMT64"u\n", - that, that->pathName, seq)); - return TRUE; - } - - Warning("%s: open failed, %s (%d)\n", __FUNCTION__, strerror(error), error); - return FALSE; - } - - ret = write(wakeupHandle, "X", 1); - error = errno; - close(wakeupHandle); - if (ret != 1) { - if (ret < 0) { - if (error == EPIPE) { - /* - * If a waiter was signalled by another thread and the waiter - * just closed the read end of the pipe, we may get an EPIPE - * error. That's OK, because the waiter was already woken up. - */ - - LOG(4, (LGPFX "Waiter disappeared while waking up queue %p " - "using name %s and sequence %"FMT64"u\n", - that, that->pathName, seq)); - return TRUE; - } - Warning("%s: write failed, %s (%d)\n", __FUNCTION__, - strerror(error), error); - } else { - Warning("%s: write failed, %"FMTSZ"d bytes written\n", __FUNCTION__, ret); - } - return FALSE; - } - LOG(4, (LGPFX "Waiters woken up on queue %p for name %s and sequence %"FMT64"u\n", - that, that->pathName, seq)); - return TRUE; -}