I'm creating a periodic thread which outputs a square signal on an analogic output. I'm using Posix Skin and Analogy from the Xenomai API.
I tested the real-time performance of my code using an oscilloscope and looking at the latency on the square signal (whose frequency is 1kHz). I am supposed to achieve <100us latency. However, the signal is strongly (>250us latency) perturbated by common interruption signals, like moving the mouse, starting a new program, etc.
The flags in my makefile are set up as such:
gcc -I/usr/xenomai/include -D_GNU_SOURCE -D_REENTRANT -D__XENO__ -I/usr/xenomai/include/posix
main_posix.c -Xlinker -rpath -Xlinker /usr/xenomai/lib -Wl,#/usr/xenomai/lib/posix.wrappers
-L/usr/xenomai/lib -lpthread_rt -lxenomai -lpthread -lrt -lanalogy -lrtdm -o main_posix
and this is the code:
#define PERIOD 1e6
#define FILENAME "analogy0"
#define ANALOG_SUBD 1
#define CHANNEL 0
#define SCAN_SIZE 2
#define DELAI 5
static char *filename = FILENAME;
static int idx_subd = ANALOG_SUBD;
static int idx_chan = CHANNEL;
static int valueUp = 450000;
static int valueDown = 98500;
void *TaskCode(void *arg)
{
unsigned char sgnl = 0;
unsigned long overruns_r = 0;
a4l_desc_t dsc = { .sbdata = NULL };
a4l_chinfo_t *chinfo;
int err = 0;
unsigned int scan_size = SCAN_SIZE;
err = a4l_open(&dsc, filename);
if (err < 0) {
fprintf(stderr,
"insn_write: a4l_open %s failed (err=%d)\n",
filename, err);
return NULL;
}
while(1) {
pthread_wait_np( &overruns_r );
if(sgnl)
err = a4l_sync_write(&dsc,
idx_subd, CHAN(idx_chan), 0, &valueUp, scan_size);
else
err = a4l_sync_write(&dsc,
idx_subd, CHAN(idx_chan), 0, &valueDown, scan_size);
if (err < 0) {
fprintf(stderr,
"insn_write: a4l_sync_write failed (err=%d)\n", err);
goto out_insn_write;
}
sgnl = (sgnl + 1) % 2;
}
out_insn_write:
if (dsc.sbdata != NULL)
free(dsc.sbdata);
a4l_close(&dsc);
return NULL;
}
int main(void)
{
mlockall( MCL_CURRENT | MCL_FUTURE );
pthread_t thread;
int rc, i;
int prio = 99;
struct timespec rqtp, rmtp;
rqtp.tv_sec = 0;
rqtp.tv_nsec = PERIOD;
struct sched_param sparam;
sparam.sched_priority = 99;
rc = pthread_create(&thread, NULL, TaskCode, NULL);
assert(0 == rc);
rc = pthread_setschedparam(&thread, SCHED_FIFO, &sparam);
assert(0 == rc);
rc = clock_gettime( CLOCK_REALTIME, &rmtp );
assert(0 == rc);
rmtp.tv_sec = rmtp.tv_sec + DELAI;
rc = pthread_make_periodic_np(thread, &rmtp, &rqtp);
if(rc == ETIMEDOUT) printf("Début dépassé \n");
else if(rc == ESRCH) printf("Thread invalide \n");
assert(0 == rc);
rc = pthread_join(thread, NULL);
exit(EXIT_SUCCESS);
}
I am strongly suspecting (by looking at the Xenomai scheduler) that my program somehow enters secondary mode. I tried to remove the "assert" statements as well as the relevant printf's, but this was not successful. Any idea how to fix it?
As always, the devil is in the details.
I enabled the -Wall option in gcc, which shows all the warnings. It turned out the headers for pthread_* were not properly loaded, which prevented me from seeing that the first argument of pthread_setschedparam was wrong, and was supposed to be thread and not &thread.
Related
I am working on a MQTT client app in C for an Embedded ARM system that must send message base on GPIO change.
To do this, I have try to launch a pthread that do a epoll_wait and a read on /sys/class/gpio/gpio<x>/value to get the value on change.
First step is the configuration of the gpio as input and edge as both:
root#ad:~# cat /sys/class/gpio/gpio13/direction
in
root#ad:~# cat /sys/class/gpio/gpio13/edge
both
Second step, is the start of the pthread in the main function. (After running the prthread, main will enter in a loop to manage MQTT communication):
// Main
int main(int argc, char *argv[]) {
Network n;
MQTTClient c;
(...)
toStop = false;
pthread_create(&thread_id, NULL, detection, NULL);
while (!toStop)
{
MQTTYield(&c, 100);
(...MQTT stuff...)
}
// stop thread if exists
if(thread_id)
{
pthread_cancel(thread_id);
thread_id = NULL;
}
(...)
}
Then the pthread run the following function:
// detection function call as pthread
void *detection(void *args){
char strvalue[1];
int ret;
int nn;
int ep;
int fd;
struct epoll_event ev, events;
ep = epoll_create1(0);
fd = open("/sys/class/gpio/gpio13/value", O_RDONLY);
nn = read(fd, &strvalue, 1);
if (nn > 0) {
printf("Initial value = %c\n", strvalue[0]);
lseek(fd, 0, SEEK_SET);
}
ev.events = EPOLLIN | EPOLLET; // EPOLLPRI;
ev.data.fd = fd;
ret = epoll_ctl(ep, EPOLL_CTL_ADD, fd, &ev);
printf("ret=%d\n", ret);
while (1)
{
printf("Waiting\n");
ret = epoll_wait(ep, &events, 1, -1);
printf("ret=%d\n", ret);
if(ret > 0) {
lseek(fd, 0, SEEK_SET);
printf("fd=%d\n", events.data.fd);
nn = read(events.data.fd, &strvalue, 1);
printf("nn=%d\n", nn);
printf("value = %c\n", strvalue[0]);
}
}
}
The problem is that when gpio change, epoll_wait got it, but the thread stop during read
Here are the output:
Initial value = 1
ret=0
Waiting
ret=1
fd=7
nn=1
value = 1
Waiting
ret=-1
Waiting
ret=1
fd=7
If I call the function *detection directely in the main (so without pthread), everything is working well.
How to solve this issue ?
Specific issue. thread_id variable name was already used in an other c code included, but compiler dont warning it.
I have written some code that tries to call a function (called worker) every x seconds (in this example, I chose 1s as the interval time). The code is a minimal working example that in reality is way more complex than this.
The code works when it is this simple, but I stumble across errors when running the more complex version for a longer period of time. Thus, I want to increase the robustness of this code an would like to get some ideas on how to do that.
Basically, the worker gets some data, processes it an writes it to a file. I open the file during every call to the worker. In the tests, after some time I get an error that the file cannot be opened anymore. In this regard I also noticed that this happens (maybe just by chance) everytime the worker execution time exceeds the interval time. Reason for this is the getter function which pulls data from remote and this can take some time, depending on the network traffic.
I've been thinking of trying a multithreaded approach, but I am not sure if this is worth the hassle. I would be grateful for any pointers on how to do this in a more robust way.
#include <unistd.h>
#include <stdbool.h>
#include <stdio.h>
#include <signal.h>
#include <stdint.h>
#include <time.h>
#define ALARM_INTERVAL_SEC 1
#define ALARM_INTERVAL_USEC 0
static bool running = true;
static struct itimerval alarm_interval;
static struct timeval previous_time;
static uint64_t loop_count = 0;
static FILE* testfile;
static void
signal_handler(int signum)
{
if (signum == SIGINT || signum == SIGTERM)
{
running = false;
}
}
static void
worker(int signum)
{
// Reset the alarm interval
if(setitimer(ITIMER_REAL, &alarm_interval, NULL) < 0)
{
perror("Error: setitimer");
raise(SIGTERM);
return;
}
struct timeval current_time;
gettimeofday(¤t_time, NULL);
printf("Loop count: %lu\n", loop_count);
printf("Loop time: %f us\n\n", (current_time.tv_sec - previous_time.tv_sec) * 1e6 +
(current_time.tv_usec - previous_time.tv_usec));
previous_time = current_time;
// convert time to human-readable format
char tmbuf[64];
char buf[64];
time_t nowtime = current_time.tv_sec;
struct tm *nowtm = localtime(&nowtime);
strftime(tmbuf, sizeof(tmbuf), "%Y-%m-%d %H:%M:%S", nowtm);
snprintf(buf, sizeof(buf), "%s.%06ld", tmbuf, current_time.tv_usec);
sleep(0.5);
// DO STH
testfile = fopen("testfile.txt", "ab+");
if(testfile == NULL)
{
printf("Error: open testfile");
raise(SIGTERM);
return;
}
fprintf(testfile, "[%s] Loop count: %lu\n", buf, loop_count);
fclose(testfile);
loop_count++;
}
int
main(int argc, char* argv[])
{
signal(SIGINT, signal_handler);
signal(SIGTERM, signal_handler);
signal(SIGALRM, worker);
// Set the alarm interval
alarm_interval.it_interval.tv_sec = 0;
alarm_interval.it_interval.tv_usec = 0;
alarm_interval.it_value.tv_sec = ALARM_INTERVAL_SEC;
alarm_interval.it_value.tv_usec = ALARM_INTERVAL_USEC;
if(setitimer(ITIMER_REAL, &alarm_interval, NULL) < 0)
{
perror("Error: setitimer");
return -1;
}
gettimeofday(&previous_time, NULL);
while(running)
{
sleep(1);
}
alarm_interval.it_value.tv_sec = 0;
alarm_interval.it_value.tv_usec = 0;
if(setitimer(ITIMER_REAL, &alarm_interval, NULL) < 0)
{
perror("Error: resetting itimer failed");
return -1;
}
return 0;
}
I have a program that receives messages via a socket and starts or stops playing a certain sound file depending on the message. In order for the "stop" message to work, I need the sound to play from a separate thread. My solution is to play the sound using alsa from a function I invoke using pthread_create(), and upon receiving a stop message I end the thread using pthread_cancel(). The function that plays the sound is called play_sound(void *args);
Here's what works:
struct args *args;
args->fp = fopen("path/to/soundfile.wav", "r");
args->volume = 1;
play_sound((void *) args);
but as soon as I try to run the function from within a new thread, I get no sound and 100% CPU usage on both my threads:
struct args *args;
int sound_thread;
args->fp = fopen("path/to/soundfile.wav", "r");
args->volume = 1;
pthread_create(&sound_thread, NULL, (void *) play_sound, (void *) args);
I have no idea where to even begin troubleshooting.
My code looks as follows:
#include <alsa/asoundlib.h>
#include <alsa/mixer.h>
#include <stdbool.h>
#include <pthread.h>
#include "server.h"
#include "sound.h"
//#include "log.h"
int sound_thread;
struct args {
FILE *fp;
float volume;
};
void init_sound ()
{
sound_thread = -1;
}
void stop_sound ()
{
if (sound_thread != -1) {
pthread_cancel(sound_thread);
keep_playing = false;
sound_thread = -1;
}
}
void dispatch_sound (FILE *fp, float volume)
{
// this function serves to create a new thread for the
// sound to be played from. This is what's giving me
// headaches.
if (sound_thread != -1) {
stop_sound();
}
struct args *args = (struct args *) malloc(sizeof(struct args));
args->fp = fp;
args->volume = volume;
if (pthread_create(&sound_thread, NULL, (void *) play_sound, args) != 0)
sound_thread = -1;
}
}
bool play_sound (void *args)
{
// This function actually plays the sound using functions
// from the alsa lib. it works when invoked regularly without
// threading.
keep_playing = true;
FILE *fp;
int volume;
bool success;
unsigned int samplerate;
int bufsz;
char *buf;
snd_pcm_t *pcm;
snd_pcm_hw_params_t *params;
snd_pcm_uframes_t frames;
samplerate = SAMPLERATE;
fp = ((struct args *) args)->fp;
volume = ((struct args *) args)->volume;
// volume is not actually used right now, since I took out
// the function that sets the volume before playing the
// audio in order to make it easier to pinpoint the issue.
if (snd_pcm_open(&pcm, PCM_DEVICE, SND_PCM_STREAM_PLAYBACK, 0) < 0) {
success = false;
}
snd_pcm_hw_params_alloca(¶ms);
snd_pcm_hw_params_any(pcm, params);
if (snd_pcm_hw_params_set_access(pcm, params, SND_PCM_ACCESS_RW_INTERLEAVED) < 0) {
success = false;
}
if (snd_pcm_hw_params_set_format(pcm, params, SND_PCM_FORMAT_S16_LE) < 0) {
success = false;
}
if (snd_pcm_hw_params_set_channels(pcm, params, CHANNELS) < 0) {
success = false;
}
if (snd_pcm_hw_params_set_rate_near(pcm, params, &samplerate, 0) < 0) {
success = false;
}´
if (snd_pcm_hw_params(pcm, params) < 0) {
success = false;
}
snd_pcm_hw_params_get_period_size(params, &frames, 0);
bufsz = frames * CHANNELS * SAMPLE_SIZE;
buf = (char *) malloc(bufsz);
while (keep_playing) {
while (fread(buf, bufsz, 1, fp) != 0 && keep_playing) {
int err;
if ((err = snd_pcm_writei(pcm, buf, frames)) == -EPIPE) {
snd_pcm_prepare(pcm);
}
}
rewind(fp);
}
snd_pcm_drain(pcm);
snd_pcm_close(pcm);
free(buf);
return success;
}
From the man page of pthread_cancel:
On Linux, cancellation is implemented using signals. Under the NPTL threading implementation, the first real-time signal (i.e., signal 32)
is used for this purpose. On LinuxThreads, the second real-time signal is used, if real-time signals are available, otherwise SIGUSR2 is
used.
In your while(keep_playing) loop, you aren't yielding the thread enough to handle the cancel signal; in your main thread; you aren't waiting for the result of the cancel request, ergo both threads hog the cpu.
A small delay before you restart playing the sound and pthread_join() after you call pthread_cancel should fix your problem.
So, I'm new to shared memory and the shm functions in C.
I've got two programs; master and slave. In the most general sense: the master program creates a sharedNum integer in shared memory and forks off multiple processes that exec the slave program. The slave program processes must then increment sharedNum from shared memory (perhaps multiple times, even) and print it to a specified file. I am 100% confident that everything is working (though it may look messy) aside from the shared memory manipulation. I've been testing throughout development.
The problem I'm having is with race conditions in the slave program processes. I understand that I need to implement the Bakery algorithm in order to lock and unlock processes from accessing the critical section. The lack of this causes sharedNum manipulation to be off.
I attempted to implement a form of the Bakery algorithm in my slave program, but it doesn't seem to work... Through testing, I've discovered that the choosing and turnNum variables (which I NEED to use for the Bakery algorithm, as far as I understand) are themselves experiencing race conditions. How is this avoidable? I'm pretty sure they need to be in shared memory as well, otherwise they couldn't be updated by multiple processes...
Thanks in advance.
Program dumps follow.
master.c:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<ctype.h>
#include<string.h>
#include<errno.h>
#include<signal.h>
#include<sys/ipc.h>
#include<sys/shm.h>
// global variables
pid_t *children;
int slave_max;
// globals relating to shared memory
key_t shmkey;
int shmid_sharedNum;
int *sharedNum;
void handle_sigalrm(int signum, siginfo_t *info, void *ptr)
{
// prevents multiple interrupts
signal(SIGINT, SIG_IGN);
fprintf(stderr, "Master ran out of time\n");
// detaching and deleting shared memory
shmdt(sharedNum);
shmctl(shmid_sharedNum, IPC_RMID, NULL);
// creating tmp_children to replace children
// this way children can be freed before SIGTERM
pid_t tmp_children[slave_max];
int i;
for (i = 0; i < slave_max; i++);
{
tmp_children[i] = children[i];
}
// freeing allocated memory
free(children);
// terminate child processes
for (i = 0; i < slave_max; i++)
{
kill(tmp_children[i], SIGTERM);
}
}
void handle_sigint(int signum, siginfo_t *info, void *ptr)
{
// prevents multiple interrupts
signal(SIGINT, SIG_IGN);
signal(SIGALRM, SIG_IGN);
fprintf(stderr, " interrupt was caught by master\n");
// detaching and deleting shared memory
shmdt(sharedNum);
shmctl(shmid_sharedNum, IPC_RMID, NULL);
// creating tmp_children to replace children
// this way children can be freed before SIGTERM
pid_t tmp_children[slave_max];
int i;
for (i = 0; i < slave_max; i++)
{
tmp_children[i] = children[i];
}
// freeing allocated memory
free(children);
// terminate child processes
for (i = 0; i < slave_max; i++)
{
kill(tmp_children[i], SIGTERM);
}
}
void catch_sigalrm()
{
static struct sigaction _sigact;
memset(&_sigact, 0, sizeof(_sigact));
_sigact.sa_sigaction = handle_sigalrm;
_sigact.sa_flags = SA_SIGINFO;
sigaction(SIGALRM, &_sigact, NULL);
}
void catch_sigint()
{
static struct sigaction _sigact;
memset(&_sigact, 0, sizeof(_sigact));
_sigact.sa_sigaction = handle_sigint;
_sigact.sa_flags = SA_SIGINFO;
sigaction(SIGINT, &_sigact, NULL);
}
int main(int argc, char *argv[])
{
// default variables
int i = 0; // to be used as a counter variable
slave_max = 5;
char slave_max_str[25]; // arbitrary size
char *log_filename = NULL;
int slave_increment = 3;
char slave_increment_str[25]; // arbitrary size
int master_time = 20;
// shared memory initialization
shmkey = ftok("./master", 118371); // arbitrary key
shmid_sharedNum = shmget(shmkey, sizeof(sharedNum), 0600 | IPC_CREAT);
sharedNum = (int *)shmat(shmid_sharedNum, NULL, 0);
sharedNum[0] = 0;
// handling command line args with getopt
int c;
while((c = getopt(argc, argv, "hs:l:i:t:")) != -1)
{
switch(c)
{
// -h : program help
case 'h':
// the following if-else block makes sure
// that -h will be used by itself
if (argc == 2)
{
printf("%s -h : program help\n", argv[0]);
printf("%s -s [integer] : set max number of slave processes\n", argv[0]);
printf("%s -l [filename] : set log filename\n", argv[0]);
printf("%s -i [integer] : set slave process incrementer\n", argv[0]);
printf("%s -t [integer] : set number of seconds master will terminate\n", argv[0]);
exit(0);
}
else
{
fprintf(stderr, "%s: option must be used by itself -- 'h'\n", argv[0]);
exit(1);
}
// -s [integer] : set max number of slave processes
case 's':
slave_max = atoi(optarg);
break;
// -l [filename] : set log filename
case 'l':
log_filename = optarg;
break;
// -i [integer] : set slave process incrementer
case 'i':
slave_increment = atoi(optarg);
break;
// -t [integer] : set number of seconds master will terminate
case 't':
master_time = atoi(optarg);
break;
// the following case takes care of user input errors
case '?':
if (optopt == 's')
fprintf(stderr, "Error: -s requires an integer\n");
else if (optopt == 'l')
fprintf(stderr, "Error: -l requires a filename\n");
else if (optopt == 'i')
fprintf(stderr, "Error: -i requires an integer\n");
else if (optopt == 't')
fprintf(stderr, "Error: -t requires an integer\n");
else if (isprint(optopt))
fprintf(stderr, "Error: input can't be printed\n");
else
fprintf(stderr, "Error: invalid syntax\n");
exit(1);
default:
abort();
}
}
catch_sigint();
catch_sigalrm();
alarm(master_time);
// if log_filename wasn't passed in by -l,
// its default value is set here...
if (!log_filename)
log_filename = "test.out";
// setting slave_increment_str and slave_max_str
// for use in future execl
snprintf(slave_increment_str, 25, "%i", slave_increment);
snprintf(slave_max_str, 25, "%i", slave_max);
// initializing pids
if ((children = (pid_t *)(malloc(slave_max * sizeof(pid_t)))) == NULL)
{
errno = ENOMEM;
perror("children malloc");
exit(1);
}
pid_t p;
// forking off child processes
for (i = 0; i < slave_max; i++)
{
p = fork();
if (p < 0)
{
fprintf(stderr,"Error: fork failed\n");
continue;
}
if (p == 0)
{
children[i] = p;
execl("./slave", "slave", "-l", log_filename, "-s", slave_max_str, "-i", slave_increment_str, (char *) NULL);
exit(0);
}
}
// waiting for all child processes to finish
for (i = 0; i < slave_max; i++)
{
int status;
waitpid(children[i], &status, 0);
}
// clean up and finish
free(children);
shmdt(sharedNum);
shmctl(shmid_sharedNum, IPC_RMID, NULL);
return 0;
}
slave.c:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<ctype.h>
#include<sys/types.h>
#include<time.h>
#include<signal.h>
#include<sys/ipc.h>
#include<sys/shm.h>
// global variables
pid_t parent;
pid_t child;
int childProc;
// globals for shared memory
key_t shmkey;
int shmid_sharedNum, shmid_choosing, shmid_turnNum;
int *sharedNum; int *choosing; int *turnNum;
void handle_sigterm(int signum, siginfo_t *info, void *ptr)
{
// detaching and deleting shared memory
shmdt(sharedNum);
shmdt(choosing);
shmdt(turnNum);
shmctl(shmid_sharedNum, IPC_RMID, NULL);
shmctl(shmid_choosing, IPC_RMID, NULL);
shmctl(shmid_turnNum, IPC_RMID, NULL);
fprintf(stderr, "Process #%i was terminated by master\n", childProc);
exit(0);
}
void catch_sigterm()
{
static struct sigaction _sigact;
memset(&_sigact, 0, sizeof(_sigact));
_sigact.sa_sigaction = handle_sigterm;
_sigact.sa_flags = SA_SIGINFO;
sigaction(SIGTERM, &_sigact, NULL);
}
int main(int argc, char *argv[])
{
// default variables
parent = getppid();
child = getpid();
childProc = (int)(child - parent);
int i, j, maxCounter; // to be used as a counter variables
int slave_max = 1;
char *log_filename = NULL;
int slave_incrementer = 3;
srand(time(NULL));
int napTime;
// shared memory initialization
shmkey = ftok("./master", 118371); // arbitrary key
shmid_sharedNum = shmget(shmkey, sizeof(sharedNum), 0600 | IPC_CREAT);
sharedNum = (int *)shmat(shmid_sharedNum, NULL, 0);
shmid_choosing = shmget(shmkey, sizeof(choosing), 0600 | IPC_CREAT);
choosing = (int *)shmat(shmid_choosing, NULL, 0);
shmid_turnNum = shmget(shmkey, sizeof(turnNum), 0600 | IPC_CREAT);
turnNum = (int *)shmat(shmid_turnNum, NULL, 0);
catch_sigterm();
signal(SIGINT, SIG_IGN);
// implementing getopt to handle command line args
int c;
while((c = getopt(argc, argv, "s:l:i:")) != -1)
{
switch(c)
{
// -s [integer] : number of slave processes
case 's':
slave_max = atoi(optarg);
// -l [filename] : set log filename
case 'l':
log_filename = optarg;
break;
// -i [integer] : set slave process incrementer
case 'i':
slave_incrementer = atoi(optarg);
break;
// this case takes care of user input errors
case '?':
if (optopt == 's')
fprintf(stderr, "Error: -s requires an integer\n");
else if (optopt == 'l')
fprintf(stderr, "Error: -l requires a filename\n");
else if (optopt == 'i')
fprintf(stderr, "Error: -i requires an integer\n");
else if (isprint(optopt))
fprintf(stderr, "Error: input can't be printed\n");
else
fprintf(stderr, "Error: invalid syntax\n");
exit(1);
default:
abort();
}
}
// if log_filename wasn't passed in by -l,
// its default value is set here...
if (!log_filename)
log_filename = "test.out";
struct timespec now;
long curTime;
int max = 0;
for (i = 0; i < slave_incrementer; i++)
{
// execute code to enter critical section
choosing[(childProc-1)] = 1;
for (maxCounter = 0; maxCounter < slave_max; maxCounter++)
{
if((turnNum[maxCounter]) > max)
max = (turnNum[maxCounter]);
}
turnNum[(childProc-1)] = 1 + max;
printf("turnNum for process #%i = %i\n", childProc, turnNum[(childProc-1)]);
choosing[(childProc-1)] = 0;
for (j = 0; j < slave_max; j++)
{
while (choosing[j] == 1) {}
while ((turnNum[j] != 0) && (turnNum[j] < turnNum[(childProc-1)])) {}
}
// critical section
napTime = rand() % 3;
sleep(napTime);
sharedNum[0]++;
clock_gettime(CLOCK_REALTIME, &now);
curTime = ((((long)now.tv_sec) * 1000000000) + (long)now.tv_nsec);
// write message to log file here
// for testing purposes:
printf("File modified by process #%i (increment %i) at time %ld with sharedNum = %i\n", childProc, (i+1), curTime, sharedNum[0]);
napTime = rand() % 3;
sleep(napTime);
// exit from critical section
turnNum[(childProc-1)] = 0;
}
// clean up and finish
shmdt(sharedNum);
shmdt(choosing);
shmdt(turnNum);
shmctl(shmid_sharedNum, IPC_RMID, NULL);
shmctl(shmid_choosing, IPC_RMID, NULL);
shmctl(shmid_turnNum, IPC_RMID, NULL);
return 0;
}
(Broken) Bakery algorithm section of slave.c:
// execute code to enter critical section
choosing[(childProc-1)] = 1;
for (maxCounter = 0; maxCounter < slave_max; maxCounter++)
{
if((turnNum[maxCounter]) > max)
max = (turnNum[maxCounter]);
}
turnNum[(childProc-1)] = 1 + max;
printf("turnNum for process #%i = %i\n", childProc, turnNum[(childProc-1)]);
choosing[(childProc-1)] = 0;
for (j = 0; j < slave_max; j++)
{
while (choosing[j] == 1) {}
while ((turnNum[j] != 0) && (turnNum[j] < turnNum[(childProc-1)])) {}
}
// critical section
napTime = rand() % 3;
sleep(napTime);
sharedNum[0]++;
clock_gettime(CLOCK_REALTIME, &now);
curTime = ((((long)now.tv_sec) * 1000000000) + (long)now.tv_nsec);
// write message to log file here
// for testing purposes:
printf("File modified by process #%i (increment %i) at time %ld with sharedNum = %i\n", childProc, (i+1), curTime, sharedNum[0]);
napTime = rand() % 3;
sleep(napTime);
// exit from critical section
turnNum[(childProc-1)] = 0;
I discovered this a while ago, but forgot to provide the solution. It turns out that my implementation of the Bakery algorithm was fine. The real issue came from how I was initializing my shared memory segments in slave.c. By using different keys for each segment, I was able to run my master program with expected results.
updated "shared memory initialization" section of slave.c:
// shared memory initialization
shmkey = ftok("./master", 118371); // arbitrary key #1
shmid_sharedNum = shmget(shmkey, sizeof(sharedNum), 0600 | IPC_CREAT);
sharedNum = (int *)shmat(shmid_sharedNum, NULL, 0);
shmkey = ftok("./slave", 118372); // arbitrary key #2
shmid_choosing = shmget(shmkey, sizeof(choosing), 0600 | IPC_CREAT);
choosing = (int *)shmat(shmid_choosing, NULL, 0);
shmkey = ftok("./slave", 118373); // arbitrary key #3
shmid_turnNum = shmget(shmkey, sizeof(turnNum), 0600 | IPC_CREAT);
turnNum = (int *)shmat(shmid_turnNum, NULL, 0);
This small experiment was to demonstrate the gain in I/O performance by increasing I/O priority of thread .
For this purpose ioprio_set system call was used.
To see this api in action you can explore code of ionice utility.
So, I have spawned some threads to copy some files of same size(where one thread copy one file to some output file)
and for one thread I have called ioprio_set.
ioprio_set(IOPRIO_WHO_PROCESS, 0, IOPRIO_PRIO_VALUE(IOPRIO_CLASS_RT,0));
These parameter are such that it will escalate the priority of calling thread to real time(ioclass) with highest value(i.e 0). And for measurement of performance I have used the clock_gettime around io operations of threads.
But after running program I can't see the gain in execution time calling thread.
Following is the code
#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>
#include<sys/syscall.h>
#include<unistd.h>
#include<time.h>
#define NB_THREADS 3
#define IOPRIO_PRIO_VALUE(class, data) (((class) << IOPRIO_CLASS_SHIFT) | data)
#define IOPRIO_CLASS_SHIFT 13
#define BILLION 1000000000L
enum {
IOPRIO_CLASS_NONE,
IOPRIO_CLASS_RT,
IOPRIO_CLASS_BE,
IOPRIO_CLASS_IDLE,
};
enum {
IOPRIO_WHO_PROCESS = 1,
IOPRIO_WHO_PGRP,
IOPRIO_WHO_USER,
};
static inline int ioprio_set(int which, int who, int ioprio)
{
return syscall(SYS_ioprio_set, which, who, ioprio);
}
double times[NB_THREADS];
void *copyfile(void *args){
struct timespec start, stop;
double accum;
int x;
char finname[10];// name of the file to be copied
char foutname[10],ch;
int *id;
FILE *source, *target;
id = (int *) args;
sprintf(finname,"large%d.txt",*id);
sprintf(foutname,"out%d.txt",*id);
if(*id == 2){
x=ioprio_set(IOPRIO_WHO_PROCESS, 0, IOPRIO_PRIO_VALUE(IOPRIO_CLASS_RT,0));
//printf("##%d##",x);
}
if( clock_gettime( CLOCK_REALTIME, &start) == -1 ) {
perror( "clock gettime" );
exit(EXIT_FAILURE);
}
//printf("%s##%s\n\n",finname,foutname);
source = fopen(finname, "r");
if( source == NULL )
{
printf("Press any key to exit...\n");
exit(EXIT_FAILURE);
}
target = fopen(foutname, "w");
if( target == NULL )
{
fclose(source);
printf("Press any key to exit...\n");
exit(EXIT_FAILURE);
}
while( ( ch = fgetc(source) ) != EOF )
{
fputc(ch, target);
}
//printf("\ndone copying file %s to %s in %lf\n",finname,foutname,accum);
fclose(source);
fclose(target);
if( clock_gettime( CLOCK_REALTIME, &stop) == -1 ) {
perror( "clock gettime" );
exit(EXIT_FAILURE);
}
accum = ( stop.tv_sec - start.tv_sec )
+ (double)( stop.tv_nsec - start.tv_nsec )
/ (double)BILLION;
times[*id]=accum;
}
int main()
{
unsigned int ints[NB_THREADS],i;
int err;
pthread_t threads[NB_THREADS];
for (i = 0; i < NB_THREADS; ++i) {
ints[i] = i;
err=pthread_create(&threads[i], NULL, copyfile , &ints[i]);
if (err != 0)
printf("\ncan't create thread :[%s]", strerror(err));
else
printf("\n Thread %d created successfully\n",i);
//pthread_join(threads[i], NULL);
}
for (i = 0; i < NB_THREADS; ++i) {
pthread_join(threads[i], NULL);
}
for (i = 0; i < NB_THREADS; ++i) {
printf("\n %d : %lf",i,times[i]);
}
return 0;
}
Note : to call this api with IOPRIO_CLASS_RT, permissions are needed. (So already ran with sudo). ioprio_set is returning 0(successful call, it return -1 in case of failure).
Note : to compile this code add -lpthread and -lrt to gcc.
Q. Is some thing wrong with code structure.?
Q. How can I carry out this experiment correctly?
Q. What can be done to achieve desired thing i.e increasing i/o priority of thread over other threads?