In ABCL (Armed Bear) LISP how do I create a background sub-process / background threads?

270 views Asked by At

How do I spawn off a background (named) sub-process/thread in ABCL? That is, I want to spawn the sub-process (or thread) to run in the background and leave the top-level evaluation free for other processing.

Using (apropos 'process/thread) I have found undocumented functions like those listed below, but I can't figure out the syntax. I am looking for the running example code to follow/modify. I seem to have created a process with the following make-process function, but I get an error when I try to kill it, and it runs in the foreground. There is no entry in the ABCL manual for make-process. MAKE-THREAD is listed, but as not-documented.

Where is the documentation / examples for all the functions listed in the ABCL manual with this "not-documented" designation? (Also those found with apropos?)

As a separate but related issue, is there a repository of ABCL-specific running code examples online that cover edge-case questions like these?

In other common-lisps I would use functions like:

(activate-process *initial-process*)

or

#+(:and MULTITASKING :lucid)
(defun mpd (&optional (reinit nil))
  (user::make-process :name "Pdraw-proc" :function #'pd::pdraw :args (list reinit)))

In ABCL I have muddled around getting not far:

CL-USER> (setf uu (make-thread (my-reader))) <-- runs at the top level / hogs read loop

CL-USER> (setf jj (system::%make-process (foo)))
#S(SYSTEM:PROCESS :JPROCESS 3 :INPUT NIL :OUTPUT NIL :ERROR NIL)
CL-USER> jj
#S(SYSTEM:PROCESS :JPROCESS 3 :INPUT NIL :OUTPUT NIL :ERROR NIL)


SYSTEM::MAKE-PROCESS (fbound)
SYSTEM::%PROCESS-KILL (fbound)
SYSTEM::%MAKE-PROCESS (fbound)

and

THREADS:MAKE-THREAD (fbound)
THREADS:DESTROY-THREAD (fbound)

and

(make-two-way-stream ...) 

[Syntax / examples for creating necessary streams for the threads maybe?]

Thanks in advance for pointers or code.

2

There are 2 answers

0
anquegi On

I use ABCL with roswell so it is easy to use in conjuntion with quicklisp, but there is also a project for that, but I think that you could easily use quicklisp or load libraries in ABCL. There are a lot of libraries that you can load on ABCL that are from quicklisp, unfortunately not all (Quicklisp is tested over linux & SBCL), but for concurrency you can load two great libraries that I normally use bordeaux-threads (the common threats library in common lisp) and Chanl a library that ports go channnels to common lisp. There are others that you could try but I'm not sure if they work lparallel, cl-actors ...

Let's do an example with this libraries:

CL-USER> (lisp-implementation-type)
"Armed Bear Common Lisp"
CL-USER> (lisp-implementation-version)
"1.5.0"
"Java_HotSpot(TM)_64-Bit_Server_VM-Oracle_Corporation-1.8.0_162-b12"
"x86_64-Mac_OS_X-10.12.6"

CL-USER> (ql:quickload 'bt-semaphore)
To load "bt-semaphore":
  Load 1 ASDF system:
    bt-semaphore
; Loading "bt-semaphore"
[package bt-semaphore]
(BT-SEMAPHORE)
CL-USER> bt:*supports-threads-p*
T

CL-USER> (defparameter *counter* 0)
*COUNTER*
CL-USER> (defun test-update-global-variable ()
  (bt:make-thread
   (lambda ()
     (sleep 10)
     (incf *counter*)))
  *counter*)
TEST-UPDATE-GLOBAL-VARIABLE
CL-USER> *counter*
0 (0 bits, #x0, #o0, #b0)
CL-USER> (test-update-global-variable)
0 (0 bits, #x0, #o0, #b0)
CL-USER> *counter*
0 (0 bits, #x0, #o0, #b0)
CL-USER> (+ 2 3)
5 (3 bits, #x5, #o5, #b101)
CL-USER> (format t "I'm wainting for counter")
I'm wainting for counter
NIL
CL-USER> (format t "let'see the counter value ~a~%" *counter*)
let'see the counter value 1
NIL

CL-USER> (ql:quickload :chanl)
To load "chanl":
  Load 1 ASDF system:
    chanl
; Loading "chanl"

(:CHANL)

CL-USER> (chanl:pcall (lambda () (sleep 10) (incf *counter*)))
#<CHANL:TASK Anonymous task [ALIVE] {2360938E}>
CL-USER> *counter*
1 (1 bit, #x1, #o1, #b1)
CL-USER> ;; waiting
; No values
CL-USER> *counter*
2 (2 bits, #x2, #o2, #b10)

Note that this are only example purpouses a global variable is not a good use for threats, also take a look on the libraries for further documentation,that should work, Also you in ABCL it is easy tou use java libraries, so maybe you can use akka actors, or other java concurrency libraries

Also as you poiinted ABCL has a package for threats, it is easy to use, like this:

CL-USER> (threads:make-thread (lambda () (sleep 10) (incf *counter*)) :name 'patata)
#<THREAD "PATATA" {49998577}>
CL-USER> *counter*
2 (2 bits, #x2, #o2, #b10)
CL-USER> ; wait
; No values
CL-USER> *counter*
3 (2 bits, #x3, #o3, #b11)

Also it has implemented mailbox threats to pass message to the threads

0
Thunderfoot On

With the prompting of the kind Vibhu I have found what appears to be the same native solution suggested by Anquegi. Thanks for the :name arg!

CL-USER> (lisp-implementation-type)
"Armed Bear Common Lisp"
CL-USER> (lisp-implementation-version)
"1.3.3"
"Java_HotSpot(TM)_64-Bit_Server_VM-Oracle_Corporation-1.8.0_161-b12"
"amd64-Windows_7-6.1"
CL-USER> 


(setf my-val 1)

;;; Output to *standard-output* for Inferior Lisp, so careful with SLIME:
(defun make-my-thread ()
  (setf q
    (threads:make-thread
     #'(lambda ()
         (format t "I am alive!~%")
         (sleep 15)
         (format t "Goodbye Cruel World~%")
       (setf my-val (1+ my-val))))))


;;; Works, but takes a while: 
(defun kill-it () (threads:destroy-thread q))

(defun alivep () (THREADS:THREAD-ALIVE-P q))

(make-my-thread) runs in the background without hogging standard-input

my-val can be queried and is not set until after the sleep.

(alivep) returns T until the thread completes or until several seconds after (kill-it) is called, then NIL.

So this minimal functionality now allows me a great deal of flexibility to run threads in the background.