rn

Germany to hold snap election in February after government's coalition collapse - ABC News

  1. Germany to hold snap election in February after government's coalition collapse  ABC News
  2. The briefcase, the Porsche and the collapse of the German government – podcast  The Guardian
  3. Germany set for snap election following collapse of Olaf Scholz’s coalition  The Conversation
  4. President calls German early election plan 'realistic'  DW (English)




rn

NSW nurses strike heaps wage pressure on Minns government - The Australian Financial Review

  1. NSW nurses strike heaps wage pressure on Minns government  The Australian Financial Review
  2. Hundreds of elective surgeries cancelled as 10,000 nurses and midwives walk off job in NSW  ABC News
  3. Nurses took to the streets after ‘insulting’ pay offer. Next stop, court  Sydney Morning Herald
  4. Almost 700 surgeries cancelled as 12,000 NSW nurses strike for better pay  9News




rn

As it happened: Donald Trump ally taunts Kevin Rudd; WiseTech shareholders launch class action - Sydney Morning Herald

  1. As it happened: Donald Trump ally taunts Kevin Rudd; WiseTech shareholders launch class action  Sydney Morning Herald
  2. Ditching Rudd over Trump insults would be ‘worst possible signal’: Turnbull  Sydney Morning Herald
  3. Senior Liberal calls for Rudd to be sacked after Trump advisor suggests US ambassador is on thin ice  9News





rn

Legitimacy of two Victorian local government elections in question after duplicate votes detected - ABC News

  1. Legitimacy of two Victorian local government elections in question after duplicate votes detected  ABC News
  2. Victorian council election results 2024 LIVE updates: Suspected postal vote tampering in council elections  Sydney Morning Herald
  3. VEC investigates potential vote tampering in two Melbourne councils  The Age




rn

Kristian White trial: CCTV reveals final moments before Clare Nowland Tasering in Cooma nursing home - Sydney Morning Herald

  1. Kristian White trial: CCTV reveals final moments before Clare Nowland Tasering in Cooma nursing home  Sydney Morning Herald
  2. Jury shown footage of 95yo getting stuck in tree in weeks before being tasered by police officer  ABC News
  3. Elderly woman 'unable to comply' before cop Tasered her, court hears  9News




rn

Whimsical surnames, part 2 (again mostly German)

[This is a guest post by Michael Witzel] A few months ago you published a discussion of whimsical surnames. Since then I have paid attention and have found new ones in  almost every news broadcast. It is said that there are 1 million (!) surnames in the German speaking area of some 95 million people […]




rn

Biblical and Budai Taiwanese: vernacular, literary; oral, written

[This is a guest post by Denis Mair]      Cai Xutie was a Taiwanese woman who ran a family farm with her husband in a village near Jiayi in central Taiwan. She was a rice farmer and had never attended a public school. After her husband died in middle age, she sold some of the land, […]




rn

Yarnold secures skeleton bronze

Great Britain's Elizabeth Yarnold wins a bronze medal at the women's skeleton World Championship.




rn

Yarnold acclaims adaptable Brits

Sevenoaks slider Lizzy Yarnold says the fact Britain has no real purpose built tracks is the main reason behind British success in the sport




rn

Explore the Cosmos with The Planetary Society and Lerner Publishing

The Planetary Society and Lerner Publishing Group have teamed up to bring young readers an engaging series of books that make space science fun and accessible.




rn

Inside, underneath, backward, upside-down

From holes on Mars to a spun-around moon and a flipped reflection, space science involves looking at things from all different angles.




rn

Europa Clipper launches on its journey to Jupiter’s icy moon

NASA’s Europa Clipper spacecraft launched today aboard a SpaceX Falcon Heavy rocket from NASA’s Kennedy Space Center in Cape Canaveral, Florida.




rn

Journeys worth making

Perseverance faces a hard climb, but New Horizons proves it’s worth going the distance.




rn

Why Taylor-Serrano deserves top billing over Tyson-Paul carnival

How the inclusion of Katie Taylor v Amanda Serrano on the bill legitimises the carnival of Mike Tyson v Jake Paul in Texas




rn

Learned something new

Learned something new today— resin fidget spinner. ????




rn

Operation Beorn Again.

The great bear himself finally turns up... bloody late as usual.
After waiting a while for the second bear to arrive...the cutting and hacking could begin.
The plastic was very tough to get through with my craft saw. After what seems an age, the great heavy head fell off and the Orcs cheered. They soon stopped when they saw the angry head being wired into place.




I had to try a couple of different fur techniques, for this chunky, thick fur, I went for the classic GW wolf pelt look. This is the one where you cut lots of little triangles into the putty and push them up.
Beorn laughs at his new coat... Hopefully that's a good sign he likes it.
I did take the opportunity to bulk up his hump while applying the putty. I will also add some to his forelegs and make them a bit more shaggy.
 It's all about patience with greenstuff, don't rush it and give the first stuff time to harden.

By the time he realized it was too late.

The funniest thing I've seen in weeks, this bought a tear to my eye.


Sorry, this never gets old.

The model I selected for Beorn looked great, he was big and furry, but didn't look fierce enough. I soon came up with a cunning plan to buy another toy I had seen and splice them together. Luckily the toys were the same scale and operation Beorn again was on.
 
Battle of the beasts.

Update: Beorn is now has thick fur to stop any orc blade. Hopefully when painted, he should look a bit more ragged and crazy.






He is now looking a look more the part...






  • lord Of The Rings
  • lord Of The Rings.

rn

Lake Town and Beorn

I've been messing around adding to the bear to days and trying not to rush myself. I'm so desperate to get some paint on it that I almost started today. However, I stopped myself and just added a bit more fur. This time I also ad buyded some to the bears face and now I'm finally happy with it.




Cheeky!
Some more help turns up to bolster Lake town and just in the nick of time.


