|  | @@ -25,6 +25,7 @@ import contextlib
 | 
	
		
			
				|  |  |  import importlib
 | 
	
		
			
				|  |  |  import importlib.machinery
 | 
	
		
			
				|  |  |  import sys
 | 
	
		
			
				|  |  | +import threading
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  from grpc_tools import _protoc_compiler
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -91,6 +92,7 @@ def _protos_and_services(protobuf_path, include_paths=None):
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  _proto_code_cache = {}
 | 
	
		
			
				|  |  | +_proto_code_cache_lock = threading.RLock()
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  class ProtoLoader(importlib.abc.Loader):
 | 
	
	
		
			
				|  | @@ -113,26 +115,27 @@ class ProtoLoader(importlib.abc.Loader):
 | 
	
		
			
				|  |  |      def exec_module(self, module):
 | 
	
		
			
				|  |  |          assert module.__name__ == self._module_name
 | 
	
		
			
				|  |  |          code = None
 | 
	
		
			
				|  |  | -        if self._module_name in _proto_code_cache:
 | 
	
		
			
				|  |  | -            code = _proto_code_cache[self._module_name]
 | 
	
		
			
				|  |  | -            six.exec_(code, module.__dict__)
 | 
	
		
			
				|  |  | -        else:
 | 
	
		
			
				|  |  | -            files = self._codegen_fn(
 | 
	
		
			
				|  |  | -                self._protobuf_path.encode('ascii'),
 | 
	
		
			
				|  |  | -                [path.encode('ascii') for path in sys.path])
 | 
	
		
			
				|  |  | -            # NOTE: The files are returned in topological order of dependencies. Each
 | 
	
		
			
				|  |  | -            # entry is guaranteed to depend only on the modules preceding it in the
 | 
	
		
			
				|  |  | -            # list and the last entry is guaranteed to be our requested module. We
 | 
	
		
			
				|  |  | -            # cache the code from the first invocation at module-scope so that we
 | 
	
		
			
				|  |  | -            # don't have to regenerate code that has already been generated by protoc.
 | 
	
		
			
				|  |  | -            for f in files[:-1]:
 | 
	
		
			
				|  |  | -                module_name = self._generated_file_to_module_name(
 | 
	
		
			
				|  |  | -                    f[0].decode('ascii'))
 | 
	
		
			
				|  |  | -                if module_name not in sys.modules:
 | 
	
		
			
				|  |  | -                    if module_name not in _proto_code_cache:
 | 
	
		
			
				|  |  | -                        _proto_code_cache[module_name] = f[1]
 | 
	
		
			
				|  |  | -                    importlib.import_module(module_name)
 | 
	
		
			
				|  |  | -            six.exec_(files[-1][1], module.__dict__)
 | 
	
		
			
				|  |  | +        with _proto_code_cache_lock:
 | 
	
		
			
				|  |  | +            if self._module_name in _proto_code_cache:
 | 
	
		
			
				|  |  | +                code = _proto_code_cache[self._module_name]
 | 
	
		
			
				|  |  | +                six.exec_(code, module.__dict__)
 | 
	
		
			
				|  |  | +            else:
 | 
	
		
			
				|  |  | +                files = self._codegen_fn(
 | 
	
		
			
				|  |  | +                    self._protobuf_path.encode('ascii'),
 | 
	
		
			
				|  |  | +                    [path.encode('ascii') for path in sys.path])
 | 
	
		
			
				|  |  | +                # NOTE: The files are returned in topological order of dependencies. Each
 | 
	
		
			
				|  |  | +                # entry is guaranteed to depend only on the modules preceding it in the
 | 
	
		
			
				|  |  | +                # list and the last entry is guaranteed to be our requested module. We
 | 
	
		
			
				|  |  | +                # cache the code from the first invocation at module-scope so that we
 | 
	
		
			
				|  |  | +                # don't have to regenerate code that has already been generated by protoc.
 | 
	
		
			
				|  |  | +                for f in files[:-1]:
 | 
	
		
			
				|  |  | +                    module_name = self._generated_file_to_module_name(
 | 
	
		
			
				|  |  | +                        f[0].decode('ascii'))
 | 
	
		
			
				|  |  | +                    if module_name not in sys.modules:
 | 
	
		
			
				|  |  | +                        if module_name not in _proto_code_cache:
 | 
	
		
			
				|  |  | +                            _proto_code_cache[module_name] = f[1]
 | 
	
		
			
				|  |  | +                        importlib.import_module(module_name)
 | 
	
		
			
				|  |  | +                six.exec_(files[-1][1], module.__dict__)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  class ProtoFinder(importlib.abc.MetaPathFinder):
 |