diff --git a/src/emc/tooldata/tooldata_mmap.cc b/src/emc/tooldata/tooldata_mmap.cc index 7ba07b48f7b..e24a015975e 100644 --- a/src/emc/tooldata/tooldata_mmap.cc +++ b/src/emc/tooldata/tooldata_mmap.cc @@ -22,7 +22,9 @@ #include // write(2),lseek(2) #include // open(2) #include +#include // fstat(2) #include +#include #include "config.h" #include #include "tooldata.hh" @@ -185,10 +187,24 @@ int tool_mmap_creator(EMC_TOOL_STAT const * ptr,int random_toolchanger) //typ: milltask, guis (emcmodule,emcsh,...), halui int tool_mmap_user() { + static bool warned = false; + char why[LINELEN] = {}; int fd = open(tool_mmap_fname(), TOOL_MMAP_USER_OPEN_FLAGS, TOOL_MMAP_MODE); + struct stat st; if (fd < 0) { + snprintf(why, sizeof(why), "open: %s", strerror(errno)); + } else if (fstat(fd, &st) < 0) { + snprintf(why, sizeof(why), "fstat: %s", strerror(errno)); + } else if ((size_t)st.st_size < (size_t)(TOOL_MMAP_SIZE)) { + // The creator truncates the file before extending it: mapping + // it while short would SIGBUS on first access. + snprintf(why, sizeof(why), "size %zu < %zu", + (size_t)st.st_size, (size_t)(TOOL_MMAP_SIZE)); + } + if (*why) { + if (fd >= 0) { close(fd); } /* ** For the LinuxCNC application, tool_mmap_creator() ** should start first and create the mmap file needed here. @@ -197,7 +213,12 @@ int tool_mmap_user() ** continue execution if no mmap file is open. ** So print message and return fail indicator. */ - fprintf(stderr,"tool_mmap_user(): tool mmap not available\n"); + // callers may retry (emcmodule poll()), so warn only once + if (!warned) { + warned = true; + fprintf(stderr,"tool_mmap_user(): tool mmap not available: %s: %s\n", + tool_mmap_fname(), why); + } tool_mmap_base = (char*)NULL; return(-1); } diff --git a/src/emc/usr_intf/axis/extensions/emcmodule.cc b/src/emc/usr_intf/axis/extensions/emcmodule.cc index d1a3fa4df0d..649226d8924 100644 --- a/src/emc/usr_intf/axis/extensions/emcmodule.cc +++ b/src/emc/usr_intf/axis/extensions/emcmodule.cc @@ -1006,13 +1006,19 @@ static PyObject *poll(pyStatChannel *s, PyObject * /*o*/) { initialized=1; } #else //}{ - static bool mmap_available = 1; - if (!mmap_available) return NULL; + // task creates the mmap file; a GUI may poll before that, so retry + // the attach on every poll until it succeeds. Until then + // initialized stays 0 and the tool accessors report no data. + static bool warned = 0; if (!initialized) { - initialized=1; - if (tool_mmap_user()) { - mmap_available = 0; - fprintf(stderr,"poll(): continuing without tool mmap data\n"); + if (tool_mmap_user() == 0) { + initialized=1; + if (warned) { + fprintf(stderr,"poll(): tool mmap available now\n"); + } + } else if (!warned) { + warned = 1; + fprintf(stderr,"poll(): tool mmap not available yet, retrying\n"); } } #endif //} diff --git a/tests/ui-smoke/_lib/drive.py b/tests/ui-smoke/_lib/drive.py index 2136b669d02..796d681e0ca 100755 --- a/tests/ui-smoke/_lib/drive.py +++ b/tests/ui-smoke/_lib/drive.py @@ -99,10 +99,8 @@ def connect_and_wait_ready(timeout): still initialising; recreate the stat object on every iteration so a stale invalid buffer does not stick after linuxcncsvr is ready. - Catch the full Exception hierarchy: in early startup stat.poll() - can raise SystemError ('error return without exception set') when - the underlying C function reports failure without setting a Python - exception. Treat that the same as linuxcnc.error and retry.""" + Catch the full Exception hierarchy so an unexpected error during + startup is retried like linuxcnc.error.""" deadline = time.monotonic() + timeout last_err = None while time.monotonic() < deadline: