@@ -7,6 +7,7 @@ from __future__ import annotations
77cimport cython
88from libc.stddef cimport size_t
99from libc.stdint cimport intptr_t
10+ from libcpp.mutex cimport py_safe_call_once
1011
1112from collections import namedtuple
1213from os import fsencode, fspath, PathLike
@@ -459,8 +460,11 @@ cdef class Kernel:
459460 @cython.critical_section
460461 def attributes (self ) -> KernelAttributes:
461462 """Get the read-only attributes of this kernel."""
463+ cdef KernelAttributes attributes
462464 if self._attributes is None:
463- self._attributes = KernelAttributes._init(self ._h_kernel)
465+ attributes = KernelAttributes._init(self ._h_kernel)
466+ if self._attributes is None:
467+ self._attributes = attributes
464468 return self._attributes
465469
466470 cdef tuple _get_arguments_info(self , bint param_info = False ):
@@ -507,8 +511,11 @@ cdef class Kernel:
507511 @cython.critical_section
508512 def occupancy(self ) -> KernelOccupancy:
509513 """Get the occupancy information for launching this kernel."""
514+ cdef KernelOccupancy occupancy
510515 if self._occupancy is None:
511- self._occupancy = KernelOccupancy._init(self ._h_kernel)
516+ occupancy = KernelOccupancy._init(self ._h_kernel)
517+ if self._occupancy is None:
518+ self._occupancy = occupancy
512519 return self._occupancy
513520
514521 @property
@@ -584,6 +591,31 @@ CodeTypeT = bytes | bytearray | str
584591
585592cdef tuple _supported_code_type = tuple (ObjectCodeFormatType.__members__ .values())
586593
594+
595+ cdef void _lazy_load_module_once(void * self_v) except * :
596+ # Call-once helper for the lazy module loading, we want to avoid unloading
597+ # a module in case of threads racing, so use `call_once`.
598+ cdef ObjectCode self = < ObjectCode> self_v
599+ cdef LibraryHandle h_library
600+ cdef bytes path_bytes
601+ module = self ._module
602+ if isinstance (module, str ):
603+ path_bytes = module.encode()
604+ h_library = create_library_handle_from_file(< const char * > path_bytes)
605+ elif isinstance (module, (bytes, bytearray)):
606+ h_library = create_library_handle_from_data(< const void * >< char * > module)
607+ elif isinstance (module, PathLike):
608+ path_bytes = fsencode(module)
609+ h_library = create_library_handle_from_file(< const char * > path_bytes)
610+ else :
611+ assert_type_str_or_bytes_like(module)
612+ raise_code_path_meant_to_be_unreachable()
613+ return
614+ if not h_library:
615+ HANDLE_RETURN(get_last_error())
616+ self ._h_library = h_library
617+
618+
587619cdef class ObjectCode:
588620 """ Represent a compiled program to be loaded onto the device.
589621
@@ -746,26 +778,8 @@ cdef class ObjectCode:
746778
747779 # TODO: do we want to unload in a finalizer? Probably not..
748780
749- @cython.critical_section
750781 cdef int _lazy_load_module(self ) except -1:
751- if self._h_library:
752- return 0
753- module = self ._module
754- cdef bytes path_bytes
755- if isinstance(module , str ):
756- path_bytes = module.encode()
757- self ._h_library = create_library_handle_from_file(< const char * > path_bytes)
758- elif isinstance (module, (bytes, bytearray)):
759- self ._h_library = create_library_handle_from_data(< const void * >< char * > module)
760- elif isinstance (module, PathLike):
761- path_bytes = fsencode(module)
762- self ._h_library = create_library_handle_from_file(< const char * > path_bytes)
763- else :
764- assert_type_str_or_bytes_like(module)
765- raise_code_path_meant_to_be_unreachable()
766- return - 1
767- if not self ._h_library:
768- HANDLE_RETURN(get_last_error())
782+ py_safe_call_once(self._load_once , _lazy_load_module_once , <void *>self )
769783 return 0
770784
771785 def get_kernel(self , name: str | bytes ) -> Kernel:
0 commit comments