;;;; This file contains special proclamations for variables that are
;;;; referenced in the code sources before they are defined.

;;;; This software is part of the SBCL system. See the README file for
;;;; more information.
;;;;
;;;; This software is derived from the CMU CL system, which was
;;;; written at Carnegie Mellon University and released into the
;;;; public domain. The software is in the public domain and is
;;;; provided with absolutely no warranty. See the COPYING and CREDITS
;;;; files for more information.

(in-package "SB-IMPL")

;;; Define a variable that is assigned into TLS either in INIT-INITIAL-THREAD
;;; or NEW-LISP-THREAD-TRAMPOLINE before any other Lisp code runs.
;;; !COLD-INIT gets these assignents via INIT-INITIAL-THREAD.
;;; In particular, *RESTART-CLUSTERS* and *HANDLER-CLUSTERS* need a value prior
;;; to running into any errors, or else you've got twice the trouble.
;;;
;;; There are other thread-locals which are used by C code, and those are all
;;; defined in PER-THREAD-C-INTERFACE-SYMBOLS. Those other symbols must have
;;; initial values of either a fixnum or a static symbol (typically T or NIL).
;;;
;;; In contrast, DEFINE-THREAD-LOCAL allows more-or-less an arbitrary form,
;;; subject to being evaluable without need of values computed later of course.
;;;
;;; This macro represents a departure from our normal modus operandi with respect
;;; to getting the cross-compiler to be born knowing certain aspects of the target.
;;; Traditionally we process some defining macros (such as SB-XC:DEFMACRO) in both
;;; make-host passes. Doing so makes recompilation of pass 2 depend on all of pass 1.
;;; Here we avoid such depedency by imparting knowlege into genesis built up during
;;; make-host-2, namely the mapping of symbol to TLS index for symbols that are
;;; neither thread slots nor the blessed "C interface" symbols.
(defmacro define-thread-local
    (name &optional (initform '(make-unbound-marker) always-boundp)
                    docstring)
  `(locally
       (declare (notinline (setf info)))
     ;; This initial value expression is a mite confusing.
     ;; Most thread-locals that we use are of the :ALWAYS-BOUNDP kind, and since the
     ;; value is stuffed in the moment a thread is created, no value is ever needed here,
     ;; because this value form is evaluated exactly once (in cold-init) and never again.
     ;; But if not always bound, we have a problem: INIT-THREAD-LOCAL-STORAGE writes
     ;; an unbound marker, which makes this DEFVAR want to compute something.
     ;; But that "something" has to be the unbound marker.
     (defvar ,name ,(if always-boundp
                        `(error "~S had no value in cold-init" ',name)
                        initform)
             ,docstring)
     ;; :EXECUTE is for parallelized make-host-2 to see the compile-toplevel effect
     (eval-when (:compile-toplevel :execute)
       (%define-thread-local ',name ',initform ',always-boundp))
     (eval-when (:load-toplevel)
       #+sb-thread (setf (info :variable :wired-tls ',name) :always-thread-local)
       ,@(when always-boundp
           `((setf (info :variable :always-bound ',name) :always-bound))))))

(eval-when (:compile-toplevel :execute)
  (defvar sb-thread::*thread-local-specials* (list :not-final))
  (defun %define-thread-local (symbol initform always-boundp)
    (declare (notinline (setf info)))
    (let ((list sb-thread::*thread-local-specials*))
      (aver (eq (car list) :not-final))
      (let ((found (assq symbol (cdr list))))
        (if found
            (setf (cadr found) initform)
            (let* ((thread-struct (find 'sb-vm::thread sb-vm:*primitive-objects*
                                        :key #'sb-vm:primitive-object-name))
                   (n-fixed (+ (sb-vm:primitive-object-length thread-struct)
                               (count-if-not
                                (lambda (x)
                                  (find (car (ensure-list x))
                                        (sb-vm:primitive-object-slots thread-struct)
                                        :key #'sb-vm:slot-special))
                                sb-vm::per-thread-c-interface-symbols)))
                   (tls-slot (+ n-fixed (length (cdr list)))))
              (nconc list (list (list symbol initform)))
              (setf (info :variable :wired-tls symbol) (ash tls-slot sb-vm:word-shift))
              (when always-boundp
                (setf (info :variable :always-bound symbol) :always-bound))))))))

;;; a list of lists of currently active RESTART instances. maintained
;;; by RESTART-BIND.
(define-thread-local *restart-clusters* nil)

(!define-load-time-global sb-kernel::**initial-handler-clusters** '(nil))
;;; a list of handlers maintained by HANDLER-BIND
(define-thread-local *handler-clusters* sb-kernel::**initial-handler-clusters**)

(declaim (special sb-debug:*in-the-debugger*
                  sb-debug:*stack-top-hint*
                  *gc-inhibit* *gc-pending*
                  #+sb-thread *stop-for-gc-pending*
                  *posix-argv*))
;;; This constant is assigned by Genesis and never read by Lisp code.
;;; (To prove that it isn't used, it's not a toplevel form)
(let ()
  (defconstant sb-vm::+required-foreign-symbols+
    (symbol-value 'sb-vm::+required-foreign-symbols+)))
