|Version 2 (modified by rme, 4 years ago)|
As a debugging aid, many lisp objects can be watched so that a condition will be signaled when the object is about to be written to.
WATCH &optional object [function]
The WATCH function arranges for the specified object to be monitored for writes. This is done via the system's memory-management hardware, so reading the object takes place at full speed.
When any write to the object is attempted, the condition WRITE-TO-WATCHED-OBJECT will be signaled.
WATCH knows about a few complicated objects (hash tables, fancy arrays, CLOS standard instances), and gives them special treatment:
hash tables: the underlying hash table vector will be watched
arrays: the underlying data vector will be watched
standard instances: the slot vector will be watched
If you don't want WATCH to do this for you, call PRIMITIVE-WATCH instead.
WATCH can monitor cons cells, but in order to watch a chain of cons cells, they have to be watched individually. Each cons cell will take up its own virtual memory page, so it's only really feasible to watch short lists.
When called with no arguments, WATCH returns a list of objects currently being watched. These objects will always be the underlying data vectors, and not complicated user-level objects like hash tables, etc.
PRIMITIVE-WATCH object [function]
Like WATCH, except it doesn't give object any special treatment; it simply uses it as-is.
UNWATCH object [function]
The UNWATCH function ensures that the specified object is in normal, non-monitored memory. If the object is not currently being watched, UNWATCH does nothing and returns NIL. Otherwise, the newly unwatched object is returned.
A note on thread-safety: avoid unwatching an object from a thread while some other thread might be signaling or handling a WRITE-TO-WATCHED-OBJECT condition. The lisp tries to detect when it would be unsafe to unwatch an object; UNWATCH will return NIL and not unwatch the object in such a case. Nevertheless, there may be subtle race conditions lurking here.
This condition is signaled when a watched object is written to. There are three slots of interest: the "object" slot, the "containing-object" slot, and the "location" slot.
The object slot is the actual object that was the target of a write operation.
If the object slot contains something that is a part of some larger composite object, then the containing-object slot will contain that object. For example, if object is a hash-table-vector, then the contents of the containing-object will be the hash table of which the hash-table-vector is a part. If the contents of object slot aren't part of any larger object, then the containing-object and object slots will contain the same thing.
The contents of the location slot differs according to what is in the object slot. In the case of arrays, it will be the row-major index of the element that was about to be written. For hash tables, location will contain the hash table key, or #<Unbound> if the key can't be determined (as might happen if you are adding a new key-value pair to the hash table). For CLOS objects, location will contain the slot name. If object is a cons cell, location will be either the actual car or cdr of the cons cell, depending on which half of the cons cell was written to. If the object is some other uvector, then location will contain the uvector index of the element that was about to be written. If location is NIL, the location could not be determined.