es

Texas Ironman 70.3 (Galveston)!


I just completed my second Ironman 70.3 ("half-Ironman") in Galveston, Texas!

It was different, but not too different than the first. In some ways it was more challenging than Austin, although I did manage to eke out a personal best, so I count myself pleased, although I've identified several areas where I can improve :-).


The Galveston site presented a couple more difficulties than the Austin one, viz., transportation of people and bicycles, as well as housing therefor.  The race site was at Moody Gardens, which has its own resort hotel, which we might have done if we had planned on going on Friday and staying until Monday.  Ultimately, we decided to drive down the morning before the race with our bikes, rent a house via Airbnb, and drive back the evening of the race.

Leaving Austin at 7 AM...
With two bikes on the back, three people (and one bike) in the car, and all our gear, we were pretty packed, but the drive only took about three and a half hours, even accounting for coffee breaks.

We drove directly to Moody Gardens to pick up our race packets and drop off our bikes.  The first thing we discovered when we arrived was that it was cold and rainy and the swim venue (Offats Bayou) was a bit choppy...

Under the triumphal arch the day before the race...
Registration/packet pickup was in a big white tent overlooking the bayou (the same big white tent Moody Gardens had used for a cool dinosaur thing a couple years back).

The white tent with dinosaurs five years ago...
The pickup and registration and dropoff were strangely inefficient: You wait in line at a first table to show your ID and USA Triathlon membership; then you go to a second table to pick up your waiver and other forms. Next, you go to a third table to fill in and sign your waiver and forms, and go to a fourth table to drop off your signed waiver and forms. Thereafter, you go to a fifth table to pick up your wristband and swim cap and bike and helmet stickers; go to a sixth table to pick up your backpack/goodie bag and T-shirt; and finally, go to a seventh table to pick up your timing chip.

Then you exit via the Ironman store and vendor village where you can wait in line to buy stuff.


Inside the big white tent
The course talk was outside and the rain had died down enough so that it wasn't all that unpleasant. Behind us was the paddlewheeler Colonel and a whole lot of choppy water with whitecaps. We were assured that the weather could be better the next day, however. No rain and significantly less wind, although there would be a headwind on the return portion of the bike route.

The paddlewheeler Colonel.
The swim course, with the finish right by the paddlehweeler
The Airbnb was a nice little three-bedroom cottage about five miles from the event site and across from a large cemetery.  It had a complete kitchen that would come in useful later that night when we couldn't get into any of the nearby Italian restaurants without reservations.


The house we rented
Cemetery across street.

Making spaghetti dinner
That evening, we gathered all of our gear and made dinner and looked forward to the race. I went for a three mile run around the nieghborhood to loosen up a bit, and then we made dinner.

I managed to get around five or six hours of sleep and only hit the snooze button once when the alarm went off at 4:30 in the morning. After a couple cups of coffee, a banana, and a bagel, we were off!

The temperature felt good: low fifties, not too much of a breeze. I decided I didn't need gloves, but would take along my arm warmers for the bike just in case. We arrived at Moody Gardens a little after six and discovered we had had to park about a fifteen minute walk from the actual transition area.  A remarkably long line greeted us to enter transition (for body marking), but since we had already done so, we were able to get in with only a brief wait and some judicious weaving through the crowd.

Testing out the wetsuit when I first got it.
There was then the ritual of putting on the wetsuit and pumping the bike tires and then transition closed! (We might have cut the timing a little close).

We then made our way separately to the swim start: they did a wave start by age group. You jump off the pier (see above map), hang around in the water for a couple minutes, and then swim like the wind when you hear the starter's horn.

The water was a nice 72 degrees, about twenty degrees warmer than the outside temperature. The water felt good, although it took a few minutes to get used to the waves and occasional mouthful of salt water. I felt pretty good, though, and noticed myself passing a lot of people.  More importantly, I managed to beat my swim time from last fall's Austin Ironman 70.3.


 Once I was out of the water, the wetsuit strippers were efficient and I had no problem getting to my bike (even without my glasses), stowing my wetsuit and taking off. Because it was relatively warm, I decided not to bother with my sleeves, and I wouldn't have worn gloves even if I had brought them.

The first half of the bike portion was glorious. The temperature was perfect and I had no problems staying in aero position almost the whole way.  Unfortunately, at the turnaround, I was made to realize just how much of a tailwind I had been benefitting from. The rest of the ride felt like I was pulling a mobile home...

Grimacing with miles to go...

Also about that time, the temperature dropped by about ten degrees, and it started to rain. And then my back started to hurt from being in aero position for so long.  In short, the ride back was completely miserable...But I managed to break three hours, which had been my goal.

