<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[140641] trunk/base/src</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="https://trac.macports.org/changeset/140641">140641</a></dd>
<dt>Author</dt> <dd>cal@macports.org</dd>
<dt>Date</dt> <dd>2015-09-28 14:22:19 -0700 (Mon, 28 Sep 2015)</dd>
</dl>

<h3>Log Message</h3>
<pre>base: tracelib: Refactor, enable process tracking

Refactor both the synchronization and error handling in tracelib to
clean up some of the copy-n-paste cruft that has accumulated over time.
Additionally, add the code that fills and depletes the list of connected
sockets and their processes at the remote ends to implement cleaning
them up after the build finishes.

This simplifies how the synchronization between &quot;tracelib run&quot; and
&quot;tracelib closesocket&quot; works. &quot;tracelib run&quot; now uses a simple event
loop idiom that receives and process asynchronous events and terminates
at the request of &quot;tracelib closesocket&quot; using a well-defined channel (a
selfpipe, since it's the only portable method to do this without kqueue
user events, which are not available on older OS X versions).</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkbasesrcpextlib10tracelibc">trunk/base/src/pextlib1.0/tracelib.c</a></li>
<li><a href="#trunkbasesrcport10porttracetcl">trunk/base/src/port1.0/porttrace.tcl</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkbasesrcpextlib10tracelibc"></a>
<div class="modfile"><h4>Modified: trunk/base/src/pextlib1.0/tracelib.c (140640 => 140641)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/base/src/pextlib1.0/tracelib.c        2015-09-28 21:04:57 UTC (rev 140640)
+++ trunk/base/src/pextlib1.0/tracelib.c        2015-09-28 21:22:19 UTC (rev 140641)
</span><span class="lines">@@ -112,9 +112,29 @@
</span><span class="cx"> static int selfpipe[2];
</span><span class="cx"> static int enable_fence = 0;
</span><span class="cx"> static Tcl_Interp *interp;
</span><del>-static pthread_mutex_t sock_mutex = PTHREAD_MUTEX_INITIALIZER;
-static int cleanuping = 0;
</del><span class="cx"> 
</span><ins>+/**
+ * Mutex that shall be acquired to exclusively lock checking and acting upon
+ * the value of kq, indicating whether the event loop has started. If it has
+ * started, shutdown of the event loop shall occur by writing to the write end
+ * of the selfpipe (which is non-blocking), which will in turn trigger the
+ * event loop termination and a signal on the evloop_signal condition variable
+ * when the loop has been terminated and it is safe to free the resources that
+ * were used by the loop.
+ *
+ * If kq is -1, the event loop has not been started and resources can
+ * immediately be free(3)d (under the lock to avoid concurrent set up of the
+ * event loop in a different thread).
+ */
+static pthread_mutex_t evloop_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+/**
+ * Condition variable that shall be used to signal the end of the event loop
+ * after a termination signal has been sent to it via the write end of
+ * selfpipe. The associated mutex is evloop_mtx.
+ */
+static pthread_cond_t evloop_signal = PTHREAD_COND_INITIALIZER;
+
</ins><span class="cx"> static void send_file_map(int sock);
</span><span class="cx"> static void dep_check(int sock, char *path);
</span><span class="cx"> 
</span><span class="lines">@@ -278,6 +298,38 @@
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> /**
</span><ins>+ * Closes the two sockets given in \a p and sets their values to -1.
+ */
+static void pipe_cleanup(int p[2]) {
+    for (size_t i = 0; i &lt; 2; ++i) {
+        if (p[i] != -1) {
+            close(p[i]);
+            p[i] = -1;
+        }
+    }
+}
+
+/**
+ * Helper function to simplify error handling. Converts the error indicated by
+ * \a msg, appended with a string representation of the UNIX error \a errno
+ * into a Tcl error by setting up the result of the Tcl interpreter \a interp
+ * accordingly.
+ *
+ * Returns TCL_ERROR to be used as the return value of the caller.
+ */
+static int error2tcl(const char *msg, int err, Tcl_Interp *interp) {
+    Tcl_SetErrno(err);
+    Tcl_ResetResult(interp);
+    if (err != 0) {
+        Tcl_AppendResult(interp, msg, (char *) Tcl_PosixError(interp), NULL);
+    } else {
+        Tcl_AppendResult(interp, msg, NULL);
+    }
+
+    return TCL_ERROR;
+}
+
+/**
</ins><span class="cx">  * Sets the path of the tracelib unix socket where darwintrace should attempt
</span><span class="cx">  * to connect to. This path should be specific to the port being installed.
</span><span class="cx">  * Different sockets should be used for different ports (and maybe even
</span><span class="lines">@@ -599,20 +651,10 @@
</span><span class="cx">     struct sockaddr_un sun;
</span><span class="cx">     struct rlimit rl;
</span><span class="cx"> 
</span><del>-    cleanuping = 0;
-
-    pthread_mutex_lock(&amp;sock_mutex);
</del><span class="cx">     if (-1 == (sock = socket(PF_LOCAL, SOCK_STREAM, 0))) {
</span><del>-        Tcl_SetErrno(errno);
-        Tcl_ResetResult(interp);
-        Tcl_AppendResult(interp, &quot;socket: &quot;, (char *) Tcl_PosixError(interp), NULL);
-        pthread_mutex_unlock(&amp;sock_mutex);
-        return TCL_ERROR;
</del><ins>+        return error2tcl(&quot;socket: &quot;, errno, in);
</ins><span class="cx">     }
</span><del>-    pthread_mutex_unlock(&amp;sock_mutex);
</del><span class="cx"> 
</span><del>-    interp = in;
-
</del><span class="cx">     /* raise the limit of open files to the maximum from the default soft limit
</span><span class="cx">      * of 256 */
</span><span class="cx">     if (getrlimit(RLIMIT_NOFILE, &amp;rl) == -1) {
</span><span class="lines">@@ -633,23 +675,22 @@
</span><span class="cx">     strlcpy(sun.sun_path, name, sizeof(sun.sun_path));
</span><span class="cx"> 
</span><span class="cx">     if (-1 == (bind(sock, (struct sockaddr *) &amp;sun, sizeof(sun)))) {
</span><del>-        Tcl_SetErrno(errno);
-        Tcl_ResetResult(interp);
-        Tcl_AppendResult(interp, &quot;bind: &quot;, (char *) Tcl_PosixError(interp), NULL);
</del><ins>+        int err = errno;
</ins><span class="cx">         close(sock);
</span><span class="cx">         sock = -1;
</span><del>-        return TCL_ERROR;
</del><ins>+        return error2tcl(&quot;bind: &quot;, err, in);
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     if (-1 == listen(sock, SOMAXCONN)) {
</span><del>-        Tcl_SetErrno(errno);
-        Tcl_ResetResult(interp);
-        Tcl_AppendResult(interp, &quot;listen: &quot;, (char *) Tcl_PosixError(interp), NULL);
</del><ins>+        int err = errno;
</ins><span class="cx">         close(sock);
</span><span class="cx">         sock = -1;
</span><del>-        return TCL_ERROR;
</del><ins>+        return error2tcl(&quot;bind: &quot;, err, in);
</ins><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    // keep a reference to the interpreter that opened the socket
+    interp = in;
+
</ins><span class="cx">     return TCL_OK;
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="lines">@@ -684,210 +725,192 @@
</span><span class="cx"> 
</span><span class="cx"> static int TracelibRunCmd(Tcl_Interp *in) {
</span><span class="cx">     struct kevent kev;
</span><ins>+    int retval = TCL_ERROR;
</ins><span class="cx">     int flags;
</span><del>-    int oldsock;
</del><span class="cx">     int opensockcount = 0;
</span><ins>+    bool break_eventloop = false;
</ins><span class="cx"> 
</span><del>-    pthread_mutex_lock(&amp;sock_mutex);
</del><ins>+    pthread_mutex_lock(&amp;evloop_mutex);
+    /* bring all variables into a defined state so the cleanup code can be
+     * called from anywhere */
+    selfpipe[0] = -1;
+    selfpipe[1] = -1;
+    kq = -1;
+
</ins><span class="cx">     if (-1 == (kq = kqueue())) {
</span><del>-        Tcl_SetErrno(errno);
-        Tcl_ResetResult(in);
-        Tcl_AppendResult(in, &quot;kqueue: &quot;, (char *) Tcl_PosixError(in), NULL);
-        return TCL_ERROR;
</del><ins>+        error2tcl(&quot;kqueue: &quot;, errno, in);
+        goto error_locked;
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     if (sock != -1) {
</span><del>-        oldsock = sock;
-
</del><span class="cx">         /* mark listen socket non-blocking in order to prevent a race condition
</span><span class="cx">          * that would occur between kevent(2) and accept(2), if a incoming
</span><span class="cx">          * connection is aborted before it is accepted. Using a non-blocking
</span><span class="cx">          * accept(2) prevents the problem.*/
</span><del>-        flags = fcntl(oldsock, F_GETFL, 0);
-        if (-1 == fcntl(oldsock, F_SETFL, flags | O_NONBLOCK)) {
-            Tcl_SetErrno(errno);
-            Tcl_ResetResult(in);
-            Tcl_AppendResult(in, &quot;fcntl(F_SETFL, += O_NONBLOCK): &quot;, (char *) Tcl_PosixError(in), NULL);
-            pthread_mutex_unlock(&amp;sock_mutex);
-            return TCL_ERROR;
</del><ins>+        flags = fcntl(sock, F_GETFL, 0);
+        if (-1 == fcntl(sock, F_SETFL, flags | O_NONBLOCK)) {
+            error2tcl(&quot;fcntl(F_SETFL, += O_NONBLOCK): &quot;, errno, in);
+            goto error_locked;
</ins><span class="cx">         }
</span><span class="cx"> 
</span><span class="cx">         /* register the listen socket in the kqueue */
</span><del>-        EV_SET(&amp;kev, oldsock, EVFILT_READ, EV_ADD | EV_RECEIPT, 0, 0, NULL);
</del><ins>+        EV_SET(&amp;kev, sock, EVFILT_READ, EV_ADD | EV_RECEIPT, 0, 0, NULL);
</ins><span class="cx">         if (1 != kevent(kq, &amp;kev, 1, &amp;kev, 1, NULL)) {
</span><del>-            Tcl_SetErrno(errno);
-            Tcl_ResetResult(in);
-            Tcl_AppendResult(in, &quot;kevent (listen socket): &quot;, (char *) Tcl_PosixError(in), NULL);
-            close(kq);
-            pthread_mutex_unlock(&amp;sock_mutex);
-            return TCL_ERROR;
</del><ins>+            error2tcl(&quot;kevent (listen socket): &quot;, errno, in);
+            goto error_locked;
</ins><span class="cx">         }
</span><span class="cx">         /* kevent(2) on EV_RECEIPT: When passed as input, it forces EV_ERROR to
</span><span class="cx">          * always be returned. When a filter is successfully added, the data field
</span><span class="cx">          * will be zero. */
</span><span class="cx">         if ((kev.flags &amp; EV_ERROR) == 0 || ((kev.flags &amp; EV_ERROR) &gt; 0 &amp;&amp; kev.data != 0)) {
</span><del>-            Tcl_SetErrno(kev.data);
-            Tcl_ResetResult(in);
-            Tcl_AppendResult(in, &quot;kevent (listen socket receipt): &quot;, (char *) Tcl_PosixError(in), NULL);
-            close(kq);
-            pthread_mutex_unlock(&amp;sock_mutex);
-            return TCL_ERROR;
</del><ins>+            error2tcl(&quot;kevent (listen socket receipt): &quot;, kev.data, in);
+            goto error_locked;
</ins><span class="cx">         }
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">         /* use the self-pipe trick to trigger returning from kevent(2) when
</span><span class="cx">          * tracelib closesocket is called. */
</span><span class="cx">         if (-1 == pipe(selfpipe)) {
</span><del>-            Tcl_SetErrno(errno);
-            Tcl_ResetResult(in);
-            Tcl_AppendResult(in, &quot;pipe: &quot;, (char *) Tcl_PosixError(in), NULL);
-            pthread_mutex_unlock(&amp;sock_mutex);
-            return TCL_ERROR;
</del><ins>+            error2tcl(&quot;pipe: &quot;, errno, in);
+            goto error_locked;
</ins><span class="cx">         }
</span><span class="cx"> 
</span><span class="cx">         /* mark the write side of the pipe non-blocking */
</span><span class="cx">         flags = fcntl(selfpipe[1], F_GETFL, 0);
</span><span class="cx">         if (-1 == fcntl(selfpipe[1], F_SETFL, flags | O_NONBLOCK)) {
</span><del>-            Tcl_SetErrno(errno);
-            Tcl_ResetResult(in);
-            Tcl_AppendResult(in, &quot;fcntl(F_SETFL, += O_NONBLOCK): &quot;, (char *) Tcl_PosixError(in), NULL);
-            pthread_mutex_unlock(&amp;sock_mutex);
-            return TCL_ERROR;
</del><ins>+            error2tcl(&quot;fcntl(F_SETFL, += O_NONBLOCK): &quot;, errno, in);
+            goto error_locked;
</ins><span class="cx">         }
</span><span class="cx"> 
</span><span class="cx">         /* wait for the user event on the listen socket, as sent by CloseCmd as
</span><span class="cx">          * deathpill */
</span><span class="cx">         EV_SET(&amp;kev, selfpipe[0], EVFILT_READ, EV_ADD | EV_RECEIPT, 0, 0, NULL);
</span><span class="cx">         if (1 != kevent(kq, &amp;kev, 1, &amp;kev, 1, NULL)) {
</span><del>-            Tcl_SetErrno(errno);
-            Tcl_ResetResult(in);
-            Tcl_AppendResult(in, &quot;kevent (selfpipe): &quot;, (char *) Tcl_PosixError(in), NULL);
-            close(kq);
-            pthread_mutex_unlock(&amp;sock_mutex);
-            return TCL_ERROR;
</del><ins>+            error2tcl(&quot;kevent (selfpipe): &quot;, errno, in);
+            goto error_locked;
</ins><span class="cx">         }
</span><span class="cx">         /* kevent(2) on EV_RECEIPT: When passed as input, it forces EV_ERROR to
</span><span class="cx">          * always be returned. When a filter is successfully added, the data field
</span><span class="cx">          * will be zero. */
</span><span class="cx">         if ((kev.flags &amp; EV_ERROR) == 0 || ((kev.flags &amp; EV_ERROR) &gt; 0 &amp;&amp; kev.data != 0)) {
</span><del>-            Tcl_SetErrno(kev.data);
-            Tcl_ResetResult(in);
-            Tcl_AppendResult(in, &quot;kevent (selfpipe receipt): &quot;, (char *) Tcl_PosixError(in), NULL);
-            close(kq);
-            pthread_mutex_unlock(&amp;sock_mutex);
-            return TCL_ERROR;
</del><ins>+            error2tcl(&quot;kevent (selfpipe receipt): &quot;, kev.data, in);
+            goto error_locked;
</ins><span class="cx">         }
</span><span class="cx">     }
</span><del>-    pthread_mutex_unlock(&amp;sock_mutex);
</del><ins>+    pthread_mutex_unlock(&amp;evloop_mutex);
</ins><span class="cx"> 
</span><del>-    while (sock != -1 &amp;&amp; !cleanuping) {
</del><ins>+    while (sock != -1 &amp;&amp; !break_eventloop) {
</ins><span class="cx">         int keventstatus;
</span><del>-        int i;
</del><ins>+        bool incoming = false;
</ins><span class="cx"> 
</span><span class="cx">         /* run kevent(2) until new activity is available */
</span><span class="cx">         do {
</span><span class="cx">             if (-1 == (keventstatus = kevent(kq, NULL, 0, res_kevents, MAX_SOCKETS, NULL))) {
</span><del>-                Tcl_SetErrno(errno);
-                Tcl_ResetResult(in);
-                Tcl_AppendResult(in, &quot;kevent (main loop): &quot;, (char *) Tcl_PosixError(in), NULL);
-                close(kq);
-                return TCL_ERROR;
</del><ins>+                error2tcl(&quot;kevent (main loop): &quot;, errno, in);
+                goto error_unlocked;
</ins><span class="cx">             }
</span><span class="cx">         } while (keventstatus == 0);
</span><span class="cx"> 
</span><del>-        for (i = 0; i &lt; keventstatus; ++i) {
</del><ins>+        for (int i = 0; i &lt; keventstatus; ++i) {
</ins><span class="cx">             /* handle traffic on the selfpipe */
</span><span class="cx">             if ((int) res_kevents[i].ident == selfpipe[0]) {
</span><del>-                pthread_mutex_lock(&amp;sock_mutex);
-                close(selfpipe[0]);
-                close(selfpipe[1]);
-                selfpipe[0] = -1;
-                selfpipe[1] = -1;
-                pthread_mutex_unlock(&amp;sock_mutex);
-                break;
-            }
</del><ins>+                /* traffic on the selfpipe means we should clean up */
+                break_eventloop = true;
+                /* finish processing this batch */
+                continue;
+            } else if ((int) res_kevents[i].ident != sock) {
+                /* if the socket is to be closed, or */
+                if ((res_kevents[i].flags &amp; (EV_EOF | EV_ERROR)) &gt; 0
+                    /* new data is available, and its processing tells us to
+                     * close the socket */
+                    || (!process_line(res_kevents[i].ident))) {
+                        /* an error occured or process_line suggested closing
+                         * this socket */
+                        close(res_kevents[i].ident);
+                        /* closing the socket will automatically remove it from the
+                         * kqueue :) */
+                        opensockcount--;
</ins><span class="cx"> 
</span><del>-            /* the control socket has activity – we might have a new
-             * connection. We use a copy of sock here, because sock might have
-             * been set to -1 by the close command */
-            if ((int) res_kevents[i].ident == oldsock) {
-                int s;
</del><ins>+#ifdef HAVE_PEERPID_LIST
+                        if (peerpid_list_dequeue(res_kevents[i].ident) == (pid_t) -1) {
+                            fprintf(stderr, &quot;tracelib: didn't find PID for closed socket %d\n&quot;, (int) res_kevents[i].ident);
+                        }
+#endif
+                }
+            } else {
+                /* the control socket has activity – we might have a new
+                 * connection. */
</ins><span class="cx"> 
</span><span class="cx">                 /* handle error conditions */
</span><span class="cx">                 if ((res_kevents[i].flags &amp; (EV_ERROR | EV_EOF)) &gt; 0) {
</span><del>-                    if (cleanuping) {
-                        break;
-                    }
-                    Tcl_ResetResult(in);
-                    Tcl_SetResult(in, &quot;control socket closed&quot;, NULL);
-                    close(kq);
-                    return TCL_ERROR;
</del><ins>+                    error2tcl(&quot;control socket closed&quot;, 0, in);
+                    goto error_unlocked;
</ins><span class="cx">                 }
</span><span class="cx"> 
</span><del>-                /* else: new connection attempt(s) */
-                for (;;) {
-                    if (-1 == (s = accept(sock, NULL, NULL))) {
-                        if (cleanuping) {
-                            break;
-                        }
-                        if (errno == EWOULDBLOCK) {
-                            break;
-                        }
-                        Tcl_SetErrno(errno);
-                        Tcl_ResetResult(in);
-                        Tcl_AppendResult(in, &quot;accept: &quot;, (char *) Tcl_PosixError(in), NULL);
-                        close(kq);
-                        return TCL_ERROR;
-                    }
</del><ins>+                /* delay processing, process data on existing sockets first */
+                incoming = true;
+            }
+        }
</ins><span class="cx"> 
</span><del>-                    flags = fcntl(s, F_GETFL, 0);
-                    if (-1 == fcntl(s, F_SETFL, flags &amp; ~O_NONBLOCK)) {
-                        ui_warn(interp, &quot;tracelib: couldn't mark socket as blocking&quot;);
-                        close(s);
-                        continue;
-                    }
</del><ins>+        if (incoming) {
+            /* new connection attempt(s) */
+            for (;;) {
+                int s;
</ins><span class="cx"> 
</span><del>-                    /* register the new socket in the kqueue */
-                    EV_SET(&amp;kev, s, EVFILT_READ, EV_ADD | EV_RECEIPT, 0, 0, NULL);
-                    if (1 != kevent(kq, &amp;kev, 1, &amp;kev, 1, NULL)) {
-                        ui_warn(interp, &quot;tracelib: error adding socket to kqueue&quot;);
-                        close(s);
-                        continue;
</del><ins>+                if (-1 == (s = accept(sock, NULL, NULL))) {
+                    if (errno == EWOULDBLOCK) {
+                        break;
</ins><span class="cx">                     }
</span><del>-                    /* kevent(2) on EV_RECEIPT: When passed as input, it forces EV_ERROR to
-                     * always be returned. When a filter is successfully added, the data field
-                     * will be zero. */
-                    if ((kev.flags &amp; EV_ERROR) == 0 || ((kev.flags &amp; EV_ERROR) &gt; 0 &amp;&amp; kev.data != 0)) {
-                        ui_warn(interp, &quot;tracelib: error adding socket to kqueue&quot;);
-                        close(s);
-                        continue;
-                    }
</del><span class="cx"> 
</span><del>-                    opensockcount++;
</del><ins>+                    error2tcl(&quot;accept: &quot;, errno, in);
+                    goto error_unlocked;
</ins><span class="cx">                 }
</span><span class="cx"> 
</span><del>-                if (cleanuping) {
-                    break;
</del><ins>+                flags = fcntl(s, F_GETFL, 0);
+                if (-1 == fcntl(s, F_SETFL, flags &amp; ~O_NONBLOCK)) {
+                    ui_warn(interp, &quot;tracelib: couldn't mark socket as blocking&quot;);
+                    close(s);
+                    continue;
</ins><span class="cx">                 }
</span><del>-            } else {
-                /* if the socket is to be closed, or */
-                if ((res_kevents[i].flags &amp; (EV_EOF | EV_ERROR)) &gt; 0
-                    /* new data is available, and its processing tells us to
-                     * close the socket */
-                    || (!process_line(res_kevents[i].ident))) {
-                    /* an error occured or process_line suggested closing this
-                     * socket */
-                    close(res_kevents[i].ident);
-                    /* closing the socket will automatically remove it from the
-                     * kqueue :) */
-                    opensockcount--;
</del><ins>+
+                /* register the new socket in the kqueue */
+                EV_SET(&amp;kev, s, EVFILT_READ, EV_ADD | EV_RECEIPT, 0, 0, NULL);
+                if (1 != kevent(kq, &amp;kev, 1, &amp;kev, 1, NULL)) {
+                    ui_warn(interp, &quot;tracelib: error adding socket to kqueue&quot;);
+                    close(s);
+                    continue;
</ins><span class="cx">                 }
</span><ins>+                /* kevent(2) on EV_RECEIPT: When passed as input, it forces EV_ERROR to
+                 * always be returned. When a filter is successfully added, the data field
+                 * will be zero. */
+                if ((kev.flags &amp; EV_ERROR) == 0 || ((kev.flags &amp; EV_ERROR) &gt; 0 &amp;&amp; kev.data != 0)) {
+                    ui_warn(interp, &quot;tracelib: error adding socket to kqueue (receipt)&quot;);
+                    close(s);
+                    continue;
+                }
+
+#ifdef HAVE_PEERPID_LIST
+                pid_t peer_pid = (pid_t) -1;
+                socklen_t peer_pid_len = sizeof(peer_pid);
+                if (getsockopt(s, SOL_LOCAL, LOCAL_PEERPID, &amp;peer_pid, &amp;peer_pid_len) == 0) {
+                    // We found a PID for the remote side
+                    peerpid_list_enqueue(s, peer_pid);
+                } else {
+                    // Error occured, process has probably already terminated
+                    close(s);
+                    continue;
+                }
+#endif
+                opensockcount++;
</ins><span class="cx">             }
</span><span class="cx">         }
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    /* NOTE: We aren't necessarily closing all client sockets here! */
</del><ins>+    retval = TCL_OK;
</ins><span class="cx"> 
</span><ins>+error_unlocked:
+    pthread_mutex_lock(&amp;evloop_mutex);
+error_locked:
</ins><span class="cx">     // Close remainig sockets to avoid dangling processes
</span><span class="cx">     if (opensockcount &gt; 0) {
</span><span class="cx"> #ifdef HAVE_PEERPID_LIST
</span><span class="lines">@@ -898,51 +921,67 @@
</span><span class="cx">         ui_warn(interp, &quot;tracelib: %d open sockets leaking at end of runcmd\n&quot;, opensockcount);
</span><span class="cx"> #endif
</span><span class="cx">     }
</span><del>-    pthread_mutex_lock(&amp;sock_mutex);
-    close(kq);
-    kq = -1;
-    pthread_mutex_unlock(&amp;sock_mutex);
</del><span class="cx"> 
</span><del>-    return TCL_OK;
</del><ins>+    // cleanup selfpipe and set it to -1
+    pipe_cleanup(selfpipe);
+
+    // close kqueue(2) socket
+    if (kq != -1) {
+        close(kq);
+        kq = -1;
+    }
+
+    pthread_mutex_unlock(&amp;evloop_mutex);
+    // wake up any waiting threads in TracelibCloseSocketCmd
+    pthread_cond_broadcast(&amp;evloop_signal);
+
+    return retval;
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> static int TracelibCleanCmd(Tcl_Interp *interp UNUSED) {
</span><span class="cx"> #define safe_free(x) do{free(x); x=0;}while(0);
</span><del>-    cleanuping = 1;
-    pthread_mutex_lock(&amp;sock_mutex);
</del><span class="cx">     if (sock != -1) {
</span><span class="cx">         close(sock);
</span><span class="cx">         sock = -1;
</span><span class="cx">     }
</span><del>-    pthread_mutex_unlock(&amp;sock_mutex);
</del><ins>+
</ins><span class="cx">     if (name) {
</span><span class="cx">         unlink(name);
</span><span class="cx">         safe_free(name);
</span><span class="cx">     }
</span><del>-    if (depends) {
-        safe_free(depends);
-    }
</del><ins>+
+    safe_free(depends);
+
</ins><span class="cx">     enable_fence = 0;
</span><ins>+    return TCL_OK;
+
</ins><span class="cx"> #undef safe_free
</span><del>-    return TCL_OK;
</del><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> static int TracelibCloseSocketCmd(Tcl_Interp *interp UNUSED) {
</span><del>-    cleanuping = 1;
-    pthread_mutex_lock(&amp;sock_mutex);
-    if (sock != -1) {
-        close(sock);
-        sock = -1;
</del><ins>+    pthread_mutex_lock(&amp;evloop_mutex);
+    if (kq != -1 &amp;&amp; selfpipe[1] != -1) {
+        /* We know the pipes have been created because kq != -1 and we have the
+         * lock. We don't have to check for errors, because none should occur
+         * but when the pipe is full, which we wouldn't care about. */
+        write(selfpipe[1], &quot;!&quot;, 1);
</ins><span class="cx"> 
</span><del>-        if (kq != -1) {
-            /* We know the pipes have been created because kq != -1 and we have
-             * the lock. We don't have to check for errors, because none should
-             * occur but when the pipe is full, which we wouldn't care about.
-             * */
-            write(selfpipe[1], &quot;!&quot;, 1);
</del><ins>+        /* Wait for the kqueue event loop to terminate. We must not return
+         * earlier than that because the next call will be to tracelib clean,
+         * and that frees up memory that would be used by the event loop
+         * otherwise. */
+        pthread_cond_wait(&amp;evloop_signal, &amp;evloop_mutex);
+    } else {
+        /* The kqueue(2) loop isn't running yet, so we can just close the
+         * socket and make sure it stays closed. In this situation, the kqueue
+         * will not be created. */
+        if (sock != -1) {
+            close(sock);
+            sock = -1;
</ins><span class="cx">         }
</span><span class="cx">     }
</span><del>-    pthread_mutex_unlock(&amp;sock_mutex);
</del><ins>+    pthread_mutex_unlock(&amp;evloop_mutex);
+
</ins><span class="cx">     return TCL_OK;
</span><span class="cx"> }
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkbasesrcport10porttracetcl"></a>
<div class="modfile"><h4>Modified: trunk/base/src/port1.0/porttrace.tcl (140640 => 140641)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/base/src/port1.0/porttrace.tcl        2015-09-28 21:04:57 UTC (rev 140640)
+++ trunk/base/src/port1.0/porttrace.tcl        2015-09-28 21:22:19 UTC (rev 140641)
</span><span class="lines">@@ -266,6 +266,7 @@
</span><span class="cx">                 }
</span><span class="cx"> 
</span><span class="cx">                 # Kill socket
</span><ins>+                tracelib closesocket
</ins><span class="cx">                 tracelib clean
</span><span class="cx">                 # Delete the socket file
</span><span class="cx">                 file delete -force $fifo
</span></span></pre>
</div>
</div>

</body>
</html>