mi Midwest Schools and Bookstores By greglsblog.blogspot.com Published On :: Thu, 12 May 2016 15:08:00 +0000 I'm just back from a twelve day trip up to Chicago, Milwaukee, and Minneapolis-St. Paul, where I did a bit of research and visited a bunch of schools and children's indie bookstores. The trip started inauspiciously, when my flight was canceled because the wind blew the plane onto a belt conveyor. Eventually, I made it to Chicago, though, where the weather looked like this: Still, I had arrived ahead of time so I could go down to the Museum of Science and Industry, which has a World War II German u-boat and a chicken incubator. Next two days were the actual school visits, arranged at Henry Puffer Elementary and Liberty Elementary by Anderson's Book Shop and at Attea Glenview School and Rondout School by The Book Stall. Afterwards, I got to hang out with Robert from The Book Stall and stopped by for a couple of pics. Posing with posters Then I was off to Milwaukee for a school visit at Atwater Elementary arranged through the Boswell Book Company. It was my first time I'd ever been to Milwaukee, but sadly didn't have a chance to sightsee, because it was off to Minneapolis-St. Paul for three days of school visits. Visits at North Trail Elementary and Brimhall Elementary were through Addendum Books; those at Crestview Elementary and Little Canada Elementary were through the Red Balloon Bookshop; and at Valley View Middle School, through Wild Rumpus Books. Snake! I had some free time, so I went over to Addendum Books for some pics and had a fun lunch with Katherine and Marcus, the proprietors. In front of the "Purple Rain" wall Since I was there over the weekend, I spoke at Red Balloon for the Minnesota SCBWI about Research and the Suspension of disbelief. I also had the chance to go run a couple times on the Mississippi Riverfront trail and visit the Science Museum of Minnesota. T.rex! Triceratops Stegosaurus! After Monday's school visit I had a fun lunch with Drew and Jordan of Wild Rumpus Books at Pizzeria Lola (a separate Pizza-a-Day Diet post will be forthcoming). Then I visited the bookstore, where I met the menagerie. Copper oven and decorative birch logs Chicken! Ferret! Then I was back to Chicago and spent a day at the Field Museum of Natural History and showed Madeline Smoot of CBAY Books a bit of the city! Many thanks to all the librarians and booksellers and Blue Slip Media and everyone else who made this happen. Thanks also to Quinette Cook and all the folks from MN SCBWI who came out for the workshop. It was great fun meeting you! For information on how to book me for school visits for the 2016-2017 school year, contact Carmen Oliver at The Booking Biz. Full Article school visits
mi U.S. Senate advances their FY 2025 budget proposal for NASA amid deep cuts By www.planetary.org Published On :: Wed, 31 Jul 2024 11:50:00 -0700 An analysis of the U.S. Senate's FY 2025 budget request for NASA. Full Article
mi Ramses: A new mission racing to land on asteroid Apophis By www.planetary.org Published On :: Thu, 22 Aug 2024 07:00:00 -0700 When a skyscraper-sized asteroid narrowly misses Earth in 2029, three spacecraft may be along for the ride. Full Article
mi Europa Clipper: A mission backed by advocates By www.planetary.org Published On :: Tue, 01 Oct 2024 07:00:00 -0700 Europa Clipper will soon head for Jupiter's icy, potentially habitable moon. Without the advocacy efforts of The Planetary Society and our members, the mission may never have been possible. Full Article
mi Schmidt Bros. Carbon Carving Set By uncrate.com Published On :: Tue, 12 Nov 2024 13:00:00 -0500 Full Article Kitchen
mi D1 Milano Polychrono Watch By uncrate.com Published On :: Tue, 12 Nov 2024 13:00:00 -0500 Full Article Watches
mi The Mighty Megatron By iron-mitten.blogspot.com Published On :: Sat, 28 Sep 2024 12:35:00 +0000 Full Article War for Cybertron War for Cybertron.
mi The Armies march to Partizan By iron-mitten.blogspot.com Published On :: Fri, 11 Oct 2024 09:43:00 +0000 The last Dwarf base done just in time for the big weekend. It's the Other Partizan show this weekend and I'll be there with my armies. So if you fancy seeing a massive Hobbit battle...pop along.The battle will be fought using the new Midguard rules by James Morris. It proves to be a great Sunday especially if you're a Tolkien fan.Today I have collect everything together and pack it as best I can to stop any travel casualties. I have always painted my figures to play at home so have never magnetised them onto metal sheets. I'm going to rely on nonslip matting and bubble wrap to get them there...I'm planned for a few accidents by hopefully, touch wood, all will be okay. It's been a lot of last minute painting but I'm looking forward to it.Most of these Dwarves are old sculpts from Nick Lund.That Reaper figure jumped the painting que and made the last company to leave the Iron Hills.A couple of last minute Bolg bodyguard bases. I had planned to do more but time caught up with me. At least all the important stuff is done, like Beorn and the Eagles.A coat of gloss on weapon edges and armour helps to catch the eye on the metal work. Dwarves are master smiths after all, their armour and weapons would be of high quality. Come along on Sunday it would be nice to see you.Finally the two meet, Bolg and bigger Beorn.My old Beorn figure is dwarfed by the new addition. The new bear has a lot more presence to him and should look the part on Sunday. Full Article lord Of The Rings lord Of The Rings.
mi The Battle of the Five Armies at Partizan. By iron-mitten.blogspot.com Published On :: Sun, 13 Oct 2024 21:47:00 +0000 well...what a bloody marvellous couple of days I've just had. Taking all my figures to a show for the first time was a little nerve racking, but the effort really paid off. I had an excellent time and met some great new friends too in the process.The board and mountain were excellent too, crafted by the talented James Morris. I had never deployed my whole army in one go before, so what better time to do it than on a set up like this. Everything seemed to come together perfectly.I started my battle with the orcs at the bottom of the hill advancing across the mountain river. However, due to my terrible dice rolling and the luck of the Elves, I was cut and shot to pieces. It wasn't long before my tattered ranks were tumbling back through the freezing water. The Elves just had time to redress their lines before another dark wave of Goblin folk arrived.I love the walled up gate that James made, the little gaps in the stonework were perfect. It really reminded me of one of Alan Lees illustrations. Also a very clever use of silver fabric for the river...very effective!The Eagles arrived later in the game and swept the Goblins from the mountain sides. The game was masterfully run by David Hunter, who made sure the flo was just right.My Eagles looked very realistic on the slopes of the mountain too, better than I had hoped for. The extra poses were well worth collecting over the last ten years. The flying ones on stands were a very recent purchase and they really stood out.The game in full flo, it drew a big crowd and it was great to talk figures with people. A lot of people wanted to know where I got my Orcs from and of course the answer was...everywhere. Also the amount of plastic model kits used was very hard to calculate. Just about every Dark age and Fantasy box set all mashed up and mixed together.I love my Wood elves after lovingly creating each one individually and got a huge buzz from seeing them in action. I didn't really care they were cutting my forces to ribbons, just seeing them used in anger was enough for me.Hordes of Goblins still plagued the mountainside, their numbers were too much even for the Eagles.Thorin was cut down by Bolg's bodyguard as he tried to cut through their great shields. Bard of Laketown too, fell to the hacking Goblin blades. Dain of the Iron hills was holding his own but was badly wounded. At last it seemed like the Free people's luck had run out. Then with a roar like a thunderclap Beorn burst onto the scene. He tore through the Goblin ranks and made his way to Bolg. Approaching the rear of Bolg's bodyguard he shattered their formation.It was at this point the Orc's morale was lost and the game was called. The rules were James' new Midguard ones and were perfect for this Dark age epic clash of men and monsters.I had a great couple of days and it was a real treat to get out and do something exciting like this. To my amazement I didn't suffer any spear snaps of breakages and everything survived intacted. I was expecting a few casualties and had even taken a little repair kit but didn't need it.Dain's Iron hills Dwarves push the Goblins from the ridge, supported by a group of Lake men.An earlier shot of the swirl of battle.The nine Black riders with their Dark lord My beloved Silvan elves holding their own against ravaging wolf packs.I said it once, I'll say it again, what a bloody marvellous couple of days! I'm sure I'll have some more pictures soon to post on here of the day... Full Article lord Of The Rings lord Of The Rings. War games
mi The Battle of the Five Armies By iron-mitten.blogspot.com Published On :: Mon, 14 Oct 2024 17:38:00 +0000 Here's a shot of James Morris' Lonely mountain with the only missing element...thousands of blood drinking bats. Full Article lord Of The Rings lord Of The Rings.
mi TurtleWare: Dynamic Vars - A New Hope By turtleware.eu Published On :: Tue, 22 Oct 2024 00:00:00 GMT Table of Contents Dynamic Bindings The problem The solution Dynamic slots The context Summary Dynamic Bindings Common Lisp has an important language feature called dynamic binding. It is possible to rebind a dynamic variable somewhere on the call stack and downstream functions will see that new value, and when the stack is unwound, the old value is brought back. While Common Lisp does not specify multi-threading, it seems to be a consensus among various implementations that dynamic bindings are thread-local, allowing for controlling the computing context in a safe way. Before we start experiments, let's define a package to isolate our namespace: (defpackage "EU.TURTLEWARE.BLOG/DLET" (:local-nicknames ("MOP" #+closer-mop "C2MOP" #+(and (not closer-mop) ecl) "MOP" #+(and (not closer-mop) ccl) "CCL" #+(and (not closer-mop) sbcl) "SB-MOP")) (:use "CL")) (in-package "EU.TURTLEWARE.BLOG/DLET") Dynamic binding of variables is transparent to the programmer, because the operator LET is used for both lexical and dynamic bindings. For example: (defvar *dynamic-variable* 42) (defun test () (let ((*dynamic-variable* 15) (lexical-variable 12)) (lambda () (print (cons *dynamic-variable* lexical-variable))))) (funcall (test)) ;;; (42 . 12) (let ((*dynamic-variable* 'xx)) (funcall (test))) ;;; (xx . 12) Additionally the language specifies a special operator PROGV that gives the programmer a control over the dynamic binding mechanism, by allowing passing the dynamic variable by value instead of its name. Dynamic variables are represented by symbols: (progv (list '*dynamic-variable*) (list 'zz) (funcall (test))) ;;; (zz . 12) The problem Nowadays it is common to encapsulate the state in the instance of a class. Sometimes that state is dynamic. It would be nice if we could use dynamic binding to control it. That said slots are not variables, and if there are many objects of the same class with different states, then using dynamic variables defined with DEFVAR is not feasible. Consider the following classes which we want to be thread-safe: (defgeneric call-with-ink (cont window ink)) (defclass window-1 () ((ink :initform 'red :accessor ink))) (defmethod call-with-ink (cont (win window-1) ink) (let ((old-ink (ink win))) (setf (ink win) ink) (unwind-protect (funcall cont) (setf (ink win) old-ink)))) (defclass window-2 () ()) (defvar *ink* 'blue) (defmethod ink ((window window-2)) *ink*) (defmethod call-with-ink (cont (win window-2) ink) (let ((*ink* ink)) (funcall cont))) The first example is clearly not thread safe. If we access the WINDOW-1 instance from multiple threads, then they will overwrite a value of the slot INK. The second example is not good either, because when we have many instances of WINDOW-2 then they share the binding. Nesting CALL-WITH-INK will overwrite the binding of another window. The solution The solution is to use PROGV: (defclass window-3 () ((ink :initform (gensym)))) (defmethod initialize-instance :after ((win window-3) &key) (setf (symbol-value (slot-value win 'ink)) 'red)) (defmethod call-with-ink (cont (win window-3) ink) (progv (list (slot-value win 'ink)) (list ink) (funcall cont))) This way each instance has its own dynamic variable that may be rebound with a designated operator CALL-WITH-INK. It is thread-safe and private. We may add some syntactic sugar so it is more similar to let: (defmacro dlet (bindings &body body) (loop for (var val) in bindings collect var into vars collect val into vals finally (return `(progv (list ,@vars) (list ,@vals) ,@body)))) (defmacro dset (&rest pairs) `(setf ,@(loop for (var val) on pairs by #'cddr collect `(symbol-value ,var) collect val))) (defmacro dref (variable) `(symbol-value ,variable)) Dynamic slots While meta-classes are not easily composable, it is worth noting that we can mold it better into the language by specifying that slot itself has a dynamic value. This way CLOS aficionados will have a new tool in their arsenal. The approach we'll take is that a fresh symbol is stored as the value of each instance-allocated slot, and then accessors for the slot value will use these symbols as a dynamic variable. Here are low-level accessors: ;;; 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) (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)) Now we'll define the meta-class. We need that to specialize functions responsible for processing slot definitions and the instance allocation. Notice, that we make use of a kludge to communicate between COMPUTE-EFFECTIVE-SLOT-DEFINITION and EFFECTIVE-SLOT-DEFINITION-CLASS – this is because the latter has no access to the direct slot definitions. ;;; 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) (declare (ignore 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)) (gensym (string (mop:slot-definition-name slotd))))) 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 val) on initargs by #'cddr when (eq key :dynamic) do (return-from mop:direct-slot-definition-class (find-class 'dynamic-direct-slot))) (call-next-method)) ;;; 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-slot-p* nil) (defmethod mop:compute-effective-slot-definition ((class class-with-dynamic-slots) name direct-slotds) (if (typep (first direct-slotds) 'dynamic-direct-slot) (let* ((*kludge/mop-deficiency/dynamic-slot-p* t)) (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-slot-p* (find-class 'dynamic-effective-slot) (call-next-method))) Finally we define a direct and an effective slot classes, and specialize slot accessors that are invoked by the instance accessors. ;;; 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-slot-p))) ;;; 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) ()) (defmethod mop:slot-value-using-class ((class class-with-dynamic-slots) object (slotd dynamic-effective-slot)) (dref (slot-dvar object slotd))) (defmethod (setf mop:slot-value-using-class) (new-value (class class-with-dynamic-slots) object (slotd dynamic-effective-slot)) (dset (slot-dvar object slotd) new-value)) (defmethod mop:slot-boundp-using-class ((class class-with-dynamic-slots) object (slotd dynamic-effective-slot)) (boundp (slot-dvar object slotd))) (defmethod mop:slot-makunbound-using-class ((class class-with-dynamic-slots) object (slotd dynamic-effective-slot)) (makunbound (slot-dvar object slotd))) With this, we can finally define a class with slots that have dynamic values. What's more, we may bind them like dynamic variables. ;;; Let there be light. (defclass window-4 () ((ink :initform 'red :dynamic t :accessor ink) (normal :initform 'normal :accessor normal)) (:metaclass class-with-dynamic-slots)) (let ((object (make-instance 'window-4))) (slot-dlet (((object 'ink) 15)) (print (ink object))) (print (ink object))) ContextL provides a similar solution with dynamic slots, although it provides much more, like layered classes. This example is much more self-contained. The context Lately I'm working on the repaint queue for McCLIM. While doing so I've decided to make stream operations thread-safe, so it is possible to draw on the stream and write to it from arbitrary thread asynchronously. The access to the output record history needs to be clearly locked, so that may be solved by the mutex. Graphics state is another story, consider the following functions running from separate threads: (defun team-red () (with-drawing-options (stream :ink +dark-red+) (loop for i from 0 below 50000 do (write-string (format nil "XXX: ~5d~%" i) stream)))) (defun team-blue () (with-drawing-options (stream :ink +dark-blue+) (loop for i from 0 below 50000 do (write-string (format nil "YYY: ~5d~%" i) stream)))) (defun team-pink () (with-drawing-options (stream :ink +deep-pink+) (loop for i from 0 below 25000 do (case (random 2) (0 (draw-rectangle* stream 200 (* i 100) 250 (+ (* i 100) 50))) (1 (draw-circle* stream 225 (+ (* i 100) 25) 25)))))) (defun gonow (stream) (window-clear stream) (time (let ((a (clim-sys:make-process #'team-red)) (b (clim-sys:make-process #'team-blue)) (c (clim-sys:make-process #'team-grue))) (bt:join-thread a) (bt:join-thread b) (bt:join-thread c) (format stream "done!~%"))) ) Operations like WRITE-STRING and DRAW-RECTANGLE can be implemented by holding a lock over the shared resource without much disruption. The drawing color on the other hand is set outside of the loop, so if we had locked the graphics state with a lock, then these functions would be serialized despite being called from different processes. The solution to this problem is to make graphics context a dynamic slot that is accessed with WITH-DRAWING-OPTIONS. Summary I hope that I've convinced you that dynamic variables are cool (I'm sure that majority of readers here are already convinced), and that dynamic slots are even cooler :-). Watch forward to the upcoming McCLIM release! If you like technical writeups like this, please consider supporting me on Patreon. Full Article
mi TurtleWare: Dynamic Vars - The Empire Strikes Back By turtleware.eu Published On :: Mon, 28 Oct 2024 00:00:00 GMT Table of Contents Thread Local storage exhausted The layer of indirection I can fix her Let's write some tests! Summary Thread Local storage exhausted In the last post I've described a technique to use dynamic variables by value instead of the name by utilizing the operator PROGV. Apparently it works fine on all Common Lisp implementations I've tried except from SBCL, where the number of thread local variables is by default limited to something below 4000. To add salt to the injury, these variables are not garbage collected. Try the following code to crash into LDB: (defun foo () (loop for i from 0 below 4096 do (when (zerop (mod i 100)) (print i)) (progv (list (gensym)) (list 42) (values)))) (foo) This renders our new technique not very practical given SBCL popularity. We need to either abandon the idea or come up with a workaround. The layer of indirection Luckily for us we've already introduced a layer of indirection. Operators to access dynamic variables are called DLET, DSET and DREF. This means, that it is enough to provide a kludge implementation for SBCL with minimal changes to the remaining code. The old code works the same as previously except that instead of SYMBOL-VALUE we use the accessor DYNAMIC-VARIABLE-VALUE, and the old call to PROGV is now DYNAMIC-VARIABLE-PROGV. Moreover DYNAMIC-EFFECTIVE-SLOT used functions BOUNDP and MAKUNBOUND, so we replace these with DYNAMIC-VARIABLE-BOUND-P and DYNAMIC-VARIABLE-MAKUNBOUND. To abstract away things further we also introduce the constructor MAKE-DYNAMIC-VARIABLE (defpackage "EU.TURTLEWARE.BLOG/DLET" (:local-nicknames ("MOP" #+closer-mop "C2MOP" #+(and (not closer-mop) ecl) "MOP" #+(and (not closer-mop) ccl) "CCL" #+(and (not closer-mop) sbcl) "SB-MOP")) (:use "CL")) (in-package "EU.TURTLEWARE.BLOG/DLET") (eval-when (:compile-toplevel :execute :load-toplevel) (unless (member :bordeaux-threads *features*) (error "Please load BORDEAUX-THREADS.")) (when (member :sbcl *features*) (unless (member :fake-progv-kludge *features*) (format t "~&;; Using FAKE-PROGV-KLUDGE for SBCL.~%") (push :fake-progv-kludge *features*)))) (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)) ;;; ... (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))) With these in place we can change the portable implementation to conform. #-fake-progv-kludge (progn (defun make-dynamic-variable () (gensym)) (defun dynamic-variable-value (variable) (symbol-value variable)) (defun (setf dynamic-variable-value) (value variable) (setf (symbol-value variable) value)) (defun dynamic-variable-bound-p (variable) (boundp variable)) (defun dynamic-variable-makunbound (variable) (makunbound variable)) (defmacro dynamic-variable-progv (vars vals &body body) `(progv ,vars ,vals ,@body))) I can fix her The implementation for SBCL will mediate access to the dynamic variable value with a synchronized hash table with weak keys. The current process is the key of the hash table and the list of bindings is the value of the hash table. For compatibility between implementations the top level value of the symbol will be shared. The variable +FAKE-UNBOUND+ is the marker that signifies, that the variable has no value. When the list of bindings is EQ to +CELL-UNBOUND+, then it means that we should use the global value. We add new bindings by pushing to it. #+fake-progv-kludge (progn (defvar +fake-unbound+ 'unbound) (defvar +cell-unbound+ '(no-binding)) (defclass dynamic-variable () ((tls-table :initform (make-hash-table :synchronized t :weakness :key) :reader dynamic-variable-tls-table) (top-value :initform +fake-unbound+ :accessor dynamic-variable-top-value))) (defun make-dynamic-variable () (make-instance 'dynamic-variable)) (defun dynamic-variable-bindings (dvar) (let ((process (bt:current-thread)) (tls-table (dynamic-variable-tls-table dvar))) (gethash process tls-table +cell-unbound+))) (defun (setf dynamic-variable-bindings) (value dvar) (let ((process (bt:current-thread)) (tls-table (dynamic-variable-tls-table dvar))) (setf (gethash process tls-table +cell-unbound+) value)))) We define two readers for the variable value - one that simply reads the value, and the other that signals an error if the variable is unbound. Writer for its value either replaces the current binding, or if the value cell is unbound, then we modify the top-level symbol value. We use the value +FAKE-UNBOUND+ to check whether the variable is bound and to make it unbound. #+fake-progv-kludge (progn (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)))) (defun dynamic-variable-value (dvar) (let ((tls-value (%dynamic-variable-value dvar))) (when (eq tls-value +fake-unbound+) (error 'unbound-variable :name "(unnamed)")) tls-value)) (defun (setf dynamic-variable-value) (value dvar) (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)))) (defun dynamic-variable-bound-p (dvar) (not (eq +fake-unbound+ (%dynamic-variable-value dvar)))) (defun dynamic-variable-makunbound (dvar) (setf (dynamic-variable-value dvar) +fake-unbound+))) Finally we define the operator to dynamically bind variables that behaves similar to PROGV. Note that we PUSH and POP from the thread-local hash table DYNAMIC-VARIABLE-BINDINGS, so no synchronization is necessary. #+fake-progv-kludge (defmacro dynamic-variable-progv (vars vals &body body) (let ((svars (gensym)) (svals (gensym)) (var (gensym)) (val (gensym))) `(let ((,svars ,vars)) (loop for ,svals = ,vals then (rest ,svals) for ,var in ,svars for ,val = (if ,svals (car ,svals) +fake-unbound+) do (push ,val (dynamic-variable-bindings ,var))) (unwind-protect (progn ,@body) (loop for ,var in ,svars do (pop (dynamic-variable-bindings ,var))))))) Let's write some tests! But of course, we are going to also write a test framework. It's short, I promise. As a bonus point the API is compatibile with fiveam, so it is possible to drop tests as is in the appropriate test suite. (defvar *all-tests* '()) (defun run-tests () (dolist (test (reverse *all-tests*)) (format *debug-io* "Test ~a... " test) (handler-case (funcall test) (serious-condition (c) (format *debug-io* "Failed: ~a~%" c)) (:no-error (&rest args) (declare (ignore args)) (format *debug-io* "Passed.~%"))))) (defmacro test (name &body body) `(progn (pushnew ',name *all-tests*) (defun ,name () ,@body))) (defmacro is (form) `(assert ,form)) (defmacro pass ()) (defmacro signals (condition form) `(is (block nil (handler-case ,form (,condition () (return t))) nil))) (defmacro finishes (form) `(is (handler-case ,form (serious-condition (c) (declare (ignore c)) nil) (:no-error (&rest args) (declare (ignore args)) t)))) Now let's get to tests. First we'll test our metaclass: (defclass dynamic-let.test-class () ((slot1 :initarg :slot1 :dynamic nil :accessor slot1) (slot2 :initarg :slot2 :dynamic t :accessor slot2) (slot3 :initarg :slot3 :accessor slot3)) (:metaclass class-with-dynamic-slots)) (defparameter *dynamic-let.test-instance-1* (make-instance 'dynamic-let.test-class :slot1 :a :slot2 :b :slot3 :c)) (defparameter *dynamic-let.test-instance-2* (make-instance 'dynamic-let.test-class :slot1 :x :slot2 :y :slot3 :z)) (test dynamic-let.1 (let ((o1 *dynamic-let.test-instance-1*) (o2 *dynamic-let.test-instance-2*)) (with-slots (slot1 slot2 slot3) o1 (is (eq :a slot1)) (is (eq :b slot2)) (is (eq :c slot3))) (with-slots (slot1 slot2 slot3) o2 (is (eq :x slot1)) (is (eq :y slot2)) (is (eq :z slot3))))) (test dynamic-let.2 (let ((o1 *dynamic-let.test-instance-1*) (o2 *dynamic-let.test-instance-2*)) (signals error (slot-dlet (((o1 'slot1) 1)) nil)) (slot-dlet (((o1 'slot2) :k)) (is (eq :k (slot-value o1 'slot2))) (is (eq :y (slot-value o2 'slot2)))))) (test dynamic-let.3 (let ((o1 *dynamic-let.test-instance-1*) (exit nil) (fail nil)) (flet ((make-runner (values) (lambda () (slot-dlet (((o1 'slot2) :start)) (let ((value (slot2 o1))) (unless (eq value :start) (setf fail value))) (loop until (eq exit t) do (setf (slot2 o1) (elt values (random (length values)))) (let ((value (slot2 o1))) (unless (member value values) (setf fail value) (setf exit t)))))))) (let ((r1 (bt:make-thread (make-runner '(:k1 :k2)))) (r2 (bt:make-thread (make-runner '(:k3 :k4)))) (r3 (bt:make-thread (make-runner '(:k5 :k6))))) (sleep .1) (setf exit t) (map nil #'bt:join-thread (list r1 r2 r3)) (is (eq (slot2 o1) :b)) (is (null fail)))))) Then let's test the dynamic variable itself: (test dynamic-let.4 "Test basic dvar operators." (let ((dvar (make-dynamic-variable))) (is (eql 42 (dset dvar 42))) (is (eql 42 (dref dvar))) (ignore-errors (dlet ((dvar :x)) (is (eql :x (dref dvar))) (error "foo"))) (is (eql 42 (dref dvar))))) (test dynamic-let.5 "Test bound-p operator." (let ((dvar (make-dynamic-variable))) (is (not (dynamic-variable-bound-p dvar))) (dset dvar 15) (is (dynamic-variable-bound-p dvar)) (dynamic-variable-makunbound dvar) (is (not (dynamic-variable-bound-p dvar))))) (test dynamic-let.6 "Test makunbound operator." (let ((dvar (make-dynamic-variable))) (dset dvar t) (is (dynamic-variable-bound-p dvar)) (finishes (dynamic-variable-makunbound dvar)) (is (not (dynamic-variable-bound-p dvar))))) (test dynamic-let.7 "Test locally bound-p operator." (let ((dvar (make-dynamic-variable))) (is (not (dynamic-variable-bound-p dvar))) (dlet ((dvar 15)) (is (dynamic-variable-bound-p dvar))) (is (not (dynamic-variable-bound-p dvar))))) (test dynamic-let.8 "Test locally unbound-p operator." (let ((dvar (make-dynamic-variable))) (dset dvar t) (is (dynamic-variable-bound-p dvar)) (dlet ((dvar nil)) (is (dynamic-variable-bound-p dvar)) (finishes (dynamic-variable-makunbound dvar)) (is (not (dynamic-variable-bound-p dvar)))) (is (dynamic-variable-bound-p dvar)))) (test dynamic-let.9 "Stress test the implementation (see :FAKE-PROGV-KLUDGE)." (finishes ; at the same time (let ((dvars (loop repeat 4096 collect (make-dynamic-variable)))) ;; ensure tls variable (loop for v in dvars do (dlet ((v 1)))) (loop for i from 0 below 4096 for r = (random 4096) for v1 in dvars for v2 = (elt dvars r) do (when (zerop (mod i 64)) (pass)) (dlet ((v1 42) (v2 43)) (values)))))) (test dynamic-let.0 "Stress test the implementation (see :FAKE-PROGV-KLUDGE)." (finishes ; can be gc-ed (loop for i from 0 below 4096 do (when (zerop (mod i 64)) (pass)) (dlet (((make-dynamic-variable) 42)) (values))))) All that is left is to test both dynamic variable implementations: BLOG/DLET> (lisp-implementation-type) "ECL" BLOG/DLET> (run-tests) Test DYNAMIC-LET.1... Passed. Test DYNAMIC-LET.2... Passed. Test DYNAMIC-LET.3... Passed. Test DYNAMIC-LET.4... Passed. Test DYNAMIC-LET.5... Passed. Test DYNAMIC-LET.6... Passed. Test DYNAMIC-LET.7... Passed. Test DYNAMIC-LET.8... Passed. Test DYNAMIC-LET.9... Passed. Test DYNAMIC-LET.0... Passed. NIL And with the kludge: BLOG/DLET> (lisp-implementation-type) "SBCL" BLOG/DLET> (run-tests) Test DYNAMIC-LET.1... Passed. Test DYNAMIC-LET.2... Passed. Test DYNAMIC-LET.3... Passed. Test DYNAMIC-LET.4... Passed. Test DYNAMIC-LET.5... Passed. Test DYNAMIC-LET.6... Passed. Test DYNAMIC-LET.7... Passed. Test DYNAMIC-LET.8... Passed. Test DYNAMIC-LET.9... Passed. Test DYNAMIC-LET.0... Passed. NIL Summary In this post we've made our implementation to work on SBCL even when there are more than a few thousand dynamic variables. We've also added a simple test suite that checks the basic behavior. As it often happens, after achieving some goal we get greedy and achieve more. That's the case here as well. In the next (and the last) post in this series I'll explore the idea of adding truly thread-local variables without a shared global value. This will be useful for lazily creating context on threads that are outside of our control. We'll also generalize the implementation so it is possible to subclass and implement ones own flavor of a dynamic variable. Full Article
mi TurtleWare: Dynamic Vars - Return of the Jedi By turtleware.eu Published On :: Mon, 04 Nov 2024 00:00:00 GMT Table of Contents The protocol Control operators Synchronized hash tables with weakness First-class dynamic variables STANDARD-DYNAMIC-VARIABLE SURROGATE-DYNAMIC-VARIABLE Thread-local variables The protocol The implementation Thread-local slots 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! Full Article
mi Why Virat Kohli, Jasprit Bumrah were missing from Perth nets; India ramp up privacy amid Manchester United-like security - Hindustan Times By news.google.com Published On :: Wed, 13 Nov 2024 04:53:31 GMT Why Virat Kohli, Jasprit Bumrah were missing from Perth nets; India ramp up privacy amid Manchester United-like security Hindustan TimesVirat Kohli in focus: Intense net session begins for upcoming Test series against Australia The Times of IndiaVirat Kohli in Australia for BGT: A timeline India TodayBlack veil of secrecy: India begin training in privacy in Perth ESPNcricinfoIndia to play intra-squad warm-up match at WACA on Friday ahead of Australia Tests but BCCI denies public viewing Hindustan Times Full Article
mi 3 Children, 3 Women Missing After 10 Suspected Kuki Militants Killed In Encounter In Manipur's Jiribam - NDTV By news.google.com Published On :: Tue, 12 Nov 2024 11:11:01 GMT 3 Children, 3 Women Missing After 10 Suspected Kuki Militants Killed In Encounter In Manipur's Jiribam NDTVManipur on boil: 2 more bodies found, 6 missing The Times of IndiaAdditional paramilitary forces rushed to Manipur amid spike in ethnic violence Hindustan TimesLetters to The Editor — November 13, 2024 The Hindu2 men found dead, 6 of family missing day after militants killed in Manipur India Today Full Article
mi fuck god dammit By www.marriedtothesea.com Published On :: Sun, 23 Oct 2022 04:00:00 EDT Today on Married To The Sea: fuck god dammitThis 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! Full Article autogen_comic
mi 21 million bitcoins By www.marriedtothesea.com Published On :: Sun, 06 Nov 2022 04:00:00 EST Today on Married To The Sea: 21 million bitcoinsThis 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! Full Article autogen_comic
mi aha so you admit By www.marriedtothesea.com Published On :: Wed, 30 Oct 2024 04:00:00 EDT Today on Married To The Sea: aha so you admitThis 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! Full Article autogen_comic
mi Hallo kroket! Mike De Decker vermorzelt Michael Smith en treft Luke Littler in Grand Slam of Darts - Gazet van Antwerpen By news.google.com Published On :: Wed, 13 Nov 2024 05:28:38 GMT Hallo kroket! Mike De Decker vermorzelt Michael Smith en treft Luke Littler in Grand Slam of Darts Gazet van AntwerpenSterke Van den Bergh en De Decker stoten door in Grand Slam of Darts, onverwachte exit Van Gerwen sporza.beVIDEO. Hallo kroket! Mike De Decker vermorzelt Michael Smith en treft Luke Littler in 1/8ste finale Grand Slam of Darts Het NieuwsbladVIDEO. Dimitri Van den Bergh flitst naar 1/8ste finales Grand Slam of Darts, met dank aan een muntje: “Ik ga iets uit mijn achterzak pakken...” Gazet van AntwerpenMike De Decker bij laatste 16 op Grand Slam of Darts RTV Full Article
mi Gladiator 2: De langstverwachte film van het jaar is een mission impossible - VRT.be By news.google.com Published On :: Wed, 13 Nov 2024 04:30:06 GMT Gladiator 2: De langstverwachte film van het jaar is een mission impossible VRT.beHele verhaal bekijken via Google Nieuws Full Article
mi Matteo Simoni over geschrapte naaktscènes in ‘Callboys’: “Jan Eelen pakte mijn ‘jos’ en zwierde hem weg” - Het Laatste Nieuws By news.google.com Published On :: Wed, 13 Nov 2024 05:52:00 GMT Matteo Simoni over geschrapte naaktscènes in ‘Callboys’: “Jan Eelen pakte mijn ‘jos’ en zwierde hem weg” Het Laatste Nieuws Full Article
mi Amerikaanse militair Jack Teixeira (22), die geheime informatie over oorlog lekte, krijgt 15 jaar cel - Het Nieuwsblad By news.google.com Published On :: Wed, 13 Nov 2024 06:22:28 GMT Amerikaanse militair Jack Teixeira (22), die geheime informatie over oorlog lekte, krijgt 15 jaar cel Het NieuwsbladJonge Amerikaanse militair die Pentagon-documenten lekte veroordeeld tot 15 jaar cel VRT.beLive - Oorlog in Oekraïne. Jack Teixeira, die geheime documenten over oorlog lekte, veroordeeld tot 15 jaar cel • Noord-Koreaanse soldaten betrokken bij gevechten, zeggen VS De Morgen15 jaar cel voor Amerikaanse militair die geheime documenten lekte NOS15 jaar cel voor Amerikaanse militair die geheime informatie op internet deelde NU.nl Full Article
mi Anneleen Van Bossuyt: “Een N-VA-kiezer is niet minder Gents dan wie links stemt” - De Standaard By news.google.com Published On :: Wed, 13 Nov 2024 02:00:00 GMT Anneleen Van Bossuyt: “Een N-VA-kiezer is niet minder Gents dan wie links stemt” De StandaardBreed glimlachend op de foto, maar schaduw van N-VA hangt over Gents schepencollege: “Er is nog wantrouwen in onze afdeling” Het Nieuwsblad‘In Gent moet Filip Watteeuw de kelk tot op de bodem ledigen’ Knack.be▶ Dit is het verschil tussen het akkoord met N-VA en het akkoord met Groen in Gent De MorgenHafsa El-Bazioui (Groen) na tumultueuze weken in Gent: "Ik kan me niet excuseren voor mensen die spontaan op straat komen" VRT.be Full Article
mi Trump breidt ‘dreamteam’ uit en benoemt Elon Musk tot hoofd “ministerie voor overheidsefficiëntie": “Dit zal schokgolven sturen door het systeem” - Het Laatste Nieuws By news.google.com Published On :: Wed, 13 Nov 2024 07:18:00 GMT Trump breidt ‘dreamteam’ uit en benoemt Elon Musk tot hoofd “ministerie voor overheidsefficiëntie": “Dit zal schokgolven sturen door het systeem” Het Laatste NieuwsTrump wijst oude bekende Ratcliffe aan als hoofd CIA | Presidentsverkiezingen VS Het Laatste NieuwsTrump plaatst Elon Musk mee aan hoofd van nieuw ministerie: “Dit gaat schokgolven door het systeem jagen” Het NieuwsbladMiljardairs Elon Musk en Vivek Ramaswamy aan hoofd van Trumps nieuwe ministerie van ‘overheidsefficiëntie’ De StandaardTrump benoemt Musk tot 'minister van overheidsefficiëntie' De Tijd Full Article
mi Down fall to Cork in semi-final By www.bbc.co.uk Published On :: Sun, 15 Apr 2012 17:47:13 GMT Defending champions Cork beat Down 2-17 to 1-12 in the National League Division One semi-final at Croke Park. Full Article Northern Ireland
mi C.J. Stroud tops the list of best-selling NFL jerseys midway through 2024 season By www.foxsports.com Published On :: Tue, 12 Nov 2024 19:29:22 -0500 Three quarterbacks lead the way in top NFL jerseys sales so far this season. Full Article nfl
mi 49ers agree to 5-year, $92 million extension with CB Deommodore Lenoir By www.foxsports.com Published On :: Tue, 12 Nov 2024 19:51:46 -0500 San Francisco 49ers cornerback Deommodore Lenoir has agreed to a five-year, $92 million extension to stay with the team instead of testing the free agent market next offseason Full Article nfl
mi Deion Sanders compares Shedeur and Travis’ chemistry to Michael Irvin and Troy Aikman | Speak By www.foxsports.com Published On :: Wed, 13 Nov 2024 01:06:37 +0000 Deion Sanders talks about the strong chemistry between Shedeur Sanders and Travis Hunter, comparing it to the connection Michael Irvin had with Troy Aikman during their playing days. Full Article college-football
mi Michael Irvin asked Deion Sanders about coaching the Cowboys | Speak By www.foxsports.com Published On :: Wed, 13 Nov 2024 01:34:13 +0000 Michael Irvin asks Deion Sanders about the possibility of coaching the Dallas Cowboys, sparking a funny moment and laughter. Full Article college-football
mi sir, miss (at school) By separatedbyacommonlanguage.blogspot.com Published On :: Sat, 19 Aug 2023 23:14:00 +0000 In my last newsletter, I reacted to this news story:The article is about addressing teachers as sir or miss, which happens in American schools too (I'm sure there's a lot of variation in that across schools and regions). But in the newsletter I mentioned BrE referential use of the words when talking about the teacher (rather than talking to the teacher). I said: "I’m often taken aback when my child (like any ordinary English child) refers to her teachers as Sir and Miss"—which she often does.My former colleague David replied to say that he found this odd, since as "a moderately ordinary English child in the north of England in the 1960s," he addressed his (all male) teachers as Sir, but would refer to them by name or description (e.g., our English teacher). He concluded that "referring to teachers as Sir and Miss may be either more recent or more southern."While the usage may have been new in the 1960s, it definitely existed then, apparently even in the north.The OED's first citation for that use of Sir is from 1955 in a novel by Edward Blishen, who hailed from London: "‘The cane,’ said Sims vaguely. ‘Sir can't,’ said Pottell...’" A few other quotations can be seen in the OED snippet below (note their nice new layout!) On to Miss. The first referring-to-(not addressing)-a-teacher citation for Miss is from 1968 in a book by an author from Salford (in the northwest). (You'll spot another Miss example from that book in the Sir examples above. I've reported the error.)Did Miss really only appear a decade after referential Sir? I doubt it. We have to rely on written records, usually published ones, and there aren't a lot of written records in the voice of schoolchildren. Fiction helps, but it has its biases and gaps. And then, of course, there was the 1967 British film To Sir, with Love, in which Sir is used as if it is the name of the teacher played by Sidney Poitier. Is it a term of address there, or referential? Well, the title always seemed weird to me—certainly not a way I'd address a package. This Sir seems halfway between address and reference. We could label packages with the second-person pronouns that we usually used to address people, i.e., "To you", but we tend to use the third person: "To David". Rather than addressing the recipient, it seems to be announcing the recipient. This past academic year, for the first time, I was addressed as Miss a fair amount (no name, just Miss). This came from a new student who apparently was carrying over school habits to university, and so my colleagues were all Miss as well. I thought often about saying something about it to the student, but I also thought: I know what they mean, so why bother? I get to correct people enough in my job, I don't have to take every opportunity to do so and certainly don't need to make a big deal out of what I'm called. (Just don't call me late for dinner.) One picks one's pedantic battles. It's not a million miles from how I feel about my students calling a lecture or seminar a lesson, which I've written about back here.If you're interested, here's more I've written on:titles and address terms in higher educationthe structure of school education Full Article education names politeness
mi Times People Were Awkwardly Misjudged By cheezburger.com Published On :: Fri, 30 Oct 2020 06:00:00 -0700 Sometimes people are all-too ready to make assumptions about other people, and those assumptions get in the way of empirical facts. You hope someone doesn't misjudge you so publicly that they make a total fool of themselves. Or maybe you do. Everyone knows wrongful times people were misjudged tend to stick in their memory for years. Full Article Sad wtf Awkward lol
mi 12 Comics That Put Gender Discrimination Into Perspective By cheezburger.com Published On :: Sun, 25 Aug 2019 19:00:00 -0700 Full Article women trolling cringe
mi Woman's Insightful Facebook Post On The College Admission's Scandal Goes Viral By cheezburger.com Published On :: Mon, 26 Aug 2019 07:00:00 -0700 Everyone needs to read this woman's insightful Facebook post amidst the whole college admission's scandal dominating our news feeds. Full Article scandal news facebook social media politics college
mi The Funniest Protest Signs By People Who Think Trump's Presidency Is a Terrible Mistake By cheezburger.com Published On :: Thu, 29 Aug 2019 07:00:00 -0700 These people are not afraid to carry on hilarious protest signs and tell the world what they really think of Donald Trump's Presidency. Via: Sad and useless Full Article Protest signs donald trump president trump memes
mi Twitter Is Roasting Ivanka Trump For Claiming She Had A Punk Phase By cheezburger.com Published On :: Sat, 28 Sep 2019 07:00:00 -0700 New York Magazine published an excerpt from Ivana Trump's memoir Raising Trump - and it has since become a wildly entertaining meme. Thhe excerpt is actually a quote from Ivanka, reminiscing about her "punk" days. "During my punk phase in the nineties, I was really into Nirvana. My wardrobe consisted of ripped corduroy jeans and flannel shirts. One day after school, I dyed my hair blue. Mom wasn't a fan of this decision. She took one look at me and immediately went out to the nearest drugstore to buy a $10 box of Nice'n Easy. That night, she forced me to dye my hair back to blond. The color she picked out was actually three shades lighter than my natural color… and I have never looked back!"The quote has left Twitter users in stitches, making Photoshop memes and mocking the wealthy businesswoman's statement. The results have been delightful. Full Article fashion twitter donald trump Memes
mi Michael Bloomberg Memes That Roast The Cringey Billionaire By cheezburger.com Published On :: Fri, 21 Feb 2020 19:00:00 -0800 You're probably already aware that Michael Bloomberg, cringey billionaire extraordinaire and 2020 presidential candidate, has been reaching out to various big-name meme accounts on Instagram asking them to post content that makes him look a little more...hip to the kids, shall we say? But apparently he's doing something right with his presidential campaign, as he's been soaring in the polls and we're seeing ads for him practically everywhere.Normally we don't like to get too political, but it's clear that Bloomberg was not hugged enough as a child. Full Article michael bloomberg 2020 cringe funny memes president dank memes bloomberg rich people election 2020 politics
mi 'Today Years Old' Revelations That Might Teach Something New By cheezburger.com Published On :: Tue, 08 Sep 2020 19:00:00 -0700 The saying "you learn something new every day" may not always feel true, but on Twitter, that's definitely the case. People are very eager to share surprising and little-known facts that blow their minds - whether they're political or about something as trivial as the flavor of green Haribo gummy bears. These facts might not be revelatory for everyone, but they definitely aren't common knowledge. And it's nice to make that brain feel a little bigger. Full Article twitter funny tweets
mi 30 Middle Class Memes For Lighthearted Suburbanites By cheezburger.com Published On :: Mon, 09 Nov 2020 07:00:00 -0800 Do you take pleasure in converting old doors into coffee tables or old coffee tables into pallets? Do you spend a lot of time carefully crafting email responses? Does grabbing a beer or two at the local Chili's after a Sunday at Home Depot sound appealing to you? If you answered yes to any of these questions, then you might get a kick out of these neat memes by novelty memelord @middleclassfancy. We're big fans of this hilarious account and love to share these tongue-in-cheek gems every chance we get. Full Article relatable funny memes Memes twitter memes funny internet memes donald trump joe biden politics
mi Falco Pyramid By game.giveawayoftheday.com Published On :: Mon, 04 Nov 2024 03:00:00 -0500 Falco Pyramid is a dynamic game where you have to protect your base from an advancing wave of multi-colored cubes. The playing field is a pyramid expanding towards you, in which new cubes appear and move towards you. Your task is to shoot multi-colored cubes from a cannon, creating combinations of three or more cubes of the same color. Explosions of cubes destroy them and stop the onslaught from the pyramid. The longer you hold off the onslaught of cubes, the more points you get. But be careful, because the pyramid is constantly expanding, and the cubes are moving faster and faster. Falco Pyramid is a game that requires quick reactions, logical thinking and precise goal setting. Test your skills and hold off the onslaught of cubes in Falco Pyramid! Full Article
mi We Know Diversity is Good for Business, So Why Do Corporate Leaders Remain Predominantly White and Male? By www.diversityjobs.com Published On :: Tue, 10 Nov 2020 21:07:22 +0000 An examination of diversity in senior leadership roles at America’s top companies Diversity is more than just a buzzword, it’s a recipe for corporate success. Multiple studies have shown that a more diverse workforce in terms of age, gender, ethnicity, as well as veteran, disability, and LGBTQIA+ status can bolster innovation.i A more diverse corporate talent pool is […] The post We Know Diversity is Good for Business, So Why Do Corporate Leaders Remain Predominantly White and Male? appeared first on DiversityJobs.com. Full Article Diversity Statistics Workplace Diversity featured
mi How tech giants have been getting away with Age Discrimination By www.diversityjobs.com Published On :: Tue, 24 Nov 2020 15:27:03 +0000 Corporate diversity initiatives have focused primarily on skin color, ethnicity, and gender thus far, but age should also be imperative. Aside from it being an overlooked and unmonitored protected category, workforce demographics project a talent shortage for all diversity groups under 40. In the coming months, IBM could face hundreds of lawsuits (and/or a class […] The post How tech giants have been getting away with Age Discrimination appeared first on DiversityJobs.com. Full Article Diversity Statistics Diversity Training Workplace Diversity
mi Mike Fu’s Book Notes music playlist for his novel Masquerade By largeheartedboy.com Published On :: Thu, 31 Oct 2024 23:53:24 +0000 "Every song is a portal into the past, a chance to connect with countless mundane moments from before: where you were, what you felt, who you were with when you happened to be listening to the same thing." Full Article Author Playlists books Mike Fu music playlists
mi Heidi Bell’s playlist for her story collection “Signs of the Imminent Apocalypse” By largeheartedboy.com Published On :: Sun, 10 Nov 2024 15:36:03 +0000 "For this playlist, I’ve strung together some favorite songs that reflect the subject matter and themes of the stories and that serve as fuel for my creative process." Full Article Author Playlists books Heidi Bell music playlists
mi 31 Days, 31 Lists: 2018 Comics for Kids By blogs.slj.com Published On :: Thu, 20 Dec 2018 05:00:31 +0000 This year, ALA established the first Graphic Novels & Comics Round Table (GNCRT). Long gone are the days when we would pooh-pooh anything with a panel or a little sequential art. And so, in celebration of their status, I cast aside the "graphic novel" moniker. Long live, comics! Long live, panels! And long live speech balloons! Full Article 31 Days 31 Lists Best Books of 2018 2018 graphic novels 31 days 31 lists comics graphic novels
mi Quick Microwave Cleaning: Safe & Easy Steps By unclutterer.com Published On :: Wed, 24 Jan 2024 19:55:12 +0000 We've all been there: you're about to heat up your favorite meal, and you notice your microwave is splattered with remnants of meals past. It's not just unsightly; it can also affect the taste and smell of your food. That's why I'm here to share my go-to tips for getting your microwave sparkling clean without a fuss. The post Quick Microwave Cleaning: Safe & Easy Steps appeared first on Unclutterer. Full Article Kitchen Uncategorised Kitchen Cleaning
mi Chipotle’s new CEO Scott Boatwright will make millions leading the chain—but only half as much as ex-CEO Brian Niccol By biztoc.com Published On :: Wed, 13 Nov 2024 05:02:33 GMT Chipotle’s new chief executive is Scott Boatwright, a familiar face and name, given that he’s served as the interim CEO since August following former CEO Brian Niccol’s surprise decision to lead a turnaround at Starbucks. And while Boatwright, 52, will get a significant pay boost in his new… Full Article
mi Examining MicroStrategy’s record-shattering $21bn ATM By biztoc.com Published On :: Wed, 13 Nov 2024 05:13:31 GMT Examining MicroStrategy’s record-shattering $21bn ATM Roula Khalaf, Editor of the FT, selects her favourite stories in this weekly newsletter. History, they say, doesn’t repeat, but it often rhymes. Bitcoin-hodling MicroStrategy is an excellent example. In early 2000, MicroStrategy embodied the… Full Article
mi Infowars auction could determine whether Alex Jones is kicked off its platforms By biztoc.com Published On :: Wed, 13 Nov 2024 05:13:58 GMT Conspiracy theory purveyor Infowars and most of its assets are set to go on the auction block Wednesday, with Alex Jones waiting to see if he will be allowed to stay or if he will get kicked off its online platforms. The private auction is being held as part of Jones' personal bankruptcy, which… Full Article
mi YouTube is now letting creators remix songs through AI prompting By biztoc.com Published On :: Wed, 13 Nov 2024 05:24:47 GMT YouTube added a capability for select creators in the U.S. to create AI-generated songs via prompts using the vocals of artists like Charlie Puth, Charli XCX, Demi Lovato, John Legend, Sia, T-Pain, and Troye Sivan last year. Now, the company is adding a feature for select creators to remix a track… Full Article
mi The Asset of the Millennium Isn’t What You Think By biztoc.com Published On :: Wed, 13 Nov 2024 05:35:18 GMT Full Article