From d34ec74143dcf659de41b36f7c234bec1d21faf7 Mon Sep 17 00:00:00 2001 From: micio Date: Thu, 10 Feb 2011 20:07:08 +0000 Subject: [PATCH] Bazaar/Uniq : fixed stray pipe handling by a crashed main instance git-svn-id: svn://ultimatepp.org/upp/trunk@3191 f0d560ea-af0d-0410-9eb7-867de7ffcac7 --- bazaar/Uniq/Posix.cpp | 340 +++++++++++++++++++++++------------------- bazaar/Uniq/Uniq.h | 138 ++++++++--------- 2 files changed, 260 insertions(+), 218 deletions(-) diff --git a/bazaar/Uniq/Posix.cpp b/bazaar/Uniq/Posix.cpp index abd76660f..a3d56f2b1 100644 --- a/bazaar/Uniq/Posix.cpp +++ b/bazaar/Uniq/Posix.cpp @@ -1,151 +1,189 @@ -#include "Uniq.h" - -NAMESPACE_UPP - -#ifdef PLATFORM_POSIX - -// send command line to callback handler -bool Uniq::SendCmdLine(int pipe) -{ - char buf[256]; - Vectorv; - - fd_set rfds; - struct timeval tv; - FD_ZERO(&rfds); - FD_SET(pipe, &rfds); - tv.tv_sec = 0; - tv.tv_usec = 200000; - if(!select(pipe+1, &rfds, NULL, NULL, &tv)) - return false; - - // FILE gets handy for fgets() &C.... - FILE *f = fdopen(pipe, "r"); - - // gets number of elements in vector - if(fgets(buf, 256, f)) - { - int count = ScanInt(buf); - - // get the strings - for(int i = 0; i < count; i++) - { - if(!fgets(buf, 256, f)) - break; - String s = buf; - if(s.GetCount() && s[s.GetCount() - 1] == '\n') - s = s.Left(s.GetCount() - 1); - v.Add(s); - } - } - fclose(f); - // posts the callback to handle new instance's command line - PostCallback(callback1(WhenInstance, v)); - return true; - -} - -// polling callback -void Uniq::pollCb(void) -{ - // dont block on open, that would make thread shutdown impossible - int pipe = open(pipePath, O_RDONLY | O_NDELAY); - if(pipe == -1) - return; - -#ifdef flagMT - // multithread makes it easy... just open receiving pipe - // and wait for data from it -- it can do blocking reads - // and no need for wait cycles - - while(!Thread::IsShutdownThreads()) - { - // if there's no data waiting on pipe - // just sleep a bit and try again - if(!SendCmdLine(pipe)) - { - Sleep(200); - continue; - } - - // select doesn't handle well closed fifos on second call - // the solution is close and reopen it again - close(pipe); - pipe = open(pipePath, O_RDONLY | O_NDELAY); - } - -#else - // non multithreaded -- we shall check for data from pipe - // in non-blocking mode and read it if available - SendCmdLine(pipe); - - // respawn itself by aid of another timed callback - SetTimeCallback(200, THISBACK(pollCb)); -#endif - - close(pipe); -} - -Uniq::Uniq() -{ - // we still don't know if we're inside first app instance - // so we default for NOT - isFirstInstance = false; - - pipePath = Format("/tmp/%s_%d_UNIQ", GetExeTitle(), (int64)getuid()); - - // try to open pipe for write; if succeed, pipe is there - // so we're on a new app instance - int p = open(pipePath, O_WRONLY /* | O_NDELAY */); - if(p != -1) - { - // pipe already there, should write command line to it and leave - // the O_NDELAY is to be sure tha we don't block if active instance - // exits on the way - FILE *f = fdopen(p, "w"); - fprintf(f, "%d\n", CommandLine().GetCount()); - for(int i = 0; i < CommandLine().GetCount(); i++) - fprintf(f, "%s\n", ~CommandLine()[i]); - fclose(f); - close(p); - } - else - { - // pipe doesn't exist, create it and allow writing for other processes - if(mkfifo(pipePath, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH)) - { - Cerr() << "Error creating interprocess pipe\n"; - exit(1); - } - - // remember we're inside first app instance - isFirstInstance = true; - - // now, if we do multithreading, we use a listening thread - // for other's instances communications - // otherwise we use a timed callback (which is less efficient) -#ifdef flagMT - pollThread.Run(THISBACK(pollCb)); -#else - // use a 200 ms delayed callback to check if another instance - // gave its command line - SetTimeCallback(200, THISBACK(pollCb)); -#endif - } -} - -Uniq::~Uniq() -{ - if(isFirstInstance) - { -#ifdef flagMT - Thread::ShutdownThreads(); - pollThread.Wait(); -#endif - unlink(pipePath); - } -} - -#endif - -END_UPP_NAMESPACE +#include "Uniq.h" + +NAMESPACE_UPP + +#ifdef PLATFORM_POSIX + +// send command line to callback handler +bool Uniq::SendCmdLine(int pipe) +{ + char buf[256]; + Vectorv; + + fd_set rfds; + struct timeval tv; + FD_ZERO(&rfds); + FD_SET(pipe, &rfds); + tv.tv_sec = 0; + tv.tv_usec = 200000; + if(!select(pipe+1, &rfds, NULL, NULL, &tv)) + return false; + + // FILE gets handy for fgets() &C.... + FILE *f = fdopen(pipe, "r"); + + // gets number of elements in vector + if(fgets(buf, 256, f)) + { + int count = ScanInt(buf); + + // get the strings + for(int i = 0; i < count; i++) + { + if(!fgets(buf, 256, f)) + break; + String s = buf; + if(s.GetCount() && s[s.GetCount() - 1] == '\n') + s = s.Left(s.GetCount() - 1); + v.Add(s); + } + } + fclose(f); + // posts the callback to handle new instance's command line + PostCallback(callback1(WhenInstance, v)); + return true; + +} + +// polling callback +void Uniq::pollCb(void) +{ + // dont block on open, that would make thread shutdown impossible + int pipe = open(pipePath, O_RDONLY | O_NDELAY); + if(pipe == -1) + return; + +#ifdef flagMT + // multithread makes it easy... just open receiving pipe + // and wait for data from it -- it can do blocking reads + // and no need for wait cycles + + while(!Thread::IsShutdownThreads()) + { + // if there's no data waiting on pipe + // just sleep a bit and try again + if(!SendCmdLine(pipe)) + { + Sleep(200); + continue; + } + + // select doesn't handle well closed fifos on second call + // the solution is close and reopen it again + close(pipe); + pipe = open(pipePath, O_RDONLY | O_NDELAY); + } + +#else + // non multithreaded -- we shall check for data from pipe + // in non-blocking mode and read it if available + SendCmdLine(pipe); + + // respawn itself by aid of another timed callback + SetTimeCallback(200, THISBACK(pollCb)); +#endif + + close(pipe); +} + +Uniq::Uniq() +{ + // we still don't know if we're inside first app instance + // so we default for NOT + isFirstInstance = false; + + pipePath = Format("/tmp/%s_%d_UNIQ", GetExeTitle(), (int64)getuid()); + lockPath = Format("/tmp/%s_%d_UNIQ_LCK", GetExeTitle(), (int64)getuid()); + + // we check first if the lock file is there AND locked by main instance + // if it is, we're on a secondary instance; if not, we're main + if(FileExists(lockPath)) + { + // lock file does exist, try to open and lock it + lockFile = open(lockPath, O_RDWR); + struct flock fl; + fl.l_type = F_WRLCK; + fl.l_whence = SEEK_SET; + fl.l_start = 0; + fl.l_len = 0; + int lock = fcntl(lockFile, F_SETLK, &fl); + close(lockFile); + if(lock != -1) + { + // we could lock the file, it was a stray pipe + // so we remove both + unlink(lockPath); + unlink(pipePath); + } + } + + // try to open pipe for write; if succeed, pipe is there + // so we're on a new app instance + int p = open(pipePath, O_WRONLY /* | O_NDELAY */); + if(p != -1) + { + // pipe already there, we should write command line to it and leave + FILE *f = fdopen(p, "w"); + fprintf(f, "%d\n", CommandLine().GetCount()); + for(int i = 0; i < CommandLine().GetCount(); i++) + fprintf(f, "%s\n", ~CommandLine()[i]); + fclose(f); + close(p); + } + else + { + // pipe doesn't exist, create it and allow writing for other processes + // we also create the lock file + if(mkfifo(pipePath, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH)) + { + Cerr() << "Error creating interprocess pipe\n"; + exit(1); + } + lockFile = open(lockPath, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR); + struct flock fl; + fl.l_type = F_WRLCK; + fl.l_whence = SEEK_SET; + fl.l_start = 0; + fl.l_len = 0; + int lock = fcntl(lockFile, F_SETLK, &fl); + if(lock == -1) + { + Cerr() << "Error locking lock file\n"; + unlink(pipePath); + unlink(lockPath); + exit(1); + } + + // remember we're inside first app instance + isFirstInstance = true; + + // now, if we do multithreading, we use a listening thread + // for other's instances communications + // otherwise we use a timed callback (which is less efficient) +#ifdef flagMT + pollThread.Run(THISBACK(pollCb)); +#else + // use a 200 ms delayed callback to check if another instance + // gave its command line + SetTimeCallback(200, THISBACK(pollCb)); +#endif + } +} + +Uniq::~Uniq() +{ + if(isFirstInstance) + { +#ifdef flagMT + Thread::ShutdownThreads(); + pollThread.Wait(); +#endif + close(lockFile); + unlink(lockPath); + unlink(pipePath); + } +} + +#endif + +END_UPP_NAMESPACE diff --git a/bazaar/Uniq/Uniq.h b/bazaar/Uniq/Uniq.h index 7b325a3a5..112d97d23 100644 --- a/bazaar/Uniq/Uniq.h +++ b/bazaar/Uniq/Uniq.h @@ -1,67 +1,71 @@ -#ifndef _Uniq_Uniq_h -#define _Uniq_Uniq_h - -#include - -NAMESPACE_UPP - -class Uniq -{ - private: - - // flag indicating we're inside first instance - bool isFirstInstance; - - // path of named pipe - String pipePath; - -#ifdef flagMT - Thread pollThread; -#endif - -#ifdef PLATFORM_POSIX - // send command line to callback handler - bool SendCmdLine(int pipe); - - // polling callback -- either threaded or - // run by timed callback - void pollCb(void); - -#else - - HANDLE pipe; - OVERLAPPED overlapped; - HANDLE event; - - // send command line to callback handler - bool SendCmdLine(void); - - // polling callback -- either threaded or - // run by timed callback - void pollCb(void); - -#endif - - - protected: - - public: - - typedef Uniq CLASSNAME; - - // callback called when another app instance is run - Callback1 const &> WhenInstance; - - Uniq(); - - ~Uniq(); - - // checks whether we're inside main instance - bool IsFirstInstance(void) { return isFirstInstance; } - operator bool() { return isFirstInstance; } -}; - -END_UPP_NAMESPACE - -#endif - +#ifndef _Uniq_Uniq_h +#define _Uniq_Uniq_h + +#include + +NAMESPACE_UPP + +class Uniq +{ + private: + + // flag indicating we're inside first instance + bool isFirstInstance; + + // path of named pipe + String pipePath; + +#ifdef flagMT + Thread pollThread; +#endif + +#ifdef PLATFORM_POSIX + // lock file to test for pipe existence + String lockPath; + int lockFile; + + // send command line to callback handler + bool SendCmdLine(int pipe); + + // polling callback -- either threaded or + // run by timed callback + void pollCb(void); + +#else + + HANDLE pipe; + OVERLAPPED overlapped; + HANDLE event; + + // send command line to callback handler + bool SendCmdLine(void); + + // polling callback -- either threaded or + // run by timed callback + void pollCb(void); + +#endif + + + protected: + + public: + + typedef Uniq CLASSNAME; + + // callback called when another app instance is run + Callback1 const &> WhenInstance; + + Uniq(); + + ~Uniq(); + + // checks whether we're inside main instance + bool IsFirstInstance(void) { return isFirstInstance; } + operator bool() { return isFirstInstance; } +}; + +END_UPP_NAMESPACE + +#endif +