What would the source code of a try lock be? - c

So I think I understand the source code for a signal and a wait (the wait being a lock) but I am not sure how to implement a try lock.
Here is my code for a wait:
//if s->type is zero it is a binary semaphore type
if (s->type == 0)
{
// binary semaphore
// if state is zero, then block task
if (s->state == 0)
{
// block task
// ?? move task from ready queue to blocked queue
//reschedule the tasks
return 1;
}
// state is non-zero (semaphore already signaled)
s->state = 0; // reset state, and don't block
return 0;
}
else
{
// counting semaphore
s->state--;
// ?? implement counting semaphore
if (s->state < 0)
{
}
}
This is what I have for a try lock so far:
if (s->type == 0)
{
// binary semaphore
// if state is zero, then block task
if (s->state == 0)
{
tcb[curTask].event = s; // block task
tcb[curTask].state = S_BLOCKED;
removeNode(tcb[curTask].priority, READY_QUEUE, curTask);
enqueue(tcb[curTask].priority, curTask, BLOCKED_QUEUE);
return 1;
}
// state is non-zero (semaphore already signaled)
s->state = 1; // reset state, and don't block
return 0;
}
else
{
s->state--;
if (s->state >= 0)
{
s->state++;
}
else
{
tcb[curTask].event = s;
tcb[curTask].state = S_BLOCKED;
removeNode(tcb[curTask].priority, READY_QUEUE, curTask);
enqueue(tcb[curTask].priority, curTask, BLOCKED_QUEUE);
}
}

A regular spin lock is implemented something like this (pseudo-C-codish):
void lock(locktype_t* LockVariable)
{
while (CompareAndSwap(LockVariable,
STATE_UNLOCKED /* state to wait for */,
STATE_LOCKED /* new state to try to set */) !=
STATE_UNLOCKED /* expected state at the beginning of CAS() */)
{
// spin here, doing nothing useful, waiting for *LockVariable to
// first become STATE_UNLOCKED (CAS() returns its last value), after
// which we will set it to STATE_LOCKED (CAS() will do that atomically)
}
}
void unlock(locktype_t* LockVariable)
{
*LockVariable = STATE_UNLOCKED;
}
In case where indefinite spinning and waiting for the lock to become first unlocked is undesirable, we use a loop-less variant of the above something like this:
int tryToLock(locktype_t* LockVariable)
{
if (CompareAndSwap(LockVariable,
STATE_UNLOCKED /* state to wait for */,
STATE_LOCKED /* new state to try to set */) !=
STATE_UNLOCKED /* expected state at the beginning of CAS() */)
{
return 0; // the lock is still held by someone else, bail out
}
return 1; // the lock is now held by us, hurray!
}
Compare-and-swap

I was looking for a non-spin lock trylock.
I have figured out what to do. If it is a counting semaphore, then I decrement if the count is positive and I consume the resource. If it is zero or less, I do nothing but return an error code. I do not decrement the count or consume the resource. The program is then able to continue past that point. If it is a binary semaphore, I consume it if the resource is available. I then change the value of the binary semaphore to consumed. If it isn't available, then I return an error code.

Related

producer / consumer task. Problem with correct writing to shared buffer

