source: src/mlx/singleton.py@ 227:50c3ae93007d

Last change on this file since 227:50c3ae93007d was 182:dd806c3cc18d, checked in by István Váradi <ivaradi@…>, 12 years ago

If the program is started in several instances, the ones after the first one just show the window of the running instance

File size: 9.8 KB
Line 
1# Module to allow for a single instance of an application to run
2
3import os
4import time
5
6#----------------------------------------------------------------------------
7
8if os.name=="nt":
9 import win32event
10 import win32file
11 import win32pipe
12 import win32api
13 import winerror
14
15 import threading
16
17 class _PipeServer(threading.Thread):
18 """A server that creates a named pipe, and waits for messages on
19 that."""
20
21 BUFFER_SIZE = 4096
22
23 def __init__(self, pipeName, raiseCallback):
24 """Construct the server thread."""
25 super(_PipeServer, self).__init__()
26
27 self._pipeName = pipeName
28 self._raiseCallback = raiseCallback
29 self.daemon = True
30
31 def run(self):
32 """Perform the operation of the thread."""
33 try:
34 while True:
35 handle = self._createPipe()
36
37 if handle is None:
38 break
39
40 print "singleton._PipeServer.run: created the pipe"
41 try:
42 if win32pipe.ConnectNamedPipe(handle)==0:
43 print "singleton._PipeServer.run: client connection received"
44 (code, message) = \
45 win32file.ReadFile(handle,
46 _PipeServer.BUFFER_SIZE,
47 None)
48
49 if code==0:
50 print "singleton._PipeServer.run: message received from client"
51 self._raiseCallback()
52 else:
53 print "singleton._PipeServer.run: failed to read from the pipe"
54 except Exception, e:
55 print "singleton._PipeServer.run: exception:", str(e)
56 finally:
57 win32pipe.DisconnectNamedPipe(handle)
58 win32file.CloseHandle(handle)
59 except Exception, e:
60 print "singleton._PipeServer.run: fatal exception:", str(e)
61
62 def _createPipe(self):
63 """Create the pipe."""
64 handle = win32pipe.CreateNamedPipe(self._pipeName,
65 win32pipe.PIPE_ACCESS_INBOUND,
66 win32pipe.PIPE_TYPE_BYTE |
67 win32pipe.PIPE_READMODE_BYTE |
68 win32pipe.PIPE_WAIT,
69 win32pipe.PIPE_UNLIMITED_INSTANCES,
70 _PipeServer.BUFFER_SIZE,
71 _PipeServer.BUFFER_SIZE,
72 1000,
73 None)
74 if handle==win32file.INVALID_HANDLE_VALUE:
75 print "singleton._PipeServer.run: could not create the handle"
76 return None
77 else:
78 return handle
79
80 class SingleInstance(object):
81 """Creating an instance of this object checks if only one instance of
82 the process runs."""
83 def __init__(self, baseName, raiseCallback):
84 """Construct the single instance object.
85
86 raiseCallback is a function that will be called, if another
87 instance of the program is started."""
88 self._name = baseName + "_" + win32api.GetUserName()
89 self._mutex = win32event.CreateMutex(None, False, self._name)
90 self._isSingle = win32api.GetLastError() != \
91 winerror.ERROR_ALREADY_EXISTS
92 if self._isSingle:
93 self._startPipeServer(raiseCallback)
94 else:
95 self._notifySingleton()
96
97 def _getPipeName(self):
98 """Get the name of the pipe to be used for communication."""
99 return r'\\.\pipe\\' + self._name
100
101 def _startPipeServer(self, raiseCallback):
102 """Start the pipe server"""
103 pipeServer = _PipeServer(self._getPipeName(),
104 raiseCallback)
105 pipeServer.start()
106
107 def _notifySingleton(self):
108 """Notify the already running instance of the program of our
109 presence."""
110 pipeName = self._getPipeName()
111 for i in range(0, 3):
112 try:
113 f = open(pipeName, "wb")
114 f.write("hello")
115 f.close()
116 return
117 except Exception, e:
118 print "SingleInstance._notifySingleton: failed:", str(e)
119 time.sleep(0.5)
120
121 def __nonzero__(self):
122 """Return a boolean representation of the object.
123
124 It is True, if this is the single instance of the program."""
125 return self._isSingle
126
127 def __del__(self):
128 """Destroy the object."""
129 if self._mutex:
130 win32api.CloseHandle(self._mutex)
131#----------------------------------------------------------------------------
132
133else: # os.name=="nt"
134 import fcntl
135 import socket
136 import tempfile
137 import threading
138
139 class _SocketServer(threading.Thread):
140 """Server thread to handle the Unix socket through which we are
141 notified of other instances starting."""
142 def __init__(self, socketName, raiseCallback):
143 """Construct the server."""
144 super(_SocketServer, self).__init__()
145
146 self._socketName = socketName
147 self._raiseCallback = raiseCallback
148
149 self.daemon = True
150
151
152 def run(self):
153 """Perform the thread's operation."""
154 try:
155 try:
156 os.remove(self._socketName)
157 except:
158 pass
159
160 s = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
161 s.bind(self._socketName)
162
163 while True:
164 s.recv(64)
165 self._raiseCallback()
166 except Exception, e:
167 print "singleton._SocketServer.run: fatal exception:", str(e)
168
169 class SingleInstance(object):
170 """Creating an instance of this object checks if only one instance of
171 the process runs."""
172 def __init__(self, baseName, raiseCallback):
173 """Construct the single instance object.
174
175 raiseCallback is a function that will be called, if another
176 instance of the program is started."""
177 baseName = baseName + "_" + os.environ["LOGNAME"]
178
179 tempDir = tempfile.gettempdir()
180 self._lockName = os.path.join(tempDir, baseName + ".lock")
181 self._socketName = os.path.join(tempDir, baseName + ".sock")
182
183 self._lockFile = open(self._lockName, "w")
184
185 self._isSingle = False
186 try:
187 fcntl.lockf(self._lockFile, fcntl.LOCK_EX | fcntl.LOCK_NB)
188 self._isSingle = True
189 except Exception, e:
190 self._lockFile.close()
191 self._lockFile = None
192 pass
193
194 if self._isSingle:
195 self._startSocketServer(raiseCallback)
196 else:
197 self._notifySingleton()
198
199 def _startSocketServer(self, raiseCallback):
200 """Start the pipe server"""
201 pipeServer = _SocketServer(self._socketName, raiseCallback)
202 pipeServer.start()
203
204 def _notifySingleton(self):
205 """Notify the already running instance of the program of our
206 presence."""
207 s = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
208 for i in range(0, 3):
209 try:
210 s.connect(self._socketName)
211 s.send("hello")
212 s.close()
213 return
214 except Exception, e:
215 print "singleton.SingleInstance._notifySingleton: failed:", str(e)
216 time.sleep(0.5)
217
218 def __nonzero__(self):
219 """Return a boolean representation of the object.
220
221 It is True, if this is the single instance of the program."""
222 return self._isSingle
223
224 def __del__(self):
225 """Destroy the object."""
226 if self._isSingle:
227 self._lockFile.close()
228 try:
229 os.remove(self._lockName)
230 except:
231 pass
232 try:
233 os.remove(self._socketName)
234 except:
235 pass
236
237#----------------------------------------------------------------------------
238#----------------------------------------------------------------------------
239
240# MAVA Logger X-specific stuff
241
242#----------------------------------------------------------------------------
243
244# The callback to use
245raiseCallback = None
246
247#----------------------------------------------------------------------------
248
249def raiseCallbackWrapper():
250 """The actual function to be used as the callback.
251
252 It checks if raiseCallback is None, and if not, it calls that."""
253 callback = raiseCallback
254 if callback is not None:
255 callback()
256
257#----------------------------------------------------------------------------
258#----------------------------------------------------------------------------
259
260if __name__=="__main__":
261 def raiseCallback():
262 print "Raise the window!"
263
264 instance = SingleInstance("mlx", raiseCallback)
265 if instance:
266 print "The first instance"
267 time.sleep(10)
268 else:
269 print "The program is already running."
270
Note: See TracBrowser for help on using the repository browser.