gensym generates non-unique names when called from parallel threads
Affects | Status | Importance | Assigned to | Milestone | |
---|---|---|---|---|---|
SBCL |
Won't Fix
|
Undecided
|
Unassigned |
Bug Description
The code below shows that gensym sometimes generates non-unique names when called from parallel running threads.
gensym function documentation says : " If and only if no explicit suffix is supplied, *gensym-counter* is incremented after it is used.", which is not always the case.
*gensym-counter* should probably be managed with a lock acquired (this is the case in CCL, for instance).
Could random/
-------
;;; (with quicklisp initialized)
(ql:quickload :bordeaux-threads)
(defparameter *nb-names* 1000000)
(defparameter *list1* '())
(defparameter *list2* '())
(defparameter *table1* nil)
(defparameter *table2* nil)
(defun generate-names (function-num nb-names)
"
Generates uniques names, using gensym.
"
(let ((symbols (make-array nb-names)))
;; Create symbols
(dotimes (i nb-names)
(setf (aref symbols i) (gensym)))
;; Store names
(ecase function-num
(1 (setf *list1* (map 'list #'symbol-name symbols)))
(2 (setf *list2* (map 'list #'symbol-name symbols))))))
(format t "Creating names...~%")
(let ((thread1 (bt:make-thread (lambda ()
(thread2 (bt:make-thread (lambda ()
(format t "Threads started~%")
(bt:join-thread thread1)
(bt:join-thread thread2))
(format t "Threads stopped~%")
(format t "Storing results in hash table...~%")
(setf *table1* (make-hash-table :test #'equal))
(setf *table2* (make-hash-table :test #'equal))
(dolist (val *list1*)
(when (gethash val *table1*)
(format t "Key ~s is already present in table 1~%" val))
(setf (gethash val *table1*)
t))
(dolist (val *list2*)
(when (gethash val *table2*)
(format t "Key ~s is already present in table 2~%" val))
(setf (gethash val *table2*)
t))
(format t "~%Tables size : ~s and ~s~%" (hash-table-count *table1*) (hash-table-count *table2*))
(format t "Expected size : ~s~%" *nb-names*)
;; (format t "Checking hash tables...~%")
;; (maphash (lambda (key value)
;; (declare (ignore value))
;; (when (gethash key *table2*)
;; (format t "Key in both tables : ~s~%" key)))
;; *table1*)
-------
$ sbcl --version
SBCL 1.4.11
$ uname -a
Linux yggdrasil 4.18.12-
* *features*
(:X86-64 :64-BIT :64-BIT-REGISTERS :ALIEN-CALLBACKS :ANSI-CL :ASH-RIGHT-VOPS
:C-STACK-
:COMPARE-
:FP-AND-
:IMMOBILE-SPACE :INLINE-CONSTANTS :INTEGER-EQL-VOP :LARGEFILE :LINKAGE-TABLE
:LINUX :LITTLE-ENDIAN :MEMORY-
:OS-PROVIDES-
:OS-PROVIDES-
:OS-PROVIDES-
:RAW-SIGNED-WORD :RELOCATABLE-HEAP :SB-CORE-
:SB-FUTEX :SB-LDB :SB-PACKAGE-LOCKS :SB-SIMD-PACK :SB-SOURCE-
:SB-THREAD :SB-UNICODE :SBCL :STACK-
:STACK-
:STACK-
:UNBIND-N-VOP :UNDEFINED-
gensym is not a synchronization primitive for creating random data, if you need monotonic counters roll out your own.