edi

Pizza a Day Diet Archive [January 2015 Edition]: Hoboken Pie

This is a post I originally put only on Facebook in January 2015.  Click here for background.

And the first pizza of the January 2015 #PizzaADayDiet comes from Hoboken Pie! A thin crust sausage, mushroom, and green pepper -- all the ingredients were fresh and in abundance. The sausage and sauce were slightly spicy and the crust was really thin. It could have had a tad more body, but I liked the fact that it didn't feel like I was filling up on bread. Delivery was prompt and the pizza was warm out of the box. I will definitely order from them again.



  • pizza a day
  • Pizza a Day Diet

edi

Pizza A Day Diet Archive [January 2015 Edition]: Southside Flying Pizza

Day 8 of ‪#‎PizzaADayDiet‬ is another thin crust, this one from Southside Flying Pizza. They call it “Neapolitan style,” which I guess is a really thin crust. I chose the whole wheat crust and it was pretty good – it stood up to the ingredients but I wouldn't have minded if it had been a tad crisper. The cheese was thoroughly melted and excellent, though, as were the toppings. The sausage had a good flavor and the peppers were nicely al dente. And the side salad was really good, as well.



  • pizza a day
  • Pizza a Day Diet

edi

Pizza a Day Diet Archive [January 2015 Edition]: Home Slice Pizza

Today's ‪#‎PizzaADayDiet‬ occurred at Home Slice Pizza -- Don Tate joined me for the sausage, mushroom, and green pepper pie! This was the thickest thin crust I've had so far, and was sufficient to be not -floppy, yet not doughy, with a good, chewy texture. The cheese was flavorful and the toppings were each present in every bite.