Two of these bases are really shoddy militia types. Pitch forks are a great short hand for 'rabble'. I can't imagine they will hold for long against the goblin horde.

Another hero base .

I think he looks angry, ragged and damn right mean now...let's get him painted up.


I've added hints of blue so the whole force will tie together.


 The men start to suffer from the fierce wolf packs.


The men with their long swords and the tough veterans from the Iron hills.




  • lord Of The Rings
  • lord Of The Rings.

rn

Eugene Zaikonnikov: Breaking the Kernighan's Law

"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it.." — Brian W. Kernighan.

I'm a sucker for sage advice much as anyone else, and Kernighan is certainly right on money in the epigraph. Alas there comes a time in programmer's career when you just end up there despite the warning. It could be that you were indeed too clever for your own good, or maybe the code isn't quite yours anymore after each of your colleague's take on it over the years. Or just sometimes, the problem is indeed so hard that it strains your capacity as a coder.

It would usually start with a reasonable idea made into first iteration code. The solution looks fundamentally sound but then as you explore the problem space further it begins to seep nuance, either as manifestation of some real world complexity or your lack of foresight. When I run into this my first instinct is to instrument the code. If the problem is formidable you got to respect it: flailing around blindly modifying things or ugh, doing a rewrite at this stage is almost guaranteed to be a waste of time. It helps to find a promising spot, chisel it, gain a foothold in the problem, and repeat until you crack it. Comfortable debugging tools here can really help to erode the original Kernighan coefficient from 2 to maybe 1.6 or 1.4 where you can still have a chance.

Lisp users are fortunate with the options of interactive debugging, and one facility I reach often for is the plain BREAK. It's easy enough to wrap it into a conditional for particular matches you want to debug. However sometimes you want it to trigger after a particular sequence of events across different positions in code has taken place. While still doable it quickly becomes cumbersome and this state machine starts to occupy too much mental space which is already scarce. So one day, partly as a displacement activity from being intimidated by a Really Hard Problem I wrote down my debugging patterns as a handful of macros.

Enter BRAKE. Its features reflect my personal preferences so are not necessarily your cup of tea but it could be a starting point to explore in this direction. Things it can do:

  • act as a simple BREAK with no arguments (duh)
  • wrap an s-expression, passing through its values upon continuing
  • trigger sequentially based on the specified position for a common tag
  • allow for marks that don't trigger the break but mark the position as reached
  • provide conditional versions for the expressions above
  • print traces of tagged breakpoints/marks

If you compile functions with debug on you hopefully should be able to see the wrapped sexpr's result values.

