Getting result of exec*() from child process without waiting in any case (not using pipes and vfork()) - c

I am working on custom wrappers for Unix-specific system calls now. And the last problem I met is about how to create a complete function for creating new processes with another image. And I want this function to return TRUE or FALSE. The last piece of this puzzle is how to get a result of exec*() from a child process without waiting for it's end in case of exec*()'s success. In other words, I need to get FAIL of SUCCESS result of exec*() quickly and continue execution of a parent process.
And I don't want to use vfork() and pipes.
My current results:
Using vfork() and a volatile variable for keeping result made their work.
static int QCreateProcess(char* args, ...)
{
if (processInfoStruct == NULL)
{
return Q_ERROR;
}
volatile int result = TRUE;
pid_t procHandle = vfork();
if (procHandle == 0)
{
char* argsToExec[2] = { args, NULL };
execv(argsToExec[0], argsToExec);
result = FALSE;
_exit(EXIT_FAILURE);
}
else if (procHandle == -1)
{
processInfoStruct->processHandle = NULL;
result = FALSE;
}
else
{
if (result == TRUE)
{
waitpid(procHandle, NULL, WNOHANG);
processInfoStruct->processHandle = procHandle;
}
else
{
processInfoStruct->processHandle = 0;
result = FALSE;
}
}
return result;
}
This code works and returns correct results.
How can this be implemented using fork() and waitpid() without the status variable (it won't work with fork() anyway...) and pipes? I tried to find solutions with different options for the last function (waitpid()), but a desired combination was not found.

Related

libuv: difference between fork and uv_spawn?

Recently I have been playing around with Libuv. I don't get the programming model as far as child processes are concerned.
For example look at the following code:
uv_loop_t *loop;
uv_process_t child_req;
uv_process_options_t options;
void on_exit(uv_process_t* proc, long int io, int hj)
{
std::cout<<"on exit call back"<<std::endl;
}
int main()
{
loop = uv_default_loop();
char* args[3];
args[0] = "mkdir";
args[1] = "test-dir";
args[2] = NULL;
options.exit_cb = on_exit;
options.file = "mkdir";
options.args = args;
int r;
r = uv_spawn(loop, &child_req, &options);
std::cout<<r<<std::endl;
if (r) {
std::cout<<"from line 231"<<std::endl;
fprintf(stderr, "%s\n", uv_strerror(r));
return 1;
} else {
fprintf(stderr, "Launched process with ID %d\n", child_req.pid);
}
return uv_run(loop, UV_RUN_DEFAULT);
}
Here the output printed on console is:
0
Launched process with ID 511168
on exit call back
In my understanding uv_spawn acts like fork(). In child process the value of r is 0 and in parent process it is non-zero. So from line 231 should also be printed. But evidently it is not. I read the documentation end to end but I have no clue.
Any help will be appreciated.
n my understanding uv_spawn acts like fork()
And then like execve and child becomes mkdir. So child executes mkdir, and only parent returns to your code.

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) {
...
}

How can I cancel a thread's current task and reset the thread to a known good state without any dynamic allocations on Linux + GLibc?

