[Motiftools-xmt-cvs] CVS: xmt/Xmt BackgroundWorker.c,NONE,1.1 BackgroundWorker.h,NONE,1.1 RunApplica
Brought to you by:
motiftools
From: Grant M. <grm...@us...> - 2004-12-14 19:35:44
|
Update of /cvsroot/motiftools/xmt/Xmt In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv17853 Added Files: BackgroundWorker.c BackgroundWorker.h RunApplication.c RunApplication.h Log Message: New utilities: run parallel/background task, run application --- NEW FILE --- /* * Motif Tools Library, Version 3.1 * $Id: BackgroundWorker.c,v 1.1 2004/12/14 19:35:34 grmcdorman Exp $ * * Written by Grant McDorman * Copyright (c) 2004 Grant McDorman * All Rights Reserved. See the file COPYRIGHT for details. * This is open source software. See the file LICENSE for details. * There is no warranty for this software. See NO_WARRANTY for details. * * $Log: BackgroundWorker.c,v $ * Revision 1.1 2004/12/14 19:35:34 grmcdorman * New utilities: run parallel/background task, run application * */ #include <Xmt/BackgroundWorker.h> #include <Xmt/WorkingBox.h> #ifdef USE_PTHREADS #include <pthread.h> #else #include <sys/types.h> #include <sys/wait.h> #include <unistd.h> #endif typedef struct { int pipe[2]; /* communication pipe */ Widget dialog; /* working box dialog */ XmtWorker worker; /* worker */ XmtUIWorker uiWorker; /* UI worker */ void *worker_data;/* argument for worker */ int running; /* true (nonzero) while running */ } worker_data; static void *thread_master(void *vdata) { worker_data *data = (worker_data *) vdata; (data->worker)(vdata, data->worker_data); data->running = 0; close(data->pipe[1]); #ifdef USE_PTHREADS pthread_exit(0); #else exit(0); #endif } static void monitor(XtPointer closure, int *source, XtInputId *id) { int status; char buffer[1]; worker_data *data = (worker_data *) closure; status = read(*source, buffer, 1); if (status <= 0) { /* pipe closed */ data->running = 0; } else if (buffer[0] >= 0) { /* buffer[0] represents percent to report to working box */ if (XtIsSubclass(data->dialog, xmtWorkingBoxWidgetClass)) { XmtWorkingBoxSetScaleValue(data->dialog, buffer[0]); } } /* else application specific */ { if (data->uiWorker != NULL) { /* pass message char w/o high bit */ (data->uiWorker)(data->worker_data, buffer[0] & ~0x80); } } } /* Tell the UI thread to update a progress bar */ void XmtWorkerSetPercent(void *boxdata, int percent) { worker_data *data = (worker_data *) boxdata; char message[1]; message[0] = (char) percent; write(data->pipe[1], message, 1); } /* Cause the UI thread to wake up */ void XmtWorkerMessage(void *boxdata, char message) { worker_data *data = (worker_data *) boxdata; char buffer[1]; buffer[0] = ( (char) 0x80 ) | message; write(data->pipe[1], buffer, 1); } int XmtBackgroundWorkingBox(Widget parent, XmtWorker worker, String message, Boolean showScale, void *argument, XmtUIWorker uiWorker) { int status; Arg args[10]; int n = 0; Widget dialog; XtSetArg(args[n], XmtNmessage, message); n++; XtSetArg(args[n], XmtNshowScale, showScale); n++; dialog = XmtCreateWorkingDialog(parent, "xmtBackgroundWorker", args, n); XtManageChild(dialog); status = XmtBackgroundWorker(dialog, worker, argument, uiWorker); XtDestroyWidget(dialog); return status; } int XmtBackgroundWorker(Widget any, XmtWorker worker, void *argument, XmtUIWorker uiWorker) { worker_data data; int status; int max_fd; XtAppContext app_context = XtWidgetToApplicationContext(any); XtInputId inputId; #ifdef USE_PTHREADS pthread_t a_thread; #else pid_t a_pid; #endif status = pipe(data.pipe); if (status != 0) return status; data.worker = worker; data.worker_data = argument; data.uiWorker = uiWorker; data.running = 1; /* Put up the working dialog */ data.dialog = any; /* Arrange to listen on worker's pipe */ inputId = XtAppAddInput(app_context, data.pipe[0], (XtPointer) (XtInputReadMask | XtInputExceptMask), monitor, &data); XmUpdateDisplay(data.dialog); #ifdef USE_PTHREADS /* Using threads. */ status = pthread_create(&a_thread, NULL, thread_master, &data); if (status != 0) { close(data.pipe[0]); close(data.pipe[1]); return status; } /* X event loop until done. */ do { XtAppProcessEvent(app_context, XtIMXEvent | XtIMTimer | XtIMAlternateInput #ifdef XtIMSignal | XtIMSignal #endif ); } while (data.running); /* cleanup */ pthread_join(a_thread, NULL); #else /* use fork() */ a_pid = fork(); if (a_pid == 0) { /* child */ close(data.pipe[0]); /* close reading end */ thread_master(&data); /* run process */ exit(0); /* done */ } else if (a_pid > 0) { /* parent */ close(data.pipe[1]); /* close writing end */ do { XtAppProcessEvent(app_context, XtIMXEvent | XtIMTimer | XtIMAlternateInput #ifdef XtIMSignal | XtIMSignal #endif ); } while (data.running); /* reap zombie */ waitpid(a_pid, &status, 0); } else { /* error */ close(data.pipe[0]); close(data.pipe[1]); return a_pid; } #endif XtRemoveInput(inputId); close(data.pipe[0]); close(data.pipe[1]); return 0; } --- NEW FILE --- /* * Motif Tools Library, Version 3.1 * $Id: BackgroundWorker.h,v 1.1 2004/12/14 19:35:34 grmcdorman Exp $ * * Written by Grant McDorman * Copyright (c) 2004 by Grant McDorman * All Rights Reserved. See the file COPYRIGHT for details. * This is open source software. See the file LICENSE for details. * There is no warranty for this software. See NO_WARRANTY for details. * * $Log: BackgroundWorker.h,v $ * Revision 1.1 2004/12/14 19:35:34 grmcdorman * New utilities: run parallel/background task, run application * * */ #ifndef _XmtBackgroundWorker_h #define _XmtBackgroundWorker_h #include <X11/Intrinsic.h> #ifdef __cplusplus extern "C" { #endif /* XmtWorker runs in the background. It must *not* do any UI related operations. * It may be run as a separate process (i.e. fork()) or as a thread. To send * messages to the UI process/thread, it can call XmtWorkingBoxWorkerMessage. * * If XmtBackgroundWorkingBox is used, or XmtBackgroundWorker is passed an * XmtWorkingBox, the worker can also call XmtWorkingBoxWorkerSetPercent * to update the progress indicator on the working box. * * 'boxHandle' is an opaque pointer to be used in XmtWorkingBoxWorkerSetPercent * or XmtWorkingBoxWorkerMessage. 'argument' is the arbitrary pointer passed * to XmtBackgroundWorker or XmtBackgroundWorkingBox. */ typedef void (*XmtWorker)(void *boxHandle, void *argument); /* XmtUIWorker is called in the UI context when the worker calls * XmtWorkingBoxWorkerMessage. The message value ranges from 0 to 127. */ typedef void (*XmtUIWorker)(void *argument, char message); /* Worker: call this to set percentage on XmtWorkingBox progress indicator. */ extern void XmtWorkerSetPercent(void *boxdata, int percent); /* Worker: call this to get XmtUIWorker called with the value of 'message'. */ extern void XmtWorkerMessage(void *boxdata, char message); /* Create and display an XmtWorkingBox (with the name xmtBackgroundWorker), * and then call XmtBackgroundWorker with the passed arguments and the working box. * Returns XmtBackgroundWorker return value. XmtUIWorker can be NULL */ extern int XmtBackgroundWorkingBox(Widget parent, XmtWorker worker, String message, Boolean showProgress, void *argument, XmtUIWorker workerMsg); /* Run 'XmtWorker' in the background. Return 0 when successful; otherwise returns system * error code. XmtUIWorker can be NULL. */ extern int XmtBackgroundWorker(Widget any, XmtWorker worker, void *argument, XmtUIWorker workerMsg); #ifdef __cplusplus }; #endif #endif --- NEW FILE --- /* * Motif Tools Library, Version 3.1 * $Id: RunApplication.c,v 1.1 2004/12/14 19:35:34 grmcdorman Exp $ * * Written by Grant McDorman * Copyright (c) 2004 by Grant McDorman * All Rights Reserved. See the file COPYRIGHT for details. * This is open source software. See the file LICENSE for details. * There is no warranty for this software. See NO_WARRANTY for details. * * $Log: RunApplication.c,v $ * Revision 1.1 2004/12/14 19:35:34 grmcdorman * New utilities: run parallel/background task, run application * * */ #include <Xmt/RunApplication.h> #include <errno.h> #include <sys/types.h> #include <sys/wait.h> #include <unistd.h> #include <fcntl.h> #include <signal.h> typedef struct RunDataStruct { XmtRunApplicationCallbackStruct callbackData; XtCallbackProc doneCallback; XtPointer doneCallbackClosure; Widget any; struct RunDataStruct *next; } RunData; #define PIPE_READ_INDEX 0 #define PIPE_WRITE_INDEX 1 static int monitoring = 0; static void (*old_SIGCHLD)(int); static RunData *firstData = NULL; static int monitor_pipe[2]; static XtInputId monitor_id; #define ERROR_RETURN(x, status) {\ close(pipe_fds[PIPE_READ_INDEX]); \ close(pipe_fds[PIPE_WRITE_INDEX]); \ if (error != NULL) *error = status; \ if (data != NULL) XtFree((char *)data); \ return x; \ } #define CHECK_STATUS(x, status) if (status != 0) { ERROR_RETURN(x, status) } /* * Some child process has exited. Look in our list; if it belongs to us, * hande it. */ static void exitNotify(XtPointer closure, int *source, XtInputId *id) { RunData *data, *prior; pid_t pid; for (data = firstData, prior = NULL; data != NULL; ) { /* try to reap child */ do { pid = waitpid(data->callbackData.pid, &data->callbackData.exit_status, WNOHANG); } while (pid == -1 && errno == EINTR); if (pid == data->callbackData.pid) { (*data->doneCallback)(data->any, (XtPointer) &data->callbackData, data->doneCallbackClosure); if (prior != NULL) { prior->next = data->next; } else { firstData = data->next; } prior = data; data = data->next; XtFree((char *) prior); } else { data = data->next; } } /* if we're no longer monitoring anything, clean up */ if (firstData == NULL) { XtRemoveInput(monitor_id); close(monitor_pipe[0]); close(monitor_pipe[1]); signal(SIGCHLD, old_SIGCHLD); old_SIGCHLD = NULL; monitoring = 0; } } /* * write a byte on monitor pipe to wake up exitNotify handler */ static void handle_SIGCHLD(int value) { static char buffer[1] = { 'C' }; write(monitor_pipe[PIPE_WRITE_INDEX], buffer, 1); if (old_SIGCHLD != NULL && old_SIGCHLD != SIG_IGN && old_SIGCHLD != SIG_DFL) { (*old_SIGCHLD)(value); } } XmtRunStatus XmtRunApplicationCallback(const char *program, char * const*arguments, int detached, pid_t *pid, int *error, Widget any, XtCallbackProc doneCallback, XtPointer doneCallbackClosure) { pid_t child; int pipe_fds[2]; int status; char buffer[1]; void (*old_signal)(int); RunData *data = NULL;; if (!detached && doneCallback != NULL) { if (!monitoring) { old_SIGCHLD = signal(SIGCHLD, handle_SIGCHLD); status = pipe(monitor_pipe); CHECK_STATUS(XmtRUN_FAIL_PIPE, status); monitor_id = XtAppAddInput(XtWidgetToApplicationContext(any), monitor_pipe[PIPE_READ_INDEX], (XtPointer) XtInputReadMask, exitNotify, (XtPointer) data); monitoring = 1; } data = (RunData *) XtMalloc(sizeof *data); data->callbackData.any = any; data->callbackData.exit_status = ~0; data->doneCallback = doneCallback; data->doneCallbackClosure = doneCallbackClosure; } status = pipe(pipe_fds); CHECK_STATUS(XmtRUN_FAIL_PIPE, status) status = fcntl(pipe_fds[PIPE_READ_INDEX], F_SETFD, FD_CLOEXEC); CHECK_STATUS(XmtRUN_FAIL_FCNTL, status); status = fcntl(pipe_fds[PIPE_WRITE_INDEX], F_SETFD, FD_CLOEXEC); CHECK_STATUS(XmtRUN_FAIL_FCNTL, status); child = fork(); if (child < 0) { close(pipe_fds[PIPE_READ_INDEX]); close(pipe_fds[PIPE_WRITE_INDEX]); ERROR_RETURN(XmtRUN_FAIL_FORK, child) } if (child == 0) { /* child */ close(pipe_fds[PIPE_READ_INDEX]); if (detached) { /* fork again to detach */ pid_t child2 = fork(); if (child2 < 0) { buffer[0] = (char) XmtRUN_FAIL_FORK; write(pipe_fds[PIPE_WRITE_INDEX], buffer, 1); write(pipe_fds[PIPE_WRITE_INDEX], &child2, sizeof child2); exit(0); } if (child2 != 0) { /* process to exit */ exit(0); } } /* if this succeeds, it will close the pipe */ status = execvp(program, arguments); /* execv failure */ buffer[0] = (char) XmtRUN_FAIL_EXEC; write(pipe_fds[PIPE_WRITE_INDEX], buffer, 1); write(pipe_fds[PIPE_WRITE_INDEX], &status, sizeof status); exit(0); } /* parent process. Read from pipe; this will fail * or return a status value. */ if (data != NULL) { data->callbackData.pid = child; } /* close write side of pipe, so the read will fail if/when * the child closes it */ close(pipe_fds[PIPE_WRITE_INDEX]); /* * attempt to read a byte from the pipe */ old_signal = signal(SIGPIPE, SIG_IGN); /* prevent SIGPIPE from causing problems */ status = read(pipe_fds[PIPE_READ_INDEX], buffer, 1); (void) signal(SIGPIPE, old_signal); /* restore SIGPIPE handling */ if (status == 1 || detached) { /* reap child process if it's detached or if it failed. */ waitpid(child, NULL, NULL); } if (status == 1) { /* read worked. That means the child process failed; * the detailed error code follows. */ status = 0; read(pipe_fds[PIPE_READ_INDEX], &status, sizeof status); ERROR_RETURN(buffer[0], status); } /* * Worked. Add to list, if monitoring requested. */ if (data != NULL) { data->next = firstData; firstData = data; } close(pipe_fds[PIPE_READ_INDEX]); /* * Check for exit, in case the child process exited before processing competed. */ exitNotify(NULL, NULL, NULL); return XmtRUN_SUCCESS; } XmtRunStatus XmtRunApplication(const char *program, char *const*arguments, int detached, pid_t *pid, int *error) { return XmtRunApplicationCallback(program, arguments, detached, pid, error, NULL, NULL, NULL); } --- NEW FILE --- /* * Motif Tools Library, Version 3.1 * $Id: RunApplication.h,v 1.1 2004/12/14 19:35:34 grmcdorman Exp $ * * Written by Grant McDorman * Copyright (c) 2004 by Grant McDorman * All Rights Reserved. See the file COPYRIGHT for details. * This is open source software. See the file LICENSE for details. * There is no warranty for this software. See NO_WARRANTY for details. * * $Log: RunApplication.h,v $ * Revision 1.1 2004/12/14 19:35:34 grmcdorman * New utilities: run parallel/background task, run application * * */ #ifndef _XmtRunApplication_h #define _XmtRunApplication_h #ifdef __cplusplus extern "C" { #endif #include <sys/types.h> /* pid_t */ #include <X11/Intrinsic.h> typedef struct { pid_t pid; Widget any; int exit_status; } XmtRunApplicationCallbackStruct; typedef enum { XmtRUN_SUCCESS=0, XmtRUN_FAIL_PIPE, XmtRUN_FAIL_FORK, XmtRUN_FAIL_FCNTL, XmtRUN_FAIL_EXEC, XmtRUN_FAIL_OTHER } XmtRunStatus; /* Start program 'program' with null-terminated argument list 'arguments'. If 'detached' true, * detach (so you don't get zombie processes). Return PID of new program in 'pid' (can be NULL); * return system error code in 'error'. Call 'doneCallback' when done IF detached false (can be NULL). * * execvp is used to run the program, so if the program isn't an absolute path, $PATH is searched to * find it. */ extern XmtRunStatus XmtRunApplicationCallback(const char *program, char *const*arguments, int detached, pid_t *pid, int *error, Widget any, XtCallbackProc doneCallback, XtPointer doneCallbackClosure); /* Start program 'program' with null-terminated argument list 'arguments'. If 'detached' true, * detach (so you don't get zombie processes). Return PID of new program in 'pid' (can be NULL); * return system error code in 'error'. * * Equivalent to XmtRunApplicationCallback(program, arguments, detached,pid, error, NULL, NULL, NULL). */ extern XmtRunStatus XmtRunApplication(const char *program, char *const*arguments, int detached, pid_t *pid, int *error); #ifdef __cplusplus }; #endif #endif |