Changeset 13104


Ignore:
Timestamp:
Oct 27, 2009, 4:26:15 PM (10 years ago)
Author:
rme
Message:

Document WATCH and friends in the manual.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/source/doc/src/using.xml

    r12432 r13104  
    19221922  </refentry>
    19231923  </sect1>
     1924
     1925  <sect1 id="watched-objects"><title>Watched Objects</title>
     1926  <para>
     1927    As of release 1.4, Clozure CL provides a way for lisp objects to
     1928    be watched so that a condition will be signaled when a thread
     1929    attempts to write to the watched object. For a certain class of
     1930    bugs (someone is changing this value, but I don't know who), this
     1931    can be extremely helpful.
     1932  </para>
     1933  <sect2 id="watched-watch"><title>WATCH</title>
     1934  <refentry id="f_watch">
     1935    <indexterm zone="f_watch">
     1936      <primary>watch</primary>
     1937    </indexterm>
     1938   
     1939    <refnamediv>
     1940      <refname>WATCH</refname>
     1941      <refpurpose>
     1942        Monitor a lisp object for writes.
     1943      </refpurpose>
     1944      <refclass>Function</refclass>
     1945    </refnamediv>
     1946   
     1947    <refsynopsisdiv>
     1948      <synopsis><function>watch</function> &optional; object</synopsis>
     1949    </refsynopsisdiv>
     1950   
     1951    <refsect1>
     1952      <title>Arguments and Values</title>
     1953      <variablelist>
     1954        <varlistentry>
     1955          <term>object</term>
     1956          <listitem>
     1957            <para>
     1958              Any memory-allocated lisp object.
     1959            </para>
     1960          </listitem>
     1961        </varlistentry>
     1962      </variablelist>
     1963    </refsect1>
     1964
     1965    <refsect1>
     1966      <title>Description</title>
     1967      <para>
     1968        The WATCH function arranges for the specified object to be
     1969        monitored for writes. This is accomplished by copying the
     1970        object to its own set of virtual memory pages, which are then
     1971        write-protected. This protection is enforced by the computer's
     1972        memory-management hardware; the write-protection does not slow
     1973        down reads at all.
     1974      </para>
     1975      <para>
     1976        When any write to the object is attempted, a
     1977        WRITE-TO-WATCHED-OBJECT condition will be signaled.
     1978      </para>
     1979      <para>
     1980        When called with no arguments, WATCH returns a freshly-consed
     1981        list of the objects currently being watched.
     1982      </para>
     1983      <para>
     1984        WATCH returns NIL if the object cannot be watched (typically
     1985        because the object is in a static or pure memory area).
     1986      </para>
     1987    </refsect1>
     1988    <refsect1 id="watch-dwim"><title>DWIM</title>
     1989    <para>
     1990      WATCH operates at a fairly low level; it is not possible to
     1991      avoid the details of the internal representation of objects.
     1992      Nevertheless, as a convenience, WATCHing a standard-instance,
     1993      a hash-table, or a multi-dimensional or non-simple CL array
     1994      will watch the underlying slot-vector, hash-table-vector, or
     1995      data-vector, respectively.
     1996      </para>
     1997    </refsect1>
     1998    <refsect1 id="watch-discuss"><title>Discussion</title>
     1999    <para>
     2000      WATCH can monitor any memory-allocated lisp object.
     2001    </para>
     2002    <para>
     2003      In Clozure CL, a memory-allocated object is either a cons cell
     2004      or a uvector.
     2005    </para>
     2006    <para>
     2007      WATCH operates on cons cells, not lists. In order to watch a
     2008      chain of cons cells, each cons cell must be watched
     2009      individually. Because each watched cons cell takes up its own
     2010      own virtual memory page (4 Kbytes), it's only feasible to watch
     2011      relatively short lists.
     2012    </para>
     2013    <para>
     2014      If a memory-allocated object isn't a cons cell, then it is a
     2015      vector-like object called a uvector. A uvector is a
     2016      memory-allocated lisp object whose first word is a header that
     2017      describes the object's type and the number of elements that it
     2018      contains.
     2019    </para>
     2020    <para>
     2021      So, a hash table is a uvector, as is a string, a standard
     2022      instance, a double-float, a CL array or vector, and so forth.
     2023    </para>
     2024    <para>
     2025      Some CL objects, like strings and other simple vectors, map in a
     2026      straightforward way onto the uvector representation. It is easy
     2027      to understand what happens in such cases. The uvector index
     2028      corresponds directly to the vector index:
     2029    </para>
     2030    <programlisting>
     2031<![CDATA[
     2032? (defvar *s* "xxxxx")
     2033*S*
     2034? (watch *s*)
     2035"xxxxx"
     2036? (setf (char *s* 3) #\o)
     2037> Error: Write to watched uvector "xxxxx" at index 3
     2038>        Faulting instruction: (movl (% eax) (@ -5 (% r15) (% rcx)))
     2039> While executing: SET-CHAR, in process listener(1).
     2040> Type :POP to abort, :R for a list of available restarts.
     2041> Type :? for other options.
     2042]]>
     2043    </programlisting>
     2044    <para>
     2045      In the case of more complicated objects (e.g., a hash-table, a
     2046      standard-instance, a package, etc.), the elements of the uvector
     2047      are like slots in a structure. It's necessary to know which one
     2048      of those "slots" contains the data that will be changed when the
     2049      object is written to.
     2050    </para>
     2051    <para>
     2052      As mentioned above, watch knows about arrays, hash-tables, and
     2053      standard-instances, and will automatically watch the appropriate
     2054      data-containing element.
     2055    </para>
     2056    <para>
     2057      An example might make this clearer.
     2058    </para>
     2059    <programlisting>
     2060<![CDATA[
     2061? (defclass foo ()
     2062    (slot-a slot-b slot-c))
     2063#<STANDARD-CLASS FOO>
     2064? (defvar *a-foo* (make-instance 'foo))
     2065*A-FOO*
     2066? (watch *a-foo*)
     2067#<SLOT-VECTOR #xDB00D>
     2068;;; Note that WATCH has watched the internal slot-vector object
     2069? (setf (slot-value *a-foo* 'slot-a) 'foo)
     2070> Error: Write to watched uvector #<SLOT-VECTOR #xDB00D> at index 1
     2071>        Faulting instruction: (movq (% rsi) (@ -5 (% r8) (% rdi)))
     2072> While executing: %MAYBE-STD-SETF-SLOT-VALUE-USING-CLASS, in process listener(1).
     2073> Type :POP to abort, :R for a list of available restarts.
     2074> Type :? for other options.
     2075]]>
     2076    </programlisting>
     2077    <para>
     2078      Looking at a backtrace would presumably show what object and
     2079      slot name were written.
     2080    </para>
     2081    <para>
     2082      Note that even though the write was to slot-a, the uvector index
     2083      was 1 (not 0). This is because the first element of a
     2084      slot-vector is a pointer to the instance that owns the slots. We
     2085      can retrieve that to look at the object that was modified:
     2086    </para>
     2087    <programlisting>
     2088<![CDATA[
     20891 > (uvref (write-to-watched-object-object *break-condition*) 0)
     2090#<FOO #x30004113502D>
     20911 > (describe *)
     2092#<FOO #x30004113502D>
     2093Class: #<STANDARD-CLASS FOO>
     2094Wrapper: #<CLASS-WRAPPER FOO #x300041135EBD>
     2095Instance slots
     2096SLOT-A: #<Unbound>
     2097SLOT-B: #<Unbound>
     2098SLOT-C: #<Unbound>
     20991 >
     2100]]>
     2101    </programlisting>
     2102    </refsect1>
     2103  </refentry>
     2104</sect2>
     2105<sect2 id="watched-unwatch"><title>UNWATCH</title>
     2106<refentry id="f_unwatch">
     2107  <indexterm zone="f_unwatch">
     2108    <primary>unwatch</primary>
     2109  </indexterm>
     2110 
     2111  <refnamediv>
     2112    <refname>UNWATCH</refname>
     2113      <refpurpose>
     2114        Stop monitoring a lisp object for writes.
     2115      </refpurpose>
     2116      <refclass>Function</refclass>
     2117  </refnamediv>
     2118 
     2119  <refsynopsisdiv>
     2120    <synopsis><function>unwatch</function> object</synopsis>
     2121  </refsynopsisdiv>
     2122 
     2123  <refsect1><title>Description</title>
     2124  <para>
     2125    The UNWATCH function ensures that the specified object is in
     2126    normal, non-monitored memory. If the object is not currently
     2127    being watched, UNWATCH does nothing and returns NIL. Otherwise,
     2128    the newly unwatched object is returned.
     2129  </para>
     2130  </refsect1>
     2131</refentry>
     2132</sect2>
     2133<sect2 id="watched-write-to-watched-object">
     2134  <title>WRITE-TO-WATCHED-OBJECT</title>
     2135  <refentry id="c_write-to-watched-object">
     2136    <indexterm zone="c_write-to-watched-object">
     2137      <primary>write-to-watched-object</primary>
     2138    </indexterm>
     2139   
     2140    <refnamediv>
     2141      <refname>WRITE-TO-WATCHED-OBJECT</refname>
     2142      <refpurpose>
     2143        Condition signaled when a write to a watched object is attempted.
     2144      </refpurpose>
     2145      <refclass>Condition</refclass>
     2146    </refnamediv>
     2147
     2148    <refsect1><title>Discussion</title>
     2149    <para>
     2150      This condition is signaled when a watched object is written
     2151      to. There are three slots of interest:
     2152    </para>
     2153    <variablelist>
     2154      <varlistentry>
     2155        <term>object</term>
     2156        <listitem>
     2157          <para>
     2158            The actual object that was the destination of the write.
     2159          </para>
     2160        </listitem>
     2161      </varlistentry>
     2162      <varlistentry>
     2163        <term>offset</term>
     2164        <listitem>
     2165          <para>
     2166            The byte offset from the tagged object pointer to the
     2167            address of the write.
     2168          </para>
     2169        </listitem>
     2170      </varlistentry>
     2171      <varlistentry>
     2172        <term>instruction</term>
     2173        <listitem>
     2174          <para>
     2175            The disassembled machine instruction that attempted the write.
     2176          </para>
     2177        </listitem>
     2178      </varlistentry>
     2179    </variablelist>
     2180    </refsect1>
     2181
     2182    <refsect1><title>Restarts</title>
     2183    <para>
     2184      A few restarts are provided: one will skip over the faulting
     2185      write instruction and proceed; another offers to unwatch the
     2186      object and continue.
     2187    </para>
     2188    <para>
     2189      There is also an emulate restart. In some common cases, the
     2190      faulting write instruction can be emulated, enabling the write
     2191      to be performed without having to unwatch the object (and
     2192      therefore let other threads potentially write to it). If the
     2193      faulting instruction isn't recognized, the emulate restart will
     2194      not be offered.
     2195    </para>
     2196    </refsect1>
     2197  </refentry>
     2198</sect2>
     2199<sect2 id="watch-notes"><title>Notes</title>
     2200<para>
     2201  Although some care has been taken to minimize potential problems
     2202  arising from watching and unwatching objects from multiple
     2203  threads, there may well be subtle race conditions present that
     2204  could cause bad behavior.
     2205</para>
     2206<para>
     2207  For example, suppose that a thread attempts to write to a watched
     2208  object. This causes the operating system to generate an
     2209  exception. The lisp kernel figures out what the exception is, and
     2210  calls back into lisp to signal the write-to-watched-object
     2211  condition and perhaps handle the error.
     2212</para>
     2213<para>
     2214  Now, as soon lisp code starts running again (for the callback),
     2215  it's possible that some other thread could unwatch the very
     2216  watched object that caused the exception, perhaps before we even
     2217  have a chance to signal the condition, much less respond to it.
     2218</para>
     2219<para>
     2220  Having the object unwatched out from underneath a handler may at
     2221  least confuse it, if not cause deeper trouble. Use caution with
     2222  unwatch.
     2223</para>
     2224</sect2>
     2225<sect2 id="watch-examples"><title>Examples</title>
     2226<para>
     2227  Here are a couple more examples in addition to the above examples
     2228  of watching a string and a standard-instance.
     2229</para>
     2230<sect3><title>Fancy arrays</title>
     2231<programlisting>
     2232?  (defvar *f* (make-array '(2 3) :element-type 'double-float))
     2233*F*
     2234? (watch *f*)
     2235#(0.0D0 0.0D0 0.0D0 0.0D0 0.0D0 0.0D0)
     2236;;; Note that the above vector is the underlying data-vector for the array
     2237? (setf (aref *f* 1 2) pi)
     2238> Error: Write to watched uvector #&lt;VECTOR 6 type DOUBLE-FLOAT, simple> at index 5
     2239>        Faulting instruction: (movq (% rax) (@ -5 (% r8) (% rdi)))
     2240> While executing: ASET, in process listener(1).
     2241> Type :POP to abort, :R for a list of available restarts.
     2242> Type :? for other options.
     22431 >
     2244  </programlisting>
     2245  <para>
     2246    In this case, uvector index in the report is the row-major index
     2247    of the element that was written to.
     2248  </para>
     2249  </sect3>
     2250  <sect3><title>Hash tables</title>
     2251  <para>
     2252    Hash tables are surprisingly complicated. The representation of a
     2253    hash table includes an element called a hash-table-vector. The
     2254    keys and values of the elements are stored pairwise in this
     2255    vector.
     2256  </para>
     2257  <para>
     2258    One problem with trying to monitor hash tables for writes is that
     2259    the underlying hash-table-vector is replaced with an entirely new
     2260    one when the hash table is rehashed. A previously-watched
     2261    hash-table-vector will not be the used by the hash table after
     2262    rehashing, and writes to the new vector will not be caught.
     2263  </para>
     2264  <programlisting>
     2265? (defvar *h* (make-hash-table))
     2266*H*
     2267? (setf (gethash 'noise *h*) 'feep)
     2268FEEP
     2269? (watch *h*)
     2270#&lt;HASH-TABLE-VECTOR #xDD00D>
     2271;;; underlying hash-table-vector
     2272? (setf (gethash 'noise *h*) 'ding)
     2273> Error: Write to watched uvector #&lt;HASH-TABLE-VECTOR #xDD00D> at index 35
     2274>        Faulting instruction: (lock)
     2275>          (cmpxchgq (% rsi) (@ (% r8) (% rdx)))
     2276> While executing: %STORE-NODE-CONDITIONAL, in process listener(1).
     2277> Type :POP to abort, :R for a list of available restarts.
     2278> Type :? for other options.
     2279;;; see what value is being replaced...
     22801 > (uvref (write-to-watched-object-object *break-condition*) 35)
     2281FEEP
     2282;;; backtrace shows useful context
     22831 > :b
     2284*(1A109F8) : 0 (%STORE-NODE-CONDITIONAL ???) NIL
     2285 (1A10A50) : 1 (LOCK-FREE-PUTHASH NOISE #&lt;HASH-TABLE :TEST EQL size 1/60 #x30004117D47D> DING) 653
     2286 (1A10AC8) : 2 (CALL-CHECK-REGS PUTHASH NOISE #&lt;HASH-TABLE :TEST EQL size 1/60 #x30004117D47D> DING) 229
     2287 (1A10B00) : 3 (TOPLEVEL-EVAL (SETF (GETHASH # *H*) 'DING) NIL) 709
     2288 ...
     2289  </programlisting>
     2290  </sect3>
     2291  <sect3><title>Lists</title>
     2292  <para>
     2293    As previously mentioned, WATCH only watches individual cons cells.
     2294  </para>
     2295  <programlisting>
     2296? (defun watch-list (list)
     2297    (maplist #'watch list))
     2298WATCH-LIST
     2299? (defvar *l* (list 1 2 3))
     2300*L*
     2301? (watch-list *l*)
     2302((1 2 3) (2 3) (3))
     2303? (setf (nth 2 *l*) 'foo)
     2304> Error: Write to the CAR of watched cons cell (3)
     2305>        Faulting instruction: (movq (% rsi) (@ 5 (% rdi)))
     2306> While executing: %SETNTH, in process listener(1).
     2307> Type :POP to abort, :R for a list of available restarts.
     2308> Type :? for other options.
     2309  </programlisting>
     2310  </sect3>
     2311  </sect2>
     2312</sect1>
    19242313</chapter>
Note: See TracChangeset for help on using the changeset viewer.