diff --git a/cx_Freeze/hooks/multiprocessing.py b/cx_Freeze/hooks/multiprocessing.py index 175e4a4bd..f01618c98 100644 --- a/cx_Freeze/hooks/multiprocessing.py +++ b/cx_Freeze/hooks/multiprocessing.py @@ -25,8 +25,9 @@ def load_multiprocessing(_, module: Module) -> None: """ # Support for: # - fork in Unix (including macOS) is native; - # - spawn in Windows is native (since 4.3.4) but was improved in v6.2; - # - spawn and forkserver in Unix is implemented here. + # - spawn in Windows is native since 4.3.4, but was improved in 6.2; + # - spawn and forkserver in Unix is implemented here in 6.15.4 #1956; + # - monkeypath get_context to do automatic freeze_support in 7.1 #2382; if IS_MINGW or IS_WINDOWS: return if module.file.suffix == ".pyc": # source unavailable @@ -40,12 +41,18 @@ def load_multiprocessing(_, module: Module) -> None: if re.search(r"^from multiprocessing.* import main.*", cmd): exec(cmd) sys.exit() - # workaround for python docs: run the freeze_support to avoid infinite loop - from multiprocessing.spawn import freeze_support as spawn_freeze_support - spawn_freeze_support() - del spawn_freeze_support - # disable it, cannot run twice - freeze_support = lambda: None + # workaround: inject freeze_support call to avoid an infinite loop + from multiprocessing.spawn import freeze_support as _spawn_freeze_support + from multiprocessing.context import BaseContext + BaseContext._get_context = BaseContext.get_context + def _get_freeze_context(self, method=None): + ctx = self._get_context(method) + _spawn_freeze_support() + return ctx + BaseContext.get_context = \ + lambda self, method=None: _get_freeze_context(self, method) + # disable freeze_support, because it cannot be run twice + BaseContext.freeze_support = lambda self: None # cx_Freeze patch end """ code_string = module.file.read_text(encoding="utf_8") + dedent(source) diff --git a/doc/src/faq.rst b/doc/src/faq.rst index 5ad6a1c5b..66f1ad07c 100644 --- a/doc/src/faq.rst +++ b/doc/src/faq.rst @@ -179,3 +179,37 @@ sources. Or install patchelf from `sources `_. + + +Multiprocessing support +----------------------- + +On Linux and macOS, multiprocessing support is automatically managed by +cx_Freeze, including supporting it in pyTorch. + +However, to produce a Windows executable, you must use +`multiprocessing.freeze_support()`. + +One needs to call this function straight after the if __name__ == '__main__' +line of the main module. For example: + + .. code-block:: python + + from multiprocessing import Process, freeze_support + + + def f(): + print("Hello from cx_Freeze") + + + if __name__ == "__main__": + freeze_support() + Process(target=f).start() + +If the freeze_support() line is omitted then trying to run the frozen +executable will raise RuntimeError. + +Calling freeze_support() has no effect when invoked on any operating system +other than Windows. In addition, if the module is being run normally by the +Python interpreter on Windows (the program has not been frozen), then +freeze_support() has no effect. diff --git a/source/bases/common.c b/source/bases/common.c index 5e83b6855..ea600311e 100644 --- a/source/bases/common.c +++ b/source/bases/common.c @@ -78,13 +78,10 @@ static wchar_t *get_sys_path(wchar_t *lib_dir) wcscpy(filename, lib_dir); wcscat(filename, L"\\"); wcscat(filename, L"library.dat"); - fprintf(stderr, "--filename: %ls\n", filename); - fprintf(stderr, "--MAXPATHLEN: %zd/%zd\n", wcslen(filename), (size_t)MAXPATHLEN); if ((fp = _wfopen(filename, L"rt")) != NULL) { int i = fread(buffer, sizeof(*buffer), sizeof(buffer), fp); buffer[i] = 0; fclose(fp); - fprintf(stderr, "--buffer: %s=%zd\n", buffer, strlen(buffer)); wbuffer = Py_DecodeLocale(buffer, NULL); if (!wbuffer) { FatalError("Unable to convert path to string!"); @@ -93,23 +90,18 @@ static wchar_t *get_sys_path(wchar_t *lib_dir) wcscat(filename, L"\\"); wcscat(filename, wbuffer); PyMem_RawFree(wbuffer); - fprintf(stderr, "--filename: %ls=%zd\n", filename, wcslen(filename)); wcscat(buf_path, filename); - fprintf(stderr, "--buf_path: %ls\n", buf_path); } } if (wcslen(buf_path) != 0) wcscat(buf_path, L";"); - fprintf(stderr, "--buf_path: %ls\n", buf_path); wcscat(buf_path, lib_dir); - fprintf(stderr, "--buf_path: %ls\n", buf_path); sys_path = PyMem_RawMalloc(sizeof(wchar_t) * wcslen(buf_path) + 1); if (!sys_path) { FatalError("Out of memory creating sys_path!"); return NULL; } - fprintf(stderr, "--sys_path: %ls\n", wcscpy(sys_path, buf_path)); return wcscpy(sys_path, buf_path); } #else @@ -207,8 +199,6 @@ static char *get_sys_path(char *lib_dir) strcpy(filename, lib_dir); strcat(filename, "/"); strcat(filename, "library.dat"); - fprintf(stderr, "--filename: %s\n", filename); - fprintf(stderr, "--MAXPATHLEN: %d\n", MAXPATHLEN); if ((fp = fopen(filename, "r")) != NULL) { int i = fread(buffer, sizeof(*buffer), sizeof(buffer), fp); buffer[i] = 0; @@ -216,7 +206,6 @@ static char *get_sys_path(char *lib_dir) strcpy(filename, lib_dir); strcat(filename, "/"); strcat(filename, buffer); - fprintf(stderr, "--filename: %s\n", filename); strcat(buf_path, filename); } if (strlen(buf_path) != 0)