In addition to the lower back pain, I got a pain between my shoulders, and every time I tried to stand up in the pedals to stretch, my legs felt like they wanted to cramp up.  And my hands were so cold I could barely move them to squeeze my water bottles.

Trying to squeeze out the last drop from my water bottle.

By the time I got back to transition, my hands were so numb that I couldn't operate the clip on my helmet. Putting on my shoes and tying my laces was equally an ordeal. It didn't help that my legs and abs kept cramping up when I bent to tie the laces.  After a ridiculously long transition, I made it onto the run course and my watch died.


But my legs felt good and I enjoyed the run a lot more than I thought I was going to on the return bike. :-). My pacing was a bit off and I came in somewhat slower than I would've liked, but it still felt pretty good.

Victory!
Overall, I came in a couple minutes better than the Austin Ironman 70.3, which I'm pleased with (although I think my run could've gone better).

Mugging with the finisher's medal

Using the R8 recovery roller thing...
All in all, it was a great experience and I'd definitely do the race again. A big thanks to all the organizers, volunteers, sponsors, and first responders who made the event a success!










es

Why does Jupiter spin so fast?

The gas giant is the Solar System's largest planet. Here's why it's also the fastest-spinning planet.




es

Does Jupiter protect Earth from asteroids and comets?

Jupiter has often been thought to protect the inner Solar System from asteroids and comets, but new research has shown that the giant planet may actually increase the risk of an impact.




es

U.S. Senate advances their FY 2025 budget proposal for NASA amid deep cuts

An analysis of the U.S. Senate's FY 2025 budget request for NASA.




es

Why NASA does space science and not the private sector

With all the advances in private space exploration, why do taxpayers still pay for space science missions?




es

A billion dollars short: A progress report on the Planetary Decadal Survey

NASA is underfunding planetary exploration relative to recommendations made by the National Academies Decadal Survey report, resulting in mission delays and cancelations.




es

Ramses: A new mission racing to land on asteroid Apophis

When a skyscraper-sized asteroid narrowly misses Earth in 2029, three spacecraft may be along for the ride.




es

Why the “habitable zone” doesn’t always mean habitable

The habitable zone is a useful concept in astrobiology, but it can sometimes paint an over-simplified picture of planetary habitability.




es

The Tianlin Space Telescope

China is in the early stages of planning a huge space observatory to help answer the matter of whether we are alone in the galaxy.




es

Extraterrestrial artifacts

Could the Solar System host traces of other intelligent life?




es

New insights into asteroid properties: A STEP Grant update

A Planetary Society-funded project to understand asteroids achieved its main goals and scientific objectives this year.




es

Spacecraft, what do your robot eyes see?

Cameras on spacecraft are our eyes into the Cosmos. Sometimes they teach us things, sometimes they reveal gaps in our knowledge.




es

Where Congress Stands on NASA's 2025 budget

Weeks before the new fiscal year, Congress still hasn't finalized NASA's 2025 budget.




es

Cloudy skies, smooth sailing

A Martian cloud atlas, LightSail wins big, and multiple missions coast toward launch.




es

Europa Clipper: A mission backed by advocates

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.




es

Hera launches to study the aftermath of an asteroid deflection test

The European Space Agency’s Hera spacecraft launched on Oct. 7, 2024, from Cape Canaveral, Florida. It will travel to the Didymos-Dimorphos asteroid system to study the aftermath of the first-ever field test of an asteroid deflection technique.




es

Twinsies!

Asteroids that come in pairs, matching volcanic features on Mars and Earth, and the potential space policies of two administrations.




es

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

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




es

Upgrade for Member Services System

The Planetary Society is upgrading systems that will offer us many new capabilities and features that will enhance your membership experience.




es

Grand designs

From logos to policy to mission architectures, if you want to achieve something in space, you’ve got to design it first.




es

Best of 2024

Cast your vote for the best of space exploration and science in 2024!




es

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

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




es

The James Brand × Timex Automatic GMT Watch




es

Barbour Ladies Pendle Beanie & Scarf Gift Set





es

Christmas jewelry in progress.

Sneak peek — Christmas jewelry in progress.




es

Starry Woods and Tardigrades!

New in the shop — Starry Woods and Tardigrades! scarves.




es

Christmas serving board in progress.

Christmas serving board in progress.




es

Signed and personalized at request!

We just made the decision to ship the remaining copies of Feast to us. There are about 800 left, out of the original 2000 copy print run. Which is a lot of copies, but given that we were originally scheduled to launch March 2020, and had to cancel an entire summer’s worth of scheduled bookstore […]




