In order to lock a variable, this is what I did (kind of):
sub lock {
if ( $_[0] ) {
return 0
}
else {
$_[0] += $$;
if ( $_[0] == $$ ) {
return $$;
}
else {
$_[0] -= $$;
return 0;
}
}
}
sub unlock { $_[0] = 0 }
if ( lock( $dbfile->{lock} ) ) {
# I locked the database and now it's mine
}
else {
# somebody got there first...
}
Lock::Mechanism ?
Lock::Variable ?
Re:is += atomic
cog on 2004-09-13T16:50:30
By atomic, you mean the operation concludes in one single step, right?
I haven't verified it, no, but... does it matter?:-| After all, if the result isn't the expected one, changes are undone...
OK, I just looked under perlop... and I couldn't find anything stating that += is not atomic...:-| Re:is += atomic
merlyn on 2004-09-13T21:29:59
Very few things are atomic. By that, I mean there's no way that someone else can get "in the middle" of the operation. But normally, += is implement by "fetching the value, adding the number, then storing the value". That's three steps. You lose, if you lose the CPU between two of those steps.So, it looks good on paper, but will definitely fail in real life. You really need something like flock(), which is atomic (or uses atomic steps at the bottom on which to build).
the sub unlock must be $_[0] -= $$
If not, you could have $_[0] as a negative value, if a different thread unlocks a var while the else part of lock() is running (atfer the +=, before the if)
You must of course ensure that += is atomic, as randal pointed out.
What's your problem with IPC::Semaphore or Thread::Semaphore?
Re:It can fail
cog on 2004-09-13T17:41:36
Actually, the unlock sub is kind of a "force unlock". Hence, the direct attribution to zero:-)
My problem with IPC::Semaphore and Thread::Semaphore is that I didn't think of searching for Semaphore, only for Lock O:-)
But anyway, I don't think the complexity of IPC::Semaphore (not that there's many of it) is needed for such a simple case as the one I've got. I only need to lock one variable in a file, to ensure that the same process doesn't run twice (or more) at the same time. (hum... maybe Semaphore::Simple, or Lock::Simple would have been better choices...)Re:It can fail
melo on 2004-09-13T17:56:50
I really can't recommend that code
:). There are so many dependencies to make it work right: does += flushes the dbfile after the update? is it atomic (locks, read old value, sums with $$, writes new value, unlocks)?
I think you are better off with Fcntl lock stuff...
Re:It can fail
cog on 2004-09-13T18:16:27
does += flushes the dbfile after the update?
At least in my case, it does.
is it atomic?
it doesn't lock and read old value and sums... it simply sums $$ and checks if the resulting value is indeed $$ or not; if not, then something else changed that variable meanwhile... which means we should decrement the variable with $$ again and leave it as it was... It's not an atomic thing... it tries to do it, and then it checks out whether it succeeded or not... and it really doesn't matter if it didn't, because that means some other process is taking care of the job already:-| Re:It can fail
jmm on 2004-09-13T21:52:02
Nope, you can easily have no process take care of the job and leave the value at a non-zero negative number. Two processes can both find a zero start, and both increment. Since += is not atomic, that can leave the lock set to PID1, PID2, or (PID1+PID2). (The first two happen if the processes both start the increment by reading the current value before the other process has written it back - the one that writes it back second "wins".) In the first two cases, as long as the wrong process decides it has failed and decrements the value before the other process gets the check whether it failed they will both decide they have failed and the value will end up at -PIDn (the PID of the "successful" process). In the 3rd case, as long as they both for failure before the other get a chance to decrement, they'll both fail; and depending upon how the decrements overlap the value will be reduced either to 0, PID1, or PID2.