refactor second instance detection

- get rid of shared memory check as it was causing problems when an existing
  session was forcefully closed; instead we rely on the socket
- include a hash of the user's name in the key so that multiple users can run
  anki at the same time
This commit is contained in:
Damien Elmes 2013-10-04 07:37:19 +09:00
parent f64c07ca40
commit 0f7000db83

View file

@ -1,5 +1,6 @@
# Copyright: Damien Elmes <anki@ichi2.net> # Copyright: Damien Elmes <anki@ichi2.net>
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html # License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
import getpass
import os, sys, optparse, atexit, tempfile, __builtin__ import os, sys, optparse, atexit, tempfile, __builtin__
from aqt.qt import * from aqt.qt import *
@ -29,6 +30,8 @@ except ImportError, e:
print print
raise raise
from anki.utils import checksum
# Dialog manager - manages modeless windows # Dialog manager - manages modeless windows
########################################################################## ##########################################################################
@ -110,46 +113,43 @@ class AnkiApp(QApplication):
# Single instance support on Win32/Linux # Single instance support on Win32/Linux
################################################## ##################################################
KEY = "anki" KEY = "anki"+checksum(getpass.getuser())
TMOUT = 5000 TMOUT = 5000
def __init__(self, argv): def __init__(self, argv):
QApplication.__init__(self, argv) QApplication.__init__(self, argv)
self._argv = argv self._argv = argv
self._shmem = QSharedMemory(self.KEY)
self.alreadyRunning = self._shmem.attach()
def secondInstance(self): def secondInstance(self):
if not self.alreadyRunning: # we accept only one command line argument. if it's missing, send
# use a 1 byte shared memory instance to signal we exist # a blank screen to just raise the existing window
if not self._shmem.create(1): opts, args = parseArgs(self._argv)
raise Exception("shared memory not supported") buf = "raise"
atexit.register(self._shmem.detach) if args and args[0]:
# and a named pipe/unix domain socket for ipc buf = os.path.abspath(args[0])
if self.sendMsg(buf):
print "Already running; reusing existing instance."
return True
else:
# send failed, so we're the first instance or the
# previous instance died
QLocalServer.removeServer(self.KEY) QLocalServer.removeServer(self.KEY)
self._srv = QLocalServer(self) self._srv = QLocalServer(self)
self.connect(self._srv, SIGNAL("newConnection()"), self.onRecv) self.connect(self._srv, SIGNAL("newConnection()"), self.onRecv)
self._srv.listen(self.KEY) self._srv.listen(self.KEY)
else: return False
print "Raising existing window."
# we accept only one command line argument. if it's missing, send
# a blank screen to just raise the existing window
opts, args = parseArgs(self._argv)
buf = "raise"
if args and args[0]:
buf = os.path.abspath(args[0])
self.sendMsg(buf)
return True
def sendMsg(self, txt): def sendMsg(self, txt):
sock = QLocalSocket(self) sock = QLocalSocket(self)
sock.connectToServer(self.KEY, QIODevice.WriteOnly) sock.connectToServer(self.KEY, QIODevice.WriteOnly)
if not sock.waitForConnected(self.TMOUT): if not sock.waitForConnected(self.TMOUT):
raise Exception("existing instance not responding") # first instance or previous instance dead
return False
sock.write(txt) sock.write(txt)
if not sock.waitForBytesWritten(self.TMOUT): if not sock.waitForBytesWritten(self.TMOUT):
raise Exception("existing instance not emptying") raise Exception("existing instance not emptying")
sock.disconnectFromServer() sock.disconnectFromServer()
return True
def onRecv(self): def onRecv(self):
sock = self._srv.nextPendingConnection() sock = self._srv.nextPendingConnection()