mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-08-09 22:03:45 -06:00
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
This commit is contained in:
parent
d53fe9ac56
commit
d34ec74143
2 changed files with 260 additions and 218 deletions
|
|
@ -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];
|
||||
Vector<String>v;
|
||||
|
||||
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];
|
||||
Vector<String>v;
|
||||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -1,67 +1,71 @@
|
|||
#ifndef _Uniq_Uniq_h
|
||||
#define _Uniq_Uniq_h
|
||||
|
||||
#include <CtrlLib/CtrlLib.h>
|
||||
|
||||
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<Vector<String> 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 <CtrlLib/CtrlLib.h>
|
||||
|
||||
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<Vector<String> const &> WhenInstance;
|
||||
|
||||
Uniq();
|
||||
|
||||
~Uniq();
|
||||
|
||||
// checks whether we're inside main instance
|
||||
bool IsFirstInstance(void) { return isFirstInstance; }
|
||||
operator bool() { return isFirstInstance; }
|
||||
};
|
||||
|
||||
END_UPP_NAMESPACE
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue