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]);
    }
}

Reply via email to