Folks, apologies in advance for the long post; this is an
unusual
problem, and after scouring gmane and the cmucl
documentation as well
as other newsgroups w/o finding an answer, I thought I'd ask
here, and
note all the details.
I have a long-running process doing various file I/O (all on
the local
server) going in an infinite loop:
(defun reload ()
(loop
(unwind-protect
(when (> (- (get-universal-time) *load-interval*)
*last-load*)
(setf *last-load* (get-universal-time))
(do-various-io-stuff))
(sleep 600))))
[In case you're wondering, *load-interval* is a parameter
set to 3600
(one hour), but it can be changed as needed -- this process
runs under
detachtty, so I can always go in and increase/reduce it at
any time.]
I've made that process -- and its other public interface
functions --
available to other lisp processes using the wire package,
like this:
(defconstant +wire-port+ *port-number*)
(defvar *process-wire* nil)
(defun start-wire-listener ()
(setf *process-wire*
(handler-case
(wire:create-request-server +wire-port+)
(error nil))))
(defun stop-wire-listener ()
(when *process-wire*
(handler-case
(wire:destroy-request-server *process-wire*)
(error nil))))
(defun start-my-server ()
(mp:make-process #'(lambda ()
(start-wire-listener)
(reload))
:name "Server Reloading"))
i.e. When the server starts, (start-my-server) is invoked,
which kicks
off the (reload) infinite loop thread.
Client processes (which are all running on the same machine)
connect
to it using this code:
(defconstant +my-server-host+ "127.0.0.1")
(defconstant +my-server-port+ *port-number*)
(defvar *my-server-wire* nil)
(defun attach-to-my-server ()
(when (null *my-server-wire*)
(setf *my-server-wire*
(handler-case
(wire:connect-to-remote-server +my-server-host+
+my-server-port+ #'attach-to-my-server)
(error nil)))))
(defmacro with-my-wire (&body body)
`(handler-case
(wire:remote-value *my-server-wire* , body)
(error nil)))
i.e. Client processes call (attach-to-my-server) once at
the
beginning, then access the public functions of the server
process
using the (with-my-wire) macro.
It works well, and runs for days at a time exactly as
expected -- the
same (single) server process is up continuously, while each
of the
client processes start (off a scheduler), connect to the
server
process, make their (with-my-wire) calls, and exit.
Every 3, 4 days or so, however, the server process reports a
"bogus
handler" error.
The first restart, REMOVE-THEM, clears the problem, and the
server
goes back functioning normally.
But the backtrace (in full, below) makes no sense to me; I
cannot
recognize those as any of my functions, nor from where my
code started
the error.
I found this function, to clear bogus handlers, suggested
elsewhere:
(defun clear-bogus-handlers ()
"Invoke to prevent the
(LISP::HANDLER-DESCRIPTORS-ERROR) condition
and dropping into the debugger on error
conditions."
(dolist (h lisp: descriptor-h
andlers*)
(when (> (lisp::handler-descriptor h) 2)
(sys:remove-fd-handler h))))
and I've applied it to my access macro like this:
(defmacro with-my-wire (&body body)
`(handler-case
(wire:remote-value *my-server-wire* , body)
(error nil (clear-bogus-handlers))))
But that does not solve the problem; the same "bogus
handler" errors
continue to occur.
Here is the full backtrace from the server process.
Can anyone suggest where the error handling logic should
be?
Error in function LISP::HANDLER-DESCRIPTORS-ERROR:
NIL have bad file descriptors.
[Condition of type SIMPLE-ERROR]
Restarts:
0: [REMOVE-THEM] Remove bogus handlers.
1: [RETRY-THEM ] Retry bogus handlers.
2: [CONTINUE ] Go on, leaving handlers marked as bogus.
3: [ABORT ] Return to Top-Level.
Debug (type H for help)
(LISP::HANDLER-DESCRIPTORS-ERROR)
Source: Error finding source:
Error in function DEBUG::GET-FILE-TOP-LEVEL-FORM: Source
file no longer exists:
target:code/serve-event.lisp.
0] backtrace
0: (LISP::HANDLER-DESCRIPTORS-ERROR)
1: (LISP::SUB-SERVE-EVENT 1 0)
2: (SYSTEM:WAIT-UNTIL-FD-USABLE 0 :INPUT NIL)
3: (LISP: O-INPUT
#<Stream for Standard Input>)
4: (LISP::INPUT-CHARACTER #<Stream for Standard Input>
NIL (LISP: EOF*))
5: (LISP::SYNONYM-IN #<Synonym Stream to SYSTEM STDIN*>
NIL (LISP: EOF*))
6: (LISP::TWO-WAY-IN
#<Two-Way Stream, Input = #<Synonym Stream to
SYSTEM STDIN*>,
Output = #<Synonym Stream to SYSTEM STDOUT*>&
gt;
NIL
(LISP: EOF*))
7: (READ-CHAR
#<Two-Way Stream, Input = #<Synonym Stream to
SYSTEM STDIN*>,
Output = #<Synonym Stream to SYSTEM STDOUT*>&
gt;
NIL
(LISP: EOF*)
NIL)
8: (LISP::READ-PRESERVING-WHITESPACE-INTERNAL
#<Two-Way Stream, Input = #<Synonym Stream to
SYSTEM STDIN*>,
Output = #<Synonym Stream to SYSTEM STDOUT*>&
gt;
NIL
(:EOF)
T)
9: (LISP::READ-PRESERVING-WHITESPACE-INTERNAL
#<Two-Way Stream, Input = #<Synonym Stream to
SYSTEM STDIN*>,
Output = #<Synonym Stream to SYSTEM STDOUT*>&
gt;
NIL
(:EOF)
NIL)
10: (LISP::READ-PRESERVING-WHITESPACE-INTERNAL 4
#<Two-Way
Stream, Input
= #<Synonym Stream to SYSTEM STDIN*>,
Output = #<Synonym Stream to
SYSTEM STDOUT*>&
gt;
NIL
(:EOF)
...)[:EXTERNAL]
11: (LISP::READ-INTERNAL
#<Two-Way Stream, Input = #<Synonym Stream to
SYSTEM STDIN*>,
Output = #<Synonym Stream to SYSTEM STDOUT*>&
gt;
NIL
(:EOF)
NIL)
12: (READ
#<Two-Way Stream, Input = #<Synonym Stream to
SYSTEM STDIN*>,
Output = #<Synonym Stream to SYSTEM STDOUT*>&
gt;
NIL
(:EOF)
NIL)
13: (LISP::%TOP-LEVEL)
14: ((LABELS LISP::RESTART-LISP
SAVE-LISP))
0] 0
0
0
*
|