From: Daniel J S. <dan...@ie...> - 2014-02-08 06:57:07
|
(Ethan, hope you are following below.) On 02/07/2014 08:01 PM, Mojca Miklavec wrote: > On Fri, Feb 7, 2014 at 10:03 PM, Thomas Bleher wrote: >> * Mojca Miklavec [2014-02-07 12:37]: >>> On Fri, Feb 7, 2014 at 11:50 AM, Mojca Miklavec wrote: >>>> >>>> I wanted to test, but it seems that Qt terminal is broken on Mac now. >>> >>> The commit that broke gnuplot on Mac seems to be the following one: >>> >>> Author: sfeam<sfeam> >>> Date: Sat Oct 26 05:44:10 2013 +0000 >>> >>> explicit construction and destruction of qt_term data >> >> Hmm, this is strange. I have no idea how this patch could have broken >> the Mac build. > > Maybe it just helped a deeper problem surface now. The implementation > of the Qt terminal never really worked properly on Macs. I might be > wrong, but I would blame forking. Gnuplot starts two separate Qt > programs/windows, the proper one and a "fake/nonworking" one. Trying > to print anything crashes gnuplot (but crash during printing is not > really a showstopper) etc. On Qt 4 and Mac OS X>= 10.7 one can hide > the second fake window. On Qt 5 I have no clue how to hide it (so > users end up with two icons per running gnuplot), but at least > printing doesn't crash. > > It would be great if someone could figure out how to avoid forking > (and try to avoid it at least on mac) if that is possible at all. I'm > not skilled enough to understand how gnuplot_qt and forking works to > be able to change it. Others have asked for gnuplot to not use fork() for OSX. I think it is ultimately up to someone with an OSX machine to do this. If we could figure out how to do this with threads instead of fork, maybe that is a solution. Fork is a pretty straightforward concept--the existing process is duplicated (with a different PID) and in one process the "fork()" function returns one value, and in the second process it returns a different value. So, removing some lines of code: pid_t pid = fork(); if (pid < 0) PARENT fprintf(stderr, "Forking error\n"); else if (pid == 0) // Child: start the GUI { CHILD // Make sure the forked copy doesn't trash the history file cancel_history(); // Start the gnuplot_qt program QString filename = getenv("GNUPLOT_DRIVER_DIR"); if (filename.isEmpty()) filename = QT_DRIVER_DIR; filename += "/gnuplot_qt"; execlp(filename.toUtf8().data(), "gnuplot_qt", (char*)NULL); fprintf(stderr, "Expected Qt driver: %s\n", filename.toUtf8().data()); perror("Exec failed"); exit(EXIT_FAILURE); } The parent process will get either PID + or PID -1. The child process gets the PID 0. For example, there is no way the line labeled PARENT will ever happen in the child process because if fork() fails the test "if (pid < 0)" will not happen (because there is no such process). The parent process fails the second test ("else if") and continues onward. The child process on the other hand passes the test and proceeds to swap its process with gnuplot_qt via execlp. Mac OSX doesn't like fork, or at least it doesn't like how gnuplot is doing the fork. Is there a better way of doing this than fork? Say threads, or spawn? Before addressing that, let's see if we can make fork() work in OSX, because apparently it does have fork()...but the code we have might not be "fork-safe". Here is the best I can find on this issue: http://stackoverflow.com/questions/18854708/why-is-it-prohivited-to-use-fork-without-exec-in-mac https://mikeash.com/pyblog/friday-qa-2012-01-20-fork-safety.html The second reference is pretty good. If I'm understanding correctly, gnuplot is supposed to not be doing anything that requires a core OS function between the fork() and the exec##() family of functions. I believe the following hunk may be the problem: QString filename = getenv("GNUPLOT_DRIVER_DIR"); if (filename.isEmpty()) filename = QT_DRIVER_DIR; filename += "/gnuplot_qt"; There are a few lines here that are either requesting some system memory or are requesting some environment information...either of which could fall under core OS function. I believe this line: // Make sure the forked copy doesn't trash the history file cancel_history(); is OK because all this routine does is set this variable static char *expanded_history_filename; to NULL. Since it is static it is a memory location within the process and not requiring any sort of OS functionality. SO, with that in mind, Mojca could you try to figure out if a simple mod will make this work under OSX? Try moving that filename hunk of code before the fork, say (I'm probably being sloppy and ignoring some more direct way of creating a character string from a QString): // Start the GUI application void execGnuplotQt() { char *chfilename; // Prep the child filename before issuing the fork // and avoid any non-fork-safe OS action. QString filename = getenv("GNUPLOT_DRIVER_DIR"); if (filename.isEmpty()) filename = QT_DRIVER_DIR; filename += "/gnuplot_qt"; chfilename = malloc(filename.length() + 1); if (chfilename) { *chfilename = filename.toUtf8().data(); // Fork the GUI and exec the gnuplot_qt program in the child process pid_t pid = fork(); if (pid < 0) fprintf(stderr, "Forking error\n"); else if (pid == 0) // Child: start the GUI { // Make sure the forked copy doesn't trash the history file cancel_history(); // Start the gnuplot_qt program execlp(filename.toUtf8().data(), "gnuplot_qt", (char*)NULL); fprintf(stderr, "Expected Qt driver: %s\n", filename.toUtf8().data()); perror("Exec failed"); exit(EXIT_FAILURE); } } free(chfilename); qt_localServerName = "qtgnuplot" + QString::number(pid); qt_gnuplot_qtStarted = true; } Give that a try. You may need to do a bit of debugging to make it compile. Let us know if that solves the common Mac OSX all-caps error. Dan |