Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 19 Jul 2001 00:47:45 -0700
From:      Terry Lambert <tlambert2@mindspring.com>
To:        Louis-Philippe Gagnon <louisphilippe@macadamian.com>
Cc:        Julian Elischer <julian@elischer.org>, freebsd-hackers@FreeBSD.ORG
Subject:   Re: flock/pthread bug?
Message-ID:  <3B5690A1.8EDD0812@mindspring.com>
References:  <Pine.BSF.4.21.0107181320030.94740-100000@InterJet.elischer.org> <1fbd01c10fba$499834d0$2964a8c0@macadamian.com>

next in thread | previous in thread | raw e-mail | index | archive | help
Louis-Philippe Gagnon wrote:
> 
> From: "Julian Elischer" <julian@elischer.org>
> > probably you should try :
> >
> >      #define   LOCK_NB        0x04      /* don't block when locking */
> >
> But I do want to block; I just don't want the whole process to block.

You can't block just a thread, since there is no "queue a
lock request" interface, only a "try to get the lock, and
return if you can't" or a "block and wait for the lock".

In other words, flock() is one call that can't really be
reasonably implemented by the threads library, since
there is no way to map it safely, and still guarantee
against an order inversion deadlock.


> > Also if you have shared memory, why not use
> >
> > /* Get a spin lock, handle recursion inline (as the less common case) */


Using the "cmpxchgl" instruction is your best approach;
I personally would not use the whole thing, as Julian
suggested, since I think the SMP synchronization ops
are much, much heavier than they should be.  You don't
need that much overhead, you don't need reentrancy, and
you don't need it to do counting (neither does SMP).

The easiest way to get shared memory is to use mmap() in
both programs on a file, and to madvise() the caching and
cache writeback off.  This is much beeter than SYSV shared
memory (shmat, shmget, etc.), since it is properly resource
tracked, and you will not have a startup order of operation
problem, nor will you have the common problem of trying to
get rid of the segments, should any of your programs exit
abnormally.

> I'd rather have something portable though, and I would
> still like to know if what I was doing should have worked...

The answer is "no".  Threads are not processes, they exist
in a single process context, as they currently exist.  In
the future, what you are doing will work, but will have a
very high comparative overhead to other approaches.

If you read Stevens, you will see that flock() is much
higher overhead than doing semop() calls.

To wrap either, you would need to:

	retry:
		st = try_non_blocking_get();
		if (st == FAIL) {
			yield();
			goto retry;
		}

Note that the "yield" system call is non-standard, and you
will buzz-loop somewhat, depressing your priority.  You
will also have a race window, subject to the "thundering
herd" problem.  You could put a "sleep(1);" in place of
the "yield();" call... this would eliminate the buzzing
(somewhat), but would widen the race window.

Another alternative is to use a multiended pipe, where
you write a single character token down the pipe, and
your programs block waiting for a single character read
to complete (you could use a FIFO or socket, in place of
the pipe).  The point is that only a single reader can
get the character (the "token") at a time.

Unfortuantely, since file I/O is implemented as a
conversion of a blocking call to a non-blocking call
plus a context switch (normal for a threads library),
you still have the "thundering herd" problem.

Further, if your intent was to have the callers serviced
in the order they called the read, you can't: you will
never have a guarantee about who will be permitted to
complete the read first.

So, for FIFO ordering, there's no way for you to solve
the problem; for semop or flock, you can solve the problem
portably with a "sleep - retry" loop, and not burn too
many cycles (but still lose FIFO ordering).

If you absolutely _must_ have a FIFO, then you need to
either wait, or install the Linux threads port (Linux
threads are hideously expensive, compared to the current
FreeBSD threads implementation, since they effectively
create a new process for each thread, just like Linux
does), and use blocking operations using them.

Probably, if you need FIFO ordering, whatever you are
doing would be better implemented as a finite state
automaton, or a "work-to-do" model, where you don't
really care who wins a race in a "thundering herd",
since all of your programs waiting on the condition
are identical.

Worse comes to worse, you should consider implementing
your threads as real seperate processes (and not fake
ones, like in the Linux threads library).

-- Terry

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




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?3B5690A1.8EDD0812>