Re: How to wait on a global lock with timeout
Hi Yann, thanks for your explanation. Am 16.05.2015 um 19:05 schrieb Yann Ylavic: Trying to get locked mutex... timed out after 1 second (expected) Child trying to unlock the mutex got 1 (unexpected) Trying to get locked mutex... timed out after 2 seconds (unexpected) Did I do anything wrong? A mutex must be unlocked by the thread which locked it (error 1 is EPERM). Ok, makes sense to me. Regards, Micha
Re: How to wait on a global lock with timeout
Hi Micha, On Sat, May 16, 2015 at 5:22 PM, Micha Lenk mi...@lenk.info wrote: Sorry, took me a while to give your patch a try. As the patch didn't apply cleanly to trunk (ie. SVN rev. 1676013), I assume that it is already applied in trunk. So I went ahead without applying your patch. Is this correct? Yes, links point to svn commits in trunk. I wrote a small test program that should prove me that timed global locks work across process boundaries (see attached source). But for some reason apr_global_mutex_unlock() return 1 if called in the child process. This is the output that I get from the compiled test program: Trying to get locked mutex... timed out after 1 second (expected) Child trying to unlock the mutex got 1 (unexpected) Trying to get locked mutex... timed out after 2 seconds (unexpected) Did I do anything wrong? A mutex must be unlocked by the thread which locked it (error 1 is EPERM). Thus you can: lock() if fork() == 0: lock() else: unlock() but not: lock() if fork() == 0: unlock() else: lock() Regards, Yann.
Re: How to wait on a global lock with timeout
Hi Yann, Am 25.03.2015 um 09:56 schrieb Yann Ylavic: To mitigate that design flaw I would provide the timeout by reference and update it by the functions using it. This has also the nice benefit that the caller is able to retrieve the time it needed to wait. By doing so, you have to get the current time (call apr_time_now()) each time, and I wanted to avoid it when the native functions don't need it. The remaining time is not important if you can pass an absolute time (IMHO). Ok, understood. You're right. Sorry, took me a while to give your patch a try. As the patch didn't apply cleanly to trunk (ie. SVN rev. 1676013), I assume that it is already applied in trunk. So I went ahead without applying your patch. Is this correct? I wrote a small test program that should prove me that timed global locks work across process boundaries (see attached source). But for some reason apr_global_mutex_unlock() return 1 if called in the child process. This is the output that I get from the compiled test program: Trying to get locked mutex... timed out after 1 second (expected) Child trying to unlock the mutex got 1 (unexpected) Trying to get locked mutex... timed out after 2 seconds (unexpected) Did I do anything wrong? Best regards, Micha #include assert.h #include stdio.h #include unistd.h #include apr.h #include apr_pools.h #include apr_global_mutex.h #define LOCKFILE /tmp/apr-synctest-lockfile int main(int argc, const char *argv[]) { apr_pool_t *pool = NULL; apr_global_mutex_t *mutex = NULL; apr_time_t timeout; apr_status_t rv; pid_t child; int child_rc; int rc = 0; assert(apr_initialize() == APR_SUCCESS); assert(apr_pool_create(pool, NULL) == APR_SUCCESS); apr_global_mutex_create(mutex, LOCKFILE, APR_LOCK_DEFAULT_TIMED, pool); /* * LOCK */ apr_global_mutex_lock(mutex); timeout = apr_time_from_sec(1); printf(Trying to get locked mutex...); rv = apr_global_mutex_timedlock(mutex, timeout, 0); if (rv == APR_TIMEUP) { printf( timed out after 1 second (expected)\n); } else { printf( FAILED: %d (unexpected)\n, rv); rc = 1; goto cleanup; } child = fork(); if (child == 0) { // child process assert(apr_global_mutex_child_init(mutex, LOCKFILE, pool) == APR_SUCCESS); (void)sleep(1); /* * UNLOCK in forked child */ rv = apr_global_mutex_unlock(mutex); if (rv != APR_SUCCESS) { rc = 1; printf(Child trying to unlock the mutex got %d (unexpected)\n, rv); } } else if (child 0) { // parent process timeout = apr_time_from_sec(2); printf(Trying to get locked mutex...); rv = apr_global_mutex_timedlock(mutex, timeout, 0); if (rv == APR_SUCCESS) { printf( OK (expected)\n); } else if (rv == APR_TIMEUP) { printf( timed out after 2 seconds (unexpected)\n); rc = 1; } else { printf( FAILED: %d (unexpected)\n, rv); rc = 1; } wait(child_rc); if (rc == 0) rc = child_rc; } else { // fork() failed printf(failed to fork(): %m); rc = 1; goto cleanup; } cleanup: apr_pool_destroy(pool); apr_terminate(); return rc; }
Re: How to wait on a global lock with timeout
Hi Yann, Am 20.03.2015 um 02:59 schrieb Yann Ylavic: a few times later, commited in [1] and [2] :) Wow, thanks for coming back to that issue. Can you please check if it works for you? I only had time to review the code changes. I looked at the implementation of apr_global_mutex_timedlock(). There I noticed that, if APR_HAS_THREADS, you are doing 3 timed operations without checking the remaining time. If all operations take almost the given timeout, this can in the end result in almost 3 times the allowed value. To mitigate that design flaw I would provide the timeout by reference and update it by the functions using it. This has also the nice benefit that the caller is able to retrieve the time it needed to wait. I hope to have some time to check out the new functions in my code soon. I also attached the patch against 1.6.x, if that can help... It should also apply to 1.5.x, though it will never be backported there (due to API changes). Backports are not needed. Thanks for offering. Regards, Micha
Re: How to wait on a global lock with timeout
Hi Micha, thanks for the review. On Wed, Mar 25, 2015 at 7:19 AM, Micha Lenk mi...@lenk.info wrote: I only had time to review the code changes. I looked at the implementation of apr_global_mutex_timedlock(). There I noticed that, if APR_HAS_THREADS, you are doing 3 timed operations without checking the remaining time. If all operations take almost the given timeout, this can in the end result in almost 3 times the allowed value. The new _timedlocked() functions all take a third parameter (called absolute) which tells whether the given timeout is absolute (1) or relative (0). This is because some native implementations use an absolute time, and others a relative one, hence we do the conversion in the implementations when needed only. In apr_global_mutex_timedlock(), if the given timeout is relative, we force it to an absolute one and call apr_thread_mutex_timedlock() (if needed) and apr_proc_mutex_timedlock() accordingly, hence the timeout is assured globally. To mitigate that design flaw I would provide the timeout by reference and update it by the functions using it. This has also the nice benefit that the caller is able to retrieve the time it needed to wait. By doing so, you have to get the current time (call apr_time_now()) each time, and I wanted to avoid it when the native functions don't need it. The remaining time is not important if you can pass an absolute time (IMHO). Regards, Yann.
Re: How to wait on a global lock with timeout
Hi Micha, a few times later, commited in [1] and [2] :) Can you please check if it works for you? I also attached the patch against 1.6.x, if that can help... It should also apply to 1.5.x, though it will never be backported there (due to API changes). Thanks for your interest, Yann. [1] http://svn.apache.org/r1667900 [2] http://svn.apache.org/r1667901 On Tue, Sep 30, 2014 at 8:22 PM, Micha Lenk mi...@lenk.info wrote: Hi Yann, On 30.09.2014 18:16, Yann Ylavic wrote: On Tue, Sep 30, 2014 at 5:30 PM, Yann Ylavic ylavic@gmail.com wrote: I have been working on a patch to provide apr_[thread/proc]_mutex_timedlock() in APR, [...] I think that is exactly what I was looking for... Your idea to use the native mutex functions is probably better than nothing. It provides the needed functionality at least for some platforms. I think I'll give my patch a new chance, at least for proc-mutexes (and hence global-mutexes), but this is not a today's solution in any case since I would be at best an APR-1.6 feature (if accepted)... That would be great. I am looking forward to test that, as soon as available... ;) Regards, Micha Index: configure.in === --- configure.in (revision 1667923) +++ configure.in (working copy) @@ -2137,11 +2137,18 @@ AC_SUBST(struct_rlimit) dnl - Checking for Locking Characteristics echo ${nl}Checking for Locking... -AC_CHECK_FUNCS(semget semctl flock) -AC_CHECK_HEADERS(semaphore.h OS.h) +AC_CHECK_FUNCS(semget semctl semop semtimedop flock) +APR_IFALLYES(func:semtimedop, have_semtimedop=1, have_semtimedop=0) + +AC_CHECK_HEADERS(semaphore.h) AC_SEARCH_LIBS(sem_open, rt) -AC_CHECK_FUNCS(sem_close sem_unlink sem_post sem_wait create_sem) +AC_CHECK_FUNCS(sem_close sem_unlink sem_post sem_wait sem_timedwait) +APR_IFALLYES(func:sem_timedwait, have_sem_timedwait=1, have_sem_timedwait=0) +AC_CHECK_HEADERS(OS.h) +AC_CHECK_FUNCS(create_sem acquire_sem acquire_sem_etc) +APR_IFALLYES(header:OS.h func:acquire_sem_etc, have_acquire_sem_etc=1, have_acquire_sem_etc=0) + # Some systems return ENOSYS from sem_open. AC_CACHE_CHECK(for working sem_open,ac_cv_func_sem_open,[ AC_TRY_RUN([ @@ -2199,7 +2206,10 @@ APR_CHECK_DEFINE_FILES(POLLIN, poll.h sys/poll.h) if test $threads = 1; then APR_CHECK_DEFINE(PTHREAD_PROCESS_SHARED, pthread.h) -AC_CHECK_FUNCS(pthread_mutexattr_setpshared) +AC_CHECK_FUNCS(pthread_mutex_timedlock pthread_mutexattr_setpshared) +APR_IFALLYES(header:pthread.h func:pthread_mutex_timedlock, + have_pthread_mutex_timedlock=1, have_pthread_mutex_timedlock=0) +AC_SUBST(have_pthread_mutex_timedlock) # Some systems have setpshared and define PROCESS_SHARED, but don't # really support PROCESS_SHARED locks. So, we must validate that we # can go through the steps without receiving some sort of system error. @@ -2237,8 +2247,8 @@ fi APR_IFALLYES(header:semaphore.h func:sem_open func:sem_close dnl func:sem_unlink func:sem_post func:sem_wait, hasposixser=1, hasposixser=0) -APR_IFALLYES(func:semget func:semctl define:SEM_UNDO, hassysvser=1, - hassysvser=0) +APR_IFALLYES(func:semget func:semctl func:semop define:SEM_UNDO, + hassysvser=1, hassysvser=0) APR_IFALLYES(func:flock define:LOCK_EX, hasflockser=1, hasflockser=0) APR_IFALLYES(header:fcntl.h define:F_SETLK, hasfcntlser=1, hasfcntlser=0) # note: the current APR use of shared mutex requires /dev/zero @@ -2263,9 +2273,9 @@ APR_IFALLYES(func:flock define:LOCK_EX, APR_DECIDE(USE_FLOCK_SERIALIZE, [4.2BSD-style flock()])) APR_IFALLYES(header:fcntl.h define:F_SETLK, APR_DECIDE(USE_FCNTL_SERIALIZE, [SVR4-style fcntl()])) -APR_IFALLYES(func:semget func:semctl define:SEM_UNDO, +APR_IFALLYES(func:semget func:semctl func:semop define:SEM_UNDO, APR_DECIDE(USE_SYSVSEM_SERIALIZE, [SysV IPC semget()])) -APR_IFALLYES(header:OS.h func:create_sem, +APR_IFALLYES(header:OS.h func:create_sem func:acquire_sem func:acquire_sem_etc, APR_DECIDE(USE_BEOSSEM, [BeOS Semaphores])) if test x$apr_lock_method != x; then APR_DECISION_FORCE($apr_lock_method) Index: include/apr_global_mutex.h === --- include/apr_global_mutex.h (revision 1667923) +++ include/apr_global_mutex.h (working copy) @@ -29,6 +29,7 @@ #if APR_PROC_MUTEX_IS_GLOBAL #include apr_proc_mutex.h #endif +#include apr_time.h #ifdef __cplusplus extern C { @@ -66,6 +67,7 @@ typedef struct apr_global_mutex_t apr_global_mutex *APR_LOCK_POSIXSEM *APR_LOCK_PROC_PTHREAD *APR_LOCK_DEFAULT pick the default mechanism for the platform + *APR_LOCK_DEFAULT_TIMED pick the default timed mechanism * /PRE * @param pool the pool from which to allocate the mutex. * @warning Check APR_HAS_foo_SERIALIZE
Re: How to wait on a global lock with timeout
Hi Micha, On Mon, Sep 29, 2014 at 3:18 PM, Micha Lenk mi...@lenk.info wrote: in an Apache module I am in the need to wait for a global lock (e.g. an apr_global_mutex_t), but in theory the lock might not get released by the other process in a timely manner, so I would like to limit the time to wait for the lock. What do you suggest me to do? I have been working on a patch to provide apr_[thread/proc]_mutex_timedlock() in APR, but did not finish the work mostly because of APR_ENOTIMPL on some mutex mechanisms (mainly Windows CRITICAL_SECTIONs which lack the functionality, making the APR abstraction quite useless IMO, or at least unix specific). Maybe I'll have a new look at it these days if I have the time to. For now you may have the possibility to use native mutex functions (eg. pthread_mutex_timedlock or WaitForSingleObject) with the native object retrieved by ap_os_[thread/proc]_mutex_get(), but this requires either to create the mutex with a mechanism supporting the timeout, or the existing mutex to have such a mechanism. Hence this may be non-portable code, or with many APR_HAS_*_SERIALIZE, and still the problem with platforms that don't support it... Regards, Yann.
Re: How to wait on a global lock with timeout
On Tue, Sep 30, 2014 at 5:30 PM, Yann Ylavic ylavic@gmail.com wrote: I have been working on a patch to provide apr_[thread/proc]_mutex_timedlock() in APR, but did not finish the work mostly because of APR_ENOTIMPL on some mutex mechanisms (mainly Windows CRITICAL_SECTIONs which lack the functionality, making the APR abstraction quite useless IMO, or at least unix specific). Maybe I'll have a new look at it these days if I have the time to. Hmm, this remark about Windows only concerns thread-mutexes, proc-mutexes don't use a CRITICAL_SECTION but a HANDLE (which is then usable with WaitForSingleObject() that accepts a timeout). I think I'll give my patch a new chance, at least for proc-mutexes (and hence global-mutexes), but this is not a today's solution in any case since I would be at best an APR-1.6 feature (if accepted)...
Re: How to wait on a global lock with timeout
Hi Yann, On 30.09.2014 18:16, Yann Ylavic wrote: On Tue, Sep 30, 2014 at 5:30 PM, Yann Ylavic ylavic@gmail.com wrote: I have been working on a patch to provide apr_[thread/proc]_mutex_timedlock() in APR, [...] I think that is exactly what I was looking for... Your idea to use the native mutex functions is probably better than nothing. It provides the needed functionality at least for some platforms. I think I'll give my patch a new chance, at least for proc-mutexes (and hence global-mutexes), but this is not a today's solution in any case since I would be at best an APR-1.6 feature (if accepted)... That would be great. I am looking forward to test that, as soon as available... ;) Regards, Micha
Re: How to wait on a global lock with timeout
I join the line of those waiting for this feature in APR. I really would like to have it available -- Massimo Il 30/Set/2014 18:16 Yann Ylavic ylavic@gmail.com ha scritto: On Tue, Sep 30, 2014 at 5:30 PM, Yann Ylavic ylavic@gmail.com wrote: I have been working on a patch to provide apr_[thread/proc]_mutex_timedlock() in APR, but did not finish the work mostly because of APR_ENOTIMPL on some mutex mechanisms (mainly Windows CRITICAL_SECTIONs which lack the functionality, making the APR abstraction quite useless IMO, or at least unix specific). Maybe I'll have a new look at it these days if I have the time to. Hmm, this remark about Windows only concerns thread-mutexes, proc-mutexes don't use a CRITICAL_SECTION but a HANDLE (which is then usable with WaitForSingleObject() that accepts a timeout). I think I'll give my patch a new chance, at least for proc-mutexes (and hence global-mutexes), but this is not a today's solution in any case since I would be at best an APR-1.6 feature (if accepted)...