es

The Armies march to Partizan

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.



  • lord Of The Rings
  • lord Of The Rings.

es

The Battle of the Five Armies at Partizan.

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...
 




  • lord Of The Rings
  • lord Of The Rings.
  • War games

es

The Battle of the Five Armies



 Here's a shot of James Morris' Lonely mountain with the only missing element...thousands of blood drinking bats. 



  • lord Of The Rings
  • lord Of The Rings.

es

Bonnie Blue Flag Rules


 I couldn't believe my luck at the Other Partizan show last Sunday, I actually got to meet Kevin Calder, the creator of the Bonnie Blue Flag rules.

I first saw the Iron Brigade banner and followed it down to reveal an actual BBF game being played. I recognised Kevin instantly and shook his hand. I gushed about his rules and told him how much I like them. I also explained about play testing it a month ago and he said he had seen the post, which was nice. It was only a quick hello as I had to race back to my own game.

I would have liked to have talked for longer and ask him lots of nerdy questions, like how to adapt them for AWI etc. still, I'm sure I'll bump into him again.



  • Bonnie Blue Flag

es

Stones, Shrines and Alters of Erin


Here are the finished standing stones from Alternative Armies amongst others.
I've added a few extra skulls to the base once flocked.
A bear hide has been left as an offering too.

Battle shine, again extra weapons have been added to the base. The rocks form a sacred circle in which to knee and offer your items.

Paper ferns were added to this base. I'm not sure how they will last as they are fragile. Being hard up against the stone will help them.
This place must be strong in fey magic because flowers have sprung up around the stones. Flowers are a nice way to show the affinity of a place. Flowers equal a nice holy area, where dead and dying grass says the opposite.


The great fertility rock with it's comfy furs to lie on. I wanted to give this the impression of a giant lady in a fur cloak. The cloak is made from moss and ivy.
Ancient pillar, I added some ivy to this to weather it in. Also putting flock to act as moss up one side helped to age it. Moss normally grows on the north side of trees, so I added this trick to a lot of the stones.



This is a miniature from Reaper Bones, I think it's called 'Evil tower'. I thought it would make a good Fomorian piece or maybe something older and darker. What ever it is, it has been thrust up from the ground and killed the surrounding grass. It must be cursed...


Ivy again helps weather these small stones. This isn't a bad place as flowers grow around the mound, new life from old.

I've rediscovered Celtos as a figure range that might be useful for this growing project.


 


It's time to dig out the old stones and compare them. A lot of these previous models had a greenstuff carving in them.







The two weapon shrines. One is a place of worship, the other, sacrifices.


The two stones of the dead. These could be used for any undead project.






Recently I thought about making more of an effort to photograph my miniatures. I have seen other people use backdrops and so thought I'd play around with the idea. By using a photo from a book as a background, the results can be quite different. Rather than a cluttered work table full of detail taking away from an image, the background actual enhances and compliments it. This simple book set up has really improved these stone pictures and it's something I'll do again for sure.




es

FIW using Bonnie Blue Flag Rules


Here are the craziest scribblings of a madman. Having selected BBF for my black powder games, I'm now just trying to down scale it for a skirmish type game. Smaller units but still using multi bases for figures. Units of four seem to work as a base number.
 
Here are the unit sizes in Muskets and Tomahawks that are good to see as a guide. All just early days still just throwing ideas around to see if something sticks.

Drilled Vs irregular base.

Update: Right! I've bit the bullet and revised my Indians for the game.
I decided to go with 60x60mm bases to give a more spread out look. Also these Galloping Major figures are quite large for 28mm so it suits them better.






I imagine four bases of Indians to a unit with a base of skirmishers. I just want one rule set for my black powder games and I think BBF is the one, with a few tweaks of course.
My latest thinking is make Indians 'green', so they won't stick around long once the casualties start to mount up. A +10 melee modifier makes them dangerous close up so it's worth trying to get them into hand to hand. They are just too wise to stick around when things go bad. An experienced unit of warriors would be quite imposing with their combat bonus, something to be feared.
I had thought of giving Rangers the same combat bonus but as they will be veterans, a plus 10 would be too much. I think let's class them as Elites with a +5.



  • Bonnie Blue Flag

es

War of the Roses Basing

The men of Lord Hastings' Retinue struggle through the mud of Tewkesbury.

Vallejo thick mud was the perfect solution for the grim battle conditions of the war. Adding snow to this layer would look fantastic but would rather limit the battles. I think a generic muddy field is a good all rounder for this brutal conflict.



