I am somewhat new to threads but have been reading up on pthreads and trying to write a test program that will eventually be used in an application. The basic operation for this test app is to:
spawn two threads,
allow a user to enter a number (sender thread) that is stored in a value of a conditioned variable and then use a signal to wake up a second thread (receiver thread).
I am showing the code and the output screen to show the results of the test output.
Basically, when I enter a number other than 0 the signal does not wake up the second thread. When a 0 is entered the second thread wakes up and performs as expected (i.e. exits).
The question is why does the second thread not wake up for other numbers (i.e. 1 or 2). I have tried this with both a pthread_cond_timedwait() and pthread_cond_wait() but I get the same results in either case.
I would appreciate any and all suggestions. Thanks.
//============================ terminal screen =================================
enter a message to send:
0 - exit
1 - message 1
2 - message 2
1
messageSender: msg.value: 1
messageSender: signal sent!
2
messageSender: msg.value: 2
messageSender: signal sent!
0
messageSender: msg.value: 0
messageSender: signal sent!
exit pthread_cond_timedwait, testVal: 1 value: 0
0
messageReceiverThread msg.value received: 0
exiting
//================= apps/pthread_com_example.h =================================
#ifndef PTHREAD_COM_EXAMPLE_H_INCLUDED
#define PTHREAD_COM_EXAMPLE_H_INCLUDED
// | added for thread com example RLB 04Dec2021
// V
#if(defined PTHREAD_COM_EXAMPLE)
#include <stdio.h> // standard I/O routines
#include <stdlib.h> // rand() and srand() functions
// number of threads used to service requests
#define NUM_HANDLER_THREADS 2
/* format of a message structure */
typedef struct {
pthread_mutex_t mutex; // message mutex
pthread_cond_t cond; // message condition variable
int testVal; // use this as the predicate
int value; // value to be passed
}message_t;
// ^
// | added for thread com example RLB 04Dec2021
#endif
#endif // PTHREAD_COM_EXAMPLE_H_INCLUDED
//=================== pthread_com_example app ==============================
#include <iostream>
#include <pthread.h>
#define PTHREAD_COM_EXAMPLE
#include <apps/pthread_com_example.h>
#include <unistd.h>
#include <sys/time.h> // struct timeval definition
#if(defined PTHREAD_COM_EXAMPLE)
/* global mutex for example. assignment initializes it.
note: a RECURSIVE mutex is used, since a handler
thread might try to lock it twice consecutively. */
#define MSG1 1 // trigger messasge 1
#define MSG2 2 // trigger message 2
#define MSGX 0 // message to exit
std::string message1 = "this is an example message in response to A";
std::string message2 = "this is an example message in response to B";
struct timeval now;
struct timespec timeout;
message_t msg = {
// initialize the message structure.
PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP, PTHREAD_COND_INITIALIZER, 0, 0
};
#endif
#if(defined PTHREAD_COM_EXAMPLE)
#define DEBUG
// wait for sending thread to signal variable is ready to read
int timedWaitForCond(pthread_cond_t* pCond, pthread_mutex_t* pMutex, int timeToWait)
{
gettimeofday(&now,NULL);
timeout.tv_sec = now.tv_sec + timeToWait;
timeout.tv_nsec = now.tv_usec * 1000;
int retcode = 0;
while((msg.testVal == 0) && (retcode != ETIMEDOUT))
{
// this unlocks the mutux while waiting on signal from another thread
// retcode = pthread_cond_timedwait(pCond, pMutex, &timeout);
retcode = pthread_cond_wait(pCond,pMutex);
// upon return the mutex is locked again
std::cout << "exit pthread_cond_timedwait, testVal: " << msg.testVal << " value: " << msg.value << std::endl;
if(retcode != 0)
{
switch(retcode)
{
case ETIMEDOUT: // exits while loop
std::cout << "Timed Conditional Wait, Timed Out" << std::endl;
return(-1); // conditioned variable is not ready yet
break;
default: // stays in while loop
std::cout << "Unexpected timed conditional wait result: " << retcode << std::endl;
break;
}
}else
{
return(0); // conditioned variable ready to read
break; // exit while if legitimate signal occurred
}
}
return(0);
}
void outputMenu(void)
{
std::cout << std::endl << std::endl;
std::cout << "enter a message to send: " << std::endl;
std::cout << " 0 - exit" << std::endl;
std::cout << " 1 - message 1" << std::endl;
std::cout << " 2 - message 2" << std::endl;
}
int getUserMessage(void)
{
int rc;
std::cin >> rc;
return(rc);
}
/*
* function messageReceiverThread():
* attempts to wait for message wait. If result is good
* sends signal to waiting thread and then performs a
* timed conditioned wait to receive the message from the
* sending thread.
*
* algorithm:
* input: messageWait mutex
* output: character from message_t structure
* memory: shared message_t structure
*/
void* messageReceiverThread(void* arg)
{
int timeToWait = 10; // use 10 second timeout for this example
int rc = 0;
// first, lock the mutex, to assure exclusive access to the conditioned variable
rc = pthread_mutex_lock(&msg.mutex);
if (rc)
{ // an error has occurred
std::cout << "messageReceiver: pthread_mutex_lock error: " << rc << std::endl;
pthread_exit(NULL);
}
// mutex is now locked
outputMenu();
while(1)
{
msg.testVal = 0; // clear signal value
rc = timedWaitForCond( &msg.cond , &msg.mutex, timeToWait); // cond variable is unlocked while waiting
// var is locked again after exit from timedWaitForCond()
if(0 != rc)
{
// Timeout or error, no messasge yet, for this app just loop around...
std::cout << "no message yet, rc = " << rc << std::endl;
}else
{
// return from timed wait with valid signal from sender
std::cout << msg.value << std::endl;
std::cout << "messageReceiverThread msg.value received: " << msg.value << std::endl;
switch(msg.value)
{
case MSG1:
std::cout << message1 << std::endl;
break;
case MSG2:
std::cout << message2 << std::endl;
break;
case MSGX:
std::cout << "exiting" << std::endl;
pthread_exit(arg);
break;
default:
std::cout << "unrecognized message" << std::endl;
break;
}
outputMenu();
}
}
pthread_exit(arg);
}
/*
* function messageSenderThread():
* attempts to wait for user to enter a number at the console.
* When the number is received this thread will attempt a lock
* on the message mutex and, once obtained, write the value
* to the msg.msgNbr. Then this thread will send a signal to
* the messageReceiver thread. Once completed this thread will
* again wait on the user for an additional imput, until the
* value enterd by the user is 0, which will cause an exit.
*
* algorithm:
* input: messageWait mutex
* output: character from message_t structure
* memory: shared message_t structure
*/
void* messageSenderThread(void* arg)
{
int rc = 0,msgCode = 0;
while(1)
{
rc = pthread_mutex_lock(&msg.mutex);
if(rc)
{
std::cout << "messageSender: lock request failed,result: " << rc << std::endl;
pthread_exit(arg);
}
msgCode = getUserMessage();
msg.value = msgCode;
msg.testVal = 1;
std::cout << "messageSender: msg.value: " << msg.value << std::endl;
// signal the condition variable - there's a new message to handle
rc = pthread_cond_signal(&msg.cond);
std::cout << "messageSender: signal sent! " << std::endl;
if(rc)
std::cout << "messageSender: pthread_cond_signal failed,result: " << rc << std::endl;
// unlock mutex
rc = pthread_mutex_unlock(&msg.mutex);
if(rc)
{
std::cout << "messageSender: unlock request failed,result: " << rc << std::endl;
pthread_exit(arg);
}
if(!msgCode) // user entered the exit code
pthread_exit(arg);
}
pthread_exit(arg);
}
#undef DEBUG
#endif
int main(int argc, char *argv[])
{
#if(defined PTHREAD_COM_EXAMPLE)
int thr_id[NUM_HANDLER_THREADS]; // thread IDs
pthread_t thread1,thread2; // thread's structures
// create the message handling threads
thr_id[0] = 0;
(void) pthread_create(&thread1, NULL, messageReceiverThread, (void*)&thr_id[0]);
thr_id[1] = 1;
(void) pthread_create(&thread2, NULL, messageSenderThread, (void*)&thr_id[1]);
// now wait for threads to exit...
(void) pthread_join(thread1, NULL);
(void) pthread_join(thread2, NULL);
return 0;
#endif
}
The problem here is starvation. Your messageSenderThread() function keeps the mutex locked practically all the time. Each time it releases the mutex at the bottom of the loop, the very next thing it does is (if you don't type "0") it re-locks the mutex back at the top of the loop. The messageReceiverThread() always loses the race to lock the mutex.
The reason why the message receiver does print something after you enter the "quit" command (0), is because the messageSenderThread() unlocks the mutex and it exits after you enter a zero. That finally allows the receiver to lock the mutex and do its thing.
Related
I need your help. Program A executes program B with fork(). Every 5 seconds the process belonging to program B is interrupted. If the user enters any key within a certain time, the process is continued and interrupted again after the same time interval. If no key is entered, both program A and program B are terminated prematurely. I have tried the following code, but it does not work. Any suggestions/tips that will help me?
#include <iostream>
#include <chrono>
#include <unistd.h>
#include <sys/wait.h>
#include <signal.h>
using namespace std;
using namespace chrono;
int pid;
void signal_handler(int signum) {
cout << "Programm B is interrupted. Please enter any key within 5 or the programm will be terminated" << endl;
kill(pid,SIGSTOP);
alarm(5);
pause();
alarm(5);
}
int main(int argc, char* argv[]) {
//Usage
if(string(argv[1]) == "h" || string(argv[1]) == "help"){
cout << "usage" << endl;
return 0;
}
signal(SIGALRM, signal_handler);
pid = fork();
if (pid == 0) {
cout << "Name of programm B: " << argv[1] << endl;
cout << "PID of programm B: " << getpid() << endl;
execvp(argv[1], &argv[1]);
} else if (pid > 0) {
cout << "PID of programm A: " << getpid() << endl;
high_resolution_clock::time_point t1 = high_resolution_clock::now();
waitpid(pid, nullptr, 0);
high_resolution_clock::time_point t2 = high_resolution_clock::now();
auto duration = duration_cast<milliseconds>(t2 - t1).count();
cout << "Computing time: " << duration << "ms" << endl;
} else {
cerr << "error << endl;
return 1;
}
return 0;
}
Any help or sulution. I am a beginner in c++ btw.
Signals can get tricky and there are lots of issues with your approach.
You should:
kick off the timer (alarm(5)) in main
do the sighandler registration and timer kick-off after you've spawned the child (or you somewhat risk running the signal handler in the child in between fork and execvp)
use sigaction rather than signal to register the signal, as the former has clear portable semantics unlike the latter
loop on EINTR around waitpid (as signal interruptions will cause waitpid to fail with EINTR)
As for the handler, it'll need to
use only async-signal-safe functions
register another alarm() around read
unblock SIGALRM for the alarm around read but not before you somehow mark yourself as being in your SIGALRM signal handler already so the potential recursive entry of the handler can do a different thing (kill the child and exit)
(For the last point, you could do without signal-unblocking if you register the handler with .sa_flags = SA_NODEFER, but that has the downside of opening up your application to stack-overflow caused by many externally sent (via kill) SIGALRMs. If you wanted to handle externally sent SIGALRMs precisely, you could register the handler with .sa_flags=SA_SIGINFO and use info->si_code to differentiate between user-sends and alarm-sends of SIGALRM, presumably aborting on externally-sent ones)
It could look something like this (based on your code):
#include <iostream>
#include <chrono>
#include <unistd.h>
#include <sys/wait.h>
#include <signal.h>
#include <string.h>
//AS-safe raw io helper functions
ssize_t /* Write "n" bytes to a descriptor */
writen(int fd, const char *ptr, size_t n)
{
size_t nleft;
ssize_t nwritten;
nleft = n;
while (nleft > 0) {
if ((nwritten = write(fd, ptr, nleft)) < 0) {
if (nleft == n)
return(-1); /* error, return -1 */
else
break; /* error, return amount written so far */
} else if (nwritten == 0) {
break;
}
nleft -= nwritten;
ptr += nwritten;
}
return(n - nleft); /* return >= 0 */
}
ssize_t writes(int fd, char const *str0) { return writen(fd,str0,strlen(str0)); }
ssize_t writes2(char const *str0) { return writes(2,str0); }
//AS-safe sigprockmask helpers (they're in libc too, but not specified as AS-safe)
int sigrelse(int sig){
sigset_t set; sigemptyset(&set); sigaddset(&set,sig);
return sigprocmask(SIG_UNBLOCK,&set,0);
}
int sighold(int sig){
sigset_t set; sigemptyset(&set); sigaddset(&set,sig);
return sigprocmask(SIG_BLOCK,&set,0);
}
#define INTERRUPT_TIME 5
using namespace std;
using namespace chrono;
int pid;
volatile sig_atomic_t recursing_handler_eh; //to differentiate recursive executions of signal_handler
void signal_handler(int signum) {
char ch;
if(!recursing_handler_eh){
kill(pid,SIGSTOP);
writes2("Programm B is interrupted. Please type enter within 5 seconds or the programm will be terminated\n");
alarm(5);
recursing_handler_eh = 1;
sigrelse(SIGALRM);
if (1!=read(0,&ch,1)) signal_handler(signum);
alarm(0);
sighold(SIGALRM);
writes2("Continuing");
kill(pid,SIGCONT);
recursing_handler_eh=0;
alarm(INTERRUPT_TIME);
return;
}
kill(pid,SIGTERM);
_exit(1);
}
int main(int argc, char* argv[]) {
//Usage
if(string(argv[1]) == "h" || string(argv[1]) == "help"){
cout << "usage" << endl;
return 0;
}
pid = fork();
if (pid == 0) {
cout << "Name of programm B: " << argv[1] << endl;
cout << "PID of programm B: " << getpid() << endl;
execvp(argv[1], &argv[1]);
} else if (pid < 0) { cerr << "error" <<endl; return 1; }
struct sigaction sa; sa.sa_handler = signal_handler; sigemptyset(&sa.sa_mask); sa.sa_flags=0; sigaction(SIGALRM, &sa,0);
//signal(SIGALRM, signal_handler);
alarm(INTERRUPT_TIME);
cout << "PID of programm A: " << getpid() << endl;
high_resolution_clock::time_point t1 = high_resolution_clock::now();
int r;
do r = waitpid(pid, nullptr, 0); while(r==-1 && errno==EINTR);
high_resolution_clock::time_point t2 = high_resolution_clock::now();
auto duration = duration_cast<milliseconds>(t2 - t1).count();
cout << "Computing time: " << duration << "ms" << endl;
return 0;
}
Not that the above will wait only for an enter key. To wait for any key, you'll need to put your terminal in raw/cbreak mode and restore the previous settings on exit (ideally on signal deaths too).
I want a thread to run infinity times in order to execute the task described on do_work() function that it receives. However, the function is only called on the pthread_create() subroutine.
I've tried to implement the sched_yield() and the pthread_join() routines on a while loop. But it didn't work yet.
Is there any routine in which I can call the existing thread again?
int main (int argc, char ** argv) {
int period;
int priority;
int load;
char schedule[15];
period = atoi(argv[1]);
priority = atoi(argv[2]);
load = atoi(argv[3]);
strncpy(schedule,argv[4],100);
std::cout << " period : " << period <<"\n priority : "<< priority << "\n load : "<< load << "\n schedule : " << schedule <<std::endl;
struct sched_param param;
pthread_t thread;
int rc;
sched_setscheduler (0, SCHED_FIFO , ¶m);
std::cout << "main() : creating thread " << std::endl;
rc = pthread_create(&thread, NULL, do_work, (void*)load);
if (rc) {
std::cout << "Error:unable to create thread " << rc << std::endl;
exit(-1);
}
int i=0;
struct sigaction action;
struct itimerval timer;
while(i<10000){
pthread_join(thread, NULL);
sched_yield();
i++;
}
pthread_exit(NULL);
}
You do not call a thread, you create a thread. By doing that, you specify a start_routine which will be called 'in' the new thread.
If you want to call repeatedly a function in a loop, then you can do the following in your start_routine:
void* start_routine(void *arg) {
while (active) { // active: atomic global boolean value
do_work();
}
// if no longer active,
// there could be an option to wait to become active again,
// or exit the thread
pthread_exit(NULL);
}
pthread_join() is only called, if you want to join a thread with other thread(s). pthread_join() waits until the target thread has terminated. By joining the thread, all resources are given back to the system (cleanup).
Just wanted to show you the code I implemented.
I was not sure about the meaning of Threads when I made this question and you helped me to understand that I cannot access the function on a thread multiple times, but I have to create it on each use.
My main objective was to find a way of calling the do_work() function on the reception of the signal SIGALRM. Thus, I just assumed the do_wordk() to be my Thread and used a sigaction struct to control the arrival of the signal.
If you guys want to test the code, it returns the execution time of the do_work() function and a message if the deadline set on your period was lost. The purpose of this work was to make an analogy with periodic threads.
To compile:
g++ teste.cpp -o exe -lrt
To run:
sudo taskset -c 0 ./exe 300 1 100000 F
sudo taskset -c 0 ./exe Period Priority Work_Load Scheduller_Policy
#include<signal.h>
#include<stdio.h>
#include<unistd.h>
#include<errno.h>
#include<sys/time.h>
#include<iostream>
#include<string>
#include<string.h>
long load=1;
void deadline();
void do_work();
void wakeup(int j){
struct itimerval aux;
int t1, t2;
getitimer( ITIMER_REAL, &aux); //Get initial resume time
t1 = aux.it_value.tv_usec;
//std::cout << "Hello World! Thread working |Resume Time : " <<t1<< std::endl;
do_work();
getitimer( ITIMER_REAL, &aux);
t2 = aux.it_value.tv_usec; //Get Final resume time
std::cout << "Execution time (usec): " <<t1 - t2<< std::endl;
if (t2==0){
deadline();
}
return;
}
void do_work(){
for ( int i = 0; i < load * 1000; i++) {
/* do nothing , keep counting */
}
}
void deadline() {
std::cout << "Lost deadline!" << std::endl;
}
int main (int argc, char ** argv) {
int i;
int period;
int priority;
char scheduler[5];
period = atoi(argv[1])*1000;
priority = atoi(argv[2]);
load = atoi(argv[3]);
strcpy(scheduler, argv[4]);
std::cout << " period : " << period <<"\n priority : "<< priority << "\n load : "<< load << "\n scheduler : " << scheduler <<std::endl;
struct sched_param param;
param.sched_priority = priority;
if (scheduler[0]=='F'){
int r = sched_setscheduler (0, SCHED_FIFO , ¶m);
if(r==-1){ perror("scheduller"); return 1;}
std::cout <<"FIFO scheduller: "<<r<<std::endl;
}else{
int r = sched_setscheduler (0, SCHED_RR , ¶m);
if(r==-1){ perror("scheduller"); return 1;}
std::cout <<"RR scheduller: "<<r<<std::endl;
}
struct itimerval val;
struct sigaction action;
sigset_t mask;
sigemptyset(&action.sa_mask);
action.sa_handler = wakeup;
action.sa_flags=SA_RESTART;
if(sigaction(SIGALRM, &action, 0)==-1){
perror("sigaction");
return 1;
}
val.it_interval.tv_sec=0;
val.it_interval.tv_usec=period;
val.it_value.tv_sec=0;
val.it_value.tv_usec=period;
if(setitimer(ITIMER_REAL, &val, 0)==-1){
perror("setitimer");
return 1;
}
if(sigwait( &mask, &i)==-1){
perror("sigwait");
}
return 0;
}
Finally, I am really grateful for your patience in understanding my problem. This is my first question on this community and I hope I'll improve them over time. Thank you all for your answers and the effort on helping me.
I am spawning a process in my application:
int status = posix_spawnp(&m_iProcessHandle, (char*)strProgramFilepath.c_str(), NULL, NULL, argsWrapper.m_pBuffer, NULL);
When I want to see if the process is still running, I use kill:
int iReturn = kill(m_iProcessHandle,0);
But after the spawned process has finished its work, it hangs around. The return value on the kill command is always 0. Not -1. I am calling kill from within the code, but if I call it from the command line, there is no error - the spawned process still exists.
Only when my application exits does the command-line kill return "No such process".
I can change this behavior in my code with this:
int iResult = waitpid(m_iProcessHandle, &iStatus, 0);
The call to waitpd closes down the spawned process and I can call kill and get -1 back, but by then I know the spawned process is dead.
And waitpd blocks my application!
How can I test a spawned processes to see if it is running, but without blocking my application?
UPDATE
Thanks for the help! I have implemented your advise and here is the result:
// background-task.cpp
//
#include <spawn.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <signal.h>
#include "background-task.h"
CBackgroundTask::CBackgroundTask()
{
// Initialize member variables
m_iProcessHandle = 0;
}
CBackgroundTask::~CBackgroundTask()
{
// Clean up (kill first)
_lowLevel_cleanup(true);
}
bool CBackgroundTask::IsRunning()
{
// Shortcuts
if (m_iProcessHandle == 0)
return false;
// Wait for the process to finish
int iStatus = 0;
int iResult = waitpid(m_iProcessHandle, &iStatus, WNOHANG);
return (iResult != -1);
}
void CBackgroundTask::Wait()
{
// Wait (clean up without killing)
_lowLevel_cleanup(false);
}
void CBackgroundTask::Stop()
{
// Stop (kill and clean up)
_lowLevel_cleanup(true);
}
void CBackgroundTask::_start(const string& strProgramFilepath, const string& strArgs, int iNice /*=0*/)
{
// Call pre-start
_preStart();
// Split the args and build array of char-strings
CCharStringAarray argsWrapper(strArgs,' ');
// Run the command
int status = posix_spawnp(&m_iProcessHandle, (char*)strProgramFilepath.c_str(), NULL, NULL, argsWrapper.m_pBuffer, NULL);
if (status == 0)
{
// Process created
cout << "posix_spawn process=" << m_iProcessHandle << " status=" << status << endl;
}
else
{
// Failed
cout << "posix_spawn: error=" << status << endl;
}
// If process created...
if(m_iProcessHandle != 0)
{
// If need to adjust nice...
if (iNice != 0)
{
// Change the nice
stringstream ss;
ss << "sudo renice -n " << iNice << " -p " << m_iProcessHandle;
_runCommand(ss.str());
}
}
else
{
// Call post-stop success=false
_postStop(false);
}
}
void CBackgroundTask::_runCommand(const string& strCommand)
{
// Diagnostics
cout << "Running command: " << COUT_GREEN << strCommand << endl << COUT_RESET;
// Run command
system(strCommand.c_str());
}
void CBackgroundTask::_lowLevel_cleanup(bool bKill)
{
// Shortcuts
if (m_iProcessHandle == 0)
return;
// Diagnostics
cout << "Cleaning up process " << m_iProcessHandle << endl;
// If killing...
if (bKill)
{
// Kill the process
kill(m_iProcessHandle, SIGKILL);
}
// Diagnostics
cout << "Waiting for process " << m_iProcessHandle << " to finish" << endl;
// Wait for the process to finish
int iStatus = 0;
int iResult = waitpid(m_iProcessHandle, &iStatus, 0);
// Diagnostics
cout << "waitpid: status=" << iStatus << " result=" << iResult << endl;
// Reset the process-handle
m_iProcessHandle = 0;
// Call post-stop with success
_postStop(true);
// Diagnostics
cout << "Process cleaned" << endl;
}
Until the parent process calls one of the wait() functions to get the exit status of a child, the child stays around as a zombie process. If you run ps during this time, you'll see that the process is still there in the Z state. So kill() returns 0 because the process exists.
If you don't need to get the child's status, see How can I prevent zombie child processes? for how you can make the child disappear immediately when it exits.
I'm putting together a Unix/Linux semi-portable thread class (i.e. using the pthread library) for a project I'm working on. Part of the project requires the ability to set the priority of certain threads to allow other threads in the same process more CPU time; which is where the pthread_setschedparam function comes in and my class hits a brick wall.
Below is a simple test I put together to illustrate my issue:
#include <iostream>
#include <unistd.h>
#include <pthread.h>
#include <sched.h>
#include <string.h>
#include <errno.h>
pthread_mutex_t m_mtx;
bool m_goahead;
void dosleep(int millis)
{
usleep(millis*1000);
}
void domsg(const char *msg)
{
pthread_mutex_lock(&m_mtx);
std::cout << msg << std::endl;
pthread_mutex_unlock(&m_mtx);
}
void dowait() {
while (!m_goahead) {
dosleep(1);
}
}
void *fn1(void *param)
{
domsg("in fn1...waiting");
dowait();
while (m_goahead) {
dosleep(1000);
domsg("in fn1 loop");
}
}
void *fn2(void *param)
{
domsg("in fn2...waiting");
dowait();
while (m_goahead) {
dosleep(1000);
domsg("in fn2 loop");
}
}
int main(int argc, char **argv)
{
// min prio = -2, max prio = 2
int t1_pri = 2, t2_pri = 0, main_pri = 1;
//SCHED_RR, SCHED_FIFO, SCHED_OTHER (POSIX scheduling policies)
int sched = SCHED_OTHER; // standard
// get the range between min and max and set the priorities base on split range
int min = sched_get_priority_min(sched);
int max = sched_get_priority_max(sched);
int skip = (max - min) / 5; // 5 since -2...2
struct sched_param main_param, t1_param, t2_param;
memset(&main_param, 0, sizeof(sched_param));
memset(&t1_param, 0, sizeof(sched_param));
memset(&t2_param, 0, sizeof(sched_param));
main_param.sched_priority = (min + ((main_pri+2) * (skip+1))) + (skip / 2);
t1_param.sched_priority = (min + ((t1_pri+2) * (skip+1))) + (skip / 2);
t2_param.sched_priority = (min + ((t2_pri+2) * (skip+1))) + (skip / 2);
std::cout << "main thread will have a prio of " << main_param.sched_priority << std::endl;
std::cout << "t1 thread will have a prio of " << t1_param.sched_priority << std::endl;
std::cout << "t2 thread will have a prio of " << t2_param.sched_priority << std::endl;
m_goahead = false;
pthread_mutex_init(&m_mtx, NULL);
pthread_t t1, t2;
// Create the threads
if (pthread_create(&t1, NULL, fn1, NULL) != 0) {
std::cout << "couldn't create t1" << std::endl;
return -1;
}
if (pthread_create(&t2, NULL, fn2, NULL) != 0) {
std::cout << "couldn't create t2" << std::endl;
return -1;
}
dosleep(1000); // sleep a second before setting priorities
// --main thread--
if (pthread_setschedparam(pthread_self(), sched, &main_param) != 0) {
std::cout << "error setting priority for main thread: (" << errno << "), " << strerror(errno) << std::endl;
}
// --t1 thread--
if (pthread_setschedparam(t1, sched, &t1_param) != 0) {
std::cout << "error setting priority for T1: (" << errno << "), " << strerror(errno) << std::endl;
}
// --t2 thread--
if (pthread_setschedparam(t2, sched, &t2_param) != 0) {
std::cout << "error setting priority for T2: (" << errno << "), " << strerror(errno) << std::endl;
}
m_goahead = true; // all start
// loop until user interupt
for (;;) {
dosleep(1000);
domsg("in main loop");
}
pthread_mutex_destroy(&m_mtx);
return 0;
}
Base on this code, if I compile this and run it on an OpenBSD system, I get the following:
main thread will have a prio of 24
t1 thread will have a prio of 31
t2 thread will have a prio of 17
in fn1...waiting
in fn2...waiting
in fn1 loop
in main loop
in fn2 loop
in fn1 loop
in main loop
in fn2 loop
in fn1 loop
in main loop
in fn2 loop
Note how it goes in the order of the thread priority, fn1, main, fn2...
If I run this same test on an Ubuntu 10.04LTS system, I get the following:
main thread will have a prio of 3
t1 thread will have a prio of 4
t2 thread will have a prio of 2
in fn1...waiting
in fn2...waiting
error setting priority for main thread: (22), Invalid argument
error setting priority for T1: (22), Invalid argument
error setting priority for T2: (22), Invalid argument
in main loop
in fn2 loop
in fn1 loop
in main loop
in fn2 loop
in fn1 loop
in main loop
in fn2 loop
in fn1 loop
I understand that the invalid argument is because I'm specifying the SCHED_OTHER priority class and trying to assign it any number other than 0; what I cannot figure is how can I make this work properly?
I've tried 'assuming' a SCHED_FIFO or SCHED_RR priority class to get the min/max values, which gives me valid min/max values and I don't get the 'invalid argument' error, but the function loop output is not in the prioritized order, it is instead in whatever order the function happened to get called (to be expected if no priority was set).
Ideally I would get the current process' priority class, then assign the thread on that class too, however, if the current process' priority is SCHED_OTHER then setting a thread based on that produces the invalid results I don't want.
Is there a more 'portable' way to set a thread's priority or grab the valid min/max values? Can I even set a thread's priority under SCHED_OTHER in certain environments, or is that functionality left to said environment?
I'm at an impasse with this problem and would appreciate any insight or pointers in the right direction.
Thanks and please let me know if my code/explanation is unclear.
Refer this if it helps increase understanding. But what little I learned, SCHED_OTHER is their only to mean that all the non real time thread will have equal priority. But then in the question referred BSD give max and min 0 and 99 even in case of SCHED_OTHER, didn't understand why, but one thing is clear, it is not a very portable and to rely on its exact value will not help. In that case putting special handling will do much better, like if range is [0-0] then using nice(if thread priority can be fixed by nice,please let me know) be used to set the priority.
Thanks
I need to implement barrier synchronization between 2 threads using mutex (only). Barrier synchronization is that 2 threads will wait for each other to meet at predefined step before proceeding.
I am able to do it using seamaphore but how can I achieve this only using mutex. I was given a hint that I need 2 mutex not 1 to do this.
Using Seamaphore:
#include <pthread.h>
#include <semaphore.h>
using namespace std;
sem_t s1;
sem_t s2;
void* fun1(void* i)
{
cout << "fun1 stage 1" << endl;
cout << "fun1 stage 2" << endl;
cout << "fun1 stage 3" << endl;
sem_post (&s1);
sem_wait (&s2);
cout << "fun1 stage 4" << endl;
}
void* fun2(void* i)
{
cout << "fun2 stage 1" << endl;
cout << "fun2 stage 2" << endl;
// sleep(5);
sem_post (&s2);
sem_wait (&s1);
cout << "fun2 stage 3" << endl;
}
main()
{
sem_init(&s1, 0, 0);
sem_init(&s2, 0, 0);
int value;
sem_getvalue(&s2, &value);
cout << "s2 = " << value << endl;
pthread_t iThreadId;
cout << pthread_create(&iThreadId, NULL, &fun2, NULL) << endl;
// cout << pthread_create(&iThreadId, NULL, &fun2, NULL) << endl;
pthread_create(&iThreadId, NULL, &fun1, NULL);
sleep(10);
}
Compile the above code as "g++ barrier.cc -lpthread"
How about NO MUTEXES and no locks? Using ATOMIC OPERATIONS only:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <signal.h>
static sigset_t _fSigSet;
static volatile int _cMax=20, _cWait = 0;
static pthread_t _aThread[1000];
void * thread(void *idIn)
{
int nSig, iThread, cWait, id = (int)idIn;
printf("Start %d\n", id, cWait, _cMax);
// do some fake weork
nanosleep(&(struct timespec){0, 500000000}, NULL);
// barrier
cWait = __sync_add_and_fetch(&_cWait, 1);
printf("Middle %d, %d/%d Waiting\n", id, cWait, _cMax);
if (cWait < _cMax)
{
// if we are not the last thread, sleep on signal
sigwait(&_fSigSet, &nSig); // sleepytime
}
else
{
// if we are the last thread, don't sleep and wake everyone else up
for (iThread = 0; iThread < _cMax; ++iThread)
if (iThread != id)
pthread_kill(_aThread[iThread], SIGUSR1);
}
// watch em wake up
cWait = __sync_add_and_fetch(&_cWait, -1);
printf("End %d, %d/%d Active\n", id, cWait, _cMax);
return 0;
}
int main(int argc, char** argv)
{
pthread_attr_t attr;
int i, err;
sigemptyset(&_fSigSet);
sigaddset(&_fSigSet, SIGUSR1);
sigaddset(&_fSigSet, SIGSEGV);
printf("Start\n");
pthread_attr_init(&attr);
if ((err = pthread_attr_setstacksize(&attr, 16384)) != 0)
{
printf("pthread_attr_setstacksize failed: err: %d %s\n", err, strerror(err));
exit(0);
}
for (i = 0; i < _cMax; i++)
{
if ((err = pthread_create(&_aThread[i], &attr, thread, (void*)i)) != 0)
{
printf("pthread_create failed on thread %d, error code: %d %s\n", i, err, strerror(err));
exit(0);
}
}
for (i = 0; i < _cMax; ++i)
pthread_join(_aThread[i], NULL);
printf("\nDone.\n");
return 0;
}
I am not sure that you need two mutexes, with one mutex and a condition variable and an extra flag might be enough. The idea is that you enter the critical section by acquiring the mutex, then you check whether you are the first thread to come, if so, you wait on the condition. If you are the second thread coming then you wake up the waiting thread and both leave.