Skip to content

Commit 8d2bccd

Browse files
diningPhilosopher64Prabhakar Kumar
authored andcommitted
Using $HOME directory instead of $TMPDIR to store matlab-proxy launch artifacts.
1 parent 04d54bb commit 8d2bccd

File tree

4 files changed

+100
-72
lines changed

4 files changed

+100
-72
lines changed

matlab_proxy/app_state.py

Lines changed: 74 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
# Copyright 2020-2021 The MathWorks, Inc.
22

33
import asyncio
4-
from matlab_proxy import mwi_embedded_connector as mwi_connector
54
from matlab_proxy import mwi_environment_variables as mwi_env
65
from matlab_proxy import util
76
import os
@@ -40,8 +39,19 @@ def __init__(self, settings):
4039
"""
4140
self.settings = settings
4241
self.processes = {"matlab": None, "xvfb": None}
42+
43+
# The port on which MATLAB(launched by this matlab-proxy process) starts on.
4344
self.matlab_port = None
45+
46+
# The file created and written by MATLAB's Embedded connector to signal readiness.
4447
self.matlab_ready_file = None
48+
49+
# The directory in which the instance of MATLAB (launched by this matlab-proxy process) will write logs to.
50+
self.matlab_ready_file_dir = None
51+
52+
# The file created by this instance of matlab-proxy to signal to other matlab-proxy processes
53+
# that this self.matlab_port will be used by this instance.
54+
self.mwi_proxy_lock_file = None
4555
self.licensing = None
4656
self.tasks = {}
4757
self.logs = {
@@ -354,8 +364,15 @@ def persist_licensing(self):
354364
with open(cached_licensing_file, "w") as f:
355365
f.write(json.dumps(self.licensing))
356366

357-
def get_free_matlab_port(self):
358-
"""Returns a free port for MATLAB Embedded Connector in the allowed range."""
367+
def prepare_lock_files_for_MATLAB_launch(self):
368+
"""Finds and reserves a free port for MATLAB Embedded Connector in the allowed range.
369+
Creates the lock file to prevent any other matlab-proxy process to use the reserved port of this
370+
process.
371+
372+
Raises:
373+
e: socket.error if the exception raised is other than port already occupied.
374+
"""
375+
359376
# NOTE It is not guranteed that the port will remain free!
360377
# FIXME Because of https://github.com/http-party/node-http-proxy/issues/1342 the
361378
# node application in development mode always uses port 31515 to bypass the
@@ -372,16 +389,53 @@ def get_free_matlab_port(self):
372389
# try-except.
373390
# s.bind(("", 0))
374391
# self.matlab_port = s.getsockname()[1]
392+
375393
for port in mw.range_matlab_connector_ports():
376394
try:
377395
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
378396
s.bind(("", port))
379-
s.close()
380-
break
397+
398+
matlab_ready_file_dir = self.settings["mwi_logs_root_dir"] / str(
399+
port
400+
)
401+
402+
mwi_proxy_lock_file = (
403+
matlab_ready_file_dir
404+
/ self.settings["mwi_proxy_lock_file_name"]
405+
)
406+
407+
matlab_ready_file = matlab_ready_file_dir / "connector.securePort"
408+
409+
# Check if the mwi_proxy_lock_file exists.
410+
# Implies there was a competing matlab-proxy process which found the same port before this process
411+
if mwi_proxy_lock_file.exists():
412+
logger.debug(
413+
f"Skipping port number {port} for MATLAB as lock file already exists at {mwi_proxy_lock_file}"
414+
)
415+
s.close()
416+
417+
else:
418+
# Create a folder to hold the matlab_ready_file that will be created by MATLAB to signal readiness.
419+
# This is the same folder to which MATLAB will write logs to.
420+
matlab_ready_file_dir.mkdir(parents=True, exist_ok=True)
421+
422+
# Creating the mwi_proxy.lock file to indicate to any other matlab-proxy processes
423+
# that this self.matlab_port number is taken up by this process.
424+
mwi_proxy_lock_file.touch()
425+
426+
# Update member variables of AppState class
427+
428+
# Store the port number on which MATLAB will be launched for this matlab-proxy process.
429+
self.matlab_port = port
430+
self.mwi_proxy_lock_file = mwi_proxy_lock_file
431+
self.matlab_ready_file_dir = matlab_ready_file_dir
432+
self.matlab_ready_file = matlab_ready_file
433+
s.close()
434+
return
435+
381436
except socket.error as e:
382437
if e.errno != errno.EADDRINUSE:
383438
raise e
384-
return port
385439

386440
async def start_matlab(self, restart_matlab=False):
387441
"""Start MATLAB.
@@ -423,15 +477,14 @@ async def start_matlab(self, restart_matlab=False):
423477
self.error = None
424478
self.logs["matlab"].clear()
425479