Altogether, a most excellent pizza -- and they put the leftovers in a tinfoil swan (I've never seen that before in real life :-)).



  • pizza a day
  • Pizza a Day Diet

edi

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!





edi

mass media cents

Today on Married To The Sea: mass media cents


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!





edi

Rakuten Sells Mizuho 15% of Credit Card Arm for Over $1 Billion




edi

Swedish Fintech Klarna Files for Widely Anticipated IPO in US




edi

Spain will propose redirecting unused EU recovery funds to flood-hit Valencia

The Spanish government will propose to the European Commission to amend the post-COVID reconstruction plan to redirect unused EU funds to the 'reconstruction and revitalisation' of the areas most affected by the recent torrential rains and floods. ‘We are going to present an addendum to the…




edi

Trump Plans To Strengthen Crypto Policy With Dedicated Advisor And Presidential Council | Bitcoinist.com

President-elect Donald Trump is poised to reshape the US government’s approach to crypto, actively seeking candidates with industry-friendly views for key regulatory positions. According to the Washington Post, as part of his strategy to establish the United States as the “crypto capital of the…




edi

‘Fart blaster’ tops Christmas toy wishlist as experts predict record sales

Despicable Me 4 tie-in smells more like popcorn while a waddling duck and an interactive pet monkey also feature A “fart blaster” with a repertoire of 15 sounds that blasts “fog fart rings” and a waddling mother duck are among the toys destined to appear on Christmas lists as experts predict a…




edi

Australia plans to ban social media for under 16s — and experts have called it a ‘momentous step’

One expert says Australia is "on the verge of reclaiming childhood after it had been stolen for 15 years."




edi

J. Zachary Mazlish on median wages under Biden

An excellent post, one of the best things written this year in economics. Here is part of the bottom line: Inflation did make the median voter poorer during Biden’s term. In no part of the income distribution did wages grow faster while Biden was President than they did 2012-2020. This is true in…




edi

Thames Water Gets Key Creditor Support to Advance Rescue Plan




edi

Swedish Fintech Klarna Files for Widely Expected IPO in US

Swedish payments firm Klarna Group Plc has filed for an initial public offering in the US, ending months of speculation that the company was readying a stock market listing. Most Read from Bloomberg Klarna confidentially submitted a draft registration statement to the Securities and Exchange…




edi

Media executives say that some brands were preparing to advertise on X once again, as Elon Musk was likely to gain an influential Trump administration role




edi

Thames Water gets backing from three-quarter of creditors; markets eye US inflation – business live

Emergency funding deal would give struggling water company £3bn lifeline Thames Water has been teetering on the brink of collapse since being described as “uninvestible” in March when shareholders refused to pour in more cash. The government has been on standby for nationalisation through a…




edi

Are Social Media Share Buttons Needed on Every Webpage?

Having the availability to share your content is always a plus, especially if it brings value to the reader. There are other reasons as well. Some will bookmark or share a site for purpose of just liking you or for the option to go back to it at a later time and date. They maybe […]




edi

Steam Deck OLED: Limited Edition White, Launching Worldwide on November 18

Steam Deck OLED: Limited Edition White will be available worldwide on November 18th, 2024 at 3PM PST. This model will cost $679 USD, and will be available in all Steam Deck shipping regions, including Japan, South Korea, Taiwan, and Hong Kong via Komodo.

The post Steam Deck OLED: Limited Edition White, Launching Worldwide on November 18 appeared first on ThinkComputers.org.








edi

Pre-order Samsung Odyssey Neo G9 57” Gaming Monitor, Get $500 Samsung Credit

https://cag.vg/odyssey57

Pre-orders between September 18 and October 1 will receive a gift of $500 Samsung instant credit.






edi

First 4 Figures - The Legend of Zelda: Breath of the Wild - Hylian Shield (Collector's Edition) $59.99 at Best Buy

$50 off
https://cag.vg/X2Et




edi

Shaws/Albertsons offering $25 credit with $150+ in gaming gift cards

From 11/17 until 12/14, get $25 off future grocery purchase when you buy $150+ in gift cards. Includes PSN, xbox, gamestop, and other random retailers.

16.6% discount on gaming credit if you were getting groceries anyway. Promos are usually valid at all regional Albertson affiliates. Star Market, Acme, Vons, etc.




edi

GameFly Black Friday sale is live - FF16 $30, AC VI $35, Jedi Survivor $25

Good deals, just grabbed FF16, AC VI, and Jedi Survivor, don't forget to use the coupon for an extra 10% off!

 

https://www.gamefly.com/games

 




edi

Meijer - $20 (20k mperks points) off $50+ PSN/Xbox credit thru 11/25

As part of their ongoing 3 day sale Meijer is giving 20k Mperks points, aka $20, when you buy at least $50 of certain gift cards.  If you buy the "One4All" gift card you can redeem it for Xbox or Playstation credit, or a bunch of other brands. 

 

I had the mperks points in my account within an hour, and just redeemed my $50 gc for PSN credit with no issues.  If you shop at Meijer anyway, this is 40% off Sony or MS credit if you do the minimum $50 gift card purchase.  If you put $70 on it that would be a new full price game for $50.  I believe this can only be used once per Mperks account.




edi

"The Left" Didn't Give Us Enough Credit

This has been trotted out for years, mostly about Afghanistan, and which "Left" are you talking about? There is no "Left" in the NYT (Bouie but it isn't really his beat), the Washington Post, CNN... MSNBC has Hayes and he did!

If you want credit from this unnamed Left, you'd better empower them. The Chapo guys gave you a lot of credit! Then they criticized you for other things! That's how it works!




edi

Harnessing knowledge for innovative and cost-effective practice: the role of the intermediary

Explores how the Institute for Research and Innovation in Social Services (IRISS) promotes the delivery of cost effective social services in Scotland that will support the achievement of positive outcomes for people accessing support. It identifies a number of principles that underpin the work of IRISS and suggests how these facilitate innovative evidence-informed practice. The approach to evidence-informed practice comprises four pillars of activity. The first pillar focuses on improving awareness and access to evidence and is exemplified by the Learning Exchange, the IRISS Insights series, and audio and video recording. The second pillar refers to strengthening the evidence base and is discussed in the context of work on self-directed support. Improving skills and confidence to use evidence forms the third pillar and is represented by work on data visualisation and peer support for self-evaluation. The final pillar is embedding evidence in organisations, through co-production, creating spaces to test and challenge evidence, and through the development of evidence-based products. Supporting people to share knowledge, learn from each other and to collectively produce new knowledge and solutions is an innovative approach but also one which should be cost-effective. Pre-print. Published in Evidence and Policy, 2014 (10)4 as Embedding research into practice through innovation and creativity: a case study from social services




edi

People affected by dementia programme. Individual awards pilot projects: Argyll &amp; Bute and Edinburgh. Evaluation report

This evaluation report is based on feedback from people living with dementia and carers who received an Individual Award from the Life Changes Trust. The Individual Awards Pilot Scheme was run in Argyll & Bute and Edinburgh in 2014-15 and aimed to provide a small amount of additional financial empowerment to a number of individuals whose lives have been affected by dementia, to help improve their well-being and quality of life. A secondary aim of the pilot scheme was to find out what people would spend the Award on when given relatively broad choice, and what benefit that might bring in the short and medium terms.




edi

FAFO: Nikki Haley Edition (With A Pompeo Assist)

In today's edition of FAFO (F*ck Around and Find Out) we have Nikki Haley (with an assist by Mike Pompeo) who is learning the hard way that MAGA loyalty only goes one way. They want your vote and your support, but won't do anything to make your life better. Once you kiss the ring and show your soft underbelly of weakness, you are no longer useful.

Nikki Haley threw her support behind Donald Trump, after running a fairly solid anti-Trump Republican campaign against him. But even after she gave up, he never forgave her. And on Saturday he put the knife in and twisted it even harder, publicly FIRING her (and Mike Pompeo) before he even takes office.

Her response was pathetic.


edi

Bluesky Social Media App Picks Up 700K New Users Since Election

Bluesky, the social media platform whose interface most resembles Twitter, has picked up more than 700,000 new users since the election, as users seek to escape misinformation and offensive posts on X. Via The Guardian:

The influx, largely from North America and the UK, has helped Bluesky reach 14.5 million users worldwide, up from 9 million in September, the company said.

Social media researcher Axel Bruns said the platform offered an alternative to X, formerly Twitter, including a more effective system for blocking or suspending problematic accounts and policing harmful behaviour.

“It’s become a refuge for people who want to have the kind of social media experience that Twitter used to provide, but without all the far-right activism, the misinformation, the hate speech, the bots and everything else,” he said.

It's not a natural fit for bloggers, because you can far too easily get kicked off for saying mean or controversial things and hurting people's feelings. So I'll straddle both.


edi

THEM & M’s: Sexualized Media and Emphasized Femininity

Over the course of the past year, M&M’s have been plastered all over the news, social media, and even Super Bowl commercials. In January 2022, Mars Wrigley gave the brown M&M shorter heels and replaced the green M&M’s boots with sneakers in a push toward more inclusive marketing.  What resulted was outrage. Tucker Carlson became […]




edi

Here’s How Social Media Turned Comedy Into the New Indie Rock

By Keegan Kelly Published: November 11th, 2024




edi

14 Incredible Firsts in Music History

By Jesse Published: November 11th, 2024




edi

How to Use a Graphics Tablet to Edit Photos: 10 Powerful Tips

The post How to Use a Graphics Tablet to Edit Photos: 10 Powerful Tips appeared first on Digital Photography School. It was authored by John McIntire.

Photo editing is simpler than ever thanks to the power of a graphics tablet! Explore our top 10 tips to level up your skills. If you’re just getting started with post-processing and retouching, you’re probably using a mouse and keyboard combo. You can get by with this setup, sure. But when the tasks become more […]

The post How to Use a Graphics Tablet to Edit Photos: 10 Powerful Tips appeared first on Digital Photography School. It was authored by John McIntire.




edi

Live at this restaurant among locally-sourced ingredients

People are paying more attention to what they eat right now than at any other point in history. There's a lot more focus on organic ingredients, fresh ingredients and locally-sourced ingredients. At the famous Steirereck restaurant, you are surrounded by farmlands where the ingredients in your food are grown. Now, PPAG Architects have completed Sterock am Pogusch. This is an offshoot of Steirereck and it will take you into the Austrian Alps.[...]




edi

Australia plans social media ban for under-16s




edi

X is the latest social media site letting 3rd parties use your data to train AI models




edi

Israelische Nachrichtenblockade, Inserateaffäre, Wikipedia und KI

1. RSF verurteilt israelische Nachrichtenblockade (reporter-ohne-grenzen.de) Die Organisation Reporter ohne Grenzen (RSF) verurteilt den Umgang der israelischen Armee mit Medienschaffenden. “Aus dem Norden des Gazastreifens dringen immer weniger Informationen heraus, und gerade deshalb wird Journalismus immer wichtiger”, so RSF-Geschäftsführerin Anja Osterhaus: “Die israelischen Streitkräfte verhindern zunehmend Bilder und Stimmen von der Realität des Krieges und […]



  • 6 vor 9

edi

Proteste gegen Strunz, Medienwende nach Mauerfall, Freiheit der Herzen

1. Euronews-Redaktionen protestieren gegen ihren neuen Chef Claus Strunz (uebermedien.de, Stefan Niggemeier) Stefan Niggemeier fasst die Diskussionen um den neuen Euronews-Chef Claus Strunz, Ex-Mitglied der “Bild”-Chefredaktion, zusammen. Mitarbeiterinnen, Mitarbeiter und Gewerkschaften in Lyon und Brüssel würfen Strunz vor, die Prinzipien der Neutralität und Unparteilichkeit zu verletzen, insbesondere durch öffentliche Pro-Trump-Äußerungen und politische Eingriffe in die […]



  • 6 vor 9

edi

Social media and the 2020 election

SPIA’s Andrew Guess and research colleagues used de-identified data from Facebook and Instagram to explore how changes in the way content was delivered affected people's attitudes and behavior.




edi

Medievalist William Chester Jordan receives Barry Prize for Distinguished Intellectual Achievement

Jordan will also receive the American Historical Society's Award for Scholarly Distinction in January.




edi

Academic Publisher Introduces Camouflaged Editions?

I was one of the outside readers1 for a volume in Cambridge University Press’s enormous “Elements” series, The New Witches of the West, by Ethan Doyle White. (Link is to Amazon US) To find that title, go to the main … Continue reading