Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 30 Nov 2002 14:48:25 -0800
From:      Terry Lambert <tlambert2@mindspring.com>
To:        Brian Smith <dbsoft@technologist.com>
Cc:        "current@FreeBSD.ORG" <current@FreeBSD.ORG>
Subject:   Re: Are SysV semaphores thread-safe on CURRENT?
Message-ID:  <3DE94039.5E1DEEFC@mindspring.com>
References:  <20021130141711.CIZH19077.mailhost.chi1.ameritech.net@bbs.dbsoft-consulting.com>

next in thread | previous in thread | raw e-mail | index | archive | help
Brian Smith wrote:
> On Mon, 18 Nov 2002 22:05:34 -0800, Terry Lambert wrote:
> >Use mmap of a backing-store file, and then use file locking to
> >do record locking in the shared memory segment.
> 
> Ok, I did this, and it actually works considerably better than
> the SysV shared memory.  However flock() has the same problem
> as the SysV semaphores, where they block the entire process,
> allowing the same deadlock situation to occur.  Has this flock()
> behavior changed in CURRENT?
> 
> It seems like this behavior is much more likely to change than
> the SysV code.

Do you mean "flock()", or do you mean "fcntl(fs, F_SETLKW, ...)"?

If you are using range locks, then you mean fcntl().

That's unfortunate: there's an easy way to convert blocking
file locks into non-blocking, plus a context switch.  I
thought th threads library already did this for you in the
fcntl() wrapper, in /usr/src/lib/libc_r/uthread/uthread_fcntl.c,
but apparently it doesn't.  8-(.


The easy way to do this is to convert the blocking request into
a non-blocking request, with a retry; e.g., where you have a
call to:

    err = fcntl( fd, F_SETLKW, &flock);

Replace it with:

    while( ( err = fcntl( fs, F_SETLK, &flock)) == -1 && errno == EAGAIN) {
	sleep( 1);	/* use nanosleep(), if 1 second is too big */
    }

This will cause the processor to be yielded to other threads for
as long as the lock can't be acquired, an acquisititon will be
retried until it succeeds (effectively, blocking only that thread
in "sleep()").  The difference between F_SETLKW and F_SETLK is why
I suggested the approach in the first place (FWIW).

The cost of doing this is that blocking requests will not be
serviced in FIFO order, as they would if F_SETLKW were being
used.  This may get expensive if you have a highly contended
resource, because you are effectively implementing a low cost
polling to obtain the lock.  The answer to this is that you
are not supposed to use semaphores for highly contended resources,
or if you do, use a spin-lock before you use the semaphore, so
you can fail early at reduced expense.

Probably making the above code into an line function and/or
actually modifiying the _fcntl() implementation in the threads
library is the way to go.


Worse comes to worse, I can give you a kernel patch so that an
fcntl() to assert a blocking lock on a non-blocking fd returns
the EWOULDBLOCK error, with a patch against _fcntl() similar to
the code in _read().

I didn't do that this time, because I don't know how much code
really depends on a lock assert on a non-blocking fd blocking
anyway, and no matter how you slice it, it's still going to
have the same non-FIFO ordering, unless I implemented a FIFO
ordered request queue, as well (it'd have to, to be correct).

-- Terry

To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-current" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?3DE94039.5E1DEEFC>