426-
# Reserve a port for MATLAB Embedded Connector
427-
self.matlab_port = self.get_free_matlab_port()
480+
# Finds and reserves a free port, then prepare lock files for the MATLAB process.
481+
self.prepare_lock_files_for_MATLAB_launch()
428482

429-
# Create a folder to hold the matlab_ready_file that will be created by MATLAB to signal readiness
430-
self.matlab_ready_file, matlab_log_dir = mwi_connector.get_matlab_ready_file(
431-
self.matlab_port
432-
)
433-
logger.debug(f"MATLAB_LOG_DIR:{str(matlab_log_dir)}")
483+
logger.debug(f"MATLAB_LOG_DIR:{str( self.matlab_ready_file_dir )}")
434484
logger.debug(f"MATLAB_READY_FILE:{str(self.matlab_ready_file)}")
485+
logger.debug(
486+
f"Created MWI proxy lock file for this matlab-proxy process at {self.mwi_proxy_lock_file}"
487+
)
435488

436489
# Configure the environment MATLAB needs to start
437490
matlab_env = os.environ.copy()
@@ -445,7 +498,8 @@ async def start_matlab(self, restart_matlab=False):
445498
)
446499
matlab_env["MWAPIKEY"] = self.settings["mwapikey"]
447500
# The matlab ready file is written into this location by MATLAB
448-
matlab_env["MATLAB_LOG_DIR"] = str(matlab_log_dir)
501+
# The matlab_ready_file_dir is where MATLAB will write any subsequent logs
502+
matlab_env["MATLAB_LOG_DIR"] = str(self.matlab_ready_file_dir)
449503
matlab_env["MW_CD_ANYWHERE_ENABLED"] = "true"
450504
if self.licensing["type"] == "mhlm":
451505
matlab_env["MLM_WEB_LICENSE"] = "true"
@@ -538,15 +592,16 @@ async def stop_matlab(self):
538592
xvfb.terminate()
539593
waiters.append(xvfb.wait())
540594

541-
# Clean up matlab_ready_file
542595
try:
596+
# Clean up both connector.securePort file and mwi_proxy_lock_file
543597
if self.matlab_ready_file is not None:
544-
logger.info(
545-
f"Cleaning up matlab_ready_file...{str(self.matlab_ready_file)}"
546-
)
547598
self.matlab_ready_file.unlink()
599+
600+
if self.mwi_proxy_lock_file is not None:
601+
self.mwi_proxy_lock_file.unlink()
602+
548603
except FileNotFoundError:
549-
# Some other process deleted this file
604+
# Files won't exist when stop_matlab is called for the first time.
550605
pass
551606

552607
# Wait for termination

matlab_proxy/devel.py

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@
44
import asyncio
55
from aiohttp import web
66
import socket, time, os, sys
7-
from matlab_proxy import mwi_environment_variables as mwi_env
8-
from matlab_proxy import mwi_embedded_connector as mwi_connector
7+
from matlab_proxy import mwi_environment_variables as mwi_env, settings
98

109
desktop_html = b"""
1110
<h1>Fake MATLAB Web Desktop</h1>
@@ -134,15 +133,20 @@ async def fake_matlab_started(app):
134133

135134
# Real MATLAB always uses $MATLAB_LOG_DIR/connection.securePort as the ready file
136135
# We mock reading from the environment variable by calling the helper functions
137-
matlab_ready_file, matlab_log_dir = mwi_connector.get_matlab_ready_file(app["port"])
136+
matlab_ready_file_dir = settings.get(dev=True)["mwi_logs_root_dir"] / str(
137+
app["port"]
138+
)
139+
matlab_ready_file_dir.mkdir(parents=True, exist_ok=True)
140+
141+
app["matlab_ready_file"] = matlab_ready_file_dir / "connector.securePort"
138142

139143
ready_delay = app["ready_delay"]
140144
try:
141145
await asyncio.sleep(ready_delay)
142146
print(
143-
f"Creating fake MATLAB Embedded Connector ready file at {matlab_ready_file}"
147+
f"Creating fake MATLAB Embedded Connector ready file at {app['matlab_ready_file']}"
144148
)
145-
matlab_ready_file.touch()
149+
app["matlab_ready_file"].touch()
146150
except asyncio.CancelledError:
147151
pass
148152

@@ -164,8 +168,8 @@ async def cleanup_background_tasks(app):
164168
"""
165169
# Delete ready file on tear down
166170
# NOTE MATLAB does not delete this file on shutdown.
167-
matlab_ready_file, matlab_log_dir = mwi_connector.get_matlab_ready_file(app["port"])
168-
matlab_ready_file.unlink()
171+
172+
app["matlab_ready_file"].unlink()
169173