(use-package '(brake))

(defun fizzbuzz ()
  (loop for n from 100 downto 0
	for fizz = (zerop (mod n 3))
	for buzz = (zerop (mod n 5)) do
	(format t "~a "
		(if (not (or fizz buzz))
		    (format nil "~d" n)
		  (brake-when (= n 0)
			      (concatenate 'string
					   (if fizz "Fizz" "")
					   (if buzz "Buzz" "")))))))

These macros try to detect common cases for tagged sequences being either aborted via break or completed to the last step, resetting them after to the initial state. However it is possible for a sequence to end up "abandoned", which can be cleaned up by a manual command.

Say in the example below we want to break when the two first branches were triggered in a specific order. The sequence of 1, 3, 4 will reinitialize once the state 4 is reached, allowing to trigger continuously. At the same time if we blow our stack it should reset to initial when aborting.

(defun ack (m n)
  (cond ((zerop m) (mark :ack 3 (1+ n)))
        ((zerop n) (mark :ack 1 (ack (1- m) 1)))
        (t (brake :ack 4 (ack (1- m) (ack m (1- n)))))))

In addition there are a few utility functions to report on the state of brakepoints, enable or disable brakes based on tags and turn tracing on or off. Tracing isn't meant to replace the semantics of TRACE but to provide a souped up version of debug by print statements everyone loves.

CL-USER> (report-brakes)
Tag :M is DISABLED, traced, with 3 defined steps, current state is initial
Tag :F is DISABLED with 2 defined steps, current state is 0
Tag :ACK is ENABLED with 3 defined steps, current state is initial

Disabling breakpoints without recompilation is really handy and something I find using all the time. The ability to wrap a sexpr was often sorely missed when using BREAK in constructs without implicit body.

Sequencing across threads is sketchy as the code isn't guarded but in many cases it can work, and the appeal of it in debugging races is clear. One of those days I hope to make it more robust while avoiding potential deadlocks but it isn't there yet. Where it already shines tho is in debugging complex iterations, mutually recursive functions and state machines.




rn

TurtleWare: Dynamic Vars - Return of the Jedi

Table of Contents

  1. The protocol
  2. Control operators
  3. Synchronized hash tables with weakness
  4. First-class dynamic variables
    1. STANDARD-DYNAMIC-VARIABLE
    2. SURROGATE-DYNAMIC-VARIABLE
  5. Thread-local variables
    1. The protocol
    2. The implementation
  6. Thread-local slots
  7. What can we use it for?

In the previous two posts I've presented an implementation of first-class dynamic variables using PROGV and a surrogate implementation for SBCL.

Now we will double down on this idea and make the protocol extensible. Finally we'll implement a specialized version of dynamic variables where even the top level value of the variable is thread-local.

The protocol

Previously we've defined operators as either macros or functions. Different implementations were protected by the feature flag and symbols collided. Now we will introduce the protocol composed of a common superclass and functions that are specialized by particular implementations.

Most notably we will introduce a new operator CALL-WITH-DYNAMIC-VARIABLE that is responsible for establishing a single binding. Thanks to that it will be possible to mix dynamic variables of different types within a single DLET statement.

(defclass dynamic-variable () ())

(defgeneric dynamic-variable-bindings (dvar))
(defgeneric dynamic-variable-value (dvar))
(defgeneric (setf dynamic-variable-value) (value dvar))
(defgeneric dynamic-variable-bound-p (dvar))
(defgeneric dynamic-variable-makunbound (dvar))
(defgeneric call-with-dynamic-variable (cont dvar &optional value))

Moreover we'll define a constructor that is specializable by a key. This design will allow us to refer to the dynamic variable class by using a shorter name. We will also define the standard class to be used and an matching constructor.

(defparameter *default-dynamic-variable-class*
  #-fake-progv-kludge 'standard-dynamic-variable
  #+fake-progv-kludge 'surrogate-dynamic-variable)

(defgeneric make-dynamic-variable-using-key (key &rest initargs)
  (:method (class &rest initargs)
    (apply #'make-instance class initargs))
  (:method ((class (eql t)) &rest initargs)
    (apply #'make-instance *default-dynamic-variable-class* initargs))
  (:method ((class null) &rest initargs)
    (declare (ignore class initargs))
    (error "Making a dynamic variable that is not, huh?")))

(defun make-dynamic-variable (&rest initargs)
  (apply #'make-dynamic-variable-using-key t initargs))

Control operators

Control operators are the same as previously, that is a set of four macros that consume the protocol specified above. Note that DYNAMIC-VARIABLE-PROGV expands to a recursive call where each binding is processed separately.

(defmacro dlet (bindings &body body)
  (flet ((pred (binding)
           (and (listp binding) (= 2 (length binding)))))
    (unless (every #'pred bindings)
      (error "DLET: bindings must be lists of two values.~%~
              Invalid bindings:~%~{ ~s~%~}" (remove-if #'pred bindings))))
  (loop for (var val) in bindings
        collect var into vars
        collect val into vals
        finally (return `(dynamic-variable-progv (list ,@vars) (list ,@vals)
                           ,@body))))

(defmacro dset (&rest pairs)
  `(setf ,@(loop for (var val) on pairs by #'cddr
                 collect `(dref ,var)
                 collect val)))

(defmacro dref (variable)
  `(dynamic-variable-value ,variable))

(defun call-with-dynamic-variable-progv (cont vars vals)
  (flet ((thunk ()
           (if vals
               (call-with-dynamic-variable cont (car vars) (car vals))
               (call-with-dynamic-variable cont (car vars)))))
    (if vars
        (call-with-dynamic-variable-progv #'thunk (cdr vars) (cdr vals))
        (funcall cont))))

(defmacro dynamic-variable-progv (vars vals &body body)
  (let ((cont (gensym)))
    `(flet ((,cont () ,@body))
       (call-with-dynamic-variable-progv (function ,cont) ,vars ,vals))))

Synchronized hash tables with weakness

Previously we've used SBCL-specific options to define a synchronized hash table with weak keys. This won't do anymore, because we will need a similar object to implement the thread-local storage for top level values.

trivial-garbage is a portability layer that allows to define hash tables with a specified weakness, but it does not provide an argument that would abstract away synchronization. We will ensure thread-safety with locks instead.

(defclass tls-table ()
  ((table :initform (trivial-garbage:make-weak-hash-table
                     :test #'eq :weakness :key))
   (lock :initform (bt:make-lock))))

(defun make-tls-table ()
  (make-instance 'tls-table))

(defmacro with-tls-table ((var self) &body body)
  (let ((obj (gensym)))
    `(let* ((,obj ,self)
            (,var (slot-value ,obj 'table)))
       (bt:with-lock-held ((slot-value ,obj 'lock)) ,@body))))

First-class dynamic variables

STANDARD-DYNAMIC-VARIABLE

Previously in the default implementation we've represented dynamic variables with a symbol. The new implementation is similar except that the symbol is read from a STANDARD-OBJECT that represents the variable. This also enables us to specialize the function CALL-WITH-DYNAMIC-VARIABLE:

(defclass standard-dynamic-variable (dynamic-variable)
  ((symbol :initform (gensym) :accessor dynamic-variable-bindings)))

(defmethod dynamic-variable-value ((dvar standard-dynamic-variable))
  (symbol-value (dynamic-variable-bindings dvar)))

(defmethod (setf dynamic-variable-value) (value (dvar standard-dynamic-variable))
  (setf (symbol-value (dynamic-variable-bindings dvar)) value))

(defmethod dynamic-variable-bound-p ((dvar standard-dynamic-variable))
  (boundp (dynamic-variable-bindings dvar)))

(defmethod dynamic-variable-makunbound ((dvar standard-dynamic-variable))
  (makunbound (dynamic-variable-bindings dvar)))

(defmethod call-with-dynamic-variable (cont (dvar standard-dynamic-variable)
                                       &optional (val nil val-p))
  (progv (list (dynamic-variable-bindings dvar)) (if val-p (list val) ())
    (funcall cont)))

SURROGATE-DYNAMIC-VARIABLE

The implementation of the SURROGATE-DYNAMIC-VARIABLE is almost the same as previously. The only difference is that we use the previously defined indirection to safely work with hash tables. Also note, that we are not add the feature condition - both classes is always created.

(defvar +fake-unbound+ 'unbound)
(defvar +cell-unbound+ '(no-binding))

(defclass surrogate-dynamic-variable (dynamic-variable)
  ((tls-table
    :initform (make-tls-table)
    :reader dynamic-variable-tls-table)
   (top-value
    :initform +fake-unbound+
    :accessor dynamic-variable-top-value)))

(defmethod dynamic-variable-bindings ((dvar surrogate-dynamic-variable))
  (let ((process (bt:current-thread)))
    (with-tls-table (tls-table (dynamic-variable-tls-table dvar))
      (gethash process tls-table +cell-unbound+))))

(defmethod (setf dynamic-variable-bindings) (value (dvar surrogate-dynamic-variable))
  (let ((process (bt:current-thread)))
    (with-tls-table (tls-table (dynamic-variable-tls-table dvar))
      (setf (gethash process tls-table) value))))

(defun %dynamic-variable-value (dvar)
  (let ((tls-binds (dynamic-variable-bindings dvar)))
    (if (eq tls-binds +cell-unbound+)
        (dynamic-variable-top-value dvar)
        (car tls-binds))))

(defmethod dynamic-variable-value ((dvar surrogate-dynamic-variable))
  (let ((tls-value (%dynamic-variable-value dvar)))
    (when (eq tls-value +fake-unbound+)
      (error 'unbound-variable :name "(unnamed)"))
    tls-value))

(defmethod (setf dynamic-variable-value) (value (dvar surrogate-dynamic-variable))
  (let ((tls-binds (dynamic-variable-bindings dvar)))
    (if (eq tls-binds +cell-unbound+)
        (setf (dynamic-variable-top-value dvar) value)
        (setf (car tls-binds) value))))

(defmethod dynamic-variable-bound-p ((dvar surrogate-dynamic-variable))
  (not (eq +fake-unbound+ (%dynamic-variable-value dvar))))

(defmethod dynamic-variable-makunbound ((dvar surrogate-dynamic-variable))
  (setf (dynamic-variable-value dvar) +fake-unbound+))


;;; Apparently CCL likes to drop^Helide some writes and that corrupts bindings
;;; table. Let's ensure that the value is volatile.
#+ccl (defvar *ccl-ensure-volatile* nil)
(defmethod call-with-dynamic-variable (cont (dvar surrogate-dynamic-variable)
                                       &optional (val +fake-unbound+))
  (push val (dynamic-variable-bindings dvar))
  (let (#+ccl (*ccl-ensure-volatile* (dynamic-variable-bindings dvar)))
    (unwind-protect (funcall cont)
      (pop (dynamic-variable-bindings dvar)))))

Thread-local variables

We've refactored the previous code to be extensible. Now we can use metaobjects from the previous post without change. We can also test both implementations in the same process interchangeably by customizing the default class parameter.

It is the time now to have some fun and extend dynamic variables into variables with top value not shared between different threads. This will enable ultimate thread safety. With our new protocol the implementation is trivial!

The protocol

First we will define the protocol class. THREAD-LOCAL-VARIABLE is a variant of a DYNAMIC-VARIABLE with thread-local top values.

We specify initialization arguments :INITVAL and :INITFUN that will be used to assign the top value of a binding. The difference is that INITVAL specifies a single value, while INITFUN can produce an unique object on each invocation. INITARG takes a precedence over INTIFUN, and if neither is supplied, then a variable is unbound.

We include the constructor that builds on MAKE-DYNAMIC-VARIABLE-USING-KEY, and macros corresponding to DEFVAR and DEFPARAMETER. Note that they expand to :INITFUN - this assures that the initialization form is re-evaluated for each new thread where the variable is used.

(defclass thread-local-variable (dynamic-variable) ())

(defmethod initialize-instance :after
    ((self thread-local-variable) &key initfun initval)
  (declare (ignore self initfun initval)))

(defparameter *default-thread-local-variable-class*
  #-fake-progv-kludge 'standard-thread-local-variable
  #+fake-progv-kludge 'surrogate-thread-local-variable)

(defun make-thread-local-variable (&rest initargs)
  (apply #'make-dynamic-variable-using-key
         *default-thread-local-variable-class* initargs))

(defmacro create-tls-variable (&optional (form nil fp) &rest initargs)
  `(make-thread-local-variable 
    ,@(when fp `(:initfun (lambda () ,form)))
    ,@initargs))

(defmacro define-tls-variable (name &rest initform-and-initargs)
  `(defvar ,name (create-tls-variable ,@initform-and-initargs)))

(defmacro define-tls-parameter (name &rest initform-and-initargs)
  `(defparameter ,name (create-tls-variable ,@initform-and-initargs)))

Perhaps it is a good time to introduce a new convention for tls variable names. I think that surrounding names with the minus sign is a nice idea, because it signifies, that it is something less than a global value. For example:

DYNAMIC-VARS> (define-tls-variable -context- 
                  (progn
                    (print "Initializing context!")
                    (list :context)))
-CONTEXT-
DYNAMIC-VARS> -context-
#<a EU.TURTLEWARE.DYNAMIC-VARS::STANDARD-THREAD-LOCAL-VARIABLE 0x7f7636c08640>
DYNAMIC-VARS> (dref -context-)

"Initializing context!" 
(:CONTEXT)
DYNAMIC-VARS> (dref -context-)
(:CONTEXT)
DYNAMIC-VARS> (dset -context- :the-new-value)

:THE-NEW-VALUE
DYNAMIC-VARS> (dref -context-)
:THE-NEW-VALUE
DYNAMIC-VARS> (bt:make-thread
               (lambda ()
                 (print "Let's read it!")
                 (print (dref -context-))))
#<process "Anonymous thread" 0x7f7637a26cc0>

"Let's read it!" 
"Initializing context!" 
(:CONTEXT) 
DYNAMIC-VARS> (dref -context-)
:THE-NEW-VALUE

The implementation

You might have noticed the inconspicuous operator DYNAMIC-VARIABLE-BINDINGS that is part of the protocol. It returns an opaque object that represents values of the dynamic variable in the current context:

  • for STANDARD-DYNAMIC-VARIABLE it is a symbol
  • for SURROGATE-DYNAMIC-VARIABLE it is a thread-local list of bindings

In any case all other operators first take this object and then use it to read, write or bind the value. The gist of the tls variables implementation is to always return an object that is local to the thread. To store these objects we will use the tls-table we've defined earlier.

(defclass thread-local-variable-mixin (dynamic-variable)
  ((tls-table
    :initform (make-tls-table)
    :reader dynamic-variable-tls-table)
   (tls-initfun
    :initarg :initfun
    :initform nil
    :accessor thread-local-variable-initfun)
   (tls-initval
    :initarg :initval
    :initform +fake-unbound+
    :accessor thread-local-variable-initval)))

For the class STANDARD-THREAD-LOCAL-VARIABLE we will simply return a different symbol depending on the thread:

(defclass standard-thread-local-variable (thread-local-variable-mixin
                                         thread-local-variable
                                         standard-dynamic-variable)
  ())

(defmethod dynamic-variable-bindings ((tvar standard-thread-local-variable))
  (flet ((make-new-tls-bindings ()
           (let ((symbol (gensym))
                 (initval (thread-local-variable-initval tvar))
                 (initfun (thread-local-variable-initfun tvar)))
             (cond
               ((not (eq +fake-unbound+ initval))
                (setf (symbol-value symbol) initval))
               ((not (null initfun))
                (setf (symbol-value symbol) (funcall initfun))))
             symbol)))
    (let ((key (bt:current-thread)))
      (with-tls-table (tls-table (dynamic-variable-tls-table tvar))
        (or (gethash key tls-table)
            (setf (gethash key tls-table)
                  (make-new-tls-bindings)))))))

And for the class SURROGATE-THREAD-LOCAL-VARIABLE the only difference from the SURROGATE-DYNAMIC-VARIABLE implementation is to cons a new list as the initial value (even when it is unbound) to ensure it is not EQ to +CELL-UNBOUND+.

(defclass surrogate-thread-local-variable (thread-local-variable-mixin
                                          thread-local-variable
                                          surrogate-dynamic-variable)
  ())

(defmethod dynamic-variable-bindings ((tvar surrogate-thread-local-variable))
  (flet ((make-new-tls-bindings ()
           (let ((initval (thread-local-variable-initval tvar))
                 (initfun (thread-local-variable-initfun tvar)))
             (cond
               ((not (eq +fake-unbound+ initval))
                (list initval))
               ((not (null initfun))
                (list (funcall initfun)))
               (t
                (list +fake-unbound+))))))
    (let ((key (bt:current-thread)))
      (with-tls-table (tls-table (dynamic-variable-tls-table tvar))
        (or (gethash key tls-table)
            (setf (gethash key tls-table)
                  (make-new-tls-bindings)))))))

That's all, now we have two implementations of thread-local variables. Ramifications are similar as with "ordinary" dynamic variables - the standard implementation is not advised for SBCL, because it will crash in LDB.

Thread-local slots

First we are going to allow to defined dynamic variable types with an abbreviated names. This will enable us to specify in the slot definition that type, for example (MY-SLOT :DYNAMIC :TLS :INITFORM 34)

;;; Examples how to add shorthand type names for the dynamic slots:

(defmethod make-dynamic-variable-using-key ((key (eql :tls)) &rest initargs)
  (apply #'make-dynamic-variable-using-key
         *default-thread-local-variable-class* initargs))

(defmethod make-dynamic-variable-using-key ((key (eql :normal-tls)) &rest initargs)
  (apply #'make-dynamic-variable-using-key
         'standard-thread-local-variable initargs))

(defmethod make-dynamic-variable-using-key ((key (eql :kludge-tls)) &rest initargs)
  (apply #'make-dynamic-variable-using-key
         'surrogate-thread-local-variable initargs))

;;; For *DEFAULT-DYNAMIC-VARIABLE* specify :DYNAMIC T.

(defmethod make-dynamic-variable-using-key ((key (eql :normal-dyn)) &rest initargs)
  (apply #'make-dynamic-variable-using-key
         'standard-dynamic-variable initargs))

(defmethod make-dynamic-variable-using-key ((key (eql :kludge-dyn)) &rest initargs)
  (apply #'make-dynamic-variable-using-key
         'surrogate-dynamic-variable initargs))

In order to do that, we need to remember he value of the argument :DYNAMIC. We will read it with DYNAMIC-VARIABLE-TYPE and that value will be available in both direct and the effective slot:

;;; Slot definitions
;;; There is a considerable boilerplate involving customizing slots.
;;;
;;; - direct slot definition: local to a single defclass form
;;;
;;; - effective slot definition: combination of all direct slots with the same
;;;   name in the class and its superclasses
;;;
(defclass dynamic-direct-slot (mop:standard-direct-slot-definition)
  ((dynamic :initform nil :initarg :dynamic :reader dynamic-variable-type)))

;;; The metaobject protocol did not specify an elegant way to communicate
;;; between the direct slot definition and the effective slot definition.
;;; Luckily we have dynamic bindings! :-)
(defvar *kludge/mop-deficiency/dynamic-variable-type* nil)

;;; DYNAMIC-EFFECTIVE-SLOT is implemented to return as slot-value values of the
;;; dynamic variable that is stored with the instance.
;;;
;;; It would be nice if we could specify :ALLOCATION :DYNAMIC for the slot, but
;;; then STANDARD-INSTANCE-ACCESS would go belly up. We could make a clever
;;; workaround, but who cares?
(defclass dynamic-effective-slot (mop:standard-effective-slot-definition)
  ((dynamic :initform *kludge/mop-deficiency/dynamic-variable-type*
            :reader dynamic-variable-type)))

Moreover we specialize the function MAKE-DYNAMIC-VARIABLE-USING-KEY to the effective slot class. The initargs in this method are meant for the instance. When the dynamic variable is created, we check whether it is a thread-local variable and initialize its INITVAL and INITFUN to values derived from INITARGS, MOP:SLOT-DEFINITION-INITARGS and MOP:SLOT-DEFINITION-INITFUN:

(defmethod make-dynamic-variable-using-key
    ((key dynamic-effective-slot) &rest initargs)
  (let* ((dvar-type (dynamic-variable-type key))
         (dvar (make-dynamic-variable-using-key dvar-type)))
    (when (typep dvar 'thread-local-variable)
      (loop with slot-initargs = (mop:slot-definition-initargs key)
            for (key val) on initargs by #'cddr
            when (member key slot-initargs) do
              (setf (thread-local-variable-initval dvar) val))
      (setf (thread-local-variable-initfun dvar)
            (mop:slot-definition-initfunction key)))
    dvar))

The rest of the implementation of DYNAMIC-EFFECTIVE-SLOT is unchanged:

(defmethod mop:slot-value-using-class
    ((class standard-class)
     object
     (slotd dynamic-effective-slot))
  (dref (slot-dvar object slotd)))

(defmethod (setf mop:slot-value-using-class)
    (new-value
     (class standard-class)
     object
     (slotd dynamic-effective-slot))
  (dset (slot-dvar object slotd) new-value))

(defmethod mop:slot-boundp-using-class
  ((class standard-class)
   object
   (slotd dynamic-effective-slot))
  (dynamic-variable-bound-p (slot-dvar object slotd)))

(defmethod mop:slot-makunbound-using-class
  ((class standard-class)
   object
   (slotd dynamic-effective-slot))
  (dynamic-variable-makunbound (slot-dvar object slotd)))

The implementation of CLASS-WITH-DYNAMIC-SLOTS is also very similar. The first difference in that ALLOCATE-INSTANCE calls MAKE-DYNAMIC-VARIABLE-USING-KEY instead of MAKE-DYNAMIC-VARIABLE and supplies the effective slot definition as the key, and the instance initargs as the remaining arguments. Note that at this point initargs are already validated by MAKE-INSTANCE. The second difference is that MOP:COMPUTE-EFFECTIVE-SLOT-DEFINITION binds the flag *KLUDGE/MOP-DEFICIENCY/DYNAMIC-VARIABLE-TYPE* to DYNAMIC-VARIABLE-TYPE.

;;; This is a metaclass that allows defining dynamic slots that are bound with
;;; the operator SLOT-DLET, and, depending on the type, may have thread-local
;;; top value.
;;;
;;; The metaclass CLASS-WITH-DYNAMIC-SLOTS specifies alternative effective slot
;;; definitions for slots with an initarg :dynamic.
(defclass class-with-dynamic-slots (standard-class) ())

;;; Class with dynamic slots may be subclasses of the standard class.
(defmethod mop:validate-superclass ((class class-with-dynamic-slots)
                                    (super standard-class))
  t)

;;; When allocating the instance we initialize all slots to a fresh symbol that
;;; represents the dynamic variable.
(defmethod allocate-instance ((class class-with-dynamic-slots) &rest initargs)
  (let ((object (call-next-method)))
    (loop for slotd in (mop:class-slots class)
          when (typep slotd 'dynamic-effective-slot) do
            (setf (mop:standard-instance-access
                   object
                   (mop:slot-definition-location slotd))
                  (apply #'make-dynamic-variable-using-key slotd initargs)))
    object))

;;; To improve potential composability of CLASS-WITH-DYNAMIC-SLOTS with other
;;; metaclasses we treat specially only slots that has :DYNAMIC in initargs,
;;; otherwise we call the next method.
(defmethod mop:direct-slot-definition-class
    ((class class-with-dynamic-slots) &rest initargs)
  (loop for (key) on initargs by #'cddr
        when (eq key :dynamic)
          do (return-from mop:direct-slot-definition-class
               (find-class 'dynamic-direct-slot)))
  (call-next-method))

(defmethod mop:compute-effective-slot-definition
    ((class class-with-dynamic-slots)
     name
     direct-slotds)
  (declare (ignore name))
  (let ((latest-slotd (first direct-slotds)))
    (if (typep latest-slotd 'dynamic-direct-slot)
        (let ((*kludge/mop-deficiency/dynamic-variable-type*
                (dynamic-variable-type latest-slotd)))
          (call-next-method))
        (call-next-method))))

(defmethod mop:effective-slot-definition-class
    ((class class-with-dynamic-slots) &rest initargs)
  (declare (ignore initargs))
  (if *kludge/mop-deficiency/dynamic-variable-type*
      (find-class 'dynamic-effective-slot)
      (call-next-method)))

Finally the implementation of SLOT-DLET does not change:

;;; Accessing and binding symbols behind the slot. We don't use SLOT-VALUE,
;;; because it will return the _value_ of the dynamic variable, and not the
;;; variable itself.
(defun slot-dvar (object slotd)
  (check-type slotd dynamic-effective-slot)
  (mop:standard-instance-access
   object (mop:slot-definition-location slotd)))

(defun slot-dvar* (object slot-name)
  (let* ((class (class-of object))
         (slotd (find slot-name (mop:class-slots class)
                      :key #'mop:slot-definition-name)))
    (slot-dvar object slotd)))

(defmacro slot-dlet (bindings &body body)
  `(dlet ,(loop for ((object slot-name) val) in bindings
                collect `((slot-dvar* ,object ,slot-name) ,val))
     ,@body))

Finally we can define a class with slots that do not share the top value:

DYNAMIC-VARS> (defclass c1 ()
                  ((slot1 :initarg :slot1 :dynamic nil :accessor slot1)
                   (slot2 :initarg :slot2 :dynamic t   :accessor slot2)
                   (slot3 :initarg :slot3 :dynamic :tls :accessor slot3))
                  (:metaclass class-with-dynamic-slots))
#<The EU.TURTLEWARE.DYNAMIC-VARS::CLASS-WITH-DYNAMIC-SLOTS EU.TURTLEWARE.DYNAMIC-VARS::C1>
DYNAMIC-VARS> (with-slots (slot1 slot2 slot3) *object*
                (setf slot1 :x slot2 :y slot3 :z)
                (list slot1 slot2 slot3))
(:X :Y :Z)
DYNAMIC-VARS> (bt:make-thread
               (lambda ()
                 (with-slots (slot1 slot2 slot3) *object*
                   (setf slot1 :i slot2 :j slot3 :k)
                   (print (list slot1 slot2 slot3)))))

#<process "Anonymous thread" 0x7f76424c0240>

(:I :J :K) 
DYNAMIC-VARS> (with-slots (slot1 slot2 slot3) *object*
                (list slot1 slot2 slot3))
(:I :J :Z)

What can we use it for?

Now that we know how to define thread-local variables, we are left with a question what can we use it for. Consider having a line-buffering stream. One possible implementation could be sketched as:

(defclass line-buffering-stream (fancy-stream)
  ((current-line :initform (make-adjustable-string)
                 :accessor current-line)
   (current-ink :initform +black+
                :accessor current-ink)))

(defmethod stream-write-char ((stream line-buffering-stream) char)
  (if (char= char #
ewline)
      (terpri stream)
      (vector-push-extend char (current-line stream))))

(defmethod stream-terpri ((stream line-buffering-stream))
  (%put-line-on-screen (current-line stream) (current-ink stream))
  (setf (fill-pointer (current-line stream)) 0))

If this stream is shared between multiple threads, then even if individual operations and %PUT-LINE-ON-SCREEN are thread-safe , we have a problem. For example FORMAT writes are not usually atomic and individual lines are easily corrupted. If we use custom colors, these are also a subject of race conditions. The solution is as easy as making both slots thread-local. In that case the buffered line is private to each thread and it is put on the screen atomically:

(defclass line-buffering-stream (fancy-stream)
  ((current-line
    :initform (make-adjustable-string)
    :accessor current-line
    :dynamic :tls)
   (current-ink
    :initform +black+
    :accessor current-ink
    :dynamic :tls))
  (:metaclass class-with-dynamic-slots))

Technique is not limited to streams. It may benefit thread-safe drawing, request processing, resource management and more. By subclassing DYNAMIC-VARIABLE we could create also variables that are local to different objects than processes.

I hope that you've enjoyed reading this post as much as I had writing it. If you are interested in a full standalone implementation, with tests and system definitions, you may get it here. Cheers!




rn

Halcyon Afternoon and Why I Wrote It

.




This is a particularly pleasant time for me. To begin with, I'm at the World Fantasy Convention, where I'll see a lot of old friends and serve as toastmaster. And I have two new Mongolian Wizard stories coming out (I apologize for the delay), one today and one tomorrow at Reactor Magazine.

Today's story, "Halcyon Afternoon," is atypical for the series. It's not as violent as these stories tend to be. That's because I felt that after undergoing so much suffering and loss, Franz-Karl Ritter deserved at least one afternoon of contentment and bliss. Even his wolf, Freki, got the day off.

Of course... Ritter's luck being what it is, and all of Europe being entangled in a wizard war, the afternoon would not prove entirely blissful.

You can read the story here. Or you can find the entire series of Mongolian Wizard stories--ten so far--here. Or you can simply go to Reactor Magazine and wander about happily. It's full of great stories and terrific non-fiction.

And tomorrow . . .

Ritter's luck takes a downturn--along with everyone else's--in "Dragons of Paris." An old friend pops up, a relationship turns difficult, and a battle where victory seems certain goes sour.

(And now, I'm off to the convention!)


Above: Illustration by Dave Palumbo. It's not only beautiful but true to the story. I'm grateful for both of those.

*




rn

Don 039 t mess with Acorns

Don 039 t mess with Acorns



View Comic!








rn

porno hut

Today on Married To The Sea: porno hut


This RSS feed is brought to you by Drew and Natalie's podcast Garbage Brain University. Our new series Everything Is Real explores the world of cryptids, aliens, quantum physics, the occult, and more. If you use this RSS feed, please consider supporting us by becoming a patron. Patronage includes membership to our private Discord server and other bonus material non-patrons never see!




rn

congratulations borned

Today on Married To The Sea: congratulations borned


This RSS feed is brought to you by Drew and Natalie's podcast Garbage Brain University. Our new series Everything Is Real explores the world of cryptids, aliens, quantum physics, the occult, and more. If you use this RSS feed, please consider supporting us by becoming a patron. Patronage includes membership to our private Discord server and other bonus material non-patrons never see!




rn

borned in the hellscape

Today on Married To The Sea: borned in the hellscape


This RSS feed is brought to you by Drew and Natalie's podcast Garbage Brain University. Our new series Everything Is Real explores the world of cryptids, aliens, quantum physics, the occult, and more. If you use this RSS feed, please consider supporting us by becoming a patron. Patronage includes membership to our private Discord server and other bonus material non-patrons never see!




rn

born too early

Today on Married To The Sea: born too early


This RSS feed is brought to you by Drew and Natalie's podcast Garbage Brain University. Our new series Everything Is Real explores the world of cryptids, aliens, quantum physics, the occult, and more. If you use this RSS feed, please consider supporting us by becoming a patron. Patronage includes membership to our private Discord server and other bonus material non-patrons never see!





rn

Matthew Nicholson throws down a two-handed slam to help Northwestern lead over UIC going into the half

Matthew Nicholson threw down a two-handed slam to help the Northwestern Wildcats lead over the the UIC Flames going into the half.




rn

Mavs' Klay Thompson cheered by 400 Warriors employees in return to Golden State

Klay Thompson was greeted by some 400 cheering Warriors employees showing their love and appreciation for the former Golden State star and lined up along his path to the Dallas locker room




rn

The Internet Took the Opportunity to Photoshop Donald Trump With a Blank Sign and Ran With It

Has Trump seen people holding signs on the internet before? It never turns out well.




rn

Internet Had a Dangerous Amount of Fun Trolling Pic of Trump, Melania And Ivanka With The Pope

Just when we thought we'd never get anything better than Donald Trump grasping that orb, we get this dark-humored, delightfully awkward pic that just oozes cringe. Naturally, people were ready to flood Twitter with some entertaining captions. 




rn

The Queen of England Wore a Bright Green Outfit So, Naturally, the Internet Treated It Like a Green Screen

Queen Elizabeth wore a neon green outfit to her 90th birthday party. What could go wrong? Oh right, the internet...




rn

Bernie Still Needs Your Financial Support In These Fresh Dank Memes

We've been seeing these Bernie Sanders memes practically everywhere on the internet lately, and they don't appear to be stopping any time soon! Here's our last gallery in case you missed 'em. 

We sincerely hope you're not sick of political memes yet, because we've still got far to go before the 2020 presidential elections, so buckle up!




rn

Iowa Caucus's Delayed Results Have Churned Up Some Anxious Reaction Memes

Last night the 2020 Iowa Democratic Caucus stirred up quite the controversy when it was announced that the results would be delayed due to "inconsistencies" in a new app meant to speed up the reporting results of the caucus. Ironic, to say the least. 

But hang tight, because they're set to be released at 5 pm Eastern Time.

Ahead of the results being released, Pete Buttigieg gave what appeared to be a victory speech last night to the confusion of many. The bizarre move has lead many to believe that the system may have been rigged in Mayor Pete's favor.

As always, we have to give the disclaimer that we're not picking sides; we're merely reporting on what the internet has been saying, so scroll down to see some of our favorite reaction memes and tweets while we all wait impatiently for the results.




rn

Bernie Sanders Writing On A Whiteboard Gets Meme'd With Hot Takes

What started as Bernie Sanders writing an innocent thank you note to his field staff on a white board turned into memers filling it in with hotter takes and less savory messages. Seriously, people should know better at this point than to post images on the internet of people with white boards. It's just asking for bad news.




rn

Kentuckians Are Meme-ing Their Beloved Governor In These Trying Times

In these trying and crazy times, Kentuckians are looking to Governor Andy Beshear for answers. His daily 5pm livestream updates have become popular for their wholesome messages and his amusing call-outs to stubborn bingo halls that just won't close for coronavirus quarantining. He's even earned himself a Facebook meme page, entitled "Andy Beshear Memes for Social Distancing Teens." 

Check some of them out below!  




rn

Internet Reacts To Trump's Comments About Injecting Disinfectant To Cure COVID-19

The internet is reacting to comments made by Donald Trump during a COVID-19-related press briefing held yesterday. Trump claimed that the virus could be treated by bringing "light inside the body" or injecting a disinfectant. Thankfully medical professionals were quick to denounce these claims, and people on the internet have since been creating some excellent memes on the matter. 

This should go without saying, but please don't inject yourself with disinfectant, y'all.




rn

What employers can learn from Wells Fargo’s failure to recruit and retain Black talent

We’ve all seen the quotes from Wells Fargo’s CEO in a June memo in which he blamed the bank’s failure in reaching diversity goals on a lack of qualified minority talent. “While it might sound like an excuse, the unfortunate reality is that there is a very limited pool of black talent to recruit from,” […]

The post What employers can learn from Wells Fargo’s failure to recruit and retain Black talent appeared first on DiversityJobs.com.




rn

As today’s homes burn faster than ever, this year’s Fire Prevention Week campaign presents critical home escape planning and practice messages

Knowing that today’s homes burn faster than ever, the National Fire Protection Association (NFPA) announced “Every Second Counts: Plan Two Ways Out” as the official theme for this year’s Fire Prevention Week campaign, October 8-14, 2017. Experts say you may have as little as two minutes (or even less) to safely escape a typical home fire from the time the smoke alarm sounds.




rn

Fire chiefs share lessons learned from recent high profile emergencies including hurricanes, hi-rise fires and hostile shooting incidents at the Urban Fire Forum

Fire chiefs from France, the United Kingdom, and the United States gathered in Quincy, Massachusetts at the National Fire Protection Association (NFPA) Urban Fire Forum (UFF) to listen to first-hand accounts of some of the biggest emergency response incidents over the past 15 months, including hurricane response in Texas and Florida, the Grenfell Tower fire in London, and the Pulse Nightclub shooting in Orlando.




rn

S. H. Fernando Jr.’s Book Notes music playlist for his book The Chronicles of DOOM

"For an artist who departed way too soon, at age 49, Daniel Dumile aka MF DOOM also left behind a huge body of work for us to dissect, reflect upon, and enjoy from here ‘til eternity."




rn

Dogecoin spikes ~20% after Donald Trump announced the creation of the Department of Government Efficiency or DOGE; Dogecoin is up 153% since Election Day

Dogecoin shot higher on Tuesday night, extending its postelection surge after President-elect Donald Trump formally announced the creation of the Department of Government Efficiency, which he referred to as "DOGE" in his statement. Tesla CEO Elon Musk and Vivek Ramaswamy, former Republican…




rn

Trump names Elon Musk to role leading government efficiency drive

Musk and former Republican presidential candidate Vivek Ramaswamy will co-lead a newly created Department of Government Efficiency, an entity Trump indicated will operate outside the confines of government.




rn

Treasury Yields Set to Turn Positive for Yen-Hedged Investors




rn

Trump selects Elon Musk to lead government efficiency department

Musk and ex-presidential candidate Vivek Ramaswamy to head up Department of Government Efficiency (Doge) Elon Musk and former Republican presidential candidate Vivek Ramaswamy will lead the newly created Department of Government Efficiency, Donald Trump said on Tuesday. Despite the name, the…




rn

Dam blast in eastern Ukraine leaves 10 soldiers dead

At least 10 Ukrainian soldiers died in the Donetsk region after the Kurakhovo Reservoir dam was blown up, TASS reported, citing Russian security forces. The blast flooded nearby areas, trapping soldi...