From: Gustavo Martin Morcuende Date: Sun, 29 May 2016 12:58:53 +0000 (+0200) Subject: LINUX locks: fcntl and flock X-Git-Url: https://git.gumartinm.name/?a=commitdiff_plain;h=9a8fe9f5bb752f6797509d92c76754a0ca5d4332;p=CForFun.git LINUX locks: fcntl and flock --- diff --git a/locks/Makefile b/locks/Makefile new file mode 100644 index 0000000..0a216ce --- /dev/null +++ b/locks/Makefile @@ -0,0 +1,7 @@ +all: locks + +locks: locks.c + gcc -Wall -g -o locks locks.c -lpthread + +clean: + rm -f locks diff --git a/locks/locks.c b/locks/locks.c new file mode 100644 index 0000000..8654b75 --- /dev/null +++ b/locks/locks.c @@ -0,0 +1,256 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "locks.h" + + + +struct sigaction sigintAction; /*Stores the init SIGINT sigaction value*/ +char *fileName; + + + + + + +int main (int argc, char *argv[]) +{ + int c; /*Getopt parameter*/ + /*Default values*/ + struct sigaction sa; /*sig actions values*/ + bool isFileLock = false; + bool isThread = false; + + + opterr = 0; + while ((c = getopt (argc, argv, "p:tf:")) != -1) { + switch (c) { + case 'p': + fileName = optarg; + break; + case 't': + isFileLock = true; + break; + case 'f': + isThread = true; + break; + case '?': + if ((optopt == 'p') || (optopt == 't') || (optopt == 'f')) + fprintf (stderr, "Option -%c requires an argument.\n", optopt); + else if (isprint (optopt)) + fprintf (stderr, "Invalid option '-%c'.\n", optopt); + else + fprintf (stderr, "Unknown option character '\\x%x'.\n", optopt); + return -1; + default: + abort (); + } + } + + /*This program does not admit options*/ + if (optind < argc) { + fprintf (stderr, "This program does not admit options just argument elements with their values.\n"); + return -1; + } + + + /* If running from console, user may finish this process using SIGINT (Ctrl-C)*/ + /* Check to make sure that the shell has not set up an initial action of SIG_IGN before I establish my own signal handler. + * As seen on http://www.gnu.org/software/libc/manual/html_node/Initial-Signal-Actions.html#Initial-Signal-Actions + */ + memset (&sa, 0, sizeof(sa)); + memset (&sigintAction, 0, sizeof(sigaction)); + if (sigaction (SIGINT, NULL, &sa) < 0) { + fprintf (stderr, "%s: %s\n", "SIGINT retrieve current signal handler failed:", strerror(errno)); + + return -1; + } + + if (sa.sa_handler != SIG_IGN) { + /* Save the current SIGINT sigaction value. We use it to restore SIGINT handler in my custom SIGINT handler.*/ + memcpy (&sigintAction, &sa, sizeof(sigaction)); + + sa.sa_handler = &sigint_handler; + sa.sa_flags = SA_RESTART; + if (sigemptyset(&sa.sa_mask) < 0) { + fprintf (stderr, "%s: %s\n", "SIGINT empty mask", strerror(errno)); + + return -1; + } + if (sigaction(SIGINT, &sa, NULL) < 0) { + fprintf (stderr, "%s: %s\n", "SIGINT set signal handler failed", strerror(errno)); + + return -1; + } + } + + + if (main_process () < 0) + return -1; + + return 0; +} + + +int main_process () +{ + pthread_t idThreadOne; /*Thread one identifier number*/ + pthread_t idThreadTwo; /*Thread two identifier number*/ + + + if (pthread_create (&idThreadOne, NULL, thread_one, NULL) != 0 ) { + fprintf (stderr, "%s: %s\n", "thread one creation failed", strerror(errno)); + fflush (stderr); + + return -1; + } + + // Let other thread progress. Should be enough with sleep. This code is just for testing. + sleep(10); + + if (pthread_create (&idThreadTwo, NULL, thread_two, NULL) != 0 ) { + fprintf (stderr, "%s: %s\n", "thread two creation failed", strerror(errno)); + fflush (stderr); + + return -1; + } + + sleep(90); + + return 0; +} + +void *thread_one(void * arg) { + int fd; + int flockErr; + + fd = open(fileName, O_CREAT | O_RDWR, 0664); + if (fd == -1) { + fprintf (stderr, "%s: %s\n", "threadOne, open file error", strerror(errno)); + } + + fprintf (stdout, "ThreadOne: before lock\n"); + fflush (stdout); + do { + flockErr = flock(fd, LOCK_EX); + } while(flockErr == -1 && errno == EINTR); + + + if (flockErr == -1) { + fprintf (stderr, "%s: %s\n", "threadOne, flock error", strerror(errno)); + fflush(stderr); + } + fprintf (stdout, "ThreadOne: after lock\n"); + fflush (stdout); + + sleep(30); + + fprintf (stdout, "ThreadOne: before release lock\n"); + fflush (stdout); + do { + flockErr = flock(fd, LOCK_UN); + } while(flockErr == -1 && errno == EINTR); + fprintf (stdout, "ThreadOne: after release lock\n"); + fflush (stdout); + + close (fd); + + pthread_exit(0); +} + +void *thread_two(void * arg) { + int fd; + int flockErr; + + fd = open(fileName, O_CREAT | O_RDWR, 0664); + if (fd == -1) { + fprintf (stderr, "%s: %s\n", "ThreadTwo, open file error", strerror(errno)); + fflush (stderr); + } + + fprintf (stdout, "ThreadTwo: before lock\n"); + fflush (stdout); + do { + flockErr = flock(fd, LOCK_EX); + } while(flockErr == -1 && errno == EINTR); + + + if (flockErr == -1) { + fprintf (stderr, "%s: %s\n", "ThreadTwo, flock error", strerror(errno)); + fflush (stderr); + } + fprintf (stdout, "ThreadTwo: after lock\n"); + fflush (stdout); + + + fprintf (stdout, "ThreadTwo: before release lock\n"); + fflush (stdout); + do { + flockErr = flock(fd, LOCK_UN); + } while(flockErr == -1 && errno == EINTR); + fprintf (stdout, "ThreadTwo: after release lock\n"); + + close (fd); + + pthread_exit(0); +} + +int print_with_date(FILE *stream, const char *format, ...) +{ + va_list arg; + int done; + time_t rawtime; + char buff[50]; + char dateformatter[100] = "%s: "; + struct tm timeinfo; + + time ( &rawtime); + localtime_r ( &rawtime, &timeinfo); + strftime(buff, 50, "%Y-%m-%d %H:%M:%S", &timeinfo); + + va_start (arg, format); + strcat(dateformatter, format); + done = fprintf (stream, dateformatter, buff, arg); + + if (done < 0) { + fflush (stream); + return done; + } else { + done = fflush (stream); + return done; + } +} + +void sigint_handler(int sig) +{ + /*TODO: kill child processes, finish threads and release allocate memory*/ + /* From http://www.cons.org/cracauer/sigint.html + * Since a shellscript may in turn be called by a shellscript, you need to make sure that you properly + * communicate the discontinue intention to the calling program. WIFSIGNALED(status) and WTERMSIG(status) + * tell whether the child says "I exited on SIGINT". These values are used by the shell to discontinue + * the whole shell script in execution. If I use exit the shell has no way to know the user pressed Ctrl-C + * in order to stop the shell script in execution. This has just meaning when this program is executed in a shell script + * but because I do not know how the user is going to use it I must always finish every SIGINT handler in this way. + * So from a handler for SIGINT I must always finish with kill(SIGINT, SIG_DFL) (default kills the application) + */ + if (sigaction(SIGINT, &sigintAction, NULL) < 0) { + exit (EXIT_FAILURE); + } + kill(getpid(), SIGINT); +} + + diff --git a/locks/locks.h b/locks/locks.h new file mode 100644 index 0000000..a92c426 --- /dev/null +++ b/locks/locks.h @@ -0,0 +1,47 @@ +/*System V IPC keys*/ +#define SHAREMEMKEY 1 +#define SHAREMEMSEM 2 + + + +int print_with_date(FILE *stream, const char *format, ...); + +/****************************************************************************************/ +/* This method is used by pthread_create */ +/* */ +/* INPUT PARAMETER: socket file descriptor */ +/* RETURNS: void */ +/****************************************************************************************/ +void *thread_one (void *arg); + + +/****************************************************************************************/ +/* This method is used by pthread_create */ +/* */ +/* INPUT PARAMETER: socket file descriptor */ +/* RETURNS: void */ +/****************************************************************************************/ +void *thread_two (void *arg); + + +/****************************************************************************************/ +/* This method is used by pthread_create */ +/* */ +/* INPUT PARAMETER: socket file descriptor */ +/* RETURNS: int */ +/****************************************************************************************/ +int main_process (); + + + + +/****************************************************************************************/ +/* This method is used by pthread_create */ +/* */ +/* INPUT PARAMETER: socket file descriptor */ +/* RETURNS: void */ +/****************************************************************************************/ +void sigint_handler(); + + +