170174

171175
def matlab(args):

matlab_proxy/mwi_embedded_connector.py

Lines changed: 0 additions & 28 deletions
This file was deleted.

matlab_proxy/settings.py

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ def get_ws_env_settings():
4545

4646
def get_dev_settings(config):
4747
devel_file = Path(__file__).resolve().parent / "./devel.py"
48-
test_temp_dir = get_test_temp_dir()
48+
mwi_config_folder = get_test_temp_dir()
4949
ws_env, ws_env_suffix = get_ws_env_settings()
5050
return {
5151
"matlab_path": Path(),
@@ -64,14 +64,16 @@ def get_dev_settings(config):
6464
"matlab_protocol": "http",
6565
"matlab_display": ":1",
6666
"nlm_conn_str": os.environ.get(mwi_env.get_env_name_network_license_manager()),
67-
"matlab_config_file": test_temp_dir / "proxy_app_config.json",
67+
"matlab_config_file": mwi_config_folder / "proxy_app_config.json",
6868
"ws_env": ws_env,
6969
"mwa_api_endpoint": f"https://login{ws_env_suffix}.mathworks.com/authenticationws/service/v4",
7070
"mhlm_api_endpoint": f"https://licensing{ws_env_suffix}.mathworks.com/mls/service/v1/entitlement/list",
7171
"mwa_login": f"https://login{ws_env_suffix}.mathworks.com",
7272
"mwi_custom_http_headers": mwi_custom_http_headers.get(),
7373
"env_config": mwi_validators.validate_env_config(config),
7474
"ssl_context": None,
75+
"mwi_logs_root_dir": mwi_config_folder / "ports",
76+
"mwi_proxy_lock_file_name": "mwi_proxy.lock",
7577
}
7678

7779

@@ -122,6 +124,11 @@ def get(config=matlab_proxy.get_default_config_name(), dev=False):
122124
os.getenv(mwi_env.get_env_name_ssl_key_file(), None),
123125
os.getenv(mwi_env.get_env_name_ssl_cert_file(), None),
124126
)
127+
128+
# All config related to matlab-proxy will be saved to user's home folder.
129+
# This will allow for other user's to launch the integration from the same system
130+
# and not have their config's overwritten.
131+
mwi_config_folder = Path.home() / ".matlab" / "MWI"
125132
return {
126133
"matlab_path": matlab_path,
127134
"matlab_version": get_matlab_version(matlab_path),
@@ -146,7 +153,7 @@ def get(config=matlab_proxy.get_default_config_name(), dev=False):
146153
"nlm_conn_str": mwi_validators.validate_mlm_license_file(
147154
os.environ.get(mwi_env.get_env_name_network_license_manager())
148155
),
149-
"matlab_config_file": Path.home() / ".matlab" / "proxy_app_config.json",
156+
"matlab_config_file": mwi_config_folder / "proxy_app_config.json",
150157
"ws_env": ws_env,
151158
"mwa_api_endpoint": f"https://login{ws_env_suffix}.mathworks.com/authenticationws/service/v4",
152159
"mhlm_api_endpoint": f"https://licensing{ws_env_suffix}.mathworks.com/mls/service/v1/entitlement/list",
@@ -156,6 +163,11 @@ def get(config=matlab_proxy.get_default_config_name(), dev=False):
156163
"ssl_context": get_ssl_context(
157164
ssl_cert_file=ssl_cert_file, ssl_key_file=ssl_key_file
158165
),
166+
# This directory will be used to store connector.securePort(matlab_ready_file) and its corresponding files. This will be
167+
# a central place to store logs of all the running instances of MATLAB launched by matlab-proxy
168+
"mwi_logs_root_dir": mwi_config_folder / "ports",
169+
# Name of the lock file which will be created by this instance of matlab-proxy process.
170+
"mwi_proxy_lock_file_name": "mwi_proxy.lock",
159171
}
160172

161173

@@ -196,21 +208,6 @@ def create_xvfb_cmd():
196208
return xvfb_cmd, dpipe
197209

198210

199-
def get_matlab_tempdir():
200-
"""The temp directory used by MATLAB based on tempdir.m"""
201-
202-
for env_name in mwi_env.get_env_name_matlab_tempdir():
203-
matlab_tempdir = os.environ.get(env_name)
204-
if matlab_tempdir is not None:
205-
break
206-
207-
# MATLAB defaults to '/tmp'
208-
if matlab_tempdir is None:
209-
matlab_tempdir = "/tmp"
210-
211-
return matlab_tempdir
212-
213-
214211
def get_test_temp_dir():
215212
"""The temp directory to be used by tests"""
216213
test_temp_dir = Path(tempfile.gettempdir()) / "MWI" / "tests"

0 commit comments

Comments
 (0)