I want to cancel a thread's current task and reset the thread to a
known good state in a very reliable way. I plan to use mlockall and
already preallocate my threads so when I mean reliable I mean
really reliable. The traditional POSIX solution is to use
pthread_cancel to cancel and destroy the thread and then to create a
new replacement thread. However, this solution allocates user stacks
dynamically with mmap (well, GLibc caches thread stacks but this
could change at any time), and allocates kernel stacks dynamically
with clone; and these system calls could fail with errors at any
time so this solution does not work for me.
You cannot just arbitrarily 'reset' a thread of execution to a 'good' state. If you could, we would all be able to fix hung programs by doing so. The only things that can seriously influence a running thread are boolean atomic flags that the thread/task has to check or the OS, (which can always terminate threads).
If you don't want to continually create/stop/terminate threads, (a very good idea indeed), while maintaining some 'task cancellation' functionality, then the thread in question must be signaled somehow to stop running its task and go back to looking for the next one, (eg. on an input producer-consumer queue). If the task is CPU-intensive, you are going to need an atomic 'abort' boolean in the task that is checked at some reasonable frequency. The task can then exit early and allow the thread to get back to the PC-queue, (or whatever feeds it its tasks). If the task has a field that its executing thread can set with its ID, it is possible that the priority of the thread executing the task could be lowered at the same time as the abort bool is set. An oversupply of pooled threads could then ensure that the thread running the aborting task would not interfere with the running of other 'good' tasks. The thread would have to ensure that it raises its priority again after a task exit, so it's ready for the next good task.
I figured out an answer that works with blocking system calls. It's really ugly but it avoids several tricky racy conditions.
First, one registers an empty signal handler for some signal (don't use a real-time signal for this, those can queue up an trap the thread were waiting for in a never ending barrage of signals):
{
struct sigaction act = { 0 };
sigemptyset(&act.sa_mask);
act.sa_handler = do_nothing;
if (-1 == sigaction(SIGUSR1, &act, NULL)) {
perror("sigaction");
return EXIT_FAILURE;
}
}
// ..
static void do_nothing(int signo)
{
}
Then for cancelling one loops sending a signal to the thread doing the operation and polling to see if the thread has cancelled the operation.
void linted_asynch_task_cancel(struct linted_asynch_task *task)
{
int errnum;
errnum = pthread_mutex_lock(&task->owner_lock);
if (errnum != 0) {
assert(errnum != EDEADLK);
assert(false);
}
task->cancelled = true;
/* Yes, really, we do have to busy wait to prevent race
* conditions unfortunately */
while (task->owned) {
errnum = pthread_kill(task->owner, SIGUSR1);
if (errnum != 0 && errnum != EAGAIN) {
assert(errnum != ESRCH);
assert(errnum != EINVAL);
assert(false);
}
errnum = pthread_mutex_unlock(&task->owner_lock);
if (errnum != 0) {
assert(errnum != EPERM);
assert(false);
}
sched_yield();
errnum = pthread_mutex_lock(&task->owner_lock);
if (errnum != 0) {
assert(errnum != EDEADLK);
assert(false);
}
}
errnum = pthread_mutex_unlock(&task->owner_lock);
if (errnum != 0) {
assert(errnum != EPERM);
assert(false);
}
}
The for the actual thread doing the task one simply resubmits a task on EINTR:
static void run_task_sleep_until(struct linted_asynch_pool *pool,
struct linted_asynch_task *task)
{
struct linted_asynch_task_sleep_until *task_sleep = task->data;
int errnum = 0;
int flags = task_sleep->flags;
if (-1 == clock_nanosleep(CLOCK_MONOTONIC, flags, &task_sleep->request,
&task_sleep->request)) {
errnum = errno;
assert(errnum != 0);
}
if (EINTR == errnum) {
linted_asynch_pool_submit(pool, task);
return;
}
task->errnum = errnum;
linted_asynch_pool_complete(pool, task);
}
Finally, in the resubmission function one checks to see if the operation has been cancelled and then completes if with the ECANCELED error if it has:
void linted_asynch_pool_submit(struct linted_asynch_pool *pool,
struct linted_asynch_task *task)
{
bool cancelled;
int errnum;
assert(pool != NULL);
assert(!pool->stopped);
errnum = pthread_mutex_lock(&task->owner_lock);
if (errnum != 0) {
assert(errnum != EDEADLK);
assert(false);
}
task->owned = false;
cancelled = task->cancelled;
errnum = pthread_mutex_unlock(&task->owner_lock);
if (errnum != 0) {
assert(errnum != EPERM);
assert(false);
}
if (cancelled) {
task->errnum = ECANCELED;
linted_queue_send(pool->event_queue, LINTED_UPCAST(task));
} else {
linted_queue_send(pool->worker_command_queue,
LINTED_UPCAST(task));
}
}

Resources