source: src/mlx/singleton.py@ 305:ddc2dfec2080

Last change on this file since 305:ddc2dfec2080 was 298:24c67ec5cdca, checked in by István Váradi <ivaradi@…>, 12 years ago

Documented the non-GUI modules

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