Support for native threads differs between platforms as described in this section.
In LispWorks 5.1 and earlier versions, you can have many runnable
mp:process objects/native threads, but Lisp code can only run in one thread at a time and a lock is used to enforce this. This can limit performance on a computer with multiple CPU cores. When a foreign function is called using the FLI, the lock is released until the function returns. This allows other Lisp threads to run, for instance while waiting for a database query to execute.
Threads running Lisp code can be rescheduled preemptively, so if you call into Lisp from more than one thread simultaneously and one request takes a long time then it will not delay the requests in other threads.
When foreign code creates a native thread (a "foreign thread") and code running on this thread calls into Lisp, then Lisp needs to associate a Lisp process object with this thread to be able to work properly.
When there is a call on a foreign thread into Lisp which is not a recursive call (an "outer call"), Lisp first checks if there is a process associated with this thread, and if there is it uses it. Otherwise, it creates a new process and associates it with the foreign thread. Recursive calls into Lisp (when Lisp calls foreign code which calls back into Lisp) are processed in the same way as recursive calls in Lisp threads.
When the outer call returns, Lisp by default keeps the process associated with the thread, but this is not guaranteed. Keeping the process means that next call into Lisp requires less work, but comes at the cost of using more memory. Lisp eliminates the process if it detects that the thread has died, if there is call to last-callback-on-thread inside the outer call or if the process is killed by process-terminate.
Once Lisp has a process associated with the thread, it establishes it as the current process, as returned by calling get-current-process, and then calls the foreign callable Lisp code.
Part of establishing the process involves binding the variables in *process-initial-bindings*. Note that this binding happen repeatedly for each outer call. The computation of the bound value is done when the process is created, so if the process is not eliminated between outer calls (the default behavior), this happens only once. The computation of the value occurs in the dynamic environment of the new process.
Keeping the process between outer calls (the default behavior), makes each call faster, but uses memory. For few processes, this is probably the best approach. With many processes, the memory usage may become an issue.
LispWorks User Guide and Reference Manual - 20 Sep 2017