+/* Are we leaving WITH-GCING and already running with interrupts
+ * enabled, without the protection of *GC-INHIBIT* T and there is gc
+ * (or stop for gc) pending, but we haven't trapped yet? */
+int
+in_leaving_without_gcing_race_p(struct thread *thread)
+{
+ return ((SymbolValue(IN_WITHOUT_GCING,thread) != NIL) &&
+ (SymbolValue(INTERRUPTS_ENABLED,thread) != NIL) &&
+ (SymbolValue(GC_INHIBIT,thread) == NIL) &&
+ ((SymbolValue(GC_PENDING,thread) != NIL)
+#if defined(LISP_FEATURE_SB_THREAD)
+ || (SymbolValue(STOP_FOR_GC_PENDING,thread) != NIL)
+#endif
+ ));
+}
+
+/* Check our baroque invariants. */
+void
+check_interrupt_context_or_lose(os_context_t *context)
+{
+#ifndef LISP_FEATURE_WIN32
+ struct thread *thread = arch_os_get_current_thread();
+ struct interrupt_data *data = thread->interrupt_data;
+ int interrupt_deferred_p = (data->pending_handler != 0);
+ int interrupt_pending = (SymbolValue(INTERRUPT_PENDING,thread) != NIL);
+ sigset_t *sigset = os_context_sigmask_addr(context);
+ /* On PPC pseudo_atomic_interrupted is cleared when coming out of
+ * handle_allocation_trap. */
+#if defined(LISP_FEATURE_GENCGC) && !defined(LISP_FEATURE_PPC)
+ int interrupts_enabled = (SymbolValue(INTERRUPTS_ENABLED,thread) != NIL);
+ int gc_inhibit = (SymbolValue(GC_INHIBIT,thread) != NIL);
+ int gc_pending = (SymbolValue(GC_PENDING,thread) == T);
+ int pseudo_atomic_interrupted = get_pseudo_atomic_interrupted(thread);
+ int in_race_p = in_leaving_without_gcing_race_p(thread);
+ /* In the time window between leaving the *INTERRUPTS-ENABLED* NIL
+ * section and trapping, a SIG_STOP_FOR_GC would see the next
+ * check fail, for this reason sig_stop_for_gc handler does not
+ * call this function. */
+ if (interrupt_deferred_p) {
+ if (!(!interrupts_enabled || pseudo_atomic_interrupted || in_race_p))
+ lose("Stray deferred interrupt.\n");
+ }
+ if (gc_pending)
+ if (!(pseudo_atomic_interrupted || gc_inhibit || in_race_p))
+ lose("GC_PENDING, but why?\n");
+#if defined(LISP_FEATURE_SB_THREAD)
+ {
+ int stop_for_gc_pending =
+ (SymbolValue(STOP_FOR_GC_PENDING,thread) != NIL);
+ if (stop_for_gc_pending)
+ if (!(pseudo_atomic_interrupted || gc_inhibit || in_race_p))
+ lose("STOP_FOR_GC_PENDING, but why?\n");
+ if (pseudo_atomic_interrupted)
+ if (!(gc_pending || stop_for_gc_pending || interrupt_deferred_p))
+ lose("pseudo_atomic_interrupted, but why?\n");
+ }
+#else
+ if (pseudo_atomic_interrupted)
+ if (!(gc_pending || interrupt_deferred_p))
+ lose("pseudo_atomic_interrupted, but why?\n");
+#endif
+#endif
+ if (interrupt_pending && !interrupt_deferred_p)
+ lose("INTERRUPT_PENDING but not pending handler.\n");
+ if ((data->gc_blocked_deferrables) && interrupt_pending)
+ lose("gc_blocked_deferrables and interrupt pending\n.");
+ if (data->gc_blocked_deferrables)
+ check_deferrables_blocked_or_lose(sigset);
+ if (interrupt_pending || interrupt_deferred_p ||
+ data->gc_blocked_deferrables)
+ check_deferrables_blocked_or_lose(sigset);
+ else {
+ check_deferrables_unblocked_or_lose(sigset);
+ /* If deferrables are unblocked then we are open to signals
+ * that run lisp code. */
+ check_gc_signals_unblocked_or_lose(sigset);
+ }
+#endif
+}