in reply to Logical Conundrum

Neither "while $lock" nor "while ! $lock" are "atomic test and set" operations. You need to gate your critical (one-user-at-a-time) resource with some operation that both notices if a resource is available, and grabs that resource if so. Also, making such a request should block the process at the kernel level.

That's why flock is nice for that, if you have that. I have an example of that in my famous HIGHLANDER article.

-- Randal L. Schwartz, Perl hacker
Be sure to read my standard disclaimer if this is a reply.

Replies are listed 'Best First'.
Re: •Re: Logical Conundrum
by mcogan1966 (Monk) on Dec 01, 2003 at 18:23 UTC
    I've worked this out to something of the following design:
    if (locked) { while (locked) { if (unlocked) { lock print unlock break; } } } else { lock print unlock }
    The only thing I need to make this work is a flock() test to see if it CAN to an exclusive lock (the lock file is not already locked).
      if (unlocked) { lock
      No, you've got a race condition there. Two processes can both see "unlocked" at the same time, then they both "lock" and they think they've got it.

      That's why you need "atomic test and set". There's really no way around it. The thing that checks for unsettedness needs to also set it if it can, in a way that can't be interrrupted or observed by another process/thread/entity. This usually requires operating-system-level locks.

      -- Randal L. Schwartz, Perl hacker
      Be sure to read my standard disclaimer if this is a reply.

      To make merlyn's point clearer, what you need is
      sleep(rand) until lock(exclusive); print close

      The lock() function must return whether it was able to get a lock, and you must not use a separate is_locked() function in this process. Actually, you should never need one at all, because you know when you have the lock and you're not interested in whether someone else has one if you don't have it.

      sleep() is only necessary if your locking function does not block. rand() must be used if this is only one of several locks or other synchronization mechanisms used by multiple processes, because they might otherwise get in a deadlock situation when each process holds a lock on some of the resources but not on others.

      You may also unlock() instead of close() if you know what you're doing, but it has only become safe in recent versions of Perl, and even so it poses the danger that you'll attempt to read data from the file while another process is writing to it. Ideally, you should lock(shared) if you're going to be reading from the file, and the locking mechanism should honour exclusive lock requests from others before your lock degradation request. In that way, no writing process starves, while no reading process gets to read inconsistent data.

      Do not underestimate the complexity of locking. Unfortunately if you don't get it perfect, you might as well not bother at all.

      Makeshifts last the longest.