summaryrefslogtreecommitdiff
path: root/ACE/ace/Process_Manager.h
diff options
context:
space:
mode:
Diffstat (limited to 'ACE/ace/Process_Manager.h')
-rw-r--r--ACE/ace/Process_Manager.h449
1 files changed, 449 insertions, 0 deletions
diff --git a/ACE/ace/Process_Manager.h b/ACE/ace/Process_Manager.h
new file mode 100644
index 00000000000..050c2f47e2e
--- /dev/null
+++ b/ACE/ace/Process_Manager.h
@@ -0,0 +1,449 @@
+// -*- C++ -*-
+
+//=============================================================================
+/**
+ * @file Process_Manager.h
+ *
+ * $Id$
+ *
+ * @author Douglas C. Schmidt <schmidt@cs.wustl.edu>
+ */
+//=============================================================================
+
+#ifndef ACE_PROCESS_MANAGER_H
+#define ACE_PROCESS_MANAGER_H
+
+#include /**/ "ace/pre.h"
+
+#include "ace/ACE_export.h"
+
+#if !defined (ACE_LACKS_PRAGMA_ONCE)
+# pragma once
+#endif /* ACE_LACKS_PRAGMA_ONCE */
+
+#include "ace/Process.h"
+#include "ace/Event_Handler.h"
+#include "ace/Time_Value.h"
+
+#if defined (ACE_HAS_THREADS)
+# include "ace/Recursive_Thread_Mutex.h"
+#endif /* ACE_HAS_THREADS */
+
+ACE_BEGIN_VERSIONED_NAMESPACE_DECL
+
+class ACE_Reactor;
+
+/**
+ * @class ACE_Process_Descriptor
+ *
+ * @brief Information describing each process that's controlled by an
+ * \<ACE_Process_Manager\>.
+ */
+class ACE_Export ACE_Process_Descriptor
+{
+private:
+ friend class ACE_Process_Manager;
+
+ /// Default ctor/dtor.
+ ACE_Process_Descriptor (void);
+ ~ACE_Process_Descriptor (void);
+
+ /// Describes the process itself.
+ ACE_Process *process_;
+
+ /// function to call when process exits
+ ACE_Event_Handler *exit_notify_;
+
+ /// Dump the state of an object.
+ void dump (void) const;
+};
+
+/**
+ * @class ACE_Process_Manager
+ *
+ * @brief Manages a group of processes.
+ *
+ * This class allows applications to control groups of processes,
+ * similar to how the \<ACE_Thread_Manager\> controls groups of
+ * threads. Naturally, it doesn't work at all on platforms, such
+ * as VxWorks or pSoS, that don't support process.
+ * There are two (main) ways of using \<ACE_Process_Manager\>,
+ * depending on how involved you wish to be with the termination
+ * of managed <ACE_Process>es. If you just want <Process>es to
+ * go away when they're finished, simply register the
+ * <Process_Manager> with an \<ACE_Reactor\>:
+ * ACE_Process_Manager mgr( 100, some_reactor )
+ * -or-
+ * ACE_Process_Manager mgr;
+ * ...
+ * mgr.open( 100, some_reactor );
+ * Then, the <Process_Manager> will clean up after any
+ * <Process>es that it spawns. (On Unix, this means executing a
+ * wait(2) to collect the exit status -- and avoid zombie
+ * processes; on Win32, it means closing the process and thread
+ * HANDLEs that are created when CreateProcess is called.)
+ * If, on the other hand (and for some inexplicable reason) you
+ * want to explicitly invoke the terminated <Process> cleanup
+ * code, then *don't* register the <Process_Manager> with a
+ * Reactor, and be sure to call one of the
+ * <Process_Manager::wait> functions whenever there might be
+ * managed <Process>es that have exited.
+ * Note that in either case, <Process_Manager> allows you to
+ * register "<Event_Handlers>" to be called when a specific
+ * <Process> exits, or when any <Process> without a specific
+ * <Event_Handler> exits. When a <Process> exits, the
+ * appropriate <Event_Handler>'s <handle_input> is called; the
+ * ACE_HANDLE passed is either the Process' HANDLE (on Win32),
+ * or its pid cast to an ACE_HANDLE (on unix).
+ * It is also possible to call the <Process_Manager::wait>
+ * functions even though the <Process_Manager> is registered with
+ * a <Reactor>.
+ * Note also that the wait functions are "sloppy" on Unix,
+ * because there's no good way to wait for a subset of the
+ * children of a process. The wait functions may end up
+ * collecting the exit status of a process that's not managed by
+ * the <Process_Manager> whose <wait> you invoked. It's best to
+ * only use a single <Process_Manager>, and to create all
+ * subprocesses by calling that <Process_Manager>'s <spawn>
+ * method.
+ * Incidentally, when you register your <Process_Manager> with a
+ * <Reactor> its notification pipe is used to help "reap" the
+ * available exit statuses. Therefore, you must not use a
+ * <Reactor> whose notify pipe has been disabled. Here's the
+ * sequence of steps used to reap the exit statuses in this case:
+ * + The <Process_Manager> registers a signal handler for
+ * SIGCHLD.
+ * + The SIGCHLD handler, when invoked, uses the <Reactor>'s
+ * <notify> method to inform the <Reactor> to wake up.
+ * + Next, the <Reactor> calls the <Process_Manager>'s
+ * <handle_input>, this happens synchronously, not in
+ * sighandler-space.
+ * + The <handle_input> method collects all available exit
+ * statuses.
+ */
+class ACE_Export ACE_Process_Manager : protected ACE_Event_Handler
+{
+public:
+ friend class ACE_Process_Control;
+
+ enum
+ {
+ DEFAULT_SIZE = 100
+ };
+
+ // = Initialization and termination methods.
+ /**
+ * Initialize an <ACE_Process_Manager> with a table containing up to
+ * <size> processes. This table resizes itself automatically as
+ * needed. If a non-NULL <reactor> is provided, this
+ * <ACE_Process_Manager> uses it to notify an application when a
+ * process it controls exits. By default, however, we don't use an
+ * ACE_Reactor.
+ */
+ ACE_Process_Manager (size_t size = ACE_Process_Manager::DEFAULT_SIZE,
+ ACE_Reactor *reactor = 0);
+
+ /**
+ * Initialize an <ACE_Process_Manager> with a table containing up to
+ * <size> processes. This table resizes itself automatically as
+ * needed. If a non-NULL <reactor> is provided, this
+ * <ACE_Process_Manager> uses it to notify an application when a
+ * process it controls exits. By default, however, we don't use an
+ * ACE_Reactor.
+ */
+ int open (size_t size = DEFAULT_SIZE,
+ ACE_Reactor *r = 0);
+
+ /// Release all resources. Do not wait for processes to exit.
+ int close (void);
+
+ /// Destructor releases all resources and does not wait for processes
+ /// to exit.
+ virtual ~ACE_Process_Manager (void);
+
+ // = Singleton accessors.
+ /// Get pointer to a process-wide <ACE_Process_Manager>.
+ static ACE_Process_Manager *instance (void);
+
+ /// Set pointer to a process-wide <ACE_Process_Manager> and return
+ /// existing pointer.
+ static ACE_Process_Manager *instance (ACE_Process_Manager *);
+
+ /// Delete the dynamically allocated singleton.
+ static void close_singleton (void);
+
+ /// Cleanup method, used by the <ACE_Object_Manager> to destroy the
+ /// singleton.
+ static void cleanup (void *instance, void *arg);
+
+ // = Process creation methods.
+
+ /**
+ * Create a new process by passing <options> to <proc.spawn>.
+ * Register <event_handler> to be called back when the process exits.
+ *
+ * On success, returns the process id of the child that was created.
+ * On failure, returns ACE_INVALID_PID.
+ */
+ pid_t spawn (ACE_Process *proc,
+ ACE_Process_Options &options,
+ ACE_Event_Handler *event_handler = 0);
+
+ /**
+ * Create a new process by passing <options> to <ACE_Process::spawn>.
+ * Register <event_handler> to be called back when the process exits.
+ *
+ * On success, returns the process id of the child that was created.
+ * On failure, returns ACE_INVALID_PID.
+ */
+ pid_t spawn (ACE_Process_Options &options,
+ ACE_Event_Handler *event_handler = 0);
+
+ /**
+ * Create <n> new processes by passing <options> to
+ * <ACE_Process::spawn>, which is called <n> times. If <child_pids>
+ * is non-0 it is expected to be an array of <n> <pid_t>'s, which
+ * are filled in with the process ids of each newly created process.
+ * Register <event_handler> to be called back when each process exits.
+ * Returns 0 on success and -1 on failure.
+ */
+ int spawn_n (size_t n,
+ ACE_Process_Options &options,
+ pid_t *child_pids = 0,
+ ACE_Event_Handler *event_Handler = 0);
+
+ // = Process synchronization operations.
+
+ /**
+ * Block until there are no more child processes running that were
+ * @c spawn()ed by this @c ACE_Process_Manager. Unlike the @c wait
+ * call below, this method does not require a signal handler or
+ * @c ACE_OS::sigwait because it simply blocks synchronously waiting
+ * for all the children managed by this @c ACE_Process_Manager to
+ * exit. Note that this does not return any status information
+ * about the success or failure of exiting child processes, although
+ * any registered exit_handlers are called. Returns 0 on success
+ * (and @c remove()s the corresponding @c ACE_Process_Descriptor
+ * entries from the @c Process_Manager; otherwise, returns -1 on
+ * failure.
+ *
+ * @param timeout Relative time to wait for process to terminate
+ */
+ int wait (const ACE_Time_Value &timeout = ACE_Time_Value::max_time);
+
+ /**
+ * Wait up to @a timeout for a single process to terminate. If
+ * pid==0, waits for any of the managed <Process>es (but see the
+ * note in the class documentation above for caveats about this --
+ * "sloppy process cleanup on unix") If pid != 0, waits for that <Process>
+ * only. Returns the pid of the Process whose exit was handled, 0
+ * if a timeout occurred, or ACE_INVALID_PID on error.
+ *
+ * @param pid Process ID
+ * @param timeout Relative time to wait for process to terminate
+ * @param status Exit status of terminated process
+ */
+ pid_t wait (pid_t pid,
+ const ACE_Time_Value &timeout,
+ ACE_exitcode *status = 0);
+
+ /**
+ * Wait indefinitely for a single process to terminate. If pid==0,
+ * waits for any of the managed <Process>es (but see the note in
+ * the class documentation for caveats about this -- "sloppy Process
+ * cleanup on unix") If pid != 0, waits for that <Process> only.
+ * Returns the pid of the process whose exit was handled, or
+ * ACE_INVALID_PID on error.
+ */
+ pid_t wait (pid_t pid,
+ ACE_exitcode *status = 0);
+
+ /**
+ * @deprecated
+ * Reap the result of a single process by calling <ACE_OS::waitpid>,
+ * therefore, this method is not portable to Win32. If the child is
+ * successfully reaped, <remove> is called automatically. This
+ * method does the same thing that the <wait> method directly above
+ * it does -- It's just here for backwards compatibility.
+ */
+ int reap (pid_t pid = -1,
+ ACE_exitcode *stat_loc = 0,
+ int options = WNOHANG);
+
+ // = Utility methods.
+ /**
+ * Register an Event_Handler to be called back when the specified
+ * process exits. If pid == ACE_INVALID_PID this handler is called
+ * when any process with no specific handler exits.
+ *
+ * @note In multi-threaded applications, there is a race condition
+ * if a process exits between the time it is spawned and when its
+ * handler is registered. To avoid this, register the handler at
+ * the time the process is spawned.
+ */
+ int register_handler (ACE_Event_Handler *event_handler,
+ pid_t pid = ACE_INVALID_PID);
+
+ /**
+ * Remove process <pid> from the table. This is called
+ * automatically by the <reap> method after it successfully reaped a
+ * <SIGCHLD> signal. It's also possible to call this method
+ * directly from a signal handler, but don't call both <reap> and
+ * <remove>!
+ */
+ int remove (pid_t pid);
+
+ /**
+ * Abruptly terminate a single process with id <pid> using the
+ * <ACE::terminate_process> method. Note that this call is
+ * potentially dangerous to use since the process being terminated
+ * may not have a chance to cleanup before it shuts down. Returns 0
+ * on success and -1 on failure.
+ */
+ int terminate (pid_t pid);
+
+ /// On OSs that support signals, send the signal to the specified
+ /// process. Returns 0 on success and -1 on failure.
+ int terminate (pid_t pid,
+ int sig);
+
+ /// Return the number of managed Processes.
+ size_t managed (void) const;
+ /**
+ * Sets the scheduling parameters for the <Process> managed by
+ * <ACE_Process_Manager> identified by pid by passing <params>, <pid> to
+ * <ACE_OS::sched_params>. Returns 0 on success, -1 on failure, and
+ * ACE_INVALID_PID when given pid is not managed by
+ * <ACE_Process_Manager>.
+ */
+ int set_scheduler (const ACE_Sched_Params &params,
+ pid_t pid);
+
+ /**
+ * Sets the scheduling parameters for all the <Process>es managed by
+ * <ACE_Process_Manager> by passing <params> to
+ * <ACE_OS::sched_params>. Returns 0 on success, -1 on failure.
+ */
+ int set_scheduler_all (const ACE_Sched_Params &);
+
+ /// Dump the state of an object.
+ void dump (void) const;
+
+ /// Declare the dynamic allocation hooks.
+ ACE_ALLOC_HOOK_DECLARE;
+
+protected:
+ // = These methods allow a <Process_Manager> to be an <Event_Handler>.
+
+ // As an <Event_Handler>, the <Process_Manager> automagically
+ // detects child Processes exiting and calls notify_proc_handler()
+ // and remove(). This means that you don't have to (shouldn't!)
+ // call the wait(...) methods yourself.
+
+ // On Unix, we can't detect individual process termination very
+ // well; the best method is to catch SIGCHLD and then call the
+ // polling wait() function to collect any available exit statuses.
+ // However, we don't want to do this from within a signal handler
+ // because of the restrictions associated. Therefore (following the
+ // lead in examples/mumble) we open a bogus handle (to ACE_DEV_NULL)
+ // and register that handle with our Reactor. Then, when our
+ // SIGCHLD handler gets invoked, we tell the Reactor that the bogus
+ // handle is readable. That will cause the handle_input() function
+ // to be called once we're out of the interrupt context, and
+ // handle_input() collects exit statuses.
+
+ // On Win32, we simply register ourself with the Reactor to deal
+ // with the Process handle becoming signaled. No muss, no fuss, no
+ // signal handler, and no dummy handle.
+
+#if !defined(ACE_WIN32)
+ /// Collect one (or more, on unix) process exit status.
+ virtual int handle_input (ACE_HANDLE proc);
+#endif // !defined(ACE_WIN32)
+
+ /**
+ * On Unix, this routine is called asynchronously when a SIGCHLD is
+ * received. We just tweak the reactor so that it'll call back our
+ * <handle_input> function, which allows us to handle Process exits
+ * synchronously.
+ *
+ * On Win32, this routine is called synchronously, and is passed the
+ * HANDLE of the Process that exited, so we can do all our work here
+ */
+ virtual int handle_signal (int signum,
+ siginfo_t * = 0,
+ ucontext_t * = 0);
+
+private:
+ /// Resize the pool of Process_Descriptors.
+ int resize (size_t);
+
+ /// Locate the index of the table slot occupied by <process_id>.
+ /// Returns -1 if <process_id> is not in the <process_table_>
+ ssize_t find_proc (pid_t process_id);
+
+#if defined (ACE_WIN32)
+ /// Locate the index of the table slot occupied by <process_handle>.
+ /// Returns ~0 if <process_handle> is not in the <process_table_>
+ ssize_t find_proc (ACE_HANDLE process_handle);
+#endif /* ACE_WIN32 */
+
+ /// Insert a process in the table (checks for duplicates). Omitting
+ /// the process handle won't work on Win32...
+ /// Register <event_handler> to be called back when the process exits.
+ int insert_proc (ACE_Process *process,
+ ACE_Event_Handler *event_handler = 0);
+
+ /**
+ * Append information about a process, i.e., its <process_id> in the
+ * <process_table_>. Each entry is added at the end, growing the
+ * table if necessary.
+ * Register <event_handler> to be called back when the process exits.
+ */
+ int append_proc (ACE_Process *process,
+ ACE_Event_Handler *event_handler = 0);
+
+ /// Actually removes the process at index <n> from the table. This method
+ /// must be called with locks held.
+ int remove_proc (size_t n);
+
+ /// If there's a specific handler for the Process at index <n> in the
+ /// table, or there's a default handler, call it.
+ int notify_proc_handler (size_t n,
+ ACE_exitcode status);
+
+ /// Vector that describes process state within the Process_Manager.
+ ACE_Process_Descriptor *process_table_;
+
+ /// Maximum number of processes we can manage (should be dynamically
+ /// allocated).
+ size_t max_process_table_size_;
+
+ /// Current number of processes we are managing.
+ size_t current_count_;
+
+ /// This event handler is used to notify when a process we control
+ /// exits.
+ ACE_Event_Handler *default_exit_handler_;
+
+ /// Singleton pointer.
+ static ACE_Process_Manager *instance_;
+
+ /// Controls whether the <Process_Manager> is deleted when we shut
+ /// down (we can only delete it safely if we created it!)
+ static int delete_instance_;
+
+#if defined (ACE_HAS_THREADS)
+ /// This lock protects access/ops on <process_table_>.
+ ACE_Recursive_Thread_Mutex lock_;
+#endif /* ACE_HAS_THREADS */
+};
+
+ACE_END_VERSIONED_NAMESPACE_DECL
+
+#if defined (__ACE_INLINE__)
+#include "ace/Process_Manager.inl"
+#endif /* __ACE_INLINE__ */
+
+#include /**/ "ace/post.h"
+#endif /* ACE_PROCESS_MANAGER_H */