From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!.POSTED!not-for-mail From: Gemini Lasswell Newsgroups: gmane.emacs.bugs Subject: bug#33014: 26.1.50; 27.0.50; Fatal error after re-evaluating a thread's function Date: Wed, 10 Oct 2018 22:30:29 -0700 Message-ID: <87d0sh9hje.fsf@runbox.com> NNTP-Posting-Host: blaine.gmane.org Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" X-Trace: blaine.gmane.org 1539235816 19737 195.159.176.226 (11 Oct 2018 05:30:16 GMT) X-Complaints-To: usenet@blaine.gmane.org NNTP-Posting-Date: Thu, 11 Oct 2018 05:30:16 +0000 (UTC) To: 33014@debbugs.gnu.org Original-X-From: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane.org@gnu.org Thu Oct 11 07:30:11 2018 Return-path: Envelope-to: geb-bug-gnu-emacs@m.gmane.org Original-Received: from lists.gnu.org ([208.118.235.17]) by blaine.gmane.org with esmtp (Exim 4.84_2) (envelope-from ) id 1gATY0-0004z4-Fh for geb-bug-gnu-emacs@m.gmane.org; Thu, 11 Oct 2018 07:30:09 +0200 Original-Received: from localhost ([::1]:60641 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1gATa6-0006Q6-Uh for geb-bug-gnu-emacs@m.gmane.org; Thu, 11 Oct 2018 01:32:18 -0400 Original-Received: from eggs.gnu.org ([2001:4830:134:3::10]:57286) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1gATZu-0006Pk-AP for bug-gnu-emacs@gnu.org; Thu, 11 Oct 2018 01:32:08 -0400 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1gATZq-0008R0-QO for bug-gnu-emacs@gnu.org; Thu, 11 Oct 2018 01:32:06 -0400 Original-Received: from debbugs.gnu.org ([208.118.235.43]:39812) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1gATZq-0008M2-2m for bug-gnu-emacs@gnu.org; Thu, 11 Oct 2018 01:32:02 -0400 Original-Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from ) id 1gATZp-0003Bp-SH for bug-gnu-emacs@gnu.org; Thu, 11 Oct 2018 01:32:01 -0400 X-Loop: help-debbugs@gnu.org Resent-From: Gemini Lasswell Original-Sender: "Debbugs-submit" Resent-CC: bug-gnu-emacs@gnu.org Resent-Date: Thu, 11 Oct 2018 05:32:01 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: report 33014 X-GNU-PR-Package: emacs X-GNU-PR-Keywords: X-Debbugs-Original-To: bug-gnu-emacs@gnu.org Original-Received: via spool by submit@debbugs.gnu.org id=B.153923587312204 (code B ref -1); Thu, 11 Oct 2018 05:32:01 +0000 Original-Received: (at submit) by debbugs.gnu.org; 11 Oct 2018 05:31:13 +0000 Original-Received: from localhost ([127.0.0.1]:44070 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1gATZ2-0003Al-AY for submit@debbugs.gnu.org; Thu, 11 Oct 2018 01:31:13 -0400 Original-Received: from eggs.gnu.org ([208.118.235.92]:47532) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1gATYz-0003AY-5v for submit@debbugs.gnu.org; Thu, 11 Oct 2018 01:31:10 -0400 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1gATYr-0007N3-NZ for submit@debbugs.gnu.org; Thu, 11 Oct 2018 01:31:03 -0400 Original-Received: from lists.gnu.org ([2001:4830:134:3::11]:36465) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1gATYr-0007Mv-IA for submit@debbugs.gnu.org; Thu, 11 Oct 2018 01:31:01 -0400 Original-Received: from eggs.gnu.org ([2001:4830:134:3::10]:57133) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1gATYp-0006N4-1W for bug-gnu-emacs@gnu.org; Thu, 11 Oct 2018 01:31:01 -0400 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1gATYl-0007LK-Pf for bug-gnu-emacs@gnu.org; Thu, 11 Oct 2018 01:30:58 -0400 Original-Received: from aibo.runbox.com ([91.220.196.211]:57288) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1gATYl-0007Ke-8D for bug-gnu-emacs@gnu.org; Thu, 11 Oct 2018 01:30:55 -0400 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=runbox.com; s=rbselector1; h=Content-Type:MIME-Version:Message-ID:Date:Subject:To:From; bh=+pY50AqAwfWR0NRK4Eg5QmTQjDESqT9kfoSRHWbhs7E=; b=l90xjCWG7ELUabwP+jaQHFQq4 v4823XjyR7GKB5N8tlUhL1QbsZrC/fO9pa8Je+QRWp9mLpCRXqOD/I7b79mz+Lm0dQZ+NtCboCOrG FqZmw7+sCSgGzBzTgR/yDwIvHu4VijRyJ59g9Nu56rzIwnCeeq3F4i5Te8H5xH2oAOCd0mlrQTN+B DCmvakJk0wOgxUTJ4f8lBcitaDsuzq+kJUE+DP80RTAyF72PnKEmBxPYZTnb+fWfM6xdkEZFVhfAc No3Ubg9md0OKqISNf5afjSu65rHqyj+yCptAc3fx2NYIlRq05ZbL9iEcj2Fl3dL/blr2DhdzVPN9f x3ixPaZug==; Original-Received: from [10.9.9.210] (helo=mailfront10.runbox.com) by mailtransmit02.runbox with esmtp (Exim 4.86_2) (envelope-from ) id 1gATYi-0007lT-Ug for bug-gnu-emacs@gnu.org; Thu, 11 Oct 2018 07:30:53 +0200 Original-Received: by mailfront10.runbox.com with esmtpsa (uid:179284 ) (TLS1.2:RSA_AES_256_CBC_SHA1:256) (Exim 4.82) id 1gATYO-000439-1I for bug-gnu-emacs@gnu.org; Thu, 11 Oct 2018 07:30:34 +0200 X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.6.x X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 208.118.235.43 X-BeenThere: bug-gnu-emacs@gnu.org List-Id: "Bug reports for GNU Emacs, the Swiss army knife of text editors" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane.org@gnu.org Original-Sender: "bug-gnu-emacs" Xref: news.gmane.org gmane.emacs.bugs:151093 Archived-At: --=-=-= Content-Type: text/plain When I run some byte-compiled code which creates some threads, and then, while a thread is blocked, interactively evaluate the function which was used to create that thread, Emacs has a fatal error or segmentation fault when the thread becomes unblocked. To reproduce: Build Emacs from master with this patch, in which I've pasted some excerpts from my current project onto the end of lisp/thread.el. It's going to be like ERT but designed to run benchmarks instead of tests, but right now all it does is to create a buffer and three threads, set the threads up to communicate with each other, log their progress to *Messages*, and update the buffer when they finish: --=-=-= Content-Type: text/plain Content-Disposition: attachment; filename=0001-Reproduce-Bswitch-segfault.patch >From 4b9e3ccb2376ea532051d5ff7d2d308df56576c9 Mon Sep 17 00:00:00 2001 From: Gemini Lasswell Date: Tue, 9 Oct 2018 09:27:54 -0700 Subject: [PATCH] Reproduce Bswitch segfault --- lisp/thread.el | 454 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 454 insertions(+) diff --git a/lisp/thread.el b/lisp/thread.el index 7974a2603c..9c4774e961 100644 --- a/lisp/thread.el +++ b/lisp/thread.el @@ -196,5 +196,459 @@ thread-list--name (and (eq thread main-thread) "Main") (prin1-to-string thread))) +;;; Thread-safe messages + +(cl-defstruct + (thread--message + (:constructor + thread-make-message (&optional name + &aux + (mutex (make-mutex name)) + (condition + (make-condition-variable mutex name))))) + name mutex value condition) + +(defun thread-message-available-p (message) + "Return the value of MESSAGE." + (thread--message-value message)) + +(defun thread-message-send (message value) + "Set the VALUE of MESSAGE, and awaken all threads waiting for it." + (with-mutex (thread--message-mutex message) + (setf (thread--message-value message) value) + (condition-notify (thread--message-condition message) t))) + +(defun thread-message-cancel (message) + "Cancel MESSAGE by setting its value to nil." + (with-mutex (thread--message-mutex message) + (setf (thread--message-value message) nil))) + +(defun thread-message-wait (message &optional cancel) + "If MESSAGE's value is nil, block until it is set to something else. +Return the value of MESSAGE. If CANCEL is non-nil, clear MESSAGE +by setting its value to nil. If multiple threads are waiting on +the same message, and all pass a non-nil CANCEL, then only one +thread will unblock and receive the message's value, and the +others will continue to block." + (with-mutex (thread--message-mutex message) + (while (not (thread--message-value message)) + (condition-wait (thread--message-condition message))) + (let ((value (thread--message-value message))) + (when cancel + (setf (thread--message-value message) nil)) + value))) + +;;; Thread-safe queues + +(cl-defstruct (thread--queue + (:constructor + thread-make-queue (&optional + size-limit + type + &aux + (fifo (eq type 'fifo)) + (limit (when (natnump size-limit) size-limit)) + (mutex (make-mutex)) + (not-full (make-condition-variable mutex)) + (not-empty (make-condition-variable mutex))))) + fifo + limit + items + mutex + not-full + not-empty) + +(defun thread-queue-empty-p (queue) + "Return non-nil if QUEUE is empty. +There is no guarantee that QUEUE will contain the same number of +items the next time you access it." + (with-mutex (thread--queue-mutex queue) + (null (thread--queue-items queue)))) + +(defun thread-queue-full-p (queue) + "Return non-nil if QUEUE is full. +There is no guarantee that QUEUE will contain the same number of +items the next time you access it." + (when (thread--queue-limit queue) + (with-mutex (thread--queue-mutex queue) + (= (length (thread--queue-items queue)) (thread--queue-limit queue))))) + +(defun thread-queue-length (queue) + "Return the number of items in QUEUE. +There is no guarantee that QUEUE will contain the same number of +items the next time you access it." + (with-mutex (thread--queue-mutex queue) + (length (thread--queue-items queue)))) + +(defun thread-queue-remove-all (queue) + "Discard any items in QUEUE." + (with-mutex (thread--queue-mutex queue) + (setf (thread--queue-items queue) nil) + (condition-notify (thread--queue-not-full queue)))) + +(defun thread-queue-put (item queue) + "Put ITEM into QUEUE. +If QUEUE was created with a size limit, and already contains that many items, +block until one is removed." + (with-mutex (thread--queue-mutex queue) + (while (and (thread--queue-limit queue) + (= (length (thread--queue-items queue)) (thread--queue-limit queue))) + (condition-wait (thread--queue-not-full queue))) + (if (thread--queue-fifo queue) + (setf (thread--queue-items queue) + (nconc (thread--queue-items queue) (list item))) + (push item (thread--queue-items queue))) + (condition-notify (thread--queue-not-empty queue)))) + +(defun thread-queue-get (queue) + "Remove an item from QUEUE and return it. +If there are no items in QUEUE, block until one is added." + (with-mutex (thread--queue-mutex queue) + (while (null (thread--queue-items queue)) + (condition-wait (thread--queue-not-empty queue))) + (let ((item (pop (thread--queue-items queue)))) + (condition-notify (thread--queue-not-full queue)) + item))) + +;;; Bswitch bug + +(require 'seq) + +(defvar erb--job (thread-make-message) + "This contains all the information needed about what benchmark job to run. +It is created by `erb-run-start' and cleared when the benchmark job is +finished by `erb-run--benchmarker'.") + +(defvar erb--status nil + "The state of the benchmark runner. +Possible values are `idle', `building', `benchmarking' and +`done'. Maintained by `erb-run--monitor' and used to update the +`erb-run' buffer.") + +(defvar erb--status-commits nil + "The commits being processed by the benchmark runner.") + +(defvar erb--status-waiting-to-build nil + "The list of commits that have not yet started building. +Maintained by `erb-run--monitor' and used to update the +`erb-run' buffer.") + +(defvar erb--status-building nil + "The list of commits that are currently being built. +Maintained by `erb-run--monitor' and used to update the +`erb-run' buffer.") + +(defvar erb--status-built nil + "The list of commits that have been built. +Maintained by `erb-run--monitor' and used to update the +`erb-run' buffer.") + +(defvar erb--status-failed-builds nil + "The list of commits that failed to build. +Maintained by `erb-run--monitor' and used to update the +`erb-run' buffer.") + +(defvar erb--status-waiting-to-benchmark nil + "The commits that are built and waiting to be benchmarked. +Maintained by `erb-run--monitor' and used to update the +`erb-run' buffer.") + +(defvar erb--status-benchmarking nil + "The commit that is currently being benchmarked. +Maintained by `erb-run--monitor' and used to update the +`erb-run' buffer.") + +(defvar erb--status-benchmark-failures nil + "The commits that had task failures during benchmarking. +Maintained by `erb-run--monitor' and used to update the +`erb-run' buffer.") + +(defvar erb--status-finished nil + "The commits that are finished benchmarking. +Maintained by `erb-run--monitor' and used to update the +`erb-run' buffer.") + +(defvar erb-run--cancel-request (thread-make-message) + "Signal the user's request that a benchmark job be stopped. +Cleared when the benchmark job is cleaned up, by +`erb-run-benchmarker'.") + + +;;; ERB Run Benchmarks mode + +(defvar erb-run-refresh-seconds 0.2 + "Delay between updates of `erb-run' buffers.") + +;; Options settable in the erb-run-mode buffer. +(defvar-local erb-run--commit-range '("commitA" "commitB" "commitC")) +(defvar-local erb-run--count-to-select nil) + +(defvar erb-run-mode-map + (let ((map (copy-keymap special-mode-map))) + (set-keymap-parent map button-buffer-map) + (define-key map "n" 'next-line) + (define-key map "p" 'previous-line) + (define-key map "s" 'erb-run-start) + (define-key map "c" 'erb-run-cancel) + (define-key map [follow-link] 'mouse-face) + (define-key map [mouse-2] 'mouse-select-window) + + map) + "Local keymap for `erb-run-mode' buffers.") + +(define-derived-mode erb-run-mode special-mode "ERB-run" + "Mode for configuring and running benchmarks. +\\ +ERB is documented in info node `(erb)'." + :group 'erb-mode + (setq-local revert-buffer-function #'erb-run-revert-buffer)) + +;; TODO make only one buffer? +(defun erb-run-generate-new-buffer () + (let* ((name (format "*ERB-run: %s*" (file-name-nondirectory + (directory-file-name default-directory)))) + (buffer (generate-new-buffer name))) + (with-current-buffer buffer + (erb-run-mode) + (run-at-time erb-run-refresh-seconds nil + #'erb-run--timer-func buffer) + (add-to-list 'uniquify-list-buffers-directory-modes 'erb-run-mode)) + buffer)) + +;; TODO autoload is just for now +;;;###autoload +(defun erb-summary-run () + "Switch to or create an `erb-run-mode' buffer for running benchmarks." + (interactive) + (let* ((dir default-directory) + (buffer (or (seq-find (lambda (buf) + (with-current-buffer buf + (eq major-mode 'erb-run-mode))) + (buffer-list)) + (erb-run-generate-new-buffer)))) + (switch-to-buffer buffer) + (erb-run-revert-buffer buffer))) + +(defun erb-run-revert-buffer (&rest _ignored) + (let ((inhibit-read-only t)) + (erase-buffer) + ;; (erb--update-config-cache) + (insert + (format "Commit range: %s\n" erb-run--commit-range) + (format "Number to select: %s\n" (if erb-run--count-to-select + erb-run--count-to-select "All")) + (if (memq erb--status '(idle done)) + "\nStart running benchmarks\n" + "\nCancel\n") + + "\n" + (format "Benchmarked: %s\n" (if erb--status-finished + (length erb--status-finished) "")) + (format "Build Failures: %s\n" (if (not (eq erb--status 'idle)) + (length erb--status-failed-builds) "")) + (format "Task Failures: %s\n" (if (not (eq erb--status 'idle)) + (length erb--status-benchmark-failures) "")) + "Total: %s/%s\n\n" + + "Started at: \n" + (cl-case erb--status + (building "Building.") + (benchmarking "Benchmarking.") + (idle "Ready.") + (done "Finished.") + (t (format "erb--status=%s" erb--status))) + "\nFinished at: \n\n") + + (unless (eq erb--status 'idle) + (dolist (commit erb--status-commits) + (insert + (cond + ((memq commit erb--status-finished) ".") + ((memq commit erb--status-building) "B") + ((memq commit erb--status-benchmark-failures) "F") + ((memq commit erb--status-failed-builds) "E") + ((memq commit erb--status-benchmarking) "@") + ((memq commit erb--status-built) "b") + (t "w")))) + (insert "\n")) + + (set-buffer-modified-p nil))) + +(defun erb-run--timer-func (buffer) + "Revert BUFFER and set a timer to do it again." + (when (buffer-live-p buffer) + (with-current-buffer buffer + (revert-buffer)) + (run-at-time erb-run-refresh-seconds nil + #'erb-run--timer-func buffer))) + +(defun erb-run-start () + "Start running benchmarks." + (interactive) + (when (thread-message-available-p erb--job) + (user-error "Benchmarks are already running")) + + (erb--start-benchmark-controller-thread) + (erb--start-builder-threads) + (erb--start-benchmark-monitor-thread) + + (unless erb-run--commit-range + (user-error "Choose a commit or range of commits to benchmark")) + + (erb--status-clear) + (thread-message-send erb--job erb-run--commit-range)) + +(defun erb-run-cancel () + "Stop running benchmarks." + (interactive) + (erb--status-clear)) + +;;; The benchmark runner: the controller thread + +(defvar erb-simultaneous-build-count 1 + "The number of builds to run simultaneously.") + +(defvar erb--builders nil + "The list of threads which have been created to run builds.") + +(defvar erb--unbuilt-commits (thread-make-queue) + "A thread-safe queue of commits waiting to be built.") +(defvar erb--built-commits (thread-make-queue) + "A thread-safe queue of commits which have been built.") + +(defvar erb--benchmark-controller nil) + +(defun erb--start-benchmark-controller-thread () + "Start the benchmark controller thread if it is not already started." + (unless erb--benchmark-controller + (setq erb--benchmark-controller + (make-thread #'erb--benchmark-control-func "control")))) + +(defun erb--benchmark-control-func () + "Process benchmark jobs. +Watch for incoming jobs arriving by a thread-safe message in +`erb--job'. When a job becomes available, build and +benchmark all the commits and then clear the message." + (while t + (condition-case err + (let* ((job (thread-message-wait erb--job)) + (count (length job)) + builds) + + ;; First, do all the builds. There may be more than one + ;; builder thread. + (erb--status-change 'erb--status 'building) + (dolist (commit job) + (erb--status-add commit 'erb--status-waiting-to-build) + (thread-queue-put commit erb--unbuilt-commits)) + + (while (> count 0) + (let ((build (thread-queue-get erb--built-commits))) + (push build builds)) + (cl-decf count)) + + (erb--status-change 'erb--status 'benchmarking) + ;; Then benchmark the build results, one at a time. + + (erb--status-change 'erb--status 'done) + (thread-message-cancel erb--job)) + ((error quit) (message "Error in ERB benchmark control thread: %s" err))))) + +;;; The benchmark runner: the builder threads + +;; TODO Watch for a signal to shut down + +(defun erb--start-builder-threads () + "Create the desired number of commit-building threads. +Get the number from `erb-run-simultaneous-build-count'. +TODO: adjust the number down as well as up." + (let ((needed (- erb-simultaneous-build-count + (length erb--builders)))) + (while (> needed 0) + (push (make-thread #'erb--builder-func + (format "builder %s" (length erb--builders))) + erb--builders) + (cl-decf needed)))) + +(defun erb--builder-func () + "Build commits from `erb-run--commmits-to-build'." + (while t + (condition-case err + (let ((commit (thread-queue-get erb--unbuilt-commits))) + (message "building %s" commit) + (erb--status-remove commit 'erb--status-waiting-to-build) + (erb--status-add commit 'erb--status-building) + + (unwind-protect + (sleep-for (random 10)) + + (erb--status-remove commit 'erb--status-building) + (erb--status-add commit 'erb--status-built) + (thread-queue-put commit erb--built-commits))) + + ((error quit) (message "Error in ERB benchmark build thread: %s" err))))) + +;;; The benchmark runner: the status monitor thread + +(defvar erb--benchmark-monitor nil + "The thread which keeps track of build and benchmark job status.") +(defvar erb--status-updates (thread-make-queue nil 'fifo) + "A thread-safe queue of status updates yet to be processed.") + +(defun erb--status-clear () + (thread-queue-put '(clear) erb--status-updates)) +(defun erb--status-change (symbol value) + (thread-queue-put `(change ,symbol ,value) erb--status-updates)) +(defun erb--status-add (value symbol) + (thread-queue-put `(add ,value ,symbol) erb--status-updates)) +(defun erb--status-remove (value symbol) + (thread-queue-put `(remove ,value ,symbol) erb--status-updates)) + +(defun erb--start-benchmark-monitor-thread () + "Start the benchmark monitor thread if it is not already started." + (unless erb--benchmark-monitor + (setq erb--benchmark-monitor + (make-thread #'erb--benchmark-monitor-func "monitor")))) + +(defvar erb--logging t) +(defun erb--benchmark-monitor-func () + "Process benchmark job status update. +Collect status update requests from `erb--status-updates' +and update the various global variables accordingly." + (while t + (condition-case err + (pcase (thread-queue-get erb--status-updates) + (`(clear) + (when erb--logging (message "** Clear **")) + (erb--clear-status)) + (`(add ,value ,variable) + (when erb--logging + (message "** Add %s to %s **" value variable)) + (push value (symbol-value variable))) + (`(remove ,value ,variable) + (when erb--logging + (message "** Remove %s from %s **" value variable)) + (setf (symbol-value variable) + (delq value (symbol-value variable)))) + (`(change ,variable ,value) + (when erb--logging + (message "** Set %s to %s **" variable value)) + (setf (symbol-value variable) value)) + (update (error "Unrecognized status update: %s" update))) + ((error quit) (message "Error in ERB benchmark control thread: %s" err))))) + +(defun erb--clear-status () + "Reset all the ERB benchmarking status variables to their initial state." + (setq erb--status 'idle + erb--status-waiting-to-build nil + erb--status-building nil + erb--status-built nil + erb--status-failed-builds nil + erb--status-waiting-to-benchmark nil + erb--status-benchmarking nil + erb--status-finished nil)) + + + (provide 'thread) ;;; thread.el ends here -- 2.16.4 --=-=-= Content-Type: text/plain Run Emacs with -Q, and then type: M-x erb-summary-run RET s Wait several seconds for the second to the last line in the buffer to change to "Finished". There will also be an echo area message about erb--status being set to done. Navigate to lisp/thread.el, select everything from the definition of erb--benchmark-monitor to the end of the file, and use: M-x eval-region RET Return to the buffer created by erb-summary-run, and type 's' again. Result: lisp.h:2241: Emacs fatal error: assertion failed: HASH_TABLE_P (a) My suspicion is that the garbage collector is freeing something needed by the blocked thread. Setting gc-cons-threshold to 500M before doing the steps above stops the error from happening. Here's the backtrace. While trying to sort out how to reproduce this, I also saw it segfault in Ffuncall, in styled_format, and in the Bswitch case of exec_byte_code just past where this error occurs, when it tries to access h->count. Thread 7 (Thread 0x7f1cd4dec700 (LWP 21837)): #0 terminate_due_to_signal (sig=sig@entry=6, backtrace_limit=backtrace_limit@entry=2147483647) at emacs.c:369 #1 0x00000000005a4d99 in die (msg=msg@entry=0x678d52 "HASH_TABLE_P (a)", file=file@entry=0x6768a5 "lisp.h", line=line@entry=2241) at alloc.c:7094 #2 0x00000000006122b5 in XHASH_TABLE (a=...) at lisp.h:2241 #3 exec_byte_code (bytestr=..., vector=..., maxdepth=..., args_template=..., nargs=nargs@entry=0, args=, args@entry=0x16eac38 ) at bytecode.c:1403 #4 0x00000000005cb972 in funcall_lambda (fun=..., nargs=nargs@entry=0, arg_vector=0x16eac38 , arg_vector@entry=0x158ec58 ) at eval.c:3057 #5 0x00000000005c818b in Ffuncall (nargs=nargs@entry=1, args=args@entry=0x158ec50 ) at eval.c:2870 #6 0x000000000064443b in invoke_thread_function () at thread.c:684 #7 0x00000000005c728f in internal_condition_case ( bfun=bfun@entry=0x644400 , handlers=..., handlers@entry=XIL(0xc3c0), hfun=hfun@entry=0x644320 ) at eval.c:1373 #8 0x0000000000644dd1 in run_thread (state=0x158ec30 ) at thread.c:723 #9 0x00007f1cebf602a7 in start_thread () from /nix/store/hwwqshlmazzjzj7yhrkyjydxamvvkfd3-glibc-2.26-131/lib/libpthread.so.0 #10 0x00007f1ceb5fd57f in clone () from /nix/store/hwwqshlmazzjzj7yhrkyjydxamvvkfd3-glibc-2.26-131/lib/libc.so.6 Thread 7 (Thread 0x7f1cd4dec700 (LWP 21837)): "erb--benchmark-monitor-func" (0x158ec58) In GNU Emacs 27.0.50 (build 1, x86_64-pc-linux-gnu, GTK+ Version 3.22.28) of 2018-10-09 built on sockeye Repository revision: 708444efad7a2ce1e309532898b844527e2d9c64 Windowing system distributor 'The X.Org Foundation', version 11.0.11906000 System Description: NixOS 18.03.git.bd06547 (Impala) Recent messages: For information about GNU Emacs and the GNU system, type C-h C-a. Configured using: 'configure --prefix=/home/gem/src/emacs/master/bin --with-modules --with-x-toolkit=gtk3 --with-xft --config-cache --enable-checking=yes,glyphs --enable-check-lisp-object-type' Configured features: XPM JPEG TIFF GIF PNG RSVG SOUND DBUS GSETTINGS GLIB NOTIFY LIBSELINUX GNUTLS LIBXML2 FREETYPE XFT ZLIB TOOLKIT_SCROLL_BARS GTK3 X11 XDBE XIM MODULES THREADS GMP Important settings: value of $EMACSLOADPATH: value of $LANG: en_US.UTF-8 locale-coding-system: utf-8-unix Major mode: Lisp Interaction Minor modes in effect: tooltip-mode: t global-eldoc-mode: t eldoc-mode: t electric-indent-mode: t mouse-wheel-mode: t tool-bar-mode: t menu-bar-mode: t file-name-shadow-mode: t global-font-lock-mode: t font-lock-mode: t blink-cursor-mode: t auto-composition-mode: t auto-encryption-mode: t auto-compression-mode: t line-number-mode: t transient-mark-mode: t Load-path shadows: None found. Features: (shadow sort mail-extr emacsbug message rmc puny seq byte-opt gv bytecomp byte-compile cconv dired dired-loaddefs format-spec rfc822 mml easymenu mml-sec password-cache epa derived epg epg-config gnus-util rmail rmail-loaddefs time-date mm-decode mm-bodies mm-encode mail-parse rfc2231 mailabbrev gmm-utils mailheader cl-loaddefs cl-lib sendmail rfc2047 rfc2045 ietf-drums mm-util mail-prsvr mail-utils elec-pair mule-util tooltip eldoc electric uniquify ediff-hook vc-hooks lisp-float-type mwheel term/x-win x-win term/common-win x-dnd tool-bar dnd fontset image regexp-opt fringe tabulated-list replace newcomment text-mode elisp-mode lisp-mode prog-mode register page menu-bar rfn-eshadow isearch timer select scroll-bar mouse jit-lock font-lock syntax facemenu font-core term/tty-colors frame cl-generic cham georgian utf-8-lang misc-lang vietnamese tibetan thai tai-viet lao korean japanese eucjp-ms cp51932 hebrew greek romanian slovak czech european ethiopic indian cyrillic chinese composite charscript charprop case-table epa-hook jka-cmpr-hook help simple abbrev obarray minibuffer cl-preloaded nadvice loaddefs button faces cus-face macroexp files text-properties overlay sha1 md5 base64 format env code-pages mule custom widget hashtable-print-readable backquote threads dbusbind inotify dynamic-setting system-font-setting font-render-setting move-toolbar gtk x-toolkit x multi-tty make-network-process emacs) Memory information: ((conses 16 94967 9472) (symbols 48 20045 1) (strings 32 28456 1769) (string-bytes 1 816313) (vectors 16 14265) (vector-slots 8 504082 12268) (floats 8 47 70) (intervals 56 213 0) (buffers 992 11)) --=-=-=--