I'm working on a project that solves the classic problem of producer / consumer scheduling.
Linux Open Suse 42.3 Leep, API System V, C language
The project consists of three programs: producer, consumer and scheduler.
The purpose of schedulers is to create 3 semaphores, shared memory in which there is a buffer (array) in which write (producer) and read (consumer) and to run n producer and m consumer processes.
Each producer must perform k write cycles to the buffer, and the consumer must perform k read cycles.
3 semaphores were used: mutex, empty and full. The value of the full semaphore is used in the program as an index in the array.
The problem is that: for example, when the buffer size is 3, producers write 4 portions of data, when the buffer size is 4 - 5 portions of data (although there should be 4) ...
Consumers read normally.
In addition, the program does not behave predictably when calling get_semVal fucntion.
Please help, I will be very, very grateful for the answer.
producer
#define BUFFER_SIZE 3
#define MY_RAND_MAX 99 // Highest integer for random number generator
#define LOOP 3 //the number of write / read cycles for each process
#define DATA_DIMENSION 4 // size of portion of data for 1 iteration
struct Data {
int buf[DATA_DIMENSION];
};
typedef struct Data buffer_item;
buffer_item buffer[BUFFER_SIZE];
void P(int semid)
{
struct sembuf op;
op.sem_num = 0;
op.sem_op = -1;
op.sem_flg = 0;
semop(semid,&op,1);
}
void V(int semid)
{
struct sembuf op;
op.sem_num = 0;
op.sem_op = +1;
op.sem_flg = 0;
semop(semid,&op,1);
}
void Init(int semid,int index,int value)
{
semctl(semid,index,SETVAL,value);
}
int get_semVal(int sem_id)
{
int value = semctl(sem_id,0,GETVAL,0);
return value;
}
int main()
{
sem_mutex = semget(KEY_MUTEX,1,0);
sem_empty = semget(KEY_EMPTY,1,0);
sem_full = semget(KEY_FULL,1,0);
srand(time(NULL));
const int SIZE = sizeof(buffer[BUFFER_SIZE]);
shm_id = shmget(KEY_SHARED_MEMORY,SIZE, 0);
int i=0;
buffer_item *adr;
do {
buffer_item nextProduced;
P(sem_empty);
P(sem_mutex);
//prepare portion of data
for(int j=0;j<DATA_DIMENSION;j++)
{
nextProduced.buf[j]=rand()%5;
}
adr = (buffer_item*)shmat(shm_id,NULL,0);
int full_value = get_semVal(sem_full);//get index of array
printf("-----%d------\n",full_value-1);//it’s for test the index of array in buffer
// write the generated portion of data by index full_value-1
adr[full_value-1].buf[0] = nextProduced.buf[0];
adr[full_value-1].buf[1] = nextProduced.buf[1];
adr[full_value-1].buf[2] = nextProduced.buf[2];
adr[full_value-1].buf[3] = nextProduced.buf[3];
shmdt(adr);
printf("producer %d produced %d %d %d %d\n", getpid(), nextProduced.buf[0],nextProduced.buf[1],nextProduced.buf[2],nextProduced.buf[3]);
V(sem_mutex);
V(sem_full);
i++;
} while (i<LOOP);
V(sem_empty);
sleep(1);
}
consumer
…
int main()
{
sem_mutex = semget(KEY_MUTEX,1,0);
sem_empty = semget(KEY_EMPTY,1,0);
sem_full = semget(KEY_FULL,1,0);
srand(time(NULL));
const int SIZE = sizeof(buffer[BUFFER_SIZE]);
shm_id = shmget(KEY_SHARED_MEMORY,SIZE,0);
int i=0;
buffer_item *adr;
do
{
buffer_item nextConsumed;
P(sem_full);
P(sem_mutex);
int full_value = get_semVal(sem_full);
adr = (buffer_item*)shmat(shm_id,NULL,0);
for(int i=0;i<BUFFER_SIZE;i++)
{
printf("--%d %d %d %d\n",adr[i].buf[0],adr[i].buf[1],adr[i].buf[2],adr[i].buf[3]);
}
for(int i=0;i<BUFFER_SIZE;i++)
{
buffer[i].buf[0] = adr[i].buf[0];
buffer[i].buf[1] = adr[i].buf[1];
buffer[i].buf[2] = adr[i].buf[2];
buffer[i].buf[3] = adr[i].buf[3];
}
tab(nextConsumed);
nextConsumed.buf[0]=buffer[full_value-1].buf[0];
nextConsumed.buf[1]=buffer[full_value-1].buf[1];
nextConsumed.buf[2]=buffer[full_value-1].buf[2];
nextConsumed.buf[3]=buffer[full_value-1].buf[3];
// Set buffer to 0 since we consumed that item
for(int j=0;j<DATA_DIMENSION;j++)
{
buffer[full_value-1].buf[j]=0;
}
for(int i=0;i<BUFFER_SIZE;i++)
{
adr[i].buf[0]=buffer[i].buf[0];
adr[i].buf[1]=buffer[i].buf[1];
adr[i].buf[2]=buffer[i].buf[2];
adr[i].buf[3]=buffer[i].buf[3];
}
shmdt(adr);
printf("consumer %d consumed %d %d %d %d\n", getpid() ,nextConsumed.buf[0],nextConsumed.buf[1],nextConsumed.buf[2],nextConsumed.buf[3]);
V(sem_mutex);
// increase empty
V(sem_empty);
i++;
} while (i<LOOP);
V(sem_full);
sleep(1);
}
Scheduler
…
struct Data {
int buf[DATA_DIMENSION];
};
typedef struct Data buffer_item;
buffer_item buffer[BUFFER_SIZE];
struct TProcList
{
pid_t processPid;
};
typedef struct TProcList ProcList;
…
ProcList createProcess(char *name)
{
pid_t pid;
ProcList a;
pid = fork();
if (!pid){
kill(getpid(),SIGSTOP);
execl(name,name,NULL);
exit(0);
}
else if(pid){
a.processPid=pid;
}
else
cout<<"error forking"<<endl;
return a;
}
int main()
{
sem_mutex = semget(KEY_MUTEX,1,IPC_CREAT|0600);
sem_empty = semget(KEY_EMPTY,1,IPC_CREAT|0600);
sem_full = semget(KEY_FULL,1,IPC_CREAT|0600);
Init(sem_mutex,0,1);//unlock mutex
Init(sem_empty,0,BUFFER_SIZE);
Init(sem_full,0,0);//unlock empty
const int SIZE = sizeof(buffer[BUFFER_SIZE]);
shm_id = shmget(KEY_SHARED_MEMORY,SIZE,IPC_CREAT|0600);
buffer_item *adr;
adr = (buffer_item*)shmat(shm_id,NULL,0);
for(int i=0;i<BUFFER_SIZE;i++)
{
buffer[i].buf[0]=0;
buffer[i].buf[1]=0;
buffer[i].buf[2]=0;
buffer[i].buf[3]=0;
}
for(int i=0;i<BUFFER_SIZE;i++)
{
adr[i].buf[0] = buffer[i].buf[0];
adr[i].buf[1] = buffer[i].buf[1];
adr[i].buf[2] = buffer[i].buf[2];
adr[i].buf[3] = buffer[i].buf[3];
}
int consumerNumber = 2;
int produserNumber = 2;
ProcList producer_pids[produserNumber];
ProcList consumer_pids[consumerNumber];
for(int i=0;i<produserNumber;i++)
{
producer_pids[i]=createProcess("/home/andrey/build-c-unknown-Debug/c");//create sleeping processes
}
for(int i=0;i<consumerNumber;i++)
{
consumer_pids[i]=createProcess("/home/andrey/build-p-unknown-Debug/p");
}
sleep(3);
for(int i=0;i<produserNumber;i++)
{
kill(producer_pids[i].processPid,SIGCONT);//continue processes
sleep(1);
}
for(int i=0;i<consumerNumber;i++)
{
kill(consumer_pids[i].processPid,SIGCONT);
sleep(1);
}
for(int i=0;i<produserNumber;i++)
{
waitpid(producer_pids[i].processPid,&stat,WNOHANG);//wait
}
for(int i=0;i<consumerNumber;i++)
{
waitpid(consumer_pids[i].processPid,&stat,WNOHANG);
}
shmdt(adr);
semctl(sem_mutex,0,IPC_RMID);
semctl(sem_full,0,IPC_RMID);
semctl(sem_empty,0,IPC_RMID);
}
It is not fun to try and unravel uncommented code someone else has written, so instead, I'll explain a verified working scheme.
(Note that comments should always explain programmer intent or idea, and never what the code does; we can read the code to see what it does. The problem is, we need to first understand the programmer idea/intent first, before we can compare that to the implementation. Without comments, I would need to first read the code to try and guess at the intent, then compare that to the code itself; it's like double the work.)
(I suspect OP's underlying problem is trying to use semaphore values as buffer indexes, but didn't pore through all of the code to be 100% certain.)
Let's assume the shared memory structure is something like the following:
struct shared {
sem_t lock; /* Initialized to value 1 */
sem_t more; /* Initialized to 0 */
sem_t room; /* Initialized to MAX_ITEMS */
size_t num_items; /* Initialized to 0 */
size_t next_item; /* Initialized to 0 */
item_type item[MAX_ITEMS];
};
and we have struct shared *mem pointing to the shared memory area.
Note that you should, at runtime, include <limits.h>, and verify that MAX_ITEMS <= SEM_VALUE_MAX. Otherwise MAX_ITEMS is too large, and this semaphore scheme may fail. (SEM_VALUE_MAX on Linux is usually INT_MAX, so big enough, but it may vary. And, if you use -O to optimize when compiling, the check will be optimized completely away. So it is a very cheap and reasonable check to have.)
The mem->lock semaphore is used like a mutex. That is, to lock the structure for exclusive access, a process waits on it. When it is done, it posts on it.
Note that while sem_post(&(mem->lock)) will always succeed (ignoring bugs like mem being NULL or pointing to uninitialized memory or having been overwritten with garbage), technically, sem_wait() can be interrupted by a signal delivery to an userspace handler installed without SA_RESTART flag. This is why I recommend using a static inline helper function instead of sem_wait():
static inline int semaphore_wait(sem_t *const s)
{
int result;
do {
result = sem_wait(s);
} while (result == -1 && errno == EINTR);
return result;
}
static inline int semaphore_post(sem_t *const s)
{
return sem_post(s);
}
In cases where signal delivery should not interrupt waiting on the semaphore, you use semaphore_wait(). If you do want a signal delivery to interrupt waiting on a semaphore, you use sem_wait(); if it returns -1 with errno == EINTR, the operation was interrupted due to signal delivery, and the semaphore wasn't actually decremented. (Many other low-level functions, like read(), write(), send(), recv(), can be interrupted in the exact same way; they can also just return a short count, in case the interruption occurred part way.)
The semaphore_post() is just a wrapper, so that you can use "matching` post and wait operations. Doing that sort of "useless" wrappers does help understand the code, you see.
The item[] array is used as a circular queue. The num_items indicates the number of items in it. If num_items > 0, the next item to be consumed is item[next_item]. If num_items < MAX_ITEMS, the next item to be produced is item[(next_item + num_items) % MAX_ITEMS].
The % is the modulo operator. Here, because next_item and num_items are always positive, (next_item + num_items) % MAX_ITEMS is always between 0 and MAX_ITEMS - 1, inclusive. This is what makes the buffer circular.
When a producer has constructed a new item, say item_type newitem;, and wants to add it to the shared memory, it basically does the following:
/* Omitted: Initialize and fill in 'newitem' members */
/* Wait until there is room in the buffer */
semaphore_wait(&(mem->room));
/* Get exclusive access to the structure members */
semaphore_wait(&(mem->lock));
mem->item[(mem->next_item + mem->num_items) % MAX_ITEMS] = newitem;
mem->num_items++;
sem_post(&(mem->more));
semaphore_post(&(mem->lock));
The above is often called enqueue, because it appends an item to a queue (which happends to be implemented via a circular buffer).
When a consumer wants to consume an item (item_type nextitem;) from the shared buffer, it does the following:
/* Wait until there are items in the buffer */
semaphore_wait(&(mem->more));
/* Get exclusive access to the structure members */
semaphore_wait(&(mem->lock));
nextitem = mem->item[mem->next_item];
mem->next_item = (mem->next_item + 1) % MAX_ITEMS;
mem->num_items = mem->num_items - 1;
semaphore_post(&(mem->room));
mem->item[(mem->next_item + mem->num_items) % MAX_ITEMS] = newitem;
mem->num_items++;
sem_post(&(mem->more));
semaphore_post(&(mem->lock));
/* Omitted: Do work on 'nextitem' here. */
This is often called dequeue, because it obtains the next item from the queue.
I would recommend you first write a single-process test case, which enqueues MAX_ITEMS, then dequeues them, and verifies the semaphore values are back to initial values. That is not a guarantee of correctness, but it takes care of the most typical bugs.
In practice, I would personally write the queueing functions as static inline helpers in the same header file that describes the shared memory structure. Pretty much
static inline int shared_get(struct shared *const mem, item_type *const into)
{
int err;
if (!mem || !into)
return errno = EINVAL; /* Set errno = EINVAL, and return EINVAL. */
/* Wait for the next item in the buffer. */
do {
err = sem_wait(&(mem->more));
} while (err == -1 && errno == EINTR);
if (err)
return errno;
/* Exclusive access to the structure. */
do {
err = sem_wait(&(mem->lock));
} while (err == -1 && errno == EINTR);
/* Copy item to caller storage. */
*into = mem->item[mem->next_item];
/* Update queue state. */
mem->next_item = (mem->next_item + 1) % MAX_ITEMS;
mem->num_items--;
/* Account for the newly freed slot. */
sem_post(&(mem->room));
/* Done. */
sem_post(&(mem->lock));
return 0;
}
and
static inline int shared_put(struct shared *const mem, const item_type *const from)
int err;
if (!mem || !into)
return errno = EINVAL; /* Set errno = EINVAL, and return EINVAL. */
/* Wait for room in the buffer. */
do {
err = sem_wait(&(mem->room));
} while (err == -1 && errno == EINTR);
if (err)
return errno;
/* Exclusive access to the structure. */
do {
err = sem_wait(&(mem->lock));
} while (err == -1 && errno == EINTR);
/* Copy item to queue. */
mem->item[(mem->next_item + mem->num_items) % MAX_ITEMS] = *from;
/* Update queue state. */
mem->num_items++;
/* Account for the newly filled slot. */
sem_post(&(mem->more));
/* Done. */
sem_post(&(mem->lock));
return 0;
}
but note that I wrote these from memory, and not copy-pasted from my test program, because I want you to learn and not to just copy-paste code from others without understanding (and being suspicious of) it.
Why do we need separate counters (first_item, num_items) when we have the semaphores, with corresponding values?
Because we cannot capture the semaphore value at the point where sem_wait() succeeded/continued/stopped blocking.
For example, initially the room semaphore is initialized to MAX_ITEMS, so up to that many producers can run in parallel. Any one of them running sem_getvalue() immediately after sem_wait() will get some later value, not the value or transition that caused sem_wait() to return. (Even with SysV semaphores you cannot obtain the semaphore value that caused wait to return for this process.)
So, instead of indexes or counters to the buffer, we think of the more semaphore as having the value of how many times one can dequeue from the buffer without blocking, and room as having the value of how many times one can enqueue to the buffer without blocking. The lock semaphore grants exclusive access, so that we can modify the shared memory structures (well, next_item and num_items) atomically, without different processes trying to change the values at the same time.
I am not 100% certain that this is the best or optimum pattern, this is one of the most commonly used ones. It is not as robust as I'd like: for each increment (of one) in num_items, one must post on more exactly once; and for each decrement (of one) in num_items, one must increment next_item by exactly one and post on room exactly once, or the scheme falls apart.
There is one final wrinkle, though:
How do producers indicate they are done?
How would the scheduler tell producers and/or consumers to stop?
My preferred solution is to add a flag into the shared memory structure, say unsigned int status;, with specific bit masks telling the producers and consumers what to do, that is examined immediately after waiting on the lock:
#define STOP_PRODUCING (1 << 0)
#define STOP_CONSUMING (1 << 1)
static inline int shared_get(struct shared *const mem, item_type *const into)
{
int err;
if (!mem || !into)
return errno = EINVAL; /* Set errno = EINVAL, and return EINVAL. */
/* Wait for the next item in the buffer. */
do {
err = sem_wait(&(mem->more));
} while (err == -1 && errno == EINTR);
if (err)
return errno;
/* Exclusive access to the structure. */
do {
err = sem_wait(&(mem->lock));
} while (err == -1 && errno == EINTR);
/* Need to stop consuming? */
if (mem->state & STOP_CONSUMING) {
/* Ensure all consumers see the state immediately */
sem_post(&(mem->more));
sem_post(&(mem->lock));
/* ENOMSG == please stop. */
return errno = ENOMSG;
}
/* Copy item to caller storage. */
*into = mem->item[mem->next_item];
/* Update queue state. */
mem->next_item = (mem->next_item + 1) % MAX_ITEMS;
mem->num_items--;
/* Account for the newly freed slot. */
sem_post(&(mem->room));
/* Done. */
sem_post(&(mem->lock));
return 0;
}
static inline int shared_put(struct shared *const mem, const item_type *const from)
int err;
if (!mem || !into)
return errno = EINVAL; /* Set errno = EINVAL, and return EINVAL. */
/* Wait for room in the buffer. */
do {
err = sem_wait(&(mem->room));
} while (err == -1 && errno == EINTR);
if (err)
return errno;
/* Exclusive access to the structure. */
do {
err = sem_wait(&(mem->lock));
} while (err == -1 && errno == EINTR);
/* Time to stop? */
if (mem->state & STOP_PRODUCING) {
/* Ensure all producers see the state immediately */
sem_post(&(mem->lock));
sem_post(&(mem->room));
/* ENOMSG == please stop. */
return errno = ENOMSG;
}
/* Copy item to queue. */
mem->item[(mem->next_item + mem->num_items) % MAX_ITEMS] = *from;
/* Update queue state. */
mem->num_items++;
/* Account for the newly filled slot. */
sem_post(&(mem->more));
/* Done. */
sem_post(&(mem->lock));
return 0;
}
which return ENOMSG to the caller if the caller should stop. When the state is changed, one should of course be holding the lock. When adding STOP_PRODUCING, one should also post on the room semaphore (once) to start a "cascade" so all producers stop; and when adding STOP_CONSUMING, post on the more semaphore (once) to start the consumer stop cascade. (Each of them will post on it again, to ensure each producer/consumer sees the state as soon as possible.)
There are other schemes, though; for example signals (setting a volatile sig_atomic_t flag), but it is generally hard to ensure there are no race windows: a process checking the flag just before it is changed, and then blocking on a semaphore.
In this scheme, it would be good to verify that both MAX_ITEMS + NUM_PRODUCERS <= SEM_VALUE_MAX and MAX_ITEMS + NUM_CONSUMERS <= SEM_VALUE_MAX, so that even during the stop cascades, the semaphore value will not overflow.

