here's the tiny test program I wrote that shows the incorrect behavior of apr_file_lock vs. flock :
if you compile with "gcc testlock.c -I/usr/include/apr-1.0 -lapr-1", the output is: thread aquired lock thread aquired lock thread released lock thread released lock adding -DUSE_FLOCK to the compilation uses flock instead of apr_file_lock, the expected output is: thread aquired lock thread released lock thread aquired lock thread released lock As the file is never closed, I would say that either the apr documentation or implementation is flawed for apr_file_lock. best regards, thomas On Thu, Sep 29, 2011 at 17:55, Ben Noordhuis <i...@bnoordhuis.nl> wrote: > On Thu, Sep 29, 2011 at 16:54, thomas bonfort <thomas.bonf...@gmail.com> > wrote: >> Hi all, sorry in advance if this is a dumb question. >> >> The apr documentation for apr_file_lock states "Locks are established >> on a per-thread/process basis; a second lock by the same thread will >> not block." but this is not the behavior I am seeing. As apr_file_lock >> on unix uses fcntl by default, a second lock by another thread of the >> same process will not lock either. > > You're probably running into (POSIX mandated!) behaviour that requires > that when a process closes a file descriptor for file X, *all* locks > for X held by that process are released. > > Absolutely brain dead. I can't begin to fathom the mind that thought it up. > >> I was using apr_file_lock as I need all my httpd threads/process to be >> synchronized on an named ressource, and chose to create a lockfile >> who's filename matches my named ressource. This does not work as with >> a multi-threaded mpm the threads of the same process that created the >> lockfile will not block on the call to apr_file_lock call. >> >> From my readings, it seems that file locking is a hazardous task to >> get right, so what are my options to attain my goal: >> >> - use my own implementation mimicking apr_file_lock, but that >> unconditionnaly uses flock() instead of fcntl() ? I suspect that this >> would not be a safe solution as some platforms fall back to fcntl for >> flock. > > flock() is not available on SunOS and it has nasty fork() semantics: > process acquires lock, forks, child releases lock, parent loses lock > (without getting told). Once again, brain dead. > > You also cannot rely on it working correctly (or at all) on NFS > mounts. That's not really flock()'s fault, it's a shortcoming of the > NFS protocol. fcntl() and lock() have the same issue. > > In my experience, the most reliable and portable approach is to create > a lock file with open(O_CREAT|O_EXCL) that you unlink() afterwards. On > EEXIST, sleep for a bit and try again. > >> - I tried using a posix semaphore which worked quite well, except in >> the cases where either the process crashed or was terminated by httpd >> because of a Timeout, and in that case the semaphore is never released >> until a server reboot or manually messing in /dev/shm. If I attach a >> cleanup call to the request pool, will it be called in the case where >> the process is terminated after the Timeout delay ? > > I don't think you can guarantee that your cleanup action always runs. > If a worker process hangs, the master will eventually send it a > SIGKILL. >
#include <apr_file_io.h> #include <apr_thread_proc.h> #include <time.h> #include <sys/file.h> apr_pool_t *pool; static void* APR_THREAD_FUNC lock_thread(apr_thread_t *thread, void *data) { #ifdef USE_FLOCK int f = open("/tmp/test.lck",O_RDWR); flock(f,LOCK_EX); printf("thread aquired lock\n"); sleep(2); flock(f,LOCK_UN); printf("thread released lock\n"); close(f); #else apr_file_t *f; apr_file_open(&f,"/tmp/test.lck",APR_WRITE|APR_CREATE,APR_OS_DEFAULT,pool); apr_file_lock(f,APR_FLOCK_EXCLUSIVE); printf("thread aquired lock\n"); sleep(2); apr_file_unlock(f); printf("thread released lock\n"); apr_file_close(f); #endif } int main(int argc, char **argv) { apr_thread_t **threads; apr_threadattr_t *thread_attrs; int n; apr_status_t rv; apr_initialize(); apr_pool_create(&pool,NULL); apr_threadattr_create(&thread_attrs, pool); threads = (apr_thread_t**)apr_pcalloc(pool, 2*sizeof(apr_thread_t*)); for(n=0;n<2;n++) { apr_thread_create(&threads[n], thread_attrs, lock_thread, NULL, pool); sleep(1); } for(n=0;n<2;n++) { apr_thread_join(&rv, threads[n]); } }