I simply implement Peterson Lock Algorithm, but it doesn't work rightly.
Here is the code:
#include <pthread.h>
typedef struct {
volatile bool flag[2];
volatile bool victim;
} peterson_lock_t;
void peterson_lock_init(peterson_lock_t &lock) {
lock.flag[0] = lock.flag[1] = false;
lock.victim = 0;
}
void peterson_lock(peterson_lock_t &lock, int id) {
lock.victim = id;
lock.flag[id] = true;
__asm__ __volatile__("" : : : "memory");
while (lock.flag[1 - id] == false && lock.victim != id);
}
void peterson_unlock(peterson_lock_t &lock, int id) {
lock.flag[id] = false;
}
What's wrong with this code?
main.cpp:
#include <stdio.h>
#include "peterson_lock.h"
peterson_lock_t lock;
int count = 0;
void *routine0(void *arg) {
int *cnt = (int *)arg;
for (int i = 0; i < *cnt; ++i) {
peterson_lock(lock, 0);
++count;
peterson_unlock(lock, 0);
}
return NULL;
}
void *routine1(void *arg) {
int *cnt = (int *)arg;
for (int i = 0; i < *cnt; ++i) {
peterson_lock(lock, 1);
++count;
peterson_unlock(lock, 1);
}
}
int main(int argc, char **argv) {
peterson_lock_init(lock);
pthread_t thread0, thread1;
int count0 = 10000;
int count1 = 20000;
pthread_create(&thread0, NULL, routine0, (void *)&count0);
pthread_create(&thread1, NULL, routine1, (void *)&count1);
pthread_join(thread0, NULL);
pthread_join(thread1, NULL);
printf("Expected: %d\n", (count0 + count1));
printf("Reality : %d\n", count);
return 0;
}
And the result is not right:
Expected: 30000
Reality : 24304
Expected: 30000
Reality : 24316
OS:
Linux ip-172-31-43-244 3.14.35-28.38.amzn1.x86_64 #1 SMP Wed Mar 11 22:50:37 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux
While condition also needs to be improved :
while (lock.flag[1 - id] == true && lock.victim == id)
continue;
Be in busy wait until the other thread has lock and you are the victim.
Peterson Lock
Related
I want to setup an asynchronous bulk transfer to a callback routine but I never enter the callback routine. I changed the code to a synchronous transfer and it works. Please help me understand what I'm doing wrong with the asynchronous transfer. Below is the synchronous transfer
#include <stdio.h>
#include <libusb.h>
#define LB04_VID 4302
#define LB04_PID 60307
unsigned char in_buf[32];
void hexdump(unsigned char* data, int len)
{
int i;
for (i = 0; i < len; i++) printf("%02X ", data[i]);
puts("\n");
}
int main(int argc, char* argv[])
{
libusb_device** devs;
libusb_device_handle* dev_handle;
libusb_context* context = NULL;
size_t list;
int ret;
int iLen;
ret = libusb_init(&context);
if (ret < 0) {
perror("libusb_init");
return 1;
}
libusb_set_option(context, LIBUSB_OPTION_MAX);
list = libusb_get_device_list(context, &devs);
if (list < 0) {
perror("libusb_get_device_list");
return 1;
}
dev_handle = libusb_open_device_with_vid_pid(context, LB04_VID, LB04_PID);
libusb_free_device_list(devs, 1);
printf("found XHC-HB04 device\n");
if (dev_handle) {
if (libusb_kernel_driver_active(dev_handle, 0) == 1) {
libusb_detach_kernel_driver(dev_handle, 0);
}
ret = libusb_claim_interface(dev_handle, 0);
if (ret < 0) {
perror("libusb_claim_interface");
return 1;
}
ret = libusb_set_configuration(dev_handle, 1);
while (1) {
ret = libusb_bulk_transfer(dev_handle, (0x01 | LIBUSB_ENDPOINT_IN), in_buf, sizeof(in_buf), &iLen, 0);
hexdump((unsigned char*)&in_buf, iLen);
}
libusb_release_interface(dev_handle, 0);
libusb_close(dev_handle);
}
libusb_exit(context);
}
This loops forever like I want. However, when I try to use asynchronous transfer like the below, my callback never gets called. Please help as I'm stuck with what to try next.
#include <stdio.h>
#include <libusb.h>
#define LB04_VID 4302
#define LB04_PID 60307
unsigned char in_buf[32];
libusb_device** devs;
libusb_device_handle* dev_handle;
libusb_context* context = NULL;
struct libusb_transfer* transfer_in = NULL;
int setup_asynch_transfer(libusb_device_handle* dev_handle);
void hexdump(unsigned char* data, int len)
{
int i;
for (i = 0; i < len; i++)
printf("%02X ", data[i]);
puts("\n");
}
void cb_response_in(struct libusb_transfer* transfer)
{
printf("cb_response_in\n");
if (transfer->actual_length > 0)
hexdump((unsigned char*)&in_buf, transfer->actual_length);
setup_asynch_transfer(transfer->dev_handle);
}
int setup_asynch_transfer(libusb_device_handle* dev_handle)
{
int ret;
printf("setup_asynch_transfer\n");
transfer_in = libusb_alloc_transfer(0);
libusb_fill_bulk_transfer(transfer_in, dev_handle, (0x01 | LIBUSB_ENDPOINT_IN),
in_buf, sizeof(in_buf),
cb_response_in, NULL, 500); // no user data
return (ret = libusb_submit_transfer(transfer_in));
}
int main(int argc, char* argv[])
{
size_t list;
int ret;
ret = libusb_init(&context);
if (ret < 0) {
perror("libusb_init");
return 1;
}
libusb_set_option(context, LIBUSB_OPTION_MAX);
list = libusb_get_device_list(context, &devs);
if (list < 0) {
perror("libusb_get_device_list");
return 1;
}
dev_handle = libusb_open_device_with_vid_pid(context, LB04_VID, LB04_PID);
libusb_free_device_list(devs, 1);
printf("found XHC-HB04 device\n");
if (dev_handle) {
if (libusb_kernel_driver_active(dev_handle, 0) == 1) {
libusb_detach_kernel_driver(dev_handle, 0);
}
ret = libusb_claim_interface(dev_handle, 0);
if (ret < 0) {
perror("libusb_claim_interface");
return 1;
}
ret = libusb_set_configuration(dev_handle, 1);
ret = setup_asynch_transfer(dev_handle);
// Loop forever
while (1);
libusb_release_interface(dev_handle, 0);
libusb_close(dev_handle);
}
libusb_exit(context);
}
I made sure I could execute a bulk synchronous transfer and then converted the code to execute asynchronous transfer. I expected the callback routine to print the data and schedule another asynchronous routine while the main routine waited in a while loop forever.
I'm using libusb-1.0 and Microsoft Visual Studio Community 2022 on Windows 10.
I wanted to write simple multithread app in C/C++. Function funProducent produces 100 values and if random generated value is in given range, char is added to buffer. Function funKonzument comsumes values from buffer. Here is my code:
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#define BUFFER_LIMIT 20
struct struktura{
pthread_mutex_t mutex;
pthread_cond_t bufferNotFull;
pthread_cond_t bufferNotEmpty;
int bufferIndex;
char * buffer;
int junk;
};
void * funProducent(void *arg){
struktura * data = (struktura *) arg;
int i = 0;
while (i < 100) {
pthread_mutex_lock(&data->mutex);
if(data->bufferIndex == BUFFER_LIMIT - 1){
pthread_cond_wait(&data->bufferNotFull, &data->mutex);
}
int randomValue = (rand() % 20) + 1;
if( randomValue < 13 ){
data->buffer[++data->bufferIndex] = 'a';
printf("%2d : Producent at index %d added %c\n", i, data->bufferIndex, data->buffer[data->bufferIndex]);
pthread_cond_signal(&data->bufferNotEmpty);
} else {
data->junk++;
}
pthread_mutex_unlock(&data->mutex);
i++;
}
printf("producent is done\n");
}
void * funKonzument(void *arg){
struktura * data = (struktura *) arg;
int i = 0;
while (i + data->junk < 100) {
printf("%d\n", i + data->junk);
pthread_mutex_lock(&data->mutex);
if(data->bufferIndex < 0){
pthread_cond_wait(&data->bufferNotEmpty, &data->mutex);
}
printf("%2d : Konzument at index %d consumed %c\n", i, data->bufferIndex, data->buffer[data->bufferIndex]);
data->bufferIndex--;
pthread_cond_signal(&data->bufferNotFull);
pthread_mutex_unlock(&data->mutex);
i++;
}
printf("konzument is done\n");
}
int main(int argc, char** argv) {
pthread_t threadProducent, threadKonzument;
struktura threadData;
threadData.buffer = (char *) malloc(sizeof(char) * BUFFER_LIMIT);
threadData.bufferIndex = -1;
threadData.bufferNotFull = PTHREAD_COND_INITIALIZER;
threadData.bufferNotEmpty = PTHREAD_COND_INITIALIZER;
threadData.mutex = PTHREAD_MUTEX_INITIALIZER;
threadData.junk = 0;
pthread_create(&threadProducent, NULL, funProducent, &threadData);
pthread_create(&threadKonzument, NULL, funKonzument, &threadData);
pthread_join(threadProducent, NULL);
pthread_join(threadKonzument, NULL);
free(threadData.buffer);
pthread_mutex_destroy(&threadData.mutex);
pthread_cond_destroy(&threadData.bufferNotFull);
pthread_cond_destroy(&threadData.bufferNotEmpty);
return 0;
}
When I try to run this code, sometimes it stucks in funKonzument at this line:
pthread_cond_wait(&data->bufferNotEmpty, &data->mutex);
But...when I change condition in funProducent method from:
if( randomValue < 13 )
to
if( randomValue > 8 )
everything works fine. Is anyone able to explain me what magic difference is between this two conditions?
You are probably suffering from spurious wakes and some problem with the junk counter. I just removed that counter and added a cond wait loop function (and a little lock context manager) and then the hangings seems to have stopped.
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <stdexcept>
#include <functional>
#define BUFFER_LIMIT 20
struct struktura{
pthread_mutex_t mutex;
pthread_cond_t bufferNotFull;
pthread_cond_t bufferNotEmpty;
int bufferIndex;
char * buffer;
};
// a lock context manager
class mlock {
pthread_mutex_t* mtx;
public:
mlock(pthread_mutex_t& Mtx) :
mtx(&Mtx)
{
int rv=pthread_mutex_lock(mtx);
if(rv) throw std::runtime_error(std::to_string(rv));
}
mlock(const mlock&) = delete;
mlock(mlock&&) = delete;
mlock& operator=(const mlock&) = delete;
mlock& operator=(mlock&&) = delete;
~mlock() {
pthread_mutex_unlock(mtx);
}
};
// silly loop to take care of spurious wakes
void cwait(pthread_cond_t& c, pthread_mutex_t& m, std::function<bool()> f) {
while(f()) pthread_cond_wait(&c, &m);
}
void* funProducent(void *arg){
struktura* data = static_cast<struktura*>(arg);
int i = 0;
while(i < 100) {
mlock dummy(data->mutex);
cwait(data->bufferNotFull, data->mutex, [&](){return data->bufferIndex == BUFFER_LIMIT - 1;});
int randomValue = (rand() % 20) + 1;
if( randomValue < 13 ){
data->buffer[++data->bufferIndex] = 'a';
printf("%2d : Producent at index %d added %c\n", i, data->bufferIndex, data->buffer[data->bufferIndex]);
i++;
pthread_cond_signal(&data->bufferNotEmpty);
}
}
printf("producent is done\n");
return nullptr;
}
void* funKonzument(void *arg){
struktura* data = static_cast<struktura*>(arg);
int i = 0;
while(i < 100) {
mlock dummy(data->mutex);
cwait(data->bufferNotEmpty, data->mutex, [&](){return data->bufferIndex<0;});
printf("\t\t\t%2d : Konzument at index %d consumed %c\n", i, data->bufferIndex, data->buffer[data->bufferIndex]);
data->bufferIndex--;
i++;
pthread_cond_signal(&data->bufferNotFull);
}
printf("\t\t\tkonzument is done\n");
return nullptr;
}
int main() {
pthread_t threadProducent, threadKonzument;
struktura threadData;
threadData.buffer = (char *) malloc(sizeof(char) * BUFFER_LIMIT);
threadData.bufferIndex = -1;
threadData.bufferNotFull = PTHREAD_COND_INITIALIZER;
threadData.bufferNotEmpty = PTHREAD_COND_INITIALIZER;
threadData.mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_create(&threadProducent, NULL, funProducent, &threadData);
pthread_create(&threadKonzument, NULL, funKonzument, &threadData);
pthread_join(threadProducent, NULL);
pthread_join(threadKonzument, NULL);
free(threadData.buffer);
pthread_mutex_destroy(&threadData.mutex);
pthread_cond_destroy(&threadData.bufferNotFull);
pthread_cond_destroy(&threadData.bufferNotEmpty);
return 0;
}
Code:
[peterson_lock.h]
#include <pthread.h>
typedef struct {
volatile bool flag[2];
volatile int victim;
} peterson_lock_t;
void peterson_lock_init(peterson_lock_t &lock) {
lock.flag[0] = lock.flag[1] = false;
lock.victim = 0;
}
void peterson_lock(peterson_lock_t &lock, int id) {
lock.victim = id; // Mark as A
lock.flag[id] = true; // Mark as B
asm volatile ("mfence" : : : "memory");
while (lock.flag[1 - id] && lock.victim == id);
}
void peterson_unlock(peterson_lock_t &lock, int id) {
lock.flag[id] = false;
lock.victim = id;
}
[main.cpp]
#include <stdio.h>
#include "peterson_lock.h"
peterson_lock_t lock;
int count = 0;
void *routine0(void *arg) {
int *cnt = (int *)arg;
for (int i = 0; i < *cnt; ++i) {
peterson_lock(lock, 0);
++count;
peterson_unlock(lock, 0);
}
return NULL;
}
void *routine1(void *arg) {
int *cnt = (int *)arg;
for (int i = 0; i < *cnt; ++i) {
peterson_lock(lock, 1);
++count;
peterson_unlock(lock, 1);
}
}
int main(int argc, char **argv) {
peterson_lock_init(lock);
pthread_t thread0, thread1;
int count0 = 10000;
int count1 = 20000;
pthread_create(&thread0, NULL, routine0, (void *)&count0);
pthread_create(&thread1, NULL, routine1, (void *)&count1);
pthread_join(thread0, NULL);
pthread_join(thread1, NULL);
printf("Expected: %d\n", (count0 + count1));
printf("Reality : %d\n", count);
return 0;
}
Run this program 1000 times, sometimes the result is not 30000. But is I switch A and B, the result is always 30000. But how could this happend?
[please ignore this line, only to make this question could be submitted.please ignore this line, only to make this question could be submitted.please ignore this line, only to make this question could be submitted.]
The algorithm requires that you do swap your A and B. In other words your posted code isn't a correct implementation of Peterson's algorithm.
Let us see what goes wrong.
First take this code:
void peterson_lock(peterson_lock_t &lock, int id) {
lock.victim = id; // Mark as A
lock.flag[id] = true; // Mark as B
asm volatile ("mfence" : : : "memory");
while (lock.flag[1 - id] && lock.victim == id);
}
and write it as a function per process:
void peterson_lock_0(peterson_lock_t &lock) {
lock.victim = 0;
lock.flag[0] = true;
asm volatile ("mfence" : : : "memory");
while (lock.flag[1] && lock.victim == 0);
}
void peterson_lock_1(peterson_lock_t &lock) {
lock.victim = 1;
lock.flag[1] = true;
asm volatile ("mfence" : : : "memory");
while (lock.flag[0] && lock.victim == 1);
}
Then let process 0 execute the first line, then switch to process 1 (executing the whole function) and then back to process 0.
peterson_lock_0: peterson_lock_1:
-------------------------------------------------------
lock.victim = 0;
lock.victim = 1;
lock.flag[1] = true;
asm volatile ("mfence" : : : "memory");
while (lock.flag[0] && lock.victim == 1);
// lock.flag[0] is false so
// the process enters critical
// section
lock.flag[0] = true;
asm volatile ("mfence" : : : "memory");
while (lock.flag[1] && lock.victim == 0);
// lock.victim is 1 so
// the process enters critical
// section
Now both processes are in the critical section. That's bad.
See https://en.wikipedia.org/wiki/Peterson%27s_algorithm for more information
I'm trying to solve my school project in C++. I have to create 15 processes and they have to run in order what means that processes run in this order 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 0. It work but when I try to remove semaphore from the memory I am getting error from semctl. On the end I use "semctl(semid, 0, IPC_RMID, 0" but I get error 22 which means EINVAL but it doesn't make sense and I try to remove semaphore from parrent process so I should have privileges to do that.
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <errno.h>
#include <sys/wait.h>
union semun {
int val;
struct semid_ds *buf;
ushort *array;
};
struct sembuf sops[1];
int semid;
int wait_sem(int index, int pid){
fprintf(stderr, "\n------- Proces %d do operation wait (-1) on semaphore %d\n",pid, index);
sops[0].sem_num = index;
sops[0].sem_op = -1;
sops[0].sem_flg = 0 ;
if (semop(semid, sops, 1)<0){
perror("semop fail wait");
return 1;
}
else
return 0;
}
int signal_sem(int index, int pid){
fprintf(stderr, "\n++++++ Proces %d vykonava operaciu signal (1) na semafore %d\n",pid,index);
sops[0].sem_num = index;
sops[0].sem_op = 1;
sops[0].sem_flg = 0;
if (semop(semid, sops, 1)<0){
perror("semop fail signal");
return 1;
}
else
return 0;
}
void createSem(key_t paKey, int paSemFlg, int paNsems)
{
printf ("uid=%d euid=%d\n", (int) getuid (), (int) geteuid ());
(semid = semget(paKey, paNsems, paSemFlg));
for (int i = 0; i < paNsems; ++i) {
semctl(semid, i, SETVAL, 0);
}
}
void kic()
{
printf("\naaaaaaaaaaaaaa\n");
}
int main() {
key_t key = 1234;
int semflg = IPC_CREAT | 0666;
int nsems = 15;
int semid;
fprintf(stderr, "%d=", sops);
createSem(IPC_PRIVATE, semflg, nsems);
if (semid == -1) {
perror("semget: semget failed");
return 1;
}
else
fprintf(stderr, "semget: semget sucess: semid = %d, parrent pid %d\n", semid, getpid());
int PROCESS_ID = 0;
pid_t PID;
for (int i = 1; i < nsems; i++) {
PID = fork();
if(PID == 0)
{
PROCESS_ID = i;
break;
}
}
if(PID == -1)
{
printf("\nPID ERROR");
}
if(PID != 0) //parrent
{
printf("\n\nparrent with ID %d", PROCESS_ID);
signal_sem(PROCESS_ID+1, PROCESS_ID);
wait_sem(PROCESS_ID, PROCESS_ID);
printf ("uid=%d euid=%d\n", (int) getuid (), (int) geteuid ());
printf("\nEND %d\n", getpid());
int s;
wait(&s);
if((semctl(semid, 0, IPC_RMID, 0))==-1)
{
int a = errno;
printf("\nERROR IPC_RMID %d\n", a);
}
}
if(PID == 0)//child
{
if(wait_sem(PROCESS_ID, PROCESS_ID) == 0){
printf("\nI am child with ID %d", PROCESS_ID);
int ID_NEXT_PROCESS = 1+PROCESS_ID;
if(ID_NEXT_PROCESS == nsems)
ID_NEXT_PROCESS = 0;
signal_sem(ID_NEXT_PROCESS, PROCESS_ID);
return 0;
}
}
return 0;
}
You have two semids. One in global scope, another local to main (which shadows global, you should see a warning). createSem only knows about global one, and initializes it. semctl is called directly by main, and is passed the local one, which is garbage.
I wrote a code to implement spin lock and mutex lock.
There is an interesting but. A magic cout can keep my program alive. If I remove the cout, my program will be sleeping forever. (This only happens in Linux. Windows is doing fine)
Any one have a clue?
#include <pthread.h>
#include <iostream>
#include <queue>
#include <sys/time.h>
#include <stdexcept>
#include <cstdio>
#include <cstdlib>
using namespace std;
#define Tcount 10
#define TheLock MutexLock
static inline int TAS(volatile int * ptr) {
unsigned long result;
asm volatile("lock;"
"xchgl %0, %1;"
: "=r"(result), "=m"(*ptr)
: "0"(1), "m"(*ptr)
: "memory");
return result;
}
class SpinLock {
private:
int lock;
pthread_t owner;
public:
SpinLock() {
lock = 0;
}
void getLock() {
while (TAS(&lock) == 1) {
}
owner = pthread_self();
}
void releaseLock() {
if (lock == 0) {
cout << "Spin no lock" << endl;
return;
} else if (owner == pthread_self()) {
owner = NULL;
lock = 0;
} else {
throw runtime_error("Spin can't release");
}
}
};
class MutexLock {
private:
int lock;
pthread_t owner;
queue<pthread_t> q;
SpinLock qLock;
public:
MutexLock() {
lock = 0;
}
void getLock(int id) {
pthread_t self = pthread_self();
cout<<"a"<<endl;// magic cout
if (TAS(&lock) == 0) {
owner = self;
return;
}
qLock.getLock();
q.push(self);
qLock.releaseLock();
while (owner != self) {
}
}
void releaseLock(int id) {
if (lock == 0) {
cout << "Mutex no lock" << endl;
return;
} else if (owner == pthread_self()) {
qLock.getLock();
if (q.empty()) {
owner = NULL;
lock = 0;
} else {
owner = q.front();
q.pop();
}
qLock.releaseLock();
} else {
throw runtime_error("Mutex can't release");
}
}
};
TheLock lock;
int g = 0;
void* run(void* pt) {
int id = (int) pt;
for (int i = 0; i < 10000; i++) {
lock.getLock(id);
//cout<<"Thread "<<id<<" get lock, g="<<g<<endl;
int next = g + 1;
g = next;
//cout<<"Thread "<<id<<" release lock, g="<<g<<endl;
lock.releaseLock(id);
}
return NULL;
}
int main() {
pthread_t th[Tcount];
long mtime, seconds, useconds;
struct timeval start, end;
gettimeofday(&start, NULL);
for (int i = 0; i < Tcount; i++) {
pthread_create(&th[i], NULL, run, (void*) (i+10));
}
for (int i = 0; i < Tcount; i++) {
pthread_join(th[i], 0);
}
gettimeofday(&end, NULL);
seconds = end.tv_sec - start.tv_sec;
useconds = end.tv_usec - start.tv_usec;
mtime = ((seconds) * 1000000 + useconds);
cout << "g=" << g << endl;
cout << "time=" << mtime << endl;
return 0;
}
You cannot implement a mutex by using the volatile keyword as the operations may not be atomic. This means that the OS might switch to a different thread before the operation has completed.
For mutex you have to use the OS. It is the only thing that knows when threads are being switched.