mirror of
git://git.code.sf.net/p/sbcl/sbcl
synced 2026-09-10 15:36:41 -04:00
Remove non-pauseless-threadstart code
Add a setup semaphore for win32 so the new code resembles the old. I don't know why it's needed, but tests fail without. The differences in safepoint logic for unix versus win32 might explain things.
This commit is contained in:
parent
deea157efe
commit
869f2acd62
|
|
@ -350,7 +350,7 @@ not supported."
|
|||
;; Dead threads aren't pruned from *ALL-THREADS* until the Pthread join.
|
||||
;; Do that now so that the forked process has only the main thread
|
||||
;; in *ALL-THREADS* and nothing in *JOINABLE-THREADS*.
|
||||
#+pauseless-threadstart (sb-thread::join-pthread-joinables #'identity)
|
||||
(sb-thread::%dispose-thread-structs)
|
||||
(when (let ((avltree sb-thread::*all-threads*))
|
||||
(or (sb-thread::avlnode-left avltree)
|
||||
(sb-thread::avlnode-right avltree)))
|
||||
|
|
|
|||
|
|
@ -293,7 +293,6 @@ the alien callback for that function with the given alien type."
|
|||
#+sb-thread
|
||||
(defun enter-foreign-callback (index return arguments)
|
||||
(let ((thread (init-thread-local-storage (make-foreign-thread))))
|
||||
#+pauseless-threadstart
|
||||
(dx-let ((startup-info (vector nil ; trampoline is n/a
|
||||
nil ; cell in *STARTING-THREADS* is n/a
|
||||
#'sb-alien::enter-alien-callback
|
||||
|
|
@ -302,7 +301,4 @@ the alien callback for that function with the given alien type."
|
|||
(copy-primitive-thread-fields thread)
|
||||
(setf (thread-startup-info thread) startup-info)
|
||||
(update-all-threads (thread-primitive-thread thread) thread)
|
||||
(run))
|
||||
#-pauseless-threadstart
|
||||
(dx-let ((args (list index return arguments)))
|
||||
(run thread nil #'sb-alien::enter-alien-callback args))))
|
||||
(run))))
|
||||
|
|
|
|||
|
|
@ -358,14 +358,12 @@ Examples:
|
|||
;;; *ALL-THREADS*, then it may be blocked on the mutex. Can we terminate it?
|
||||
;;; - During save, acquire the *MAKE-THREAD-LOCK* to get a consistent view of
|
||||
;;; which threads are starting and running.
|
||||
;;; - the :pauseless-threadstart code adds new threads to *ALL-THREADS*
|
||||
;;; before starting the posix thread. We could look there.
|
||||
;;; - new threads are added to *ALL-THREADS* before starting the posix thread.
|
||||
;;; We could look there.
|
||||
;;; The important thing is to avoid thinking that there is a thread running
|
||||
;;; when it doesn't have a posix thread ID yet.
|
||||
;;; - Does acquiring the *MAKE-THREAD-LOCK* before assigning *FINALIZER-THREAD*
|
||||
;;; help anything? Perhaps.
|
||||
;;; But first I'd like to entirely eliminate the non-pauseless-threadstart code
|
||||
;;; so that we don't have to reason about two totally different solutions.
|
||||
(defun finalizer-thread-stop ()
|
||||
#+sb-thread
|
||||
(let ((thread *finalizer-thread*))
|
||||
|
|
|
|||
|
|
@ -308,7 +308,7 @@ sufficiently motivated to do lengthy fixes."
|
|||
;; user-supplied code that might not even work in later steps of deinit.
|
||||
;; See also the comment at definition of THREAD-EPHEMERAL-P.
|
||||
(finalizer-thread-stop)
|
||||
#+pauseless-threadstart (sb-thread::join-pthread-joinables #'identity)
|
||||
(sb-thread::%dispose-thread-structs)
|
||||
(let ((threads (sb-thread:list-all-threads)))
|
||||
;; Despite calling FINALIZER-THREAD-STOP, an unlucky thread schedule
|
||||
;; courtesy of the OS might notionally start the finalizer thread but not
|
||||
|
|
|
|||
|
|
@ -279,7 +279,7 @@ created and old ones may exit at any time."
|
|||
|
||||
(sb-ext:define-load-time-global *initial-thread* nil)
|
||||
|
||||
;;; *JOINABLE-THREADS* is a list of THREAD instances used only if #+pauseless-threadstart
|
||||
;;; *JOINABLE-THREADS* is a list of THREAD instances.
|
||||
;;; I had attempted to construct the list using the thread's memory to create cons
|
||||
;;; cells but that turned out to be flawed- the cells must be freshly heap-allocated,
|
||||
;;; because ATOMIC-POP is vulnerable to the A/B/A problem if cells are reused.
|
||||
|
|
@ -1385,7 +1385,7 @@ on this semaphore, then N of them is woken up."
|
|||
;; This AVER failed when I messed up deletion from *STARTING-THREADS*.
|
||||
;; That in turn caused a failure in GC because a fixnum is not a legal value
|
||||
;; for the startup info when observed by GC.
|
||||
#+pauseless-threadstart (aver (not (memq thread *starting-threads*)))
|
||||
(aver (not (memq thread *starting-threads*)))
|
||||
;; Stash the primitive thread SAP for reuse, but clobber the PRIMITIVE-THREAD
|
||||
;; slot which makes ALIVE-P return NIL.
|
||||
;; A minor TODO: can this lock acquire/release be moved to where we actually
|
||||
|
|
@ -1404,7 +1404,9 @@ on this semaphore, then N of them is woken up."
|
|||
(when *session*
|
||||
(%delete-thread-from-session thread))
|
||||
(cond
|
||||
#+pauseless-threadstart ; If possible, logically remove from *ALL-THREADS*
|
||||
;; If possible, logically remove from *ALL-THREADS* by flipping a bit.
|
||||
;; Foreign threads remove themselves. They don't have an exit semaphore,
|
||||
;; so that's how we know which is which.
|
||||
(sem
|
||||
;; Tree pruning is the responsibility of thread creators, not dying threads.
|
||||
;; Creators have to manipulate the tree anyway, and they need access to the old
|
||||
|
|
@ -1414,8 +1416,7 @@ on this semaphore, then N of them is woken up."
|
|||
(aver (eql old 1)))
|
||||
(sb-ext:atomic-push thread *joinable-threads*))
|
||||
(t ; otherwise, physically remove from *ALL-THREADS*
|
||||
;; With #+pauseless-threadstart, this occurs for FOREIGN-THREAD because
|
||||
;; the memory allocation/deallocation is handled in C.
|
||||
;; The memory allocation/deallocation is handled in C.
|
||||
;; I would like to combine the recycle bin for foreign and lisp threads though.
|
||||
(delete-from-all-threads (get-lisp-obj-address c-thread))))
|
||||
(when sem
|
||||
|
|
@ -1631,11 +1632,14 @@ session."
|
|||
|
||||
;;;; The beef
|
||||
|
||||
#+pauseless-threadstart ; new way
|
||||
(progn
|
||||
;;; Return T if the thread was created
|
||||
(defun pthread-create (thread thread-sap)
|
||||
(defun os-thread-create (thread thread-sap)
|
||||
(aver (memq thread *starting-threads*))
|
||||
#+win32
|
||||
(/= 0 (alien-funcall (extern-alien "create_thread"
|
||||
(function unsigned system-area-pointer))
|
||||
thread-sap))
|
||||
#-win32
|
||||
(let ((attr (foreign-symbol-sap "new_lisp_thread_attr" t))
|
||||
(c-tramp
|
||||
(foreign-symbol-sap #+os-thread-stack "new_thread_trampoline_switch_stack"
|
||||
|
|
@ -1682,10 +1686,18 @@ session."
|
|||
(setf (thread-startup-info thread) 0)
|
||||
;; Clean up *ALL-THREADS*
|
||||
(delete-from-all-threads (sap-int c-thread))
|
||||
;; Free the pthread resources
|
||||
(alien-funcall (extern-alien "pthread_join" (function int unsigned unsigned))
|
||||
(thread-os-thread thread) 0) ; no result pointer
|
||||
(setf (thread-os-thread thread) 0)
|
||||
;; Release the OS and/or pthread resources
|
||||
#+win32
|
||||
(with-alien ((wait (function unsigned unsigned unsigned) :extern "WaitForSingleObject")
|
||||
(close (function int unsigned) :extern "CloseHandle"))
|
||||
(let ((os-thread (sap-ref-word c-thread
|
||||
(ash sb-vm::thread-os-thread-slot sb-vm::word-shift))))
|
||||
(alien-funcall wait os-thread #xffffffff)
|
||||
(alien-funcall close os-thread)))
|
||||
#-win32
|
||||
(with-alien ((join (function int unsigned unsigned) :extern "pthread_join"))
|
||||
(alien-funcall join (thread-os-thread thread) 0) ; no result pointer
|
||||
(setf (thread-os-thread thread) 0))
|
||||
(cond (dispose
|
||||
(free-thread-struct c-thread)
|
||||
nil)
|
||||
|
|
@ -1699,9 +1711,9 @@ session."
|
|||
;;; Heuristic decides when to stop trying to free. Passing in #'IDENTITY means that
|
||||
;;; all joinables should be processed. Passing in #'CDR or #'CDDR returns early if there
|
||||
;;; are not at least 1 or 2 threads respectively that could be joined.
|
||||
(export 'join-pthread-joinables)
|
||||
(defun join-pthread-joinables (heuristic)
|
||||
(loop (unless (funcall heuristic *joinable-threads*) (return))
|
||||
(export '%dispose-thread-structs)
|
||||
(defun %dispose-thread-structs (&key (retain 0))
|
||||
(loop (unless (nthcdr retain *joinable-threads*) (return))
|
||||
(let ((item (sb-ext:atomic-pop *joinable-threads*)))
|
||||
(if item (primitive-join item t) (return)))))
|
||||
|
||||
|
|
@ -1725,12 +1737,11 @@ session."
|
|||
;; If there is more than 1 more joinable, join all but 1.
|
||||
;; Two threads could both find > 1 thread to join, and both do
|
||||
;; a join, leaving 0 to join. That's ok.
|
||||
(join-pthread-joinables #'cdr)
|
||||
(%dispose-thread-structs :retain 1)
|
||||
(let ((thread-sap (alien-funcall (extern-alien "alloc_thread_struct"
|
||||
(function system-area-pointer
|
||||
system-area-pointer unsigned))
|
||||
(or reuse (int-sap 0))
|
||||
sb-vm:no-tls-value-marker-widetag)))
|
||||
system-area-pointer))
|
||||
(or reuse (int-sap 0)))))
|
||||
(when (and (not reuse) (/= (sap-int thread-sap) 0))
|
||||
;; these would have been done already if reusing the memory
|
||||
;; of a completed thread.
|
||||
|
|
@ -1770,25 +1781,12 @@ session."
|
|||
(sb-unix::unblock-deferrable-signals)))))
|
||||
;; notinline keeps array off the call stack by getting it out of the curent frame
|
||||
(declare (notinline unmask-signals))
|
||||
;; The semaphore works around some sort of race condition with sb-safepoint
|
||||
;; (which doesn't occur for linux + safepoint)
|
||||
#+win32 (awhen (thread-semaphore *current-thread*) (signal-semaphore it))
|
||||
;; Signals other than stop-for-GC are masked. The WITH/WITHOUT noise is
|
||||
;; pure cargo-cultism.
|
||||
(without-interrupts (with-local-interrupts ,@body))))))
|
||||
) ; end PROGN
|
||||
|
||||
#-pauseless-threadstart
|
||||
(defmacro thread-trampoline-defining-macro (&body body) ; OLD WAY
|
||||
`(defun run (thread setup-sem function arguments)
|
||||
(macrolet ((unmask-signals () '(sb-unix::unblock-deferrable-signals))
|
||||
(apply-real-function () '(apply function arguments)))
|
||||
(copy-primitive-thread-fields thread)
|
||||
(update-all-threads (thread-primitive-thread thread) thread)
|
||||
(sb-ext:atomic-push thread (session-new-enrollees *session*))
|
||||
(when setup-sem
|
||||
(signal-semaphore setup-sem)
|
||||
;; setup-sem was dx-allocated, set it to NIL so that the
|
||||
;; backtrace doesn't get confused
|
||||
(setf setup-sem nil))
|
||||
,@body)))
|
||||
|
||||
;;; All threads other than the initial thread start via this function.
|
||||
#+sb-thread
|
||||
|
|
@ -1888,7 +1886,6 @@ See also: RETURN-FROM-THREAD, ABORT-THREAD."
|
|||
|
||||
;;; This is the faster variant of RUN-THREAD that does not wait for the new
|
||||
;;; thread to start executing before returning.
|
||||
#+pauseless-threadstart
|
||||
(defun start-thread (thread function arguments)
|
||||
(let* ((trampoline
|
||||
(lambda (arg)
|
||||
|
|
@ -1919,6 +1916,11 @@ See also: RETURN-FROM-THREAD, ABORT-THREAD."
|
|||
(run)))
|
||||
(saved-sigmask (make-array (* sb-unix::sizeof-sigset_t sb-vm:n-byte-bits)
|
||||
:element-type 'bit :initial-element 0))
|
||||
;; Due to a probable bug in the safepoint state machine affecting win32
|
||||
;; (but not linux), we have to prevent this function from returning
|
||||
;; until the created thread adds itself to 'all_threads' (the C one).
|
||||
;; The exit semaphore can be used for this purpose.
|
||||
#+win32 (semaphore (thread-semaphore thread))
|
||||
(created))
|
||||
(declare (truly-dynamic-extent saved-sigmask))
|
||||
;; Block deferrables to ensure that the new thread is unaffected by signals
|
||||
|
|
@ -1966,7 +1968,7 @@ See also: RETURN-FROM-THREAD, ABORT-THREAD."
|
|||
(setf (sap-ref-lispobj thread-sap (ash sb-vm::thread-lisp-thread-slot
|
||||
sb-vm:word-shift))
|
||||
thread)
|
||||
(setq created (pthread-create thread thread-sap))
|
||||
(setq created (os-thread-create thread thread-sap))
|
||||
(cond (created
|
||||
;; Still holding the MAKE-THREAD-LOCK, expose the thread in *all-threads*.
|
||||
;; In this manner, anyone who acquires the MAKE-THREAD-LOCK can be sure that
|
||||
|
|
@ -1983,48 +1985,9 @@ See also: RETURN-FROM-THREAD, ABORT-THREAD."
|
|||
(free-thread-struct thread-sap)))
|
||||
(with-pinned-objects (saved-sigmask)
|
||||
(pthread-sigmask sb-unix::SIG_SETMASK saved-sigmask nil))
|
||||
#+win32 (when created (wait-on-semaphore semaphore))
|
||||
(if created thread (error "Could not create new OS thread."))))
|
||||
|
||||
#+(and sb-thread (not pauseless-threadstart))
|
||||
(defun start-thread (thread function arguments)
|
||||
(let* ((setup-sem (make-semaphore :name "Thread setup semaphore"))
|
||||
#+(or win32 darwin freebsd)
|
||||
(fp-modes (dpb 0 sb-vm:float-sticky-bits ;; clear accrued bits
|
||||
(sb-vm:floating-point-modes))))
|
||||
(declare (dynamic-extent setup-sem))
|
||||
(dx-flet ((start-routine ()
|
||||
;; Inherit parent thread's FP modes
|
||||
#+(or win32 darwin)
|
||||
(setf (sb-vm:floating-point-modes) fp-modes)
|
||||
;; As it is, this lambda must not cons until we are
|
||||
;; ready to run GC. Be careful.
|
||||
(init-thread-local-storage thread)
|
||||
;; I literally don't know what these WITH/WITHOUT wrappings are for,
|
||||
;; but tests fail when removed.
|
||||
;; It's the body of CALL-WITH-MUTEX omitting the grab and release.
|
||||
(without-interrupts
|
||||
(with-local-interrupts
|
||||
(run thread setup-sem function arguments)))))
|
||||
;; Holding mutexes or waiting on sempahores inside WITHOUT-GCING will lock up
|
||||
(aver (not *gc-inhibit*))
|
||||
;; Keep INITIAL-FUNCTION in the dynamic extent until the child
|
||||
;; thread is initialized properly. Wrap the whole thing in
|
||||
;; WITHOUT-INTERRUPTS (via WITH-SYSTEM-MUTEX) because we pass
|
||||
;; INITIAL-FUNCTION to another thread.
|
||||
;; (Does WITHOUT-INTERRUPTS really matter now that it's DXed?)
|
||||
(with-system-mutex (*make-thread-lock*)
|
||||
(with-alien ((create-thread (function unsigned unsigned unsigned)
|
||||
:extern "create_thread"))
|
||||
(with-pinned-objects (thread #'start-routine)
|
||||
(if (eql (alien-funcall create-thread
|
||||
(- (get-lisp-obj-address thread)
|
||||
sb-vm:instance-pointer-lowtag)
|
||||
(get-lisp-obj-address #'start-routine))
|
||||
0)
|
||||
(setq thread nil)
|
||||
(wait-on-semaphore setup-sem)))))))
|
||||
(or thread (error "Could not create a new thread.")))
|
||||
|
||||
(defun join-thread (thread &key (default nil defaultp) timeout)
|
||||
"Suspend current thread until THREAD exits. Return the result values
|
||||
of the thread function.
|
||||
|
|
@ -2058,9 +2021,8 @@ subject to change."
|
|||
|
||||
;; First, free up the pthread resources of any thread(s), not necessarily
|
||||
;; one we're tryinng to join.
|
||||
#+pauseless-threadstart
|
||||
(when (cddr *joinable-threads*) ; if strictly > 2 are joinable,
|
||||
(without-interrupts (join-pthread-joinables #'cddr)))
|
||||
(without-interrupts (%dispose-thread-structs :retain 2)))
|
||||
;; No result semaphore indicates that there's nothing to wait for-
|
||||
;; the thread is either a foreign-thread, or finished running.
|
||||
(let* ((semaphore (thread-semaphore thread))
|
||||
|
|
|
|||
|
|
@ -246,8 +246,6 @@
|
|||
;; Win32 conditionally adds :sb-futex in grovel-features.sh
|
||||
(when (featurep '(:and :sb-thread :linux) target-feature-list)
|
||||
(pushnew :sb-futex target-feature-list))
|
||||
(when (featurep '(:and :sb-thread (:not :win32)) target-feature-list)
|
||||
(push :pauseless-threadstart target-feature-list))
|
||||
(when (featurep '(:and :sb-thread (:or :darwin :openbsd)) target-feature-list)
|
||||
(push :os-thread-stack target-feature-list))
|
||||
(when (and (member :x86 target-feature-list)
|
||||
|
|
@ -294,8 +292,6 @@
|
|||
(let ((feature-compatibility-tests
|
||||
'(("(and sb-thread (not gencgc))"
|
||||
":SB-THREAD requires :GENCGC")
|
||||
("(and pauseless-threadstart (not sb-thread))"
|
||||
":PAUSELESS-THREADSTART requires :SB-THREAD")
|
||||
("(and sb-safepoint (not sb-thread))" ":SB-SAFEPOINT requires :SB-THREAD")
|
||||
("(and sb-thruption (not sb-safepoint))" ":SB-THRUPTION requires :SB-SAFEPOINT")
|
||||
("(and unix sb-safepoint-strictly)"
|
||||
|
|
|
|||
|
|
@ -213,15 +213,15 @@ static int get_nonzero_tid()
|
|||
return tid;
|
||||
}
|
||||
|
||||
// Only a single 'attributes' object is used if #+pauseless-threadstart.
|
||||
// This is ok because creation is synchronized by *MAKE-THREAD-LOCK*.
|
||||
// Because creation is synchronized by *MAKE-THREAD-LOCK*
|
||||
// we need only a single 'attributes' object.
|
||||
#if defined LISP_FEATURE_SB_THREAD && !defined LISP_FEATURE_WIN32
|
||||
pthread_attr_t new_lisp_thread_attr;
|
||||
#define init_shared_attr_object() (pthread_attr_init(&new_lisp_thread_attr)==0)
|
||||
#else
|
||||
#define init_shared_attr_object() (1)
|
||||
#endif
|
||||
struct thread *alloc_thread_struct(void*,lispobj);
|
||||
struct thread *alloc_thread_struct(void*);
|
||||
|
||||
#ifdef LISP_FEATURE_WIN32
|
||||
#define ASSOCIATE_OS_THREAD(thread) \
|
||||
|
|
@ -260,7 +260,7 @@ void create_main_lisp_thread(lispobj function) {
|
|||
InitializeCriticalSection(&recyclebin_lock);
|
||||
InitializeCriticalSection(&in_gc_lock);
|
||||
#endif
|
||||
struct thread *th = alloc_thread_struct(0, NO_TLS_VALUE_MARKER_WIDETAG);
|
||||
struct thread *th = alloc_thread_struct(0);
|
||||
if (!th || arch_os_thread_init(th)==0 || !init_shared_attr_object())
|
||||
lose("can't create initial thread");
|
||||
#if defined LISP_FEATURE_SB_THREAD && !defined LISP_FEATURE_GCC_TLS && !defined LISP_FEATURE_WIN32
|
||||
|
|
@ -433,7 +433,6 @@ unregister_thread(struct thread *th,
|
|||
#endif
|
||||
|
||||
#if defined(LISP_FEATURE_WIN32)
|
||||
CloseHandle((HANDLE)th->os_thread);
|
||||
int i;
|
||||
for (i = 0; i<
|
||||
(int) (sizeof(th->private_events.events)/
|
||||
|
|
@ -468,7 +467,6 @@ void* new_thread_trampoline(void* arg)
|
|||
struct thread* th = arg;
|
||||
ASSOCIATE_OS_THREAD(th);
|
||||
|
||||
#ifdef LISP_FEATURE_PAUSELESS_THREADSTART
|
||||
#ifdef LISP_FEATURE_SB_SAFEPOINT
|
||||
init_thread_data scribble;
|
||||
// This "scribble" thing is really quite pointless because the original sigset_t
|
||||
|
|
@ -555,22 +553,6 @@ void* new_thread_trampoline(void* arg)
|
|||
// Close the GC region and unlink from all_threads
|
||||
unregister_thread(th, SCRIBBLE);
|
||||
|
||||
#else // !PAUSELESS_THREADSTART
|
||||
|
||||
th->os_kernel_tid = get_nonzero_tid();
|
||||
init_thread_data scribble;
|
||||
|
||||
lispobj function = th->no_tls_value_marker;
|
||||
th->no_tls_value_marker = NO_TLS_VALUE_MARKER_WIDETAG;
|
||||
init_new_thread(th, &scribble,
|
||||
GUARD_CONTROL_STACK|GUARD_BINDING_STACK|GUARD_ALIEN_STACK);
|
||||
funcall0(function);
|
||||
unregister_thread(th, &scribble);
|
||||
#ifdef LISP_FEATURE_WIN32
|
||||
free_thread_struct(th); // no recycling of 'struct thread'
|
||||
#endif
|
||||
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -634,8 +616,7 @@ static void attach_os_thread(init_thread_data *scribble)
|
|||
block_deferrable_signals(&scribble->oldset);
|
||||
#endif
|
||||
void* recycled_memory = get_recyclebin_item();
|
||||
struct thread *th = alloc_thread_struct(recycled_memory,
|
||||
NO_TLS_VALUE_MARKER_WIDETAG);
|
||||
struct thread *th = alloc_thread_struct(recycled_memory);
|
||||
|
||||
#ifndef LISP_FEATURE_SB_SAFEPOINT
|
||||
/* new-lisp-thread-trampoline doesn't like when the GC signal is blocked */
|
||||
|
|
@ -700,6 +681,9 @@ static void detach_os_thread(init_thread_data *scribble)
|
|||
{
|
||||
struct thread *th = arch_os_get_current_thread();
|
||||
|
||||
#ifdef LISP_FEATURE_WIN32
|
||||
CloseHandle((HANDLE)th->os_thread);
|
||||
#endif
|
||||
unregister_thread(th, scribble);
|
||||
|
||||
/* We have to clear a STOP_FOR_GC signal if pending. Consider:
|
||||
|
|
@ -835,7 +819,7 @@ callback_wrapper_trampoline(
|
|||
*/
|
||||
|
||||
struct thread *
|
||||
alloc_thread_struct(void* spaces, lispobj start_routine) {
|
||||
alloc_thread_struct(void* spaces) {
|
||||
#if defined(LISP_FEATURE_SB_THREAD) || defined(LISP_FEATURE_WIN32)
|
||||
unsigned int i;
|
||||
#endif
|
||||
|
|
@ -882,6 +866,7 @@ alloc_thread_struct(void* spaces, lispobj start_routine) {
|
|||
th->lisp_thread = 0; // force it to be always-thread-local, of course
|
||||
th->tls_size = dynamic_values_bytes;
|
||||
#endif
|
||||
|
||||
#if defined LISP_FEATURE_X86_64 && defined LISP_FEATURE_LINUX
|
||||
tls[THREAD_MSAN_XOR_CONSTANT_SLOT] = 0x500000000000;
|
||||
#endif
|
||||
|
|
@ -950,6 +935,7 @@ alloc_thread_struct(void* spaces, lispobj start_routine) {
|
|||
// to the end of the safepoint page. The slot should probably be named
|
||||
// csp_around_foreign_call_ptr, but why even bother storing a constant at all?
|
||||
th->csp_around_foreign_call = (lispobj *)th - 1;
|
||||
*th->csp_around_foreign_call = 0;
|
||||
#endif
|
||||
|
||||
struct nonpointer_thread_data *nonpointer_data = nonpointer_data(th);
|
||||
|
|
@ -1031,7 +1017,6 @@ alloc_thread_struct(void* spaces, lispobj start_routine) {
|
|||
# define INITIALIZE_TLS(sym,val) SYMBOL(sym)->value = val
|
||||
#endif
|
||||
#include "genesis/thread-init.inc"
|
||||
th->no_tls_value_marker = start_routine;
|
||||
|
||||
#if defined(LISP_FEATURE_WIN32)
|
||||
for (i = 0; i<sizeof(th->private_events.events)/
|
||||
|
|
@ -1045,54 +1030,20 @@ alloc_thread_struct(void* spaces, lispobj start_routine) {
|
|||
}
|
||||
#ifdef LISP_FEATURE_SB_THREAD
|
||||
#ifdef LISP_FEATURE_WIN32
|
||||
/* Allocate a thread structure, call CreateThread(),
|
||||
* and return 1 for success, 0 for failure */
|
||||
uword_t create_thread(struct thread_instance* instance, lispobj start_routine)
|
||||
uword_t create_thread(struct thread* th)
|
||||
{
|
||||
struct thread *th;
|
||||
|
||||
/* Must defend against async unwinds. */
|
||||
if (read_TLS(INTERRUPTS_ENABLED, arch_os_get_current_thread()) != NIL)
|
||||
lose("create_thread is not safe when interrupts are enabled.");
|
||||
|
||||
/* Assuming that a fresh thread struct has no lisp objects in it,
|
||||
* linking it to all_threads can be left to the thread itself
|
||||
* without fear of gc lossage. 'start_routine' violates this
|
||||
* assumption and must stay pinned until the child starts up. */
|
||||
th = alloc_thread_struct(0, start_routine);
|
||||
if (!th) return 0;
|
||||
|
||||
/* The new thread inherits the restrictive signal mask set here,
|
||||
* and enables signals again when it is set up properly. */
|
||||
sigset_t oldset;
|
||||
|
||||
/* Blocking deferrable signals is enough, no need to block
|
||||
* SIG_STOP_FOR_GC because the child process is not linked onto
|
||||
* all_threads until it's ready. */
|
||||
block_deferrable_signals(&oldset);
|
||||
unsigned int tid;
|
||||
// Theoretically you should tell the new thread a signal mask to restore
|
||||
// after it finishes any uninterruptable setup code, but the way this worked
|
||||
// on windows is that we passed the mask of blocked signals in the parent
|
||||
// *after* blocking deferrables. It's immaterial what mask is passed
|
||||
// because the thread will unblock all deferrables,
|
||||
// and we don't really have posix signals anyway.
|
||||
struct nonpointer_thread_data *data = nonpointer_data(th);
|
||||
data->blocked_signal_set = deferrable_sigset;
|
||||
data->pending_signal_set = 0;
|
||||
// It's somewhat customary in the win32 API to start threads as suspended.
|
||||
th->os_thread =
|
||||
_beginthreadex(NULL, thread_control_stack_size, new_thread_trampoline, th,
|
||||
CREATE_SUSPENDED, &tid);
|
||||
boolean success = th->os_thread != 0;
|
||||
if (success) {
|
||||
instance->primitive_thread = (lispobj)th;
|
||||
th->os_kernel_tid = tid;
|
||||
ResumeThread((HANDLE)th->os_thread);
|
||||
} else {
|
||||
free_thread_struct(th);
|
||||
}
|
||||
thread_sigmask(SIG_SETMASK,&oldset,0);
|
||||
return success;
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -51,8 +51,6 @@
|
|||
(let ((born 0)
|
||||
(running 0)
|
||||
(died 0))
|
||||
#-pauseless-thread-start (setq running (sb-thread::avl-count tree))
|
||||
#+pauseless-thread-start
|
||||
(sb-int:dx-flet ((mapfun (node)
|
||||
(ecase (sb-thread::thread-%visible (sb-thread::avlnode-data node))
|
||||
(0 (incf born)) ; "can't happen" ?
|
||||
|
|
|
|||
|
|
@ -1,23 +1,23 @@
|
|||
(in-package "SB-THREAD")
|
||||
|
||||
;;; Test out-of-memory (or something) that goes wrong in pthread_create
|
||||
#+pauseless-threadstart ; no SB-THREAD::PTHREAD-CREATE symbol if not
|
||||
#+sb-thread ; missing symbols otherwise
|
||||
(test-util:with-test (:name :failed-thread-creation)
|
||||
(let ((encapsulation
|
||||
(compile nil
|
||||
'(lambda (realfun thread stack-base)
|
||||
'(lambda (realfun thread sap)
|
||||
(if (string= (sb-thread:thread-name thread) "finalizer")
|
||||
(funcall realfun thread stack-base)
|
||||
(funcall realfun thread sap)
|
||||
nil))))
|
||||
(success))
|
||||
(assert (null sb-thread::*starting-threads*))
|
||||
(unwind-protect
|
||||
(progn (sb-int:encapsulate 'sb-thread::pthread-create 'test encapsulation)
|
||||
(progn (sb-int:encapsulate 'sb-thread::os-thread-create 'test encapsulation)
|
||||
(handler-case (sb-thread:make-thread #'list :name "thisfails")
|
||||
(error (e)
|
||||
(setq success (string= (write-to-string e)
|
||||
"Could not create new OS thread.")))))
|
||||
(sb-int:unencapsulate 'sb-thread::pthread-create 'test))
|
||||
(sb-int:unencapsulate 'sb-thread::os-thread-create 'test))
|
||||
(assert (equal sb-thread::*starting-threads* nil))
|
||||
(assert (equal (sb-thread::avltree-list sb-thread::*all-threads*)
|
||||
(list sb-thread::*initial-thread*)))))
|
||||
|
|
@ -125,7 +125,8 @@
|
|||
;;; In fact, assert something stronger: there are no young objects
|
||||
;;; between the current SP and end of stack.
|
||||
(test-util:with-test (:name :expected-gc-roots
|
||||
:skipped-on (or :interpreter (not :pauseless-threadstart)))
|
||||
:skipped-on :interpreter
|
||||
:fails-on :win32)
|
||||
(let ((list (tryit :print nil)))
|
||||
;; should be not many things pointed to by the stack
|
||||
(assert (< (length list) #+x86 38 ; more junk, I don't know why
|
||||
|
|
@ -139,7 +140,7 @@
|
|||
|
||||
;; lp#1595699
|
||||
(test-util:with-test (:name :start-thread-in-without-gcing
|
||||
:skipped-on (not :pauseless-threadstart))
|
||||
:skipped-on (not :sb-thread))
|
||||
(assert (eq (sb-thread:join-thread
|
||||
(sb-sys:without-gcing
|
||||
(sb-thread:make-thread (lambda () 'hi))))
|
||||
|
|
|
|||
Loading…
Reference in a new issue