Reputation: 71
I'm trying to lock some critical resources that are accessed by multiple applications under linux.
All the applications will call the acquireLock function on the same file when entering the critical section, and the releaseLock when leaving. If the lock is not acquired for more than timeot the caller will go ahead doing something else.
The code below works whit slow processes, but under stress the lock is easily broken the lock is acquired by multiple processes, so I guess I'm stumbling in a race condition somewhere.
Can somebody point me out why it's not working and what would be the correct implementation?
Thanks a lot!
MV
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/file.h>
//************************************************************
#define CYCLETIME 1000
//************************************************************
//************************************************************
int acquireLock(char *lockFile, int msTimeout)
{
int lockFd;
int cntTimeout = 0;
if ((lockFd = open(lockFile, O_CREAT | O_RDWR, S_IRWXU | S_IRWXG | S_IRWXO)) < 0)
return -1;
while (flock(lockFd, LOCK_EX | LOCK_NB) < 0){
usleep(CYCLETIME);
cntTimeout++;
if(cntTimeout >= msTimeout){
return -1;
}
}
return lockFd;
}
//*************************************************************
void releaseLock (int lockFd)
{
flock(lockFd, LOCK_UN);
close(lockFd);
}
//************************************************************
Upvotes: 1
Views: 1784
Reputation: 71
It appears that the mistake was in another part of the code, the lock is working as expected.
I share the code I'm using in case it can be helpful to somebody else.
Those are the locking functions:
/* ----------------------------------------------------------------------- *
* Code derived by the flock.c in the "util-linux" ubuntu package
* by Peter Anvin
* ----------------------------------------------------------------------- */
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/file.h>
#include <sys/time.h>
#include <signal.h>
//************************************************************
static sig_atomic_t timeout_expired = 0;
//************************************************************
static void timeout_handler(int sig)
{
(void)sig;
timeout_expired = 1;
}
//************************************************************
int acquireLock(char *lockFile, int msTimeout)
{
struct itimerval timeout, old_timer;
struct sigaction sa, old_sa;
int err;
int sTimeout = msTimeout/1000;
memset(&timeout, 0, sizeof timeout);
timeout.it_value.tv_sec = sTimeout;
timeout.it_value.tv_usec = ((msTimeout-(sTimeout*1000))*1000);
memset(&sa, 0, sizeof sa);
sa.sa_handler = timeout_handler;
sa.sa_flags = SA_RESETHAND;
sigaction(SIGALRM, &sa, &old_sa);
setitimer(ITIMER_REAL, &timeout, &old_timer);
int lockFd;
int cntTimeout = 0;
if ((lockFd = open(lockFile, O_CREAT | O_RDWR, S_IRWXU | S_IRWXG | S_IRWXO)) < 0)
return -1;
while (flock(lockFd, LOCK_EX))
{
switch( (err = errno) ) {
case EINTR: /* Signal received */
if ( timeout_expired )
setitimer(ITIMER_REAL, &old_timer, NULL); /* Cancel itimer */
sigaction(SIGALRM, &old_sa, NULL); /* Cancel signal handler */
return -1; /* -w option set and failed to lock */
continue; /* otherwise try again */
default: /* Other errors */
return -1;
}
}
setitimer(ITIMER_REAL, &old_timer, NULL); /* Cancel itimer */
sigaction(SIGALRM, &old_sa, NULL); /* Cancel signal handler */
return lockFd;
}
//***************************************************************
void releaseLock (int lockFd)
{
flock(lockFd, LOCK_UN);
close(lockFd);
}
//************************************************************
... and those can be tried by reading and writing a FIFO
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include "lock.h"
#define LOCKED 1
void main(int argc, char **argv)
{
const char *filename;
const char *fifo_name;
const char *message;
int lockfd, fifoHandle;
filename = argv[1];
fifo_name = argv[2];
message = argv[3];
char bufin[1024];
char bufout[1024];
struct stat st;
int bufsize = strlen(message)+1;
int sleeptime = 0;
int j = 0;
if (stat(fifo_name, &st) != 0)
mkfifo(fifo_name, 0666);
while (1){
if (LOCKED)
lockfd=acquireLock(filename, 15000);
if (lockfd==-1)
printf("timeout expired \n");
fifoHandle= open(fifo_name, O_RDWR);
strcpy(bufin, message);
bufin[bufsize-1] = 0x0;
write(fifoHandle, bufin, sizeof(char)*bufsize);
sleeptime = rand() % 100000;
usleep(sleeptime);
read(fifoHandle, &bufout, sizeof(char)*(bufsize+1));
printf("%s - %d \n", bufout, j);
j= j+1;
if (LOCKED)
releaseLock(lockfd);
sleeptime = rand() % 10000;
}
unlink(fifo_name);
return;
}
by sending in two terminals
./locktestFIFO ./lck ./fifo messageA ./locktestFIFO ./lck ./fifo messageB
if LOCKED is not set to 1 the messages will mix up, otherwise the two threads will take and release the resource correctly.
Upvotes: 2