I broke my usual 69x60mm basing after seeing a friend's and decided to copy it. Partly because the cheapness of plastics allows for bigger units. I also have quite a few old Perry miniatures from the old days of Foundry. These old lead figures are great for sprinkling amongst the ranks to add character. The above photo shows the effect of these old sculpts. They have to be mounted on plastic bases etc to bring them up to the height of the newer plastics. The mud is great for covering these and making everyone level.
As the Vallejo mud was drying, I cut up some thin brush bristles and pushed them into the mixture. These make for great arrows and really helps to give the bases a War of the Roses look and feel.
The mud is also great for splashing up the legs and clothes of the soldiers. It's quite subtle but helps to  set them in the scene.


The mud isn't quite dry yet and there are a couple more things to do before they are finished. Layers of 'Rutted field' from Luke's APS should look good over the mud, as well as patches of static grass. Also the arrows will need some white goose fletching on them.


 These new bases are 80x60mm and give a more realistic look to a unit. I got a bit carried away with these bases and they grew to 10 men per base.

The figures In these units are a mix of old Foundry, Perry's plastics and Forlorn Hope metal figures. They all mix together well and make for characterful formations.



  • War of the Roses

es

TurtleWare: Dynamic Vars - The Empire Strikes Back

Table of Contents

  1. Thread Local storage exhausted
  2. The layer of indirection
  3. I can fix her
  4. Let's write some tests!
  5. 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.




es

"Dragons of Paris" and the Role of Time in the Mongolian Wizard Series

 .

The kind people at Reactor Magazine have posted my two latest Mongolian Wizard stories, one yesterday and the other today. Thursday's "Halcyon Afternoon" took place during a rare moment of peace for Franz-Karl Ritter. But in today's "Dragons of Paris," it's warfare as usual. 

Time has always been a little tricky in this series. The first story was clearly set in the Nineteenth Century but, though only a few years have passed, the series has now reached what is recognizably World War I. Mostly this occurred for reasons explained in "The Phantom in the Maze" and "Murder in the Spook House." (And which I anticipate giving me increasing difficulties in writing the next ten stories.) But also, in a more literary background sense, I wanted to cover the transition from a way of life now alien to us to something more modern, if not contemporary. 

So time may get a bit more slippery in the future. That's if, of course, the stories go in the direction I intend. Sometimes the fiction has its own ideas where it wants to go and the author can only follow along meekly in its wake.

You can read the story here. Or just go to the ezine and poke around. It's a good place to poke around.


Above: The illustration is by Dave Palumbo. I'm grateful for that.


*




es

My Halloween Season Story, "Unquiet Graves," in CLARKESWORLD

 .


 

I am always happiest when a story of mine comes into print. Today, I have the joy of introducing you to "Unquiet Graves," a seasonal tale of graveyard misbehavior and betrayal. Oh, and there's nothing supernatural about it at all.

You can read the story here. But if you're like me, you'll just go to Clarkesworld, look over the table of contents, and decide which story you want to read first. Mine by preference, but follow your whim.

 

And for those who like trivia . . .

I came up with the handheld's app many long years ago and it took forever to come up with a story for it. You'll notice that it is left unnamed in the story. That's because its secret name was "The Graveyard Reader." Which is the title of a well-known story by Theodore Sturgeon.  While I was writing the story, I thought of it as "The New Graveyard Reader." But Sturgeon's story and mine go off in totally different directions, and giving mine (or even the app) a title suggesting there was some implicit connection between the two would only cause confusion.

The title I finally came up with was derived from "The Unquiet Grave" by that most prolific of all poets, Anonymous. If you look it up, I suggest you do so after reading my story. It gives away some of the plot.


*

 








es

Popular destinations in Sweden

Popular destinations in Sweden



View Comic!







es

Don 039 t mess with Acorns

Don 039 t mess with Acorns



View Comic!







es

Estonia 039 s National Animal

Estonia 039 s National Animal



View Comic!









es

Why Virat Kohli, Jasprit Bumrah were missing from Perth nets; India ramp up privacy amid Manchester United-like security - Hindustan Times

  1. Why Virat Kohli, Jasprit Bumrah were missing from Perth nets; India ramp up privacy amid Manchester United-like security  Hindustan Times
  2. Virat Kohli in focus: Intense net session begins for upcoming Test series against Australia  The Times of India
  3. Virat Kohli in Australia for BGT: A timeline  India Today
  4. Black veil of secrecy: India begin training in privacy in Perth  ESPNcricinfo
  5. India to play intra-squad warm-up match at WACA on Friday ahead of Australia Tests but BCCI denies public viewing  Hindustan Times