// $Id$ // Asynchron Agent Interface // Copyright (C) 1995-1998 Technische Universitaet Braunschweig, Germany. // Written by Andreas Zeller <zeller@gnu.org>. // // This file is part of DDD. // // DDD is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public // License as published by the Free Software Foundation; either // version 3 of the License, or (at your option) any later version. // // DDD is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. // See the GNU General Public License for more details. // // You should have received a copy of the GNU General Public // License along with DDD -- see the file COPYING. // If not, see <http://www.gnu.org/licenses/>. // // DDD is the data display debugger. // For details, see the DDD World-Wide-Web page, // `http://www.gnu.org/software/ddd/', // or send a mail to the DDD developers <ddd@gnu.org>. #ifndef _DDD_AsyncAgent_h #define _DDD_AsyncAgent_h /* AsyncAgent(app_context, p) opens a three-way communication channel to the agent p (see Agent). In order to cooperate with other event-driven libraries (especially X), communication is handled asynchronously: every time that input data (from the agent's stdout) or error data (from the agent's stderr) is available, an input handler is called passing the agent as parameter. The outputHandler is called whenever the agent is ready to read data from its stdin. Its use is discouraged, since on some operating systems the outputHandler is called even when the agent is not ready yet. Upon terminate(), the agent is killed asynchronously, i.e. terminate() does not wait for the agent to die. Handlers are declared with addHandler(). Asynchronus communication is implemented using XtAppAddInput(3). */ #include "assert.h" #include <X11/Intrinsic.h> #include "Agent.h" // Set to 1 if you want asynchronous notification // #define ASYNC_CHILD_STATUS_CHANGE #ifndef ASYNC_CHILD_STATUS_CHANGE #define ASYNC_CHILD_STATUS_CHANGE (XtSpecificationRelease >= 6) #endif // !defined(ASYNC_CHILD_STATUS_CHANGE) class AsyncAgent; typedef void (*AsyncAgentHandler)(AsyncAgent *agent); struct AsyncAgentWorkProcInfo { DECLARE_TYPE_INFO AsyncAgent *agent; unsigned int type; void *call_data; AsyncAgentWorkProcInfo(AsyncAgent *a, unsigned t, void *c) : agent(a), type(t), call_data(c) {} private: AsyncAgentWorkProcInfo(const AsyncAgentWorkProcInfo&); AsyncAgentWorkProcInfo& operator = (const AsyncAgentWorkProcInfo&); }; struct AsyncAgentWorkProc { DECLARE_TYPE_INFO XtWorkProcId proc_id; // id of running background proc AsyncAgentWorkProcInfo *info; // info AsyncAgentWorkProc *next; // next one // Constructor AsyncAgentWorkProc(XtWorkProcId i, AsyncAgentWorkProcInfo *inf, AsyncAgentWorkProc *n): proc_id(i), info(inf), next(n) {} private: AsyncAgentWorkProc(const AsyncAgentWorkProc&); AsyncAgentWorkProc& operator = (const AsyncAgentWorkProc&); }; // Events const unsigned AsyncAgent_NTypes = Agent_NTypes; // Handlers const unsigned OutputReady = 0; // agent is ready for data const unsigned InputReady = 1; // data from agent's stdout is ready const unsigned ErrorReady = 2; // data from agent's stdin is ready const unsigned OutputException = 3; // I/O exception (urgent msg) on output const unsigned InputException = 4; // I/O exception (urgent msg) on input const unsigned ErrorException = 5; // I/O exception (urgent msg) on error const unsigned AsyncAgent_NHandlers = 6; // # of handler types class AsyncAgent: public Agent { public: DECLARE_TYPE_INFO private: XtAppContext _appContext; // the application context AsyncAgentHandler _handlers[AsyncAgent_NHandlers]; // handlers XtInputId _ids[AsyncAgent_NHandlers]; // their ids AsyncAgentWorkProc *workProcs; // working procedures // dispatch event void dispatch(int *fid, XtInputId *id); // used in childStatusChange() int new_status; bool status_change_pending; #if ASYNC_CHILD_STATUS_CHANGE XtSignalId signal_id; #else XtIntervalId signal_id; // Just for padding #endif bool killing_asynchronously; void initHandlers(); void addDeathOfChildHandler(); // delete remaining work procedure(s) void deleteWorkProc(AsyncAgentWorkProcInfo *info, bool remove = true); void deleteAllWorkProcs(); // Call when NEW_STATUS has been set void statusChange(); #if ASYNC_CHILD_STATUS_CHANGE static void _childStatusChange(XtPointer client_data, XtSignalId *id); #endif // X Event Handlers static void somethingHappened(XtPointer client_data, int *fid, XtInputId *id); static void childStatusChange(Agent *agent, void *client_data, void *call_data); static Boolean callTheHandlers(XtPointer client_data); static void callTheHandlersIfIdle(XtPointer client_data, XtIntervalId *id); // Helping functions static void terminateProcess(XtPointer, XtIntervalId *); static void hangupProcess(XtPointer, XtIntervalId *); static void killProcess(XtPointer, XtIntervalId *); protected: // Set handler AsyncAgentHandler setHandler(unsigned type, AsyncAgentHandler handler = 0); // Clear all handlers void clearHandlers(); // resources XtInputId id(unsigned type) const { assert(type < AsyncAgent_NHandlers); return _ids[type]; } AsyncAgentHandler handler(unsigned type) const { assert(type < AsyncAgent_NHandlers); return _handlers[type]; } virtual void waitToTerminate(); // Delayed event handling virtual void callHandlersWhenIdle(int type, void *call_data = 0); virtual bool isIdle() { return true; } // Hook for channel closing virtual void closeChannel(FILE *fp); private: // remove input void removeInput(unsigned type) { if (id(type)) { XtRemoveInput(id(type)); _ids[type] = 0; } } AsyncAgent& operator = (const AsyncAgent&); public: // Resources XtAppContext appContext() const { return _appContext; } // Constructors AsyncAgent(XtAppContext app_context, const string& pth, unsigned nTypes = AsyncAgent_NTypes): Agent(pth, nTypes), _appContext(app_context), workProcs(0), new_status(0), status_change_pending(false), signal_id(0), killing_asynchronously(false) { initHandlers(); addDeathOfChildHandler(); } AsyncAgent(XtAppContext app_context, FILE *in = stdin, FILE *out = stdout, FILE *err = 0, unsigned nTypes = AsyncAgent_NTypes): Agent(in, out, err, nTypes), _appContext(app_context), workProcs(0), new_status(0), status_change_pending(false), signal_id(0), killing_asynchronously(false) { initHandlers(); } AsyncAgent(XtAppContext app_context, bool dummy, unsigned nTypes = AsyncAgent_NTypes): Agent(dummy, nTypes), _appContext(app_context), workProcs(0), new_status(0), status_change_pending(false), signal_id(0), killing_asynchronously(false) { initHandlers(); } // Duplicator AsyncAgent(const AsyncAgent& c): Agent(c), _appContext(c.appContext()), workProcs(0), new_status(0), status_change_pending(false), signal_id(0), killing_asynchronously(false) { initHandlers(); } virtual Agent *dup() const { return new AsyncAgent(*this); } // Destructor ~AsyncAgent() { // Make sure the work procedure won't be called deleteAllWorkProcs(); // Inhibit further Xt selection clearHandlers(); } // Commit pending status changes virtual void commit(); // These need special management: virtual void abort(); virtual void terminate(bool onExit = false); }; #endif // _DDD_AsyncAgent_h // DON'T ADD ANYTHING BEHIND THIS #endif