this includes the process anchor and the shutdown alerter, and tests for those.
the tests are not currently compiling, but the library is up with the new classes.
TYPE = library
TARGETS = processes.lib
SOURCE = configured_applications.cpp ethread.cpp heartbeat.cpp launch_process.cpp \
- letter.cpp mailbox.cpp post_office.cpp \
- process_control.cpp process_entry.cpp rendezvous.cpp safe_callback.cpp safe_roller.cpp \
- state_machine.cpp thread_cabinet.cpp
+ letter.cpp mailbox.cpp post_office.cpp process_anchor.cpp process_control.cpp \
+ process_entry.cpp rendezvous.cpp safe_callback.cpp shutdown_alerter.cpp \
+ safe_roller.cpp state_machine.cpp thread_cabinet.cpp
include cpp/rules.def
--- /dev/null
+/*
+*
+* Name : process_anchor
+* Author : Chris Koeritz
+*
+****
+* Copyright (c) 2000-$now By Author. This program 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 2 of
+* the License or (at your option) any later version. This is online at:
+* http://www.fsf.org/copyleft/gpl.html
+* Please send any updates to: fred@gruntose.com
+*/
+
+#include "process_anchor.h"
+
+///#include <application/windoze_helper.h>
+#include <basis/array.h>
+#include <basis/definitions.h>
+#include <basis/utf_conversion.h>
+#include <basis/astring.h>
+#include <filesystem/filename.h>
+#include <loggers/program_wide_logger.h>
+#include <structures/static_memory_gremlin.h>
+
+using namespace application;
+using namespace basis;
+using namespace filesystem;
+using namespace loggers;
+using namespace structures;
+//using namespace timely;
+
+#undef LOG
+#define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger::get(), s)
+
+#ifdef __WIN32__
+const int STARTUP_MESSAGE = WM_USER + 123;
+ // the special message we send out on startup of the event loop.
+
+const int TIMER_EVENT_ID = 23;
+ // the ID we use to create a timer, if the user wants one.
+
+// an anchor_association tracks the object related to a window handle.
+struct anchor_association {
+ window_handle _handle;
+ process_anchor *_anchor_object;
+
+ anchor_association(window_handle wh = NULL_POINTER, process_anchor *wo = NULL_POINTER)
+ : _handle(wh), _anchor_object(wo) {}
+};
+
+class anchor_association_list
+: public array<anchor_association>, public virtual root_object
+{
+public:
+ DEFINE_CLASS_NAME("anchor_association_list");
+};
+
+// the associations list keeps track of anchors that have been created so
+// that the window procedure (on win32) can get to the real object.
+
+SAFE_STATIC(anchor_association_list, associations, )
+#endif //win32
+
+//////////////
+
+process_anchor::process_anchor()
+#ifdef __LINUX__
+: shutdown_alerter()
+#endif
+#ifdef __WIN32__
+: _instance(NULL_POINTER),
+ _anchor_title(new astring),
+ _anchor_class(new astring),
+ _class_reg(NULL_POINTER),
+ _wind_handle(NULL_POINTER),
+ _cycle(0),
+ _defunct(false)
+#endif
+{
+}
+
+process_anchor::~process_anchor()
+{
+ set_defunct();
+#ifdef __WIN32__
+ WHACK(_anchor_title);
+ WHACK(_anchor_class);
+ _class_reg = NULL_POINTER;
+ // remove the association for our anchor.
+ for (int i = 0; i < associations().length(); i++) {
+ if (associations()[i]._handle == _wind_handle) {
+ associations().zap(i, i);
+ break;
+ }
+ }
+#endif
+}
+
+void process_anchor::handle_startup() { /* nothing for base class. */ }
+
+void process_anchor::handle_timer() { /* nothing for base class. */ }
+
+void process_anchor::handle_shutdown() { /* nothing for base class. */ }
+
+bool process_anchor::close_this_program()
+{
+#ifdef __LINUX__
+ shutdown_alerter::close_this_program();
+ return true;
+#endif
+#ifdef __WIN32__
+ bool to_return = false;
+ for (int i = 0; i < associations().length(); i++) {
+ window_handle win = associations()[i]._handle;
+ int ret = PostMessage(win, WM_CLOSE, NULL_POINTER, NULL_POINTER);
+ // if we got a healthy return from any post, then we'll say this worked.
+ if (ret) to_return = true;
+ }
+ return to_return;
+#endif
+}
+
+bool process_anchor::defunct() const
+{
+#ifdef __LINUX__
+ return shutdown_alerter::is_defunct();
+#endif
+#ifdef __WIN32__
+ return _defunct;
+#endif
+}
+
+void process_anchor::set_defunct()
+{
+#ifdef __LINUX__
+ return shutdown_alerter::set_defunct();
+#endif
+#ifdef __WIN32__
+ _defunct = true;
+#endif
+}
+
+bool process_anchor::close_app_anchor(const astring &app_name)
+{
+ FUNCDEF("close_app_anchor");
+#ifdef __LINUX__
+ return shutdown_alerter::close_application(app_name);
+#endif
+#ifdef __WIN32__
+ astring title = process_anchor::make_well_known_title(app_name);
+#ifdef DEBUG_PROCESS_MANAGER
+ LOG(astring("title is: ") + title);
+#endif
+
+//hmmm: add support for linux...
+#ifdef __WIN32__
+ window_handle win_found = FindWindow(NULL_POINTER, to_unicode_temp(title));
+#ifdef DEBUG_PROCESS_MANAGER
+ LOG(astring(astring::SPRINTF, "found window %lx", win_found));
+#endif
+ if (!win_found) {
+ LOG(astring("Failed to find special window for [") + app_name
+ + astring("]"));
+ return false;
+ }
+
+//hmmm:
+//why would we find only one window if there were multiple apps under that
+//name? how does windows deal with that? is there a find window iterator?
+
+ int ret = PostMessage(win_found, WM_CLOSE, NULL_POINTER, NULL_POINTER);
+ if (!ret) {
+ LOG(astring("Failed to send close message to [") + app_name
+ + astring("]"));
+ return false;
+ }
+
+ LOG(astring("Sent close message to [") + app_name + astring("]"));
+#else
+ #ifdef DEBUG_PROCESS_MANAGER
+ LOG("graceful shutdown not implemented for this OS.");
+ #endif
+ return false;
+#endif
+ return true;
+#endif //win32
+}
+
+bool process_anchor::setup(application_instance instance,
+ const astring &app_name, int cycle)
+{
+#ifdef __LINUX__
+ if (instance) {}
+ return shutdown_alerter::setup(app_name, cycle);
+#endif
+#ifdef __WIN32__
+ if (_wind_handle) return true; // already initialized.
+ // simple initializations first...
+ _instance = instance;
+ _cycle = cycle;
+ *_anchor_title = make_well_known_title(app_name);
+ *_anchor_class = make_well_known_class(app_name);
+ _class_reg = register_class(); // register a new window class for this.
+ _wind_handle = CreateWindow(to_unicode_temp(*_anchor_class),
+ to_unicode_temp(*_anchor_title), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT,
+ 0, CW_USEDEFAULT, 0, NULL_POINTER, NULL_POINTER, _instance, NULL_POINTER);
+ if (!_wind_handle) return false;
+
+ register_anchor(_wind_handle); // hook in our anchor to the list.
+ ShowWindow(_wind_handle, SW_HIDE);
+ UpdateWindow(_wind_handle);
+ PostMessage(_wind_handle, STARTUP_MESSAGE, 0, 0);
+#endif //win32
+ return true;
+}
+
+void process_anchor::register_anchor(window_handle wind)
+{
+#ifdef __WIN32__
+ // add the new anchor to our list of associations.
+ associations() += anchor_association(wind, this);
+#else
+ if (wind) {}
+#endif
+}
+
+#ifdef __WIN32__
+ATOM process_anchor::register_class()
+{
+ WNDCLASSEX wcex;
+
+ wcex.cbSize = sizeof(WNDCLASSEX);
+
+ wcex.style = CS_HREDRAW | CS_VREDRAW;
+ wcex.lpfnWndProc = (WNDPROC)WndProc;
+ wcex.cbClsExtra = 0;
+ wcex.cbWndExtra = 0;
+ wcex.hInstance = _instance;
+ wcex.hIcon = NULL_POINTER;
+ wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
+ wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
+ wcex.lpszMenuName = NULL_POINTER;
+ to_unicode_persist(hold_class, _anchor_class->s());
+ wcex.lpszClassName = hold_class;
+ wcex.hIconSm = NULL_POINTER;
+
+ return RegisterClassEx(&wcex);
+}
+#endif
+
+#ifdef __WIN32__
+LRESULT CALLBACK process_anchor::WndProc(HWND hWnd, UINT message,
+ WPARAM wParam, LPARAM lParam)
+{
+ switch (message) {
+ case STARTUP_MESSAGE: {
+ for (int i = 0; i < associations().length(); i++) {
+ if (associations()[i]._handle == hWnd) {
+ process_anchor &anch = *associations()[i]._anchor_object;
+ // invoke the initial callback so the anchor can initialize.
+ anch.handle_startup();
+
+ // set a timer going if they wanted one.
+ if (anch._cycle)
+ SetTimer(anch._wind_handle, TIMER_EVENT_ID, anch._cycle, 0);
+ break;
+ }
+ }
+ break;
+ }
+ case WM_CLOSE: {
+ for (int i = 0; i < associations().length(); i++) {
+ if (associations()[i]._handle == hWnd) {
+ // invoke the closing callback because we're leaving.
+ associations()[i]._anchor_object->handle_shutdown();
+ associations()[i]._anchor_object->set_defunct();
+ break;
+ }
+ }
+ return DefWindowProc(hWnd, message, wParam, lParam);
+ break;
+ }
+ case WM_DESTROY: {
+ PostQuitMessage(0);
+ break;
+ }
+ case WM_TIMER: {
+ bool found_window = false; // records if we dispatched the event.
+ for (int i = 0; i < associations().length(); i++)
+ if (associations()[i]._handle == hWnd) {
+ // always stop the timer since we're suspending time while we're busy.
+ // more of a desire than a strategy.
+ KillTimer(hWnd, TIMER_EVENT_ID);
+ // invoke the timer callback to give the user some action.
+ associations()[i]._anchor_object->handle_timer();
+ found_window = true;
+ // always set the timer going again after handling it.
+ SetTimer(hWnd, TIMER_EVENT_ID,
+ associations()[i]._anchor_object->_cycle, 0);
+ break;
+ }
+ bool to_return = 0;
+ // if the timer wasn't for one of our anchors, we pass it to the default
+ // window procedure in hopes it will know what the hell to do with it.
+ if (!found_anchor)
+ to_return = DefWindowProc(hWnd, message, wParam, lParam);
+ return to_return;
+ }
+ case WM_PAINT: {
+ PAINTSTRUCT ps;
+ HDC hdc = BeginPaint(hWnd, &ps);
+ // add drawing code here if needed.
+ EndPaint(hWnd, &ps);
+ break;
+ }
+ default: return DefWindowProc(hWnd, message, wParam, lParam);
+ }
+ return 0;
+}
+#endif
+
+#ifdef __WIN32__
+astring process_anchor::make_well_known_title(const astring &application_name)
+{
+ filename app_short = application_name;
+ return astring("Anchor_for_") + app_short.basename();
+}
+#endif
+
+#ifdef __WIN32__
+astring process_anchor::make_well_known_class(const astring &application_name)
+{
+ filename app_short = application_name;
+ return astring("Dozeclass_for_") + app_short.basename();
+}
+#endif
+
+bool process_anchor::launch(process_anchor &win, application_instance handle,
+ const astring &application_name, int cycle)
+{
+ // prepare the anchor for its role...
+ if (!win.setup(handle, application_name, cycle)) return false;
+#ifdef __LINUX__
+ return shutdown_alerter::launch_console(win, application_name, cycle);
+#endif
+#ifdef __WIN32__
+ MSG msg;
+ msg.hwnd = 0; msg.message = 0; msg.wParam = 0; msg.lParam = 0;
+ while (GetMessage(&msg, NULL_POINTER, 0, 0)) {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+#endif
+ return true;
+}
+
+
+
+
--- /dev/null
+#ifndef PROCESS_ANCHOR_CLASS
+#define PROCESS_ANCHOR_CLASS
+
+/*
+*
+* Name : process_anchor
+* Author : Chris Koeritz
+*
+****
+* Copyright (c) 2000-$now By Author. This program 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 2 of
+* the License or (at your option) any later version. This is online at:
+* http://www.fsf.org/copyleft/gpl.html
+* Please send any updates to: fred@gruntose.com
+*/
+
+#include <application/windoze_helper.h>
+#include <basis/astring.h>
+
+#ifdef __LINUX__
+ #include "shutdown_alerter.h"
+#endif
+
+//! Implements a graceful shutdown procedure for an application.
+/*!
+ The advantage of the process_anchor (where it's actually a window, which is
+ to say, under win32) is that it's hidden and has a well-known name. This
+ allows it to be found through FindWindow to facilitate a graceful shutdown
+ for the applications that use it. On linux, it is implemented with a
+ shutdown_alerter object instead and is not implemented using windowing.
+*/
+class process_anchor
+#ifdef __LINUX__
+: public shutdown_alerter
+#endif
+{
+public:
+ process_anchor();
+ //!< constructor does very little; setup() begins operation.
+
+ virtual ~process_anchor();
+
+ DEFINE_CLASS_NAME("process_anchor");
+
+ static bool close_this_program();
+ //!< causes this particular program to shut down.
+ /*!< this method is available for programs that support the graceful
+ shutdown process. it causes the machinery of the process_anchor to alert
+ the application that a graceful shutdown has been started. the method
+ returns true if this program's process_anchor was found and sent the
+ close message. */
+
+ static bool close_app_anchor(const basis::astring &app_name);
+ //!< closes the anchor object associated with "app_name".
+ /*!< note that this closes *ALL* such anchors, so if the application had
+ more than one instance running, they will all shut down.
+also note: the above doesn't seem to be implemented in the current version.
+ */
+
+ bool defunct() const;
+ //!< returns true if the object has been marked as defunct.
+ /*!< this means that it is either shutting down or soon will be. */
+
+ void set_defunct();
+ //!< used by the derived class to mark that this object is about to exit.
+
+ bool setup(application_instance handle, const basis::astring &application_name,
+ int timing_cycle = 0);
+ //!< constructs a process_anchor for the "application_name" specified.
+ /*!< the instance "handle" is the link to the application. the anchor is
+ created (and made hidden on windows) if successful. the standard here for the
+ "application_name" is that it should not have any directory components,
+ but it should be the full program name (including extension). if
+ "timing_cycle" is non-zero, then it is used as a timer interval. every
+ time the interval elapses, the handle_timer() method is invoked.
+ note that this function is invoked by launch() and does not need to be
+ called by a user unless launch() is not being used to control the anchor's
+ lifetime. */
+
+ void register_anchor(window_handle anchor);
+ //!< this supports the anchor being created elsewhere.
+ /*!< when the object is created independently but should still function
+ as an process_anchor, then this method can be used to hook in that
+ external "window" into our support. this only makes sense for the
+ windows version. */
+
+ static bool launch(process_anchor &anchor, application_instance handle,
+ const basis::astring &app, int cycle = 0);
+ //!< establishes a process_anchor for the program named "app".
+ /*!< on windows, this function will not return until WM_CLOSE is received; it is the
+ main message loop for a simple application. the "handle" parameter should
+ be coming from the main function for the program. note that the "window"
+ should be constructed but not have any functions called on it yet. this
+ is important since this function does all the setup. using a "window"
+ derived from process_anchor is also okay. the "cycle" is passed to the
+ setup() method. */
+
+ virtual void handle_startup();
+ //!< derived classes can override this to catch the application startup.
+ /*!< this function is guaranteed to be called after the event processing
+ loop has started, but before much of anything else is done in the
+ application. */
+
+ virtual void handle_timer();
+ //!< invoked periodically if the anchor was setup() with a timer "cycle".
+
+ virtual void handle_shutdown();
+ //!< invoked just prior to the shutdown of this anchor.
+
+ static basis::astring make_well_known_title(const basis::astring &application_name);
+ //!< returns the string form of the well-known window title for the process_anchor.
+ /*!< this title identifies the "application_name" on ms-windows and is used for our
+ hidden window. this is how the window can be found at runtime.
+ note that this approach will only work properly if there is only one
+ window by that name on a host at a time (due to how the windows are
+ registered). */
+ static basis::astring make_well_known_class(const basis::astring &application_name);
+ //!< same as above but for the anchor's class name.
+
+private:
+ application_instance _instance; //!< the current instance in win32.
+ basis::astring *_anchor_title; //!< the text for the title bar.
+ basis::astring *_anchor_class; //!< the name for internal registration.
+ ATOM _class_reg; //!< non-zero if the class has been registered.
+ window_handle _wind_handle; //!< the handle for this window's object.
+ int _cycle; //!< timer interval spacing out periodic activity.
+ bool _defunct; //!< is the object shutting down?
+
+ ATOM register_class();
+ //!< sets up the window's class entry for hooking into ms-windows events.
+
+#ifdef __WIN32__
+ static LRESULT CALLBACK WndProc(HWND wind, UINT msg, WPARAM wp, LPARAM lp);
+ //!< the window procedure that handles all events.
+#endif
+
+ // not appropriate.
+ process_anchor(const process_anchor &);
+ process_anchor &operator =(const process_anchor &);
+};
+
+#endif // outer guard.
+
--- /dev/null
+
+
+
+/*****************************************************************************\
+* *
+* Name : shutdown_alerter *
+* Author : Chris Koeritz *
+* *
+*******************************************************************************
+* Copyright (c) 2003-$now By Author. This program 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 2 of *
+* the License or (at your option) any later version. This is online at: *
+* http://www.fsf.org/copyleft/gpl.html *
+* Please send any updates to: fred@gruntose.com *
+\*****************************************************************************/
+
+#include "process_control.h"
+#include "process_entry.h"
+#include "shutdown_alerter.h"
+
+#include <application/command_line.h>
+#include <basis/array.h>
+#include <basis/astring.h>
+#include <basis/mutex.h>
+#include <filesystem/filename.h>
+#include <loggers/program_wide_logger.h>
+#include <structures/set.h>
+#include <structures/static_memory_gremlin.h>
+#include <timely/time_control.h>
+#include <timely/time_stamp.h>
+#include <timely/timer_driver.h>
+
+#include <signal.h>
+#include <stdio.h>
+
+using namespace application;
+using namespace basis;
+using namespace filesystem;
+using namespace loggers;
+using namespace processes;
+using namespace structures;
+using namespace timely;
+
+#define DEBUG_SHUTDOWN_ALERTER
+ // uncomment for noisy version.
+
+#undef LOG
+#define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger::get(), s)
+
+//////////////
+
+SAFE_STATIC(astring, shutdown_alerter::_app_name, )
+
+bool &shutdown_alerter::_defunct() { static bool _defu = false; return _defu; }
+
+bool &shutdown_alerter::_saw_interrupt()
+{ static bool _saw = false; return _saw; }
+
+int &shutdown_alerter::_timer_period() { static int _tim = 0; return _tim; }
+
+//////////////
+
+static shutdown_alerter *_global_shutdown_alerter = NULL_POINTER;
+ // this static object is set by the setup() method. it should only come
+ // into existence once during a program's lifetime.
+
+shutdown_alerter::shutdown_alerter()
+{
+}
+
+shutdown_alerter::~shutdown_alerter()
+{
+ set_defunct();
+ if (_global_shutdown_alerter) {
+ program_wide_timer().zap_timer(_global_shutdown_alerter);
+ }
+}
+
+void shutdown_alerter::handle_startup() { /* nothing for base class. */ }
+
+void shutdown_alerter::handle_shutdown() { /* nothing for base class. */ }
+
+void shutdown_alerter::handle_timer() { /* nothing for base class. */ }
+
+void shutdown_alerter::handle_timer_callback()
+{
+ // don't invoke the user's timer unless the object is still alive.
+ if (!is_defunct()) handle_timer();
+}
+
+void shutdown_alerter::close_this_program()
+{
+ set_defunct();
+}
+
+bool shutdown_alerter::close_application(const astring &app_name)
+{
+ FUNCDEF("close_application");
+ process_entry_array procs;
+ process_control querier;
+
+ // lookup process ids of apps.
+ bool got_list = querier.query_processes(procs);
+ if (!got_list) {
+ LOG("couldn't get process list.");
+ return false;
+ }
+ int_set pids;
+ got_list = querier.find_process_in_list(procs, app_name, pids);
+ if (!got_list) {
+ LOG("couldn't find process in the list of active ones.");
+ return true;
+ }
+
+ // zap all of them using our signal.
+ for (int i = 0; i < pids.length(); i++) {
+//would linux be better served with sigterm also?
+#ifdef __UNIX__
+ kill(pids[i], SIGHUP);
+#endif
+#ifdef __WIN32__
+//lame--goes to whole program.
+ raise(SIGTERM);
+#endif
+//hmmm: check results...
+ }
+
+ return true;
+}
+
+void shutdown_alerter::handle_OS_signal(int formal(sig_id))
+{
+ _saw_interrupt() = true; // save the status.
+ if (_global_shutdown_alerter) {
+ _global_shutdown_alerter->close_this_program();
+ }
+}
+
+void shutdown_alerter::set_defunct()
+{
+ _defunct() = true;
+}
+
+bool shutdown_alerter::setup(const astring &app_name, int timer_period)
+{
+//hmmm: make sure not already initted.
+
+ // simple initializations first...
+ _timer_period() = timer_period;
+ _app_name() = app_name;
+
+ _global_shutdown_alerter = this;
+
+ // setup signal handler for HUP signal. this is the one used to tell us
+ // to leave.
+#ifdef __UNIX__
+ signal(SIGHUP, handle_OS_signal);
+#endif
+
+ // setup a handler for interrupt (e.g. ctrl-C) also.
+ signal(SIGINT, handle_OS_signal);
+#ifdef __WIN32__
+ signal(SIGBREAK, handle_OS_signal);
+#endif
+
+ return true;
+}
+
+bool shutdown_alerter::launch_console(shutdown_alerter &alert,
+ const astring &app_name, int timer_period)
+{
+ FUNCDEF("launch_console");
+ if (!alert.setup(app_name, timer_period)) return false;
+
+ alert.handle_startup(); // tell the program it has started up.
+
+ // start a timer if they requested one.
+ if (_timer_period()) {
+ program_wide_timer().set_timer(_timer_period(), &alert);
+ }
+
+#ifdef DEBUG_SHUTDOWN_ALERTER
+ time_stamp next_report(10 * SECOND_ms);
+#endif
+
+ while (!alert.is_defunct()) {
+#ifdef DEBUG_SHUTDOWN_ALERTER
+ if (time_stamp() >= next_report) {
+ printf("%s: shout out from my main thread yo.\n", _global_argv[0]);
+ next_report.reset(10 * SECOND_ms);
+ }
+#endif
+ time_control::sleep_ms(42);
+ }
+ alert.handle_shutdown();
+ return true;
+}
+
+/*
+#ifdef __WIN32__
+bool shutdown_alerter::launch_event_loop(shutdown_alerter &alert,
+ const astring &app_name, int timer_period)
+{
+ if (!alert.setup(app_name, timer_period)) return false;
+ alert.handle_startup();
+
+ if (timer_period)
+ program_wide_timer().set_timer(timer_period, this);
+
+ MSG msg;
+ msg.hwnd = 0; msg.message = 0; msg.wParam = 0; msg.lParam = 0;
+ while (!alert.is_defunct() && (GetMessage(&msg, NULL_POINTER, 0, 0)) {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ alert.handle_shutdown();
+
+ return true;
+}
+#endif
+*/
+
+
+
+
+
--- /dev/null
+#ifndef SHUTDOWN_ALERTER_CLASS
+#define SHUTDOWN_ALERTER_CLASS
+
+/*****************************************************************************\
+* *
+* Name : shutdown_alerter *
+* Author : Chris Koeritz *
+* *
+*******************************************************************************
+* Copyright (c) 2003-$now By Author. This program 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 2 of *
+* the License or (at your option) any later version. This is online at: *
+* http://www.fsf.org/copyleft/gpl.html *
+* Please send any updates to: fred@gruntose.com *
+\*****************************************************************************/
+
+#include <basis/astring.h>
+#include <basis/definitions.h>
+#include <timely/timer_driver.h>
+
+// forward.
+//class basis::astring_object;
+
+//! A platform-independent way to alert a program that it should shut down immediately.
+/*!
+ This can provide a service management feature for graceful shutdown of an
+ application, allowing it a chance to close its objects cleanly, rather than
+ just being whacked in the middle of whatever it's doing.
+ Only one of these objects should be instantiated per program, but the
+ static methods can be used from anywhere in the program.
+*/
+class shutdown_alerter : public virtual basis::root_object, public timely::timeable
+{
+public:
+ shutdown_alerter();
+ //!< constructor does very little; setup() is what begins operation.
+
+ virtual ~shutdown_alerter();
+
+ DEFINE_CLASS_NAME("shutdown_alerter");
+
+ bool setup(const basis::astring &app_name, int timer_period = 0);
+ //!< constructs a shutdown_alerter for the "app_name" specified.
+ /*!< this can be any string, although it might be processed for certain
+ operating systems. also, for close_this_program() to work properly, it
+ must be the application's basename. the "timer_period" specifies how
+ frequently to invoke the handle_timer() method during runtime. */
+
+ static bool is_defunct() { return _defunct(); }
+ //!< returns true if the object has been marked as defunct.
+ /*!< this means that it is either shutting down or soon will be. */
+
+ static void set_defunct();
+ //!< used by the derived class to mark that this object is about to exit.
+ /*!< note that this can be used anywhere in the program to initiate an
+ exit of the program. */
+
+ inline bool saw_interrupt() { return _saw_interrupt(); }
+ //!< reports whether the process saw an interrupt from the user.
+
+ // these virtual methods can be overridden by applications derived from the
+ // shutdown_alerter. they support a graceful shutdown process by which
+ // applications can be alerted that they must shutdown, allowing them to take
+ // care of releasing resources beforehand.
+
+ virtual void handle_startup();
+ //!< this function is called once the program has begun operation.
+
+ virtual void handle_shutdown();
+ //!< called during the program's shutdown process.
+ /*!< this is invoked just prior to the destruction of this class which is
+ also just before the shutdown of the program overall. in this method,
+ the derived object must ensure that any threads the program started get
+ stopped, that any opened files get closed, and that any other resources
+ are released. this is the application's last chance to clean up. */
+
+ virtual void handle_timer();
+ //!< called periodically if a timer period was specified.
+
+ // static methods that can be used by the program for starting up or for
+ // graceful shutdown.
+
+ static bool launch_console(shutdown_alerter &alert, const basis::astring &app_name,
+ int timer_period = 0);
+ //!< this is used to begin execution of a console mode application.
+ /*!< this method does not do anything except sit while the extant threads
+ are in play. it will not return until the program must exit, as caused
+ by close_this_program() or close_application(). */
+
+#if 0 //not implemented.
+#ifdef __WIN32__
+ static bool launch_event_loop(shutdown_alerter &alert,
+ const basis::astring &app_name, int timer_period = 0);
+ //!< launches by starting up a windowing event loop.
+ /*!< this is appropriate for programs that are windowed and must
+ continually process window events. */
+#endif
+#endif
+
+ static void close_this_program();
+ //!< causes this particular application to begin shutting down.
+ /*!< this is a static method available for programs that support the
+ shutdown_alerter's graceful shutdown process. it causes the application
+ to begin the shutdown. */
+
+ static bool close_application(const basis::astring &app_name);
+ //!< attempts to close the application named "app_name".
+ /*!< this can only be done if this program possesses sufficient rights to
+ zap that program. */
+
+ // internal methods not to be used by outside objects.
+
+ static void handle_OS_signal(int sig_id);
+ //!< processes the signal from the OS when its time to shut down.
+
+private:
+ static bool &_saw_interrupt(); //!< did we see a break from the user?
+ static basis::astring &_app_name(); //!< the of this application.
+ static bool &_defunct(); //!< is the program shutting down?
+ static int &_timer_period(); //!< rate at which timer goes off.
+
+ virtual void handle_timer_callback();
+ //!< invoked by the timer driver.
+
+ // not appropriate.
+ shutdown_alerter(const shutdown_alerter &);
+ shutdown_alerter &operator =(const shutdown_alerter &);
+};
+
+#endif // outer guard.
+
--- /dev/null
+/*****************************************************************************\
+* *
+* Name : find_window *
+* Author : Chris Koeritz *
+* *
+* Purpose: *
+* *
+* Locates a window by the title. If it's found, the window handle is *
+* displayed. *
+* *
+*******************************************************************************
+* Copyright (c) 2000-$now By Author. This program 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 2 of *
+* the License or (at your option) any later version. This is online at: *
+* http://www.fsf.org/copyleft/gpl.html *
+* Please send any updates to: fred@gruntose.com *
+\*****************************************************************************/
+
+#include <basis/convert_utf.h>
+#include <basis/guards.h>
+#include <basis/istring.h>
+#include <basis/portable.h>
+#include <opsystem/command_line.h>
+#include <loggers/console_logger.h>
+#include <opsystem/filename.h>
+#include <data_struct/static_memory_gremlin.h>
+
+HOOPLE_STARTUP_CODE;
+
+window_handle matching_window = NIL;
+istring window_name_sought;
+
+BOOL CALLBACK zingers_enum_proc(window_handle hwnd, LPARAM lParam)
+{
+ const int MAX_TITLE = 1024;
+ flexichar win_name[MAX_TITLE + 8]; // add a little for good luck.
+ int chars = GetWindowText(hwnd, win_name, MAX_TITLE - 1);
+ if (chars > 0) {
+ // see if the current window matches what we're looking for.
+ if (istring(from_unicode_temp(win_name)).ifind(window_name_sought) >= 0) {
+ matching_window = hwnd;
+ return false; // don't keep going.
+ }
+ }
+ return true; // haven't found it yet.
+}
+
+int main(int argc, char *argv[])
+{
+ console_logger out;
+ command_line cmds(argc, argv);
+ if ( (cmds.entries() < 1)
+ || (cmds.get(0).type() != command_parameter::VALUE) ) {
+ out.log(cmds.program_name().basename().raw() + " usage:\n"
+ "this takes a single parameter, which is the name of a window\n"
+ "that is expected to be present in the current winstation. if the\n"
+ "window is found, its window handle is displayed.");
+ return 1;
+ }
+ istring title = cmds.get(0).text();
+ matching_window = NIL; // reset our match first.
+ window_name_sought = title;
+ // enumerate the windows looking for a match.
+ EnumWindows(zingers_enum_proc, 0);
+ if (!matching_window) {
+ out.log("no matching window could be found. ignoring request.");
+ return 1;
+ }
+//out.log("found matching window handle...");
+
+ if (matching_window)
+ out.log(isprintf("window handle is 0x%lx (or %ld) for ", matching_window,
+ matching_window) + title + ".");
+ else
+ out.log(istring("no window found for ") + title + ".");
+ return 0;
+}
+
+#ifdef __BUILD_STATIC_APPLICATION__
+ // static dependencies found by buildor_gen_deps.sh:
+ #include <basis/array.h>
+ #include <basis/byte_array.cpp>
+ #include <basis/callstack_tracker.cpp>
+ #include <basis/convert_utf.cpp>
+ #include <basis/definitions.cpp>
+ #include <basis/earth_time.cpp>
+ #include <basis/guards.cpp>
+ #include <basis/istring.cpp>
+ #include <basis/log_base.cpp>
+ #include <basis/memory_checker.cpp>
+ #include <basis/mutex.cpp>
+ #include <basis/object_base.h>
+ #include <basis/outcome.cpp>
+ #include <basis/packable.cpp>
+ #include <basis/portable.cpp>
+ #include <basis/sequence.h>
+ #include <basis/set.h>
+ #include <basis/trap_new.addin>
+ #include <basis/untrap_new.addin>
+ #include <basis/utility.cpp>
+ #include <basis/version_record.cpp>
+ #include <data_struct/amorph.h>
+ #include <data_struct/bit_vector.cpp>
+ #include <data_struct/byte_hasher.cpp>
+ #include <data_struct/configurator.cpp>
+ #include <data_struct/hash_table.h>
+ #include <data_struct/pointer_hash.h>
+ #include <data_struct/stack.h>
+ #include <data_struct/static_memory_gremlin.cpp>
+ #include <data_struct/string_hash.h>
+ #include <data_struct/string_hasher.cpp>
+ #include <data_struct/string_table.cpp>
+ #include <data_struct/symbol_table.h>
+ #include <data_struct/table_configurator.cpp>
+ #include <loggers/console_logger.cpp>
+ #include <loggers/file_logger.cpp>
+ #include <loggers/locked_logger.cpp>
+ #include <loggers/null_logger.cpp>
+ #include <loggers/program_wide_logger.cpp>
+ #include <opsystem/byte_filer.cpp>
+ #include <opsystem/command_line.cpp>
+ #include <opsystem/critical_events.cpp>
+ #include <opsystem/directory.cpp>
+ #include <opsystem/filename.cpp>
+ #include <opsystem/ini_config.cpp>
+ #include <opsystem/ini_parser.cpp>
+ #include <opsystem/path_configuration.cpp>
+ #include <opsystem/rendezvous.cpp>
+ #include <textual/byte_format.cpp>
+ #include <textual/parser_bits.cpp>
+ #include <textual/string_manipulation.cpp>
+ #include <textual/tokenizer.cpp>
+#endif // __BUILD_STATIC_APPLICATION__
+
PROJECT = test_processes
TYPE = test
SOURCE =
-TARGETS = test_safe_callback.exe test_state_machine.exe
+TARGETS = find_window.exe test_safe_callback.exe test_state_machine.exe zing_window.exe
LOCAL_LIBS_USED = unit_test application configuration filesystem loggers \
nodes processes structures textual timely structures basis
#filesystem mathematics
RUN_TARGETS = $(ACTUAL_TARGETS)
+LAST_TARGETS += build_shutdowner
include cpp/rules.def
+build_shutdowner:
+ $(MAKE) -f makefile.shutdowner
+
--- /dev/null
+CONSOLE_MODE = t
+
+include cpp/variables.def
+
+PROJECT = t_shutdown_alerter
+TYPE = test
+TARGETS = t_shutdown_alerter.exe
+LOCAL_LIBS_USED = basis i_library
+RUN_TARGETS =
+
+include cpp/rules.def
+
--- /dev/null
+/*****************************************************************************\
+* *
+* Name : t_shutdown_alerter *
+* Author : Chris Koeritz *
+* *
+* Purpose: *
+* *
+* An example of using the shutdown_alerter object to manage the runtime *
+* of a program. *
+* *
+*******************************************************************************
+* Copyright (c) 2005-$now By Author. This program 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 2 of *
+* the License or (at your option) any later version. This is online at: *
+* http://www.fsf.org/copyleft/gpl.html *
+* Please send any updates to: fred@gruntose.com *
+\*****************************************************************************/
+
+#include <basis/istring.h>
+#include <basis/log_base.h>
+#include <opsystem/filename.h>
+#include <data_struct/static_memory_gremlin.h>
+#include <processes/shutdown_alerter.h>
+
+HOOPLE_STARTUP_CODE;
+
+#define BASE_LOG(s) STAMPED_EMERGENCY_LOG(program_wide_logger(), s)
+#define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger(), s)
+
+const int TIMING_CYCLE = 1408;
+ // how frequently timer should be hit.
+
+class my_anchor : public shutdown_alerter
+{
+public:
+ virtual void handle_startup() { BASE_LOG("into startup..."); }
+ virtual void handle_shutdown() { BASE_LOG("into shutdown..."); }
+ virtual void handle_timer() { BASE_LOG("into timer..."); }
+};
+
+int main(int formal(argc), char *formal(argv)[])
+{
+ my_anchor w;
+ BASE_LOG(isprintf("timer will hit every %d ms.", TIMING_CYCLE));
+ shutdown_alerter::launch_console(w,
+ filename(portable::application_name()).basename(), TIMING_CYCLE);
+ return 0;
+}
+
--- /dev/null
+/*****************************************************************************\
+* *
+* Name : zing_window *
+* Author : Chris Koeritz *
+* *
+* Purpose: *
+* *
+* Sends a message to a window. The window can either be located by its *
+* unique window handle (if known) or by its exact title. There is currently *
+* no wildcard matching supported to find other types of titles. If the *
+* window is located, then it is sent a windows message of your choice. You *
+* can additionally add the wparam and lparam integers that get sent with *
+* the message. This is useful for shutting windows down gracefully by *
+* sending them the WM_CLOSE message. *
+* *
+*******************************************************************************
+* Copyright (c) 2000-$now By Author. This program 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 2 of *
+* the License or (at your option) any later version. This is online at: *
+* http://www.fsf.org/copyleft/gpl.html *
+* Please send any updates to: fred@gruntose.com *
+\*****************************************************************************/
+
+#include <basis/convert_utf.h>
+#include <basis/guards.h>
+#include <basis/istring.h>
+#include <basis/portable.h>
+#include <opsystem/command_line.h>
+#include <loggers/console_logger.h>
+#include <opsystem/filename.h>
+#include <data_struct/static_memory_gremlin.h>
+
+#include <stdio.h>
+
+HOOPLE_STARTUP_CODE;
+
+window_handle matching_window = NIL;
+istring window_name_sought;
+
+BOOL CALLBACK zingers_enum_proc(window_handle hwnd, LPARAM lParam)
+{
+ const int MAX_TITLE = 1024;
+ flexichar win_name[MAX_TITLE + 8]; // add a little for good luck.
+ int chars = GetWindowText(hwnd, win_name, MAX_TITLE - 1);
+ if (chars > 0) {
+ // see if the current window matches what we're looking for.
+ if (istring(from_unicode_temp(win_name)).ifind(window_name_sought) >= 0) {
+ matching_window = hwnd;
+ return false; // don't keep going.
+ }
+ }
+ return true; // haven't found it yet.
+}
+
+int main(int argc, char *argv[])
+{
+ console_logger out;
+ command_line cmds(argc, argv);
+ if ( (cmds.entries() < 2)
+ || (cmds.get(0).type() != command_parameter::VALUE) ) {
+ out.log(cmds.program_name().basename().raw() + " usage:\n"
+ "This takes at least two parameters. The first parameter is a window\n"
+ "handle that is to be contacted. Alternatively, the first parameter\n"
+ "can be the title of the window. The second parameter is a numerical\n"
+ "event that the window is to be zinged with. Zinging is synonymous\n"
+ "here with \"sending a message to the window\". Depending on the\n"
+ "message chosen, the window will behave in various different ways.\n"
+ "If there are a third or fourth parameter, then these are taken as\n"
+ "the extra data to send in the PostMessage call for zinging.");
+ out.log(isprintf("\nExample windows message values:\n\t"
+ "WM_CLOSE = %d\n\tWM_PAINT = %d", WM_CLOSE, WM_PAINT));
+ return 1;
+ }
+
+ istring junk_text = cmds.get(0).text();
+ bool saw_hex_code = false;
+ if (junk_text.begins("0x")) {
+ // we have a hex code. we will key off of that, which assumes that there
+ // is never going to be an important window whose title starts with that.
+ saw_hex_code = true;
+ }
+ int temp_handle = 0;
+ if (saw_hex_code) {
+ sscanf(junk_text.s(), "%lx", &temp_handle);
+ } else {
+ sscanf(junk_text.s(), "%ld", &temp_handle);
+ }
+
+//hmmm: this code is not so good for 64 bit.
+ #pragma warning(disable : 4312)
+ window_handle handle = (window_handle)temp_handle;
+
+ // we'll set the flag below if we find anything besides numbers in the
+ // window handle string.
+ bool has_alpha = false;
+ if (!saw_hex_code) {
+ // we only check for alphabetical characters if we didn't already
+ // decide the parameter had a hex code in front making it a number.
+ for (int i = 0; i < junk_text.length(); i++) {
+ if ( (junk_text[i] < '0') || (junk_text[i] > '9') ) {
+ has_alpha = true;
+ break;
+ }
+ }
+ }
+ if (has_alpha) {
+ // reset our match first.
+ matching_window = NIL;
+ window_name_sought = junk_text;
+//out.log("saw non-numbers in handle, trying as name.");
+ // enumerate the windows looking for a match.
+ EnumWindows(zingers_enum_proc, 0);
+ if (!matching_window) {
+ out.log("no matching window could be found. ignoring request.");
+ return 1;
+ }
+//out.log("found matching window handle...");
+ handle = matching_window;
+ }
+
+ junk_text = cmds.get(1).text();
+ ULONG event = (ULONG)junk_text.convert(0);
+ ULONG p1 = 0;
+ ULONG p2 = 0;
+ if (cmds.entries() >= 3) {
+ junk_text = cmds.get(2).text();
+ p1 = (ULONG)junk_text.convert(0);
+ }
+ if (cmds.entries() >= 4) {
+ junk_text = cmds.get(3).text();
+ p2 = (ULONG)junk_text.convert(0);
+ }
+
+ if (!handle || !event) {
+ out.log("the window handle or the event was invalid. ignoring request.");
+ return 1;
+ }
+
+ PostMessage(handle, event, p1, p2);
+
+ out.log(isprintf("posted at 0x%lx the zing (%u %u %u)", handle, event,
+ p1, p2));
+
+ return 0;
+}
+
+#ifdef __BUILD_STATIC_APPLICATION__
+ // static dependencies found by buildor_gen_deps.sh:
+ #include <basis/byte_array.cpp>
+ #include <basis/callstack_tracker.cpp>
+ #include <basis/convert_utf.cpp>
+ #include <basis/definitions.cpp>
+ #include <basis/earth_time.cpp>
+ #include <basis/guards.cpp>
+ #include <basis/istring.cpp>
+ #include <basis/log_base.cpp>
+ #include <basis/memory_checker.cpp>
+ #include <basis/mutex.cpp>
+ #include <basis/object_base.h>
+ #include <basis/outcome.cpp>
+ #include <basis/packable.cpp>
+ #include <basis/portable.cpp>
+ #include <basis/sequence.h>
+ #include <basis/set.h>
+ #include <basis/trap_new.addin>
+ #include <basis/untrap_new.addin>
+ #include <basis/utility.cpp>
+ #include <basis/version_record.cpp>
+ #include <data_struct/amorph.h>
+ #include <data_struct/bit_vector.cpp>
+ #include <data_struct/byte_hasher.cpp>
+ #include <data_struct/configurator.cpp>
+ #include <data_struct/hash_table.h>
+ #include <data_struct/pointer_hash.h>
+ #include <data_struct/stack.h>
+ #include <data_struct/static_memory_gremlin.cpp>
+ #include <data_struct/string_hash.h>
+ #include <data_struct/string_hasher.cpp>
+ #include <data_struct/string_table.cpp>
+ #include <data_struct/symbol_table.h>
+ #include <data_struct/table_configurator.cpp>
+ #include <loggers/console_logger.cpp>
+ #include <loggers/file_logger.cpp>
+ #include <loggers/locked_logger.cpp>
+ #include <loggers/null_logger.cpp>
+ #include <loggers/program_wide_logger.cpp>
+ #include <opsystem/byte_filer.cpp>
+ #include <opsystem/command_line.cpp>
+ #include <opsystem/critical_events.cpp>
+ #include <opsystem/directory.cpp>
+ #include <opsystem/filename.cpp>
+ #include <opsystem/ini_config.cpp>
+ #include <opsystem/ini_parser.cpp>
+ #include <opsystem/path_configuration.cpp>
+ #include <opsystem/rendezvous.cpp>
+ #include <textual/byte_format.cpp>
+ #include <textual/parser_bits.cpp>
+ #include <textual/string_manipulation.cpp>
+ #include <textual/tokenizer.cpp>
+#endif // __BUILD_STATIC_APPLICATION__
+