accept call blocking thread termination

I'm having trouble terminating my server in my multithreaded program (one server, multiple clients).
When the variable global_var, which counts the number of currently connected clients, gets set to 0, the server should terminate, but it doesn't.
What I think is happening is since accept() is blocking , the code never reaches the break condition in main loop.
It's breaking correctly out of thread_func but then it blocks inside the while loop, just before the accept() call and after printing "Exiting thread_func".
volatile int finished = 0; // Gets set to 1 by catching SIGINT/SIGSTOP
int global_var = 0; // When it gets to 0, server should terminate
int server_fd;
void * thread_func(void* arg)
{
do_some_pre_stuff();
while(1)
{
if(!global_var)
{
close(server_fd);
finished = 1;
break;
}
if(recv(...) > 0)
{
do_more_stuff()
}
else
{
disconnect_client();
global_var--;
break;
}
}
free_more_ressources();
return NULL;
}
int main()
{
do_initial_stuff();
init_socket();
listen();
while (!finished)
{
if( (fd = accept(server_fd,...)) == -1)
exit(-1);
global_var++;
/* Some intermediate code */
if(!global_var)
break;
// Thread for the newly connected player
if(pthread_create(&thread_id[...], NULL, thread_func, (void*)some_arg)
exit(-1);
}
free_resources();
puts("Exiting thread_func");
}
I tried the advice listed here without success (except the pipe answer, not trying to mess with pipes).
I'm new to socket programming but what I tried so far looked correct but none of the solutions worked (including semaphores, pthread_cancel,etc)
PS: synchronization has been implemented, just omitted here for readability

using flock, open and close file to implement many readers single writer lock

I've got a project that consist of multiple processes that can read or write into a single data base. I wish to implement single writer / multi readers locks synchronized by a lock file using the system calls flock/open/close.
Upon lock failure, any re-attempt to take the lock again, will be made by the higher level that requested the lock (unlike spin-lock).
Unfortunately, while testing this model, it failed on scenario of unlocking that wasn't preceded by locking.
perhaps you can help me find what did i do wrong here:
// keep read and write file descriptors as global variables.
// assuming no more than 1 thread can access db on each process.
int write_descriptor=0;
int read_descriptor=0;
int lock_write() {
if((write_descriptor = open(LOCKFILE, O_RDWR|O_CREAT,0644))<0) {
return LOCK_FAIL;
}
if(flock(write_descriptor, LOCK_EX)<0) {
close(write_descriptor);
write_descriptor = 0;
return LOCK_FAIL;
}
return LOCK_SUCCESS;
}
int unlock_write() {
if(!write_descriptor) {
// sanity: try to unlock before lock.
return LOCK_FAIL;
}
if(flock(write_descriptor,LOCK_UN)<0) {
// doing nothing because even if unlock failed, we
// will close the fd anyway to release all locks.
}
close(write_descriptor);
write_descriptor = 0;
return LOCK_SUCCESS;
}
int lock_read() {
if((read_descriptor = open(LOCKFILE,O_RDONLY))<0) {
return LOCK_FAIL;
}
if(flock(read_descriptor, LOCK_SH)<0) {
close(read_descriptor);
return LOCK_FAIL;
}
return LOCK_SUCCESS;
}
int unlock_read() {
if(!read_descriptor) {
// sanity : try to unlock before locking first.
return LOCK_FAIL;
}
if(flock(read_descriptor, LOCK_UN)<0) {
// doing nothing because even if unlock failed, we
// will close the fd anyway to release all locks.
}
close(read_descriptor);
read_descriptor = 0;
return LOCK_SUCCESS;
}
int read_db() {
if(lock_read() != LOCK_SUCCESS) {
return DB_FAIL;
}
// read from db
if(unlock_read() != LOCK_SUCCESS) {
// close fd also unlock - so we can fail here (can i assume that ?)
}
}
int write_db() {
if(lock_write() != LOCK_SUCCESS) {
return DB_FAIL;
}
//write to db.
if(unlock_write() != LOCK_SUCCESS) {
// close fd also unlock - so we can fail here (can i assume that ?)
}
}
In both lock_read and lock_write add this as the first line:
assert ((read_descriptor == 0) && (write_descriptor == 0));
In unlock_read, add this:
assert (read_descriptor != 0);
And in unlock_write, add this:
assert (write_descriptor != 0);
And change code like:
if(flock(read_descriptor, LOCK_SH)<0) {
close(read_descriptor);
return LOCK_FAIL;
}
to:
if(flock(read_descriptor, LOCK_SH)<0) {
close(read_descriptor);
read_descriptor = 0;
return LOCK_FAIL;
}
Do the same for the write code so that any time a descriptor is closed, the corresponding global is set to zero. (You really should use -1 for an invalid file descriptor since zero is legal.)
Make a debug build and run it. When an assert trips, you'll have your culprit.

Do something while sem_wait in C

I am trying to avoid using sem_wait, and have something like:
"While waiting for semaphore, do something".
So then I found out about sem_getvalue which is supposed to return 0 in case of success.
So I initiate the semaphore with:
sem_init(&sem1, 0, 0);
And instead of
sem_wait(&sem1);
I need something like:
while(sem_getvalue(&sem1, 2) < 0){
printf("do this\n");
}
I have no problems with sem_wait, everything seems to function properly.
But with the second, I am getting Segmentation fault error during execution.
Any help is greatly appreciated, thanks!
You shouldn't use sem_getvalue for this since you are losing atomicity. Use sem_trywait for the task
for (;;) {
if (sem_trywait(&sem1)) {
if (errno != EAGAIN) {
/* handle error */
abort();
}
errno = 0;
printf("do this\n");
} else {
break;
}
}
sem_getvalue returns a status, and places the semaphore value at the pointer passed as the second parameter. You need to check the return status (which is zero when the function succeeds) and only then check the semaphore value, like this:
int sval1;
if (sem_getvalue(&sem1, &sval1) == 0 && sval1 < 0) {
...
}

pthread_cond_wait deadlock in fifo circular queue

my code is only using in one producer-one consumer situation.
here is my test code:
static void *afunc(void * arg) {
Queue* q = arg;
for(int i= 0; i< 100000; i++) {
*queue_pull(q) = i; //get one element space
queue_push(q); //increase the write pointer
}
return NULL;
}
static void *bfunc(void * arg) {
Queue* q = arg;
for(;;) {
int *i = queue_fetch(q); //get the first element in queue
printf("%d\n", *i);
queue_pop(q); //increase the read pointer
}
}
int main() {
Queue queue;
pthread_t a, b;
queue_init(&queue);
pthread_create(&a, NULL, afunc, &queue);
pthread_create(&b, NULL, bfunc, &queue);
sleep(100000);
return 0;
}
and here is the implementation of the circular queue
#define MAX_QUEUE_SIZE 3
typedef struct Queue{
int data[MAX_QUEUE_SIZE] ;
int read,write;
pthread_mutex_t mutex, mutex2;
pthread_cond_t not_empty, not_full;
}Queue;
int queue_init(Queue *queue) {
memset(queue, 0, sizeof(Queue));
pthread_mutex_init(&queue->mutex, NULL);
pthread_cond_init(&queue->not_empty, NULL);
pthread_mutex_init(&queue->mutex2, NULL);
pthread_cond_init(&queue->not_full, NULL);
return 0;
}
int* queue_fetch(Queue *queue) {
int* ret;
if (queue->read == queue->write) {
pthread_mutex_lock(&queue->mutex);
pthread_cond_wait(&queue->not_empty, &queue->mutex);
pthread_mutex_unlock(&queue->mutex);
}
ret = &(queue->data[queue->read]);
return ret;
}
void queue_pop(Queue *queue) {
nx_atomic_set(queue->read, (queue->read+1)%MAX_QUEUE_SIZE);
pthread_cond_signal(&queue->not_full);
}
int* queue_pull(Queue *queue) {
int* ret;
if ((queue->write+1)%MAX_QUEUE_SIZE == queue->read) {
pthread_mutex_lock(&queue->mutex2);
pthread_cond_wait(&queue->not_full, &queue->mutex2);
pthread_mutex_unlock(&queue->mutex2);
}
ret = &(queue->data[queue->write]);
return ret;
}
void queue_push(Queue *queue) {
nx_atomic_set(queue->write, (queue->write+1)%MAX_QUEUE_SIZE);
pthread_cond_signal(&queue->not_empty);
}
after a few moments, it seems the two child threads will turn into deadlock..
EDIT: i use two semaphore, but it also has some problem.. it's pretty
weird, if if just execute ./main, it seems fine, but if i redirect into a file, like ./main > a.txt, then wc -l a.txt, the result is not equal the enqueue number..
int queue_init(Queue *queue) {
memset(queue, 0, sizeof(Queue));
pthread_mutex_init(&queue->mutex, NULL);
sem_unlink("/not_empty");
queue->not_empty = sem_open("/not_empty", O_CREAT, 644, 0);
sem_unlink("/not_full");
queue->not_full = sem_open("/not_full", O_CREAT, 644, MAX_QUEUE_SIZE);
return 0;
}
int* queue_fetch(Queue *queue) {
sem_wait(queue->not_empty);
return &(queue->data[queue->read]);
}
void queue_pop(Queue *queue) {
nx_atomic_set(queue->read, (queue->read+1)%MAX_QUEUE_SIZE);
sem_post(queue->not_full);
}
int* queue_pull(Queue *queue) {
sem_wait(queue->not_full);
return &(queue->data[queue->write]);
}
void queue_push(Queue *queue) {
nx_atomic_set(queue->write, (queue->write+1)%MAX_QUEUE_SIZE);
sem_post(queue->not_empty);
}
You are manipulating the state of the queue outside the mutex, this is inherently racey.
I would suggest using a single mutex, but take it whenever you change or test the read & write indicies. This also means that you don't need the atomic sets.
Quite possibly one of your threads is waiting for a condition to be signalled after the signalling has occurred, causing both threads to wait for each other indefinitely.
Pthreads condition variables don't remain signalled -- the signalling is a momentary action. The condition variable isn't used determine whether to wait -- it's just used to wake up a thread that's already waiting; you need a different means for determining whether or not to wait, such as checking a flag or some sort of test condition.
Normally, you signal as follows:
Lock the mutex
Do your updates, generally leaving your test condition 'true' (eg. setting your flag)
Call pthread_cond_signal() or pthread_cond_broadcast()
Unlock the mutex
...and wait as follows:
Lock the mutex
Loop until your test expression is 'true' (eg. until your flag is set), calling pthread_cond_wait() only if the test is false (inside the loop).
After the loop, when your test has succeeded, do your work.
Unlock the mutex
For example, signalling might go something like this:
pthread_mutex_lock(&mtx); /* 1: lock mutex */
do_something_important(); /* 2: do your work... */
ready_flag = 1; /* ...and set the flag */
pthread_cond_signal(&cond); /* 3: signal the condition (before unlocking) */
pthread_mutex_unlock(&mtx); /* 4: unlock mutex */
and waiting might be something like this:
pthread_mutex_lock(&mtx); /* 1: lock mutex */
while (ready_flag == 0) /* 2: Loop until flag is set... */
pthread_cond_wait(&cond, &mtx); /* ...waiting when it isn't */
do_something_else(); /* 3: Do your work... */
ready_flag = 0; /* ...and clear the flag if it's all done */
pthread_mutex_unlock(&mtx); /* 4: unlock mutex */
The waiter won't miss the condition this way, because the mutex ensures that the waiter's test-and-wait and the signaller's set-and-signal cannot occur simultaneously.
This section of your queue_fetch() function:
if (queue->read == queue->write) {
pthread_mutex_lock(&queue->mutex);
pthread_cond_wait(&queue->not_empty, &queue->mutex);
pthread_mutex_unlock(&queue->mutex);
}
ret = &(queue->data[queue->read]);
..might be rewritten as follows:
pthread_mutex_lock(&queue->mutex);
while (queue->read == queue->write)
pthread_cond_wait(&queue->not_empty, &queue->mutex);
ret = &(queue->data[queue->read]);
pthread_mutex_unlock(&queue->mutex);
...where:
The lock/unlock of the mutex are moved around the if, so the mutex is held while the test expression is evaluated, and still held until the condition wait starts
The if is changed to a while in case the condition wait is prematurely interrupted
Access to queue->read and queue->write is done with the mutex held
Similar changes would be made to queue_pull().
As for the signalling code, the following section of queue_pop():
nx_atomic_set(queue->read, (queue->read+1)%MAX_QUEUE_SIZE);
pthread_cond_signal(&queue->not_full);
..might be changed to:
pthread_mutex_lock(&queue->mutex);
queue->read = (queue->read + 1) % MAX_QUEUE_SIZE;
pthread_cond_signal(&queue->not_full);
pthread_mutex_unlock(&queue->mutex);
..where:
The mutex is held while signalling the condition (this ensures the condition can't be signalled between the waiter deciding whether to wait and actually starting to wait, since the waiter would hold the mutex during that interval)
The mutex is held while changing queue->read as well rather than using nx_atomic_set() since the mutex is needed when signalling the condition anyway
Similar changes would be made to queue_push().
Additionally, you should just use a single mutex (so that the same mutex is always held when accessing read and write), and once the while loops are added to the condition waits there's little compelling reason to use more than one condition variable. If switching to a single condition variable, just signal the condition again after completing a wait:
pthread_mutex_lock(&queue->mutex);
while (queue->read == queue->write) {
pthread_cond_wait(&queue->cond, &queue->mutex);
pthread_cond_signal(&queue->cond); /* <-- signal next waiter, if any */
}
ret = &(queue->data[queue->read]);
pthread_mutex_unlock(&queue->mutex);

Resources