ROIpad ← Back to Search
stackoverflow › answer

Answer to: How can I flock LOCK_EX if there is always a LOCK_SH?

Score: 3
Answered: Dec 31, 2025
User Rep: 27,269
Is there any way to block new shared flocks while there is an exclusive flock pending? Yes. The writer thread can indicate to the reader threads that it wants exclusive access to the lock by setting an atomic variable (such as an atomic_bool) to a certain value (e.g. true). If all reader threads check this variable before attempting to acquire the shared lock, and don't attempt to acquire the lock as long as this variable is set to that value, then the writer will effectively have priority over the reader threads. As far as I can tell, the pthreads library does not offer an efficient way for the reader threads to wait for the writer thread to be finished and the atomic variable to be reset to its original value. Using a pthreads condition variable would be inefficient, as this would require every reader thread to acquire a mutex, thereby preventing parallel execution. However, Linux allows you to create an "event" file descriptor using the eventfd system call. A writer thread can signal this event when it is finished, and the reader threads can wait for this event to become signalled using poll or epoll. See the following Stack Overflow question for further information: Linux - how to wait on event without using mutex Since the information of the atomic variable is duplicated in the event, this makes the atomic variable redundant in theory. However, since checking an atomic variable is a much cheaper operation than a system call to check the state of the event, I recommend keeping the atomic variable, and only using the event for waiting. It is also worth noting that your program contains a bug in the form of a race condition. The lines int *pthreadnum = (int *)threadnum_data; int threadnum = *pthreadnum; will read from the loop counter of the loop in the function main. However, this will only work as intended if that loop is still executing the same iteration of the loop in which the thread was started. If that loop is already executing the next loop iteration, then the loop counter will no longer have the desired value. To fix this, you can pass the value of the Thread ID (which must be cast to a pointer value), instead of a pointer to the loop counter. I have rewritten your program as stated above: /* * if VERBOSE is defined, the shared threads will * list every flock & unflock. */ #define VERBOSE #include <stdio.h> #include <stdlib.h> #include <stdatomic.h> #include <stdbool.h> #include <stdint.h> #include <unistd.h> #include <sys/file.h> #include <sys/eventfd.h> #include <pthread.h> #include <poll.h> atomic_bool g_writer_wants_access = false; int g_event; void set_event( void ) { uint64_t event_val = 1; if ( write( g_event, &event_val, sizeof event_val ) != sizeof event_val ) { fprintf(stderr, "Error setting event!\n"); exit( EXIT_FAILURE ); } } void clear_event( void ) { uint64_t event_val; if ( read( g_event, &event_val, sizeof event_val ) != sizeof event_val ) { fprintf( stderr, "Error clearing event!\n" ); exit( EXIT_FAILURE ); } } void exclusive_flock() { // tell the reader threads to back off g_writer_wants_access = true; clear_event(); int fd = open( "flockfile", O_RDWR | O_CREAT | O_TRUNC ); printf("Waiting on exclusive flock\n"); flock(fd, LOCK_EX); printf("Lock successfull!\n"); flock(fd, LOCK_UN); printf("Unlocked\n"); // tell the reader threads that they may again proceed as usual g_writer_wants_access = false; set_event(); close(fd); } void *shared_thread_routine( void *threadnum_data ) { int threadnum = (intptr_t)threadnum_data; printf("thread %d starting to juggle shared flocks\n", threadnum); // these threads get shared flocks for a while then // release them. // Since they are shared, they get issued immediately // (unless an exclusive flock succeeds). // they overlap, so there is never a time when none // of the threads have a shared flock. for (int i = 0; i < 30; i++) { int fd = open("flockfile", O_RDWR | O_CREAT | O_TRUNC); // determine whether to give way to writer thread while ( g_writer_wants_access ) { struct pollfd pfd = { g_event, POLLIN, 0 }; printf( "thread %d giving way to writer thread\n", threadnum ); // wait for writer thread to finish if ( poll( &pfd, 1, -1 ) < 1 || pfd.revents & POLLIN == 0 ) { fprintf( stderr, "Error waiting for writer thread!\n" ); exit( EXIT_FAILURE ); } } // open a shared flock flock(fd, LOCK_SH); #ifdef VERBOSE printf("thread %d has a shared flock on fd %d\n", threadnum, fd); #endif // read the file, do some work sleep(1); // release the flock; hopefully give exclusive_thread a chance: flock(fd, LOCK_UN); close(fd); #ifdef VERBOSE printf("thread %d has released it's flock on fd %d\n", threadnum, fd); #endif // a short delay between runs usleep(4000 * (threadnum) ); } printf("thread %d done\n", threadnum); } int main( int argc, char **argv ) { pthread_t threads[10]; // create event file descriptor g_event = eventfd( 1, EFD_NONBLOCK ); if (g_event < 0) { fprintf(stderr, "Error creating event!\n"); exit( EXIT_FAILURE); } // create the reader threads for ( int threadnum = 0; threadnum < 10; threadnum++ ) { pthread_create( &threads[threadnum], NULL, shared_thread_routine, (void*)(intptr_t)threadnum); usleep(1000); } // attempt to obtain exlusive access using the writer thread exclusive_flock(); // join all the threads for ( int threadnum = 0; threadnum < 10; threadnum++ ) { pthread_join( threads[threadnum], NULL ); } // cleanup close(g_event); } This program has the following behavior: thread 0 starting to juggle shared flocks thread 0 has a shared flock on fd 4 thread 1 starting to juggle shared flocks thread 1 has a shared flock on fd 5 thread 2 starting to juggle shared flocks thread 2 has a shared flock on fd 6 thread 3 starting to juggle shared flocks thread 3 has a shared flock on fd 7 thread 4 starting to juggle shared flocks thread 4 has a shared flock on fd 8 thread 5 starting to juggle shared flocks thread 5 has a shared flock on fd 9 thread 6 starting to juggle shared flocks thread 6 has a shared flock on fd 10 thread 7 starting to juggle shared flocks thread 7 has a shared flock on fd 11 thread 8 starting to juggle shared flocks thread 8 has a shared flock on fd 12 thread 9 starting to juggle shared flocks thread 9 has a shared flock on fd 13 Waiting on exclusive flock thread 0 has released it's flock on fd 4 thread 0 giving way to writer thread thread 1 has released it's flock on fd 5 thread 2 has released it's flock on fd 6 thread 3 has released it's flock on fd 7 thread 4 has released it's flock on fd 8 thread 8 has released it's flock on fd 12 thread 2 giving way to writer thread thread 5 has released it's flock on fd 9 thread 6 has released it's flock on fd 10 thread 9 has released it's flock on fd 13 Lock successfull! Unlocked thread 7 has released it's flock on fd 11 thread 0 has a shared flock on fd 4 thread 2 has a shared flock on fd 5 thread 1 has a shared flock on fd 6 thread 3 has a shared flock on fd 7 thread 4 has a shared flock on fd 8 thread 5 has a shared flock on fd 9 thread 6 has a shared flock on fd 10 thread 8 has a shared flock on fd 11 thread 7 has a shared flock on fd 12 thread 9 has a shared flock on fd 13 thread 0 has released it's flock on fd 4 thread 2 has released it's flock on fd 5 thread 0 has a shared flock on fd 4 thread 1 has released it's flock on fd 6 thread 3 has released it's flock on fd 7 thread 1 has a shared flock on fd 5 thread 4 has released it's flock on fd 8 thread 2 has a shared flock on fd 6 thread 3 has a shared flock on fd 7 thread 5 has released it's flock on fd 9 [...] The order of the events in the output above is not always accurate, because if two threads attempt to print at nearly the same time, then one thread may be faster and/or be scheduled earlier than the other thread for printing. However, using flock file locking does not seem like the ideal solution in this case, because the function flock is an expensive system call. Using a pthreads read-write lock may be a better solution. Although POSIX does not support prioritizing writers when using a read-write lock, on Linux, it is supported as an extension. See the following thread for further information: How to prevent writer starvation in a read write lock in pthreads I have rewritten the program to use a pthreads read-write lock: #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/file.h> #include <pthread.h> pthread_rwlock_t g_rwlock; void exclusive_lock() { int fd = open( "flockfile", O_RDWR | O_CREAT | O_TRUNC ); // obtain write lock printf("Waiting on write lock\n"); if ( pthread_rwlock_wrlock( &g_rwlock ) != 0 ) { fprintf( stderr, "Error obtaining write lock!\n" ); exit( EXIT_FAILURE ); } printf("Lock successfull!\n"); // release write lock pthread_rwlock_unlock( &g_rwlock ); printf("Unlocked\n"); // cleanup close(fd); } void *shared_thread_routine( void *threadnum_data ) { int threadnum = (intptr_t)threadnum_data; printf("thread %d starting to juggle read locks\n", threadnum); // these threads get shared flocks for a while then // release them. // Since they are shared, they get issued immediately // (unless an exclusive flock succeeds). // they overlap, so there is never a time when none // of the threads have a shared flock. for (int i = 0; i < 30; i++) { int fd = open("flockfile", O_RDWR | O_CREAT | O_TRUNC); // obtain read lock printf("thread %d waiting on read lock on fd %d\n", threadnum, fd); if ( pthread_rwlock_rdlock( &g_rwlock ) != 0 ) { fprintf( stderr, "Error obtaining read lock!\n" ); exit( EXIT_FAILURE ); } printf("thread %d has a read lock on fd %d\n", threadnum, fd); // read the file, do some work sleep(1); // release the read lock pthread_rwlock_unlock( &g_rwlock ); printf("thread %d has released its read lock on fd %d\n", threadnum, fd); // cleanup close(fd); // a short delay between runs usleep(4000 * (threadnum) ); } printf("thread %d done\n", threadnum); } int main( int argc, char **argv ) { pthread_t threads[10]; pthread_rwlockattr_t rwlockattr; // initialize read-write lock attributes if ( pthread_rwlockattr_init( &rwlockattr ) != 0 ) { fprintf( stderr, "Error initializing lock attributes!\n" ); exit( EXIT_FAILURE ); } // set lock attributes to prioritize writers if ( pthread_rwlockattr_setkind_np( &rwlockattr, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP ) != 0 ) { fprintf( stderr, "Error setting lock attributes!\n" ); exit( EXIT_FAILURE ); } // initialize read-write lock if ( pthread_rwlock_init( &g_rwlock, &rwlockattr ) != 0 ) { fprintf( stderr, "Error initializing lock!\n" ); exit( EXIT_FAILURE ); } // cleanup pthread_rwlockattr_destroy( &rwlockattr ); // create the reader threads for ( int threadnum = 0; threadnum < 10; threadnum++ ) { pthread_create( &threads[threadnum], NULL, shared_thread_routine, (void*)(intptr_t)threadnum); usleep(1000); } // attempt to obtain exlusive access using the writer thread exclusive_lock(); // join all the threads for ( int threadnum = 0; threadnum < 10; threadnum++ ) { pthread_join( threads[threadnum], NULL ); } // cleanup pthread_rwlock_destroy( &g_rwlock ); } This program has the following output: thread 0 starting to juggle read locks thread 0 waiting on read lock on fd 3 thread 0 has a read lock on fd 3 thread 1 starting to juggle read locks thread 1 waiting on read lock on fd 4 thread 1 has a read lock on fd 4 thread 2 starting to juggle read locks thread 2 waiting on read lock on fd 5 thread 2 has a read lock on fd 5 thread 3 starting to juggle read locks thread 3 waiting on read lock on fd 6 thread 3 has a read lock on fd 6 thread 4 starting to juggle read locks thread 4 waiting on read lock on fd 7 thread 4 has a read lock on fd 7 thread 5 starting to juggle read locks thread 5 waiting on read lock on fd 8 thread 5 has a read lock on fd 8 thread 6 starting to juggle read locks thread 6 waiting on read lock on fd 9 thread 6 has a read lock on fd 9 thread 7 starting to juggle read locks thread 7 waiting on read lock on fd 10 thread 7 has a read lock on fd 10 thread 8 starting to juggle read locks thread 8 waiting on read lock on fd 11 thread 8 has a read lock on fd 11 thread 9 starting to juggle read locks thread 9 waiting on read lock on fd 12 thread 9 has a read lock on fd 12 Waiting on write lock thread 0 has released its read lock on fd 3 thread 0 waiting on read lock on fd 3 thread 1 has released its read lock on fd 4 thread 2 has released its read lock on fd 5 thread 3 has released its read lock on fd 6 thread 4 has released its read lock on fd 7 thread 1 waiting on read lock on fd 4 thread 5 has released its read lock on fd 8 thread 6 has released its read lock on fd 9 thread 7 has released its read lock on fd 10 thread 8 has released its read lock on fd 11 thread 9 has released its read lock on fd 12 Lock successfull! Unlocked thread 0 has a read lock on fd 3 thread 1 has a read lock on fd 4 thread 2 waiting on read lock on fd 5 thread 2 has a read lock on fd 5 thread 3 waiting on read lock on fd 6 thread 3 has a read lock on fd 6 thread 4 waiting on read lock on fd 7 thread 4 has a read lock on fd 7 thread 5 waiting on read lock on fd 8 thread 5 has a read lock on fd 8 thread 6 waiting on read lock on fd 9 thread 6 has a read lock on fd 9 thread 7 waiting on read lock on fd 10 thread 7 has a read lock on fd 10 thread 8 waiting on read lock on fd 11 thread 8 has a read lock on fd 11 thread 9 waiting on read lock on fd 12 thread 9 has a read lock on fd 12 thread 0 has released its read lock on fd 3 thread 1 has released its read lock on fd 4 thread 2 has released its read lock on fd 5 thread 0 waiting on read lock on fd 3 thread 0 has a read lock on fd 3 thread 1 waiting on read lock on fd 4
c multithreading
View Question ↗
Question
Parent Entity
Score: 10 • Views: 200
Site: stackoverflow