Handling logout and system shutdown for recollindex on ms-windows.
This commit is contained in:
parent
be2e9fff65
commit
8ce89f3ac1
@ -38,6 +38,23 @@
|
|||||||
|
|
||||||
static pthread_t mainthread_id;
|
static pthread_t mainthread_id;
|
||||||
|
|
||||||
|
// Signal etc. processing. We want to be able to properly close the
|
||||||
|
// index if we are currently writing to it.
|
||||||
|
//
|
||||||
|
// This is active if the sigcleanup parameter to recollinit is set,
|
||||||
|
// which only recollindex does. We arrange for the handler to be
|
||||||
|
// called when process termination is requested either by the system
|
||||||
|
// or a user keyboard intr.
|
||||||
|
//
|
||||||
|
// The recollindex handler just sets a global termination flag (plus
|
||||||
|
// the cancelcheck thing), which are tested in all timeout loops
|
||||||
|
// etc. When the flag is seen, the main thread processing returns, and
|
||||||
|
// recollindex calls exit().
|
||||||
|
//
|
||||||
|
// The other parameter, to recollinit(), cleanup, is set as an
|
||||||
|
// atexit() routine, it does the job of actually signalling the
|
||||||
|
// workers to stop and tidy up. It's automagically called by exit().
|
||||||
|
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
static void siglogreopen(int)
|
static void siglogreopen(int)
|
||||||
{
|
{
|
||||||
@ -81,7 +98,11 @@ void initAsyncSigs(void (*sigcleanup)(int))
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#else
|
void recoll_exitready()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#else // _WIN32 ->
|
||||||
|
|
||||||
// Windows signals etc.
|
// Windows signals etc.
|
||||||
//
|
//
|
||||||
@ -92,15 +113,27 @@ void initAsyncSigs(void (*sigcleanup)(int))
|
|||||||
// the process and calls the handler. The process exits when the
|
// the process and calls the handler. The process exits when the
|
||||||
// handler returns or after at most 10S
|
// handler returns or after at most 10S
|
||||||
//
|
//
|
||||||
// In practise, only recollindex sets sigcleanup(), and the routine
|
// This should also work, with different signals (CTRL_LOGOFF_EVENT,
|
||||||
// just sets a global termination flag. So we just call it and sleep,
|
// CTRL_SHUTDOWN_EVENT) when the user exits or the system shuts down).
|
||||||
// hoping that cleanup does not take more than what Windows will let
|
//
|
||||||
// us live.
|
// Unfortunately, this is not the end of the story. It seems that in
|
||||||
|
// recent Windows version "some kinds" of apps will not reliably
|
||||||
|
// receive the signals. "Some kind" is variably defined, for example a
|
||||||
|
// simple test program works when built with vs 2015, but not
|
||||||
|
// mingw. See the following discussion thread for tentative
|
||||||
|
// explanations, it seems that importing or not from user32.dll is the
|
||||||
|
// determining factor.
|
||||||
|
// https://social.msdn.microsoft.com/Forums/windowsdesktop/en-US/abf09824-4e4c-4f2c-ae1e-5981f06c9c6e/windows-7-console-application-has-no-way-of-trapping-logoffshutdown-event?forum=windowscompatibility
|
||||||
|
// In any case, it appears that the only reliable way to be advised of
|
||||||
|
// system shutdown or user exit is to create an "invisible window" and
|
||||||
|
// process window messages, which we now do.
|
||||||
|
|
||||||
static void (*l_sigcleanup)(int);
|
static void (*l_sigcleanup)(int);
|
||||||
|
static HANDLE eWorkFinished = INVALID_HANDLE_VALUE;
|
||||||
|
|
||||||
static BOOL WINAPI CtrlHandler(DWORD fdwCtrlType)
|
static BOOL WINAPI CtrlHandler(DWORD fdwCtrlType)
|
||||||
{
|
{
|
||||||
|
LOGDEB(("CtrlHandler\n"));
|
||||||
if (l_sigcleanup == 0)
|
if (l_sigcleanup == 0)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
@ -110,17 +143,82 @@ static BOOL WINAPI CtrlHandler(DWORD fdwCtrlType)
|
|||||||
case CTRL_BREAK_EVENT:
|
case CTRL_BREAK_EVENT:
|
||||||
case CTRL_LOGOFF_EVENT:
|
case CTRL_LOGOFF_EVENT:
|
||||||
case CTRL_SHUTDOWN_EVENT:
|
case CTRL_SHUTDOWN_EVENT:
|
||||||
|
{
|
||||||
l_sigcleanup(SIGINT);
|
l_sigcleanup(SIGINT);
|
||||||
Sleep(10000);
|
LOGDEB0(("CtrlHandler: waiting for exit ready\n"));
|
||||||
|
DWORD res = WaitForSingleObject(eWorkFinished, INFINITE);
|
||||||
|
if (res != WAIT_OBJECT_0) {
|
||||||
|
LOGERR(("CtrlHandler: exit ack wait failed\n"));
|
||||||
|
}
|
||||||
|
LOGDEB0(("CtrlHandler: got exit ready event, exiting\n"));
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LRESULT CALLBACK MainWndProc(HWND hwnd , UINT msg , WPARAM wParam,
|
||||||
|
LPARAM lParam)
|
||||||
|
{
|
||||||
|
switch (msg) {
|
||||||
|
case WM_QUERYENDSESSION:
|
||||||
|
case WM_ENDSESSION:
|
||||||
|
case WM_DESTROY:
|
||||||
|
case WM_CLOSE:
|
||||||
|
{
|
||||||
|
l_sigcleanup(SIGINT);
|
||||||
|
LOGDEB(("MainWndProc: got end message, waiting for work finished\n"));
|
||||||
|
DWORD res = WaitForSingleObject(eWorkFinished, INFINITE);
|
||||||
|
if (res != WAIT_OBJECT_0) {
|
||||||
|
LOGERR(("MainWndProc: exit ack wait failed\n"));
|
||||||
|
}
|
||||||
|
LOGDEB(("MainWindowProc: got exit ready event, exiting\n"));
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return DefWindowProc(hwnd, msg, wParam, lParam);
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CreateInvisibleWindow()
|
||||||
|
{
|
||||||
|
HWND hwnd;
|
||||||
|
WNDCLASS wc = {0};
|
||||||
|
|
||||||
|
wc.lpfnWndProc = (WNDPROC)MainWndProc;
|
||||||
|
wc.hInstance = GetModuleHandle(NULL);
|
||||||
|
wc.hIcon = LoadIcon(GetModuleHandle(NULL), "TestWClass");
|
||||||
|
wc.lpszClassName = "TestWClass";
|
||||||
|
RegisterClass(&wc);
|
||||||
|
|
||||||
|
hwnd =
|
||||||
|
CreateWindowEx(0, "TestWClass", "TestWClass", WS_OVERLAPPEDWINDOW,
|
||||||
|
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
|
||||||
|
CW_USEDEFAULT, (HWND) NULL, (HMENU) NULL,
|
||||||
|
GetModuleHandle(NULL), (LPVOID) NULL);
|
||||||
|
if (!hwnd) {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD WINAPI RunInvisibleWindowThread(LPVOID lpParam)
|
||||||
|
{
|
||||||
|
MSG msg;
|
||||||
|
CreateInvisibleWindow();
|
||||||
|
while (GetMessage(&msg, (HWND) NULL , 0 , 0)) {
|
||||||
|
TranslateMessage(&msg);
|
||||||
|
DispatchMessage(&msg);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static const int catchedSigs[] = {SIGINT, SIGTERM};
|
static const int catchedSigs[] = {SIGINT, SIGTERM};
|
||||||
void initAsyncSigs(void (*sigcleanup)(int))
|
void initAsyncSigs(void (*sigcleanup)(int))
|
||||||
{
|
{
|
||||||
|
DWORD tid;
|
||||||
// Install app signal handler
|
// Install app signal handler
|
||||||
if (sigcleanup) {
|
if (sigcleanup) {
|
||||||
l_sigcleanup = sigcleanup;
|
l_sigcleanup = sigcleanup;
|
||||||
@ -130,7 +228,20 @@ void initAsyncSigs(void (*sigcleanup)(int))
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
HANDLE hInvisiblethread =
|
||||||
|
CreateThread(NULL, 0, RunInvisibleWindowThread, NULL, 0, &tid);
|
||||||
SetConsoleCtrlHandler((PHANDLER_ROUTINE)CtrlHandler, TRUE);
|
SetConsoleCtrlHandler((PHANDLER_ROUTINE)CtrlHandler, TRUE);
|
||||||
|
eWorkFinished = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||||
|
if (eWorkFinished == INVALID_HANDLE_VALUE) {
|
||||||
|
LOGERR(("initAsyncSigs: error creating exitready event\n"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void recoll_exitready()
|
||||||
|
{
|
||||||
|
LOGDEB(("recoll_exitready()\n"));
|
||||||
|
if (!SetEvent(eWorkFinished)) {
|
||||||
|
LOGERR(("recoll_exitready: SetEvent failed\n"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -57,4 +57,9 @@ extern void recoll_threadinit();
|
|||||||
// Check if main thread
|
// Check if main thread
|
||||||
extern bool recoll_ismainthread();
|
extern bool recoll_ismainthread();
|
||||||
|
|
||||||
|
// Should be called while exiting asap when critical cleanup (db
|
||||||
|
// close) has been performed. Only useful for the indexer (writes to
|
||||||
|
// the db), and only actually does something on Windows.
|
||||||
|
extern void recoll_exitready();
|
||||||
|
|
||||||
#endif /* _RCLINIT_H_INCLUDED_ */
|
#endif /* _RCLINIT_H_INCLUDED_ */
|
||||||
|
|||||||
@ -43,7 +43,9 @@ using std::vector;
|
|||||||
#include "execmd.h"
|
#include "execmd.h"
|
||||||
#include "recollindex.h"
|
#include "recollindex.h"
|
||||||
#include "pathut.h"
|
#include "pathut.h"
|
||||||
|
#ifndef _WIN32
|
||||||
#include "x11mon.h"
|
#include "x11mon.h"
|
||||||
|
#endif
|
||||||
#include "subtreelist.h"
|
#include "subtreelist.h"
|
||||||
|
|
||||||
typedef unsigned long mttcast;
|
typedef unsigned long mttcast;
|
||||||
@ -510,9 +512,13 @@ bool startMonitor(RclConfig *conf, int opts)
|
|||||||
|
|
||||||
// x11IsAlive() can't be called from ok() because both threads call it
|
// x11IsAlive() can't be called from ok() because both threads call it
|
||||||
// and Xlib is not multithreaded.
|
// and Xlib is not multithreaded.
|
||||||
|
#ifndef _WIN32
|
||||||
bool x11dead = !(opts & RCLMON_NOX11) && !x11IsAlive();
|
bool x11dead = !(opts & RCLMON_NOX11) && !x11IsAlive();
|
||||||
if (x11dead)
|
if (x11dead)
|
||||||
LOGDEB(("RclMonprc: x11 is dead\n"));
|
LOGDEB(("RclMonprc: x11 is dead\n"));
|
||||||
|
#else
|
||||||
|
bool x11dead = false;
|
||||||
|
#endif
|
||||||
if (!rclEQ.ok() || x11dead) {
|
if (!rclEQ.ok() || x11dead) {
|
||||||
rclEQ.unlock();
|
rclEQ.unlock();
|
||||||
break;
|
break;
|
||||||
@ -609,8 +615,13 @@ bool startMonitor(RclConfig *conf, int opts)
|
|||||||
}
|
}
|
||||||
LOGDEB(("Rclmonprc: calling queue setTerminate\n"));
|
LOGDEB(("Rclmonprc: calling queue setTerminate\n"));
|
||||||
rclEQ.setTerminate();
|
rclEQ.setTerminate();
|
||||||
// Wait for receiver thread before returning
|
|
||||||
pthread_join(rcv_thrid, 0);
|
// We used to wait for the receiver thread here before returning,
|
||||||
|
// but this is not useful and may waste time / risk problems
|
||||||
|
// during our limited time window for exiting. To be reviewed if
|
||||||
|
// we ever need several monitor invocations in the same process
|
||||||
|
// (can't foresee any reason why we'd want to do this).
|
||||||
|
// pthread_join(rcv_thrid, 0);
|
||||||
LOGDEB(("Monitor: returning\n"));
|
LOGDEB(("Monitor: returning\n"));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -90,6 +90,7 @@ static ConfIndexer *confindexer;
|
|||||||
static void cleanup()
|
static void cleanup()
|
||||||
{
|
{
|
||||||
deleteZ(confindexer);
|
deleteZ(confindexer);
|
||||||
|
recoll_exitready();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Global stop request flag. This is checked in a number of place in the
|
// Global stop request flag. This is checked in a number of place in the
|
||||||
|
|||||||
@ -2,6 +2,8 @@
|
|||||||
QT -= core gui
|
QT -= core gui
|
||||||
|
|
||||||
TARGET = recollindex
|
TARGET = recollindex
|
||||||
|
CONFIG += console
|
||||||
|
CONFIG -= app_bundle
|
||||||
TEMPLATE = app
|
TEMPLATE = app
|
||||||
|
|
||||||
DEFINES += BUILDING_RECOLL
|
DEFINES += BUILDING_RECOLL
|
||||||
@ -9,7 +11,7 @@ DEFINES -= UNICODE
|
|||||||
DEFINES -= _UNICODE
|
DEFINES -= _UNICODE
|
||||||
DEFINES += _MBCS
|
DEFINES += _MBCS
|
||||||
DEFINES += PSAPI_VERSION=1
|
DEFINES += PSAPI_VERSION=1
|
||||||
|
DEFINES += RCL_MONITOR
|
||||||
|
|
||||||
SOURCES += \
|
SOURCES += \
|
||||||
../../index/recollindex.cpp \
|
../../index/recollindex.cpp \
|
||||||
|
|||||||
@ -2,6 +2,8 @@
|
|||||||
QT -= core gui
|
QT -= core gui
|
||||||
|
|
||||||
TARGET = recollq
|
TARGET = recollq
|
||||||
|
CONFIG += console
|
||||||
|
CONFIG -= app_bundle
|
||||||
TEMPLATE = app
|
TEMPLATE = app
|
||||||
|
|
||||||
DEFINES += BUILDING_RECOLL
|
DEFINES += BUILDING_RECOLL
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user