11# Copyright 2020-2021 The MathWorks, Inc.
22
33import asyncio
4- from matlab_proxy import mwi_embedded_connector as mwi_connector
54from matlab_proxy import mwi_environment_variables as mwi_env
65from matlab_proxy import util
76import 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
0 commit comments