Skip to content

Commit c519c73

Browse files
committed
server: prevent multiple instances using file locks
On Linux, this was already prevented when binding socket, but it wasn't working on Windows. Regardless, this solution is a lot cleaner overall.
1 parent aa6322b commit c519c73

File tree

3 files changed

+47
-5
lines changed

3 files changed

+47
-5
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
__pycache__
22
sessions
3+
instance.lock

server.py

+7-5
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import sys
55

66
import compiler
7+
import singleton
78

89
frozen = getattr(sys, 'frozen', False)
910

@@ -17,8 +18,7 @@
1718
app = Flask('javaplay', static_url_path='', static_folder=os.path.join(root_dir, 'static'))
1819

1920
import logging
20-
log = logging.getLogger('werkzeug')
21-
log.setLevel(logging.WARNING)
21+
logging.getLogger('werkzeug').setLevel(logging.WARNING)
2222

2323
socketio = SocketIO(app, async_mode='threading')
2424

@@ -98,6 +98,8 @@ def disconnect():
9898
del sid_program_map[sid]
9999

100100
if frozen or __name__ == "__main__":
101-
if not os.path.isdir(sess_dir):
102-
os.makedirs(sess_dir)
103-
socketio.run(app, port=8040)
101+
lockpath = os.path.join(root_dir, 'instance.lock')
102+
with singleton.InstanceFileLock(lockpath):
103+
if not os.path.isdir(sess_dir):
104+
os.makedirs(sess_dir)
105+
socketio.run(app, port=8040)

singleton.py

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import logging
2+
import os, sys
3+
4+
logger = logging.getLogger("singleton")
5+
6+
# code adapted from https://github.com/pycontribs/tendo/blob/master/tendo/singleton.py
7+
class InstanceFileLock:
8+
def __init__(self, lockpath):
9+
self._lock = lockpath
10+
11+
def __enter__(self):
12+
if sys.platform == 'win32':
13+
try:
14+
if os.path.exists(self._lock):
15+
os.unlink(self._lock)
16+
self.fd = os.open(self._lock, os.O_CREAT | os.O_EXCL | os.O_RDWR)
17+
except OSError:
18+
logger.warning("Another instance is running.")
19+
sys.exit(-1)
20+
else:
21+
import fcntl
22+
self.fp = open(self._lock, 'w')
23+
try:
24+
fcntl.lockf(self.fp, fcntl.LOCK_EX | fcntl.LOCK_NB)
25+
except IOError:
26+
logger.warning("Another instance is running.")
27+
sys.exit(-1)
28+
29+
def __exit__(self, type, value, traceback):
30+
try:
31+
if sys.platform == 'win32':
32+
os.close(self.fd)
33+
else:
34+
import fcntl
35+
fcntl.lockf(self.fp, fcntl.LOCK_UN)
36+
self.fp.close()
37+
os.unlink(self._lock)
38+
except Exception as e:
39+
logger.error(e)

0 commit comments

Comments
 (0)