f

New(ish) Stuff

A friend recently asked me to cast an eye over a summons she has received for a speeding offence. She was going too fast for a speed awareness course or a fixed penalty, and was duly reported. She is going through the Single Justice Procedure, which is a new one on me, but I expect that it consists of one JP sitting alone with a clerk, dealing with the simpler cases. In days gone by I used to sit alone on Saturdays when I could usually expect about ten or fifteen cases, mostly remands or discharges. My maximum power was a penalty of one pound or one day's imprisonment, which could fill the bill for he usual overnight drunks and nuisances. The real work was deciding on bail, and that is a serious matter when you are on your own.
I shall be interested to see how the new procedure works in practice; no doubt my one-time colleagues will be able to fill me in.




f

So. Farewell then Bystander...



We're very sorry to say that Bystander (real name Richard Bristow) died at Stoke Mandeville on June 4, aged 70. He was a Justice of the Peace at Uxbridge from 1985 to 2016,  and was the first chairman of the West London Local Justice Area. He'll be sadly missed by family and friends, but not by the villains of Uxbridge, Ealing and Hounslow.  

He was fond of quoting this passage from the Seven Ages of Man speech:

And then the justice,
In fair round belly with good capon lined,
With eyes severe and beard of formal cut,
Full of wise saws and modern instances;
And so he plays his part.




f

Gillings finishes sixth in Italy

British number one Zoe Gillings finishes sixth at the penultimate round of the boarder-cross World Cup in Valmalenco.




f

Scots curlers miss the play-offs

Scotland beat Italy and Canada but fall short of reaching the World Women's Curling Championship play-offs.




f

Brit Gillings gets funding boost

Britain's number one snowboarder Zoe Gillings will receive podium funding towards the 2014 Winter Olympic Games in Russia.




f

GB ice hockey get financial boost

Great Britain's men's ice hockey team receives a grant from the IOC to help in its bid to qualify for the 2014 Winter Olympics




f

Mississippi Book Festival

Last weekend I had the pleasure of being a panelist at the first annual Mississippi Book Festival at the State Capitol in Jackson, Mississippi!


For me, the weekend started with the plane flight out on Friday and a lovely reception that evening at the Eudora Welty House. Representatives of the Eudora Welty Foundation were on hand to provide tours and answer any and all questions about Jackson's favorite daughter.  It was a great chance to talk to the organizers and volunteers, as well as other authors.

The next morning was breakfast at the Winter Archives Building, where the staff gave us a tour and showed us the forthcoming Museum of Civil Rights and Mississippi History Museum.

Then we were off to opening ceremonies, where the Jackson State University Marching Band performed on the Capitol steps, and then the panels!  The Harper Lee Reconsidered panel, held in the old Supreme Court chamber, was lively and fascinating (and also covered by C-SPAN).  I wasn't able to make it to the picture books panel due to the long line, but hear it went well, and I'd had the chance to talk with the presenters the night before :-).

My panel was the Young Readers panel, and featured moderator Margaret McMullan, and panelists Kimberly Willis Holt, Taylor Kitchings, Deborah Wiles, Carolyn Brown, and Cassie Beasley.  Margaret did a great job as moderator and kept the conversation going and on track. :-).

Many thanks to all the organizers, volunteers, sponsors, and attendees for making the event such a success!

Altogether, it was a fantastic event, with standing-room-only crowds and a terrific venue!  Here's a report on the festival from the Clarion-Ledger: Book Festival Attendance Outpaces Projections.

And here are some pics from out and about festival weekend:

My duffel bag leaves the jetway in Houston
Art deco Greyhound Station, downtown Jackson
Kerry Madden, Susan Eaddy, Hester Bass, Chris Barton in the Eudora Welty House Garden

Deborah Wiles, Kerry Madden on the Eudora Welty House lawn
In front of the Eudora Welty House
MS State Capitol
Kerry Madden, Kimberly Willis Holt
W. Ralph Eubanks, Margaret McMullan
Jackson State University Marching Band

View from the Capitol steps
Capitol interior and dome
Dome in House of Representative Chamber
Dome of Senate Chamber
Mayflower Cafe
Kimberly, Taylor, Deborah, Margaret, Me, Cassie, Carolyn




f

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

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



  • pizza a day
  • Pizza a Day Diet

f

Lone Star Book Festival!

This past weekend, I had the pleasure of being one of the authors at the inaugural Lone Star Book Festival in Kingwood, Texas (just outside Houston)!

Here are some pics:

Edward Carey, Emma Virjan, Jennifer Ziegler, Bethany Hegedus, Carmen Oliver, and me
Carmen Oliver presents BEARS MAKE THE BEST READING BUDDIES
Jennifer Ziegler and Jo Whittemore discuss encouraging reading
I present CHRONAL ENGINE and BORROWED TIME
 Thanks to all the organizers, sponsors, and attendees! It's on its way to becoming a grand, annual tradition!




f

Texas Library Association Conference (#txla16)

Just back from the Texas Library Association Conference in Houston! 

As always, it was great to see fellow authors and illustrators, as well as the librarians who've supported our books through the years.  And I always enjoy seeing what's new at the publisher booths.

We had a great time for my panel Tuesday afternoon, "What's New with Texas Middle Grade and YA Authors," organized by Susie Kralovansky, featuring Jessica Lee Anderson moderating, and fellow panelists Paige Britt, Cory Putnam Oakes, P.J. Hoover, Cynthia Levinson, Liz Garton Scanlon, Jennifer Mckissack, and Joy Preble.  Conversation was entertaining and enlightening.  

Many thanks to everyone who puts in the work to make TLA the best state library conference in the country!

Here are some pics:

Hitting the road
Rainy Houston from the hotel
Me, Cynthia Levinson, PJ Hoover
Carmen Oliver signs
Jennifer McKissack, Jennifer Ziegler, Joy Preble
Paige Britt and Donna Janell Bowman
Me and the world in the lobby of the Hilton
Signing BORROWED TIME
Janet Fox and Jennifer Ziegler
PJ, Jessica, and Joy
Me and Elaine Scott
Buffalo Bayou on my early morning run
Back in Austin!
   





f

Capital of Texas Triathlon Preview

Monday I'm going to be running in the 25th Capital of Texas Triathlon!  It's my first triathlon (Olympic distance) in twenty years and I'm pretty jazzed.

Steely-eyed determination 20 years ago. :-)

One of the great things about triathlons (and running races in general) is that you get to occupy unusual spaces: the last ones I did were Leon's Triathlon in Hammond, Indiana, a couple of Bud Light triathlons and others in Chicago. Leon's had a swim in Wolf Lake (shudder), followed by a cycle leg on an elevated highway that ran past the old U.S. Steel plant, and a run leg through an industrial downtown.  The Chicago ones were on the lakefront, just north of Navy Pier, with a bike on Lake Shore Drive and a run along the lake.

2013 CapTexTri
The CapTexTri also has a great location in downtown Austin, with a 1.5k swim in Lady Bird Lake; a 40k (24.8 mile) bike on a quadruple loop through downtown Austin, including Congress Avenue and Cesar Chavez; and a 10k (6.2 mile) run through Zilker Park.

The only thing I'm not too keen on is the bike route, since it requires you to do the same loop four times with a bunch of corresponding hairpin turns.  I don't like loop routes because I always think of how many more times I have to do the thing...Still, going up and down Congress Avenue without any cars is going to be pretty cool. As long as there are no poles in the middle of the road, I should be okay. :-).

Don't ask.

I feel fairly good about my training.  I've maintained good running mileage after the Austin Marathon and Austin Distance Festival and got some good workouts in even while traveling doing school visits.
On Stone Arch Bridge in Minneapolis
The swim is probably my weakest event -- I could stand to do more work on technique and probably do more open water swimming, but the distance won't be an issue.  Also, Lady Bird Lake isn't going to have waves (I seem to recall a couple of triathlons in Chicago with 3-4 foot waves on Lake Michigan (and this was on the near side of the breakwater).  Also, I won't have to deal with a wet suit.  My biggest concern is to not get kicked in the face. :-).
Lady Bird Lake during 2013 CapTexTri
The bike I'm feeling good about as well.  I'll be using the bike I used for my triathlons back in the day - a Trek 1000 I bought when I was in grad school for $450 (a guy at one bicycle shop here tried to sell me a new one, asking if I had a "nostalgic attachment" to it.). I do, but I also don't think a new bike is going to drastically transform my performance.  At least not $2000 worth :-). (A guy at another bike shop told me the Trek 1000 was his first road bike and he wished he still had it.  It's possible he was being kind :-)).

Tomorrow is packet pick-up, bike drop-off, and a chance to scope out the transition area, which I'll need because I can't see without my glasses...:-)

Oh, well.  Qapla!















f

Capital of Texas Triathlon/Duathlon/10K/5K

So my plan to do my first triathlon in twenty years on Monday didn't turn out so well.

It rained.

A lot. But not so much in town.

At Camp Mabry, just north of central Austin, we got less than an inch of rain last Thursday. At Bergstrom Airport, just south and east of downtown, they got about nine inches.  And it was much worse farther east, along the Colorado River (which also runs through Austin).

On Friday and Saturday, it rained in the Hill Country.  West of Austin.  Upriver.

On Sunday, there was the CapTexTri expo and packet pickup and bike dropoff.

Swag!  My first cowboy hat since I was around seven.
Bike drop off.  I got there early.
Packet pickup
Coveting my neighbor's bike, Part I.
It was clear and sunny.  But because of all the rain, the LCRA opened at least one floodgate from the Tom Miller Dam, releasing water into Lady Bird Lake.  Which was where the swim portion of the CapTexTri was supposed to take place.

At the course talk at the expo, they announced that there was a flow of about 3 mph and they were considering changing the course so that it ran point to point (downriver).
Course talk.  Could've used a projector.


Later that day, they announced the swim was canceled.  Which was disappointing, but I've weathered a couple of triathlons in Chicago where that had happened and one when it probably should've.

Monday, race day, I awoke at 5 AM, fed the cats, ate breakfast and drank coffee, and then I heard rain.  Lots of rain.

Nevertheless (discovering, to my chagrin, that I am apparently an optimist), I headed out.  When I arrived at the transition area, I was told it was closed, and that we should shelter at Palmer Events Center or its garage.  This was around 6-620.

In the garage and on the deck of the Palmer Events Center, folks seemed to take things in stride and with humor.  Some people were concerned about hairpin turns on a wet course, but were generally willing to take it slow.
Sheltering in the garage

We heard a tentative plan to cut short the bike portion to 20k, but the rain and lightning continued. Finally, at around 830, race officials called off the bike portion entirely because of flooding on the course. 
Still a bit lightning-y
They announced that Olympic distance participants could do a 10k, while sprint participants could do a 5k and that start time would be at 10 am. Most folks removed their bikes and went home or back to their hotels. There was some grumbling -- last year the event had been cut short due to flooding, as well, and I gather there had been similar problems in 2014, too.
Athletes clearing out the transition area
 
I took my bike back to my car, but decided that I'd gotten up at five that morning to run a race and so, I was going to do one.  Besides, I didn't want to waste all those carbs I'd eaten in the past few days. :-).

At ten o'clock, the rain pretty much stopped.  And then we were off!  By 10:15, the sun came out.  No, really. 
Everyone who's still there seems in good humor :-)
And we're off!


Turned out, there were only about 200 of us who stuck around for the 10k, with another 150 for the 5k (out of around 3000 original participants), but everyone seemed to be having a good time.  I was pretty happy with my race -- I'm not sure it was exactly 10k, but I still did one of my better overall times and paces. At least this century :-).
The view from the Biergarten.  Note the utter absence of rain.
Sunny skies.
On the whole, it was a bit surreal but fun, although in the moment sometimes frustrating.  And, in retrospect, kind of funny.  I think the organizers did a good job under trying conditions and kept us pretty well informed via social media.  So, thanks (And I am really glad I wasn't in charge :-)).  Thanks also to all the volunteers who stuck around to the bitter end.

 Oh, and I actually ended up getting a bit of a tan.

Coveting my neighbor's bike, Part II
Epilogue: Late Monday, the Austin Fire Department closed Lady Bird Lake and Lake Austin to all boat traffic.

And they're giving us a discount for the 2017 race. :-).

Here's what the lake looked like Tuesday morning (Normally, there is no current at all):

















 




f

How soon will Starship fly?

One rocket could decide how soon humanity returns to the Moon — and maybe one day sets foot on Mars.




f

For parts: Lunar rover, never used

NASA has canceled VIPER, a rover designed to seek out water ice and other resources in the lunar south pole.




f

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.




f

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.




f

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.




f

NASA discovers Mars rock with ancient potential for life

A single 3.5 billion-year-old rock shows signs of all the conditions life needs to thrive.




f

A big find on Mars

A big rover makes a big find on Mars. Little rovers have their place in exploration, too.




f

Eureka? Scientists’ first hints of life on other planets may not be so obvious

Knowing that you've found signs of life beyond Earth may not be as clear-cut and simple as one might think.




f

How EELS could change the future of robotic exploration

The snake-like robot is being designed to autonomously navigate the challenging terrain of Saturn’s moon Enceladus, including descending into fissures in the moon’s icy crust. The skills it needs in order to explore this distant, unfamiliar world may make EELS well equipped to explore even more alien worlds, perhaps including exoplanets.




f

Mars may host oceans’ worth of water deep underground

The tentative discovery hints at an habitat where life could potentially thrive.




f

Life in other worlds

New research suggests liquid water might be hiding under the surface of Mars. Could life be there too?




f

What would happen if we nuked an asteroid?

Detonating a nuclear weapon on or near an asteroid is one of several options for defending the Earth from an impact. Here's what nuking an asteroid might actually do, and why it isn't always the best option.




f

Extraterrestrial artifacts

Could the Solar System host traces of other intelligent life?




f

Connecting ancient life to other worlds

Looking to the past to guide the search for life.




f

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.




f

Could Europa Clipper find life?

For a mission that doesn’t aim to find alien life, Europa Clipper may come surprisingly close.




f

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.




f

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.




f

Best of 2024

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




f

Uncharted Supply First Aid Kit




f

Vollebak Eiderdown Puffer Jacket




f

Glenmorangie A Tale of Ice Cream Single Malt Scotch Whisky




f

Barbour Ladies Pendle Beanie & Scarf Gift Set




f

One of my favorite native flowers

Native wine-cups (aka purple poppy mallow) bookends. This is one of my favorite native flower, so particularly pleased with how nicely it shows up in bookends.




f

War for Cybertron game






The new Deception medic 'Wrench' gets his first taste of combat.

 I love this little cassette bot, he's so cute. I must make some more.


Skyfire brings the rain.



  • War for Cybertron
  • War for Cybertron.

f

Dwarf King in the shield wall

A Dwarf king and his elite guard take to the field.

 Amongst the Eagle helms of the shield wall is the kings champion himself. Nothing must get to the king and the champions blue axe, will see that nothing does.

I could use this base for King Balin when he tries to retake Moria. A future project of mine.
The King points out floors in the enemies formation to the chief engineer.
The Dragon standard flies over head echoing the Kings mighty dragon helm.
This base has been created for the upcoming Hobbit battle, replacing Dain's base in the ranks. This allows Dain to roam freely as a character on the field.

 The figures are beautiful old sculpts from Asgard and Citadel. There has been a bit of tweaking on a couple of them, like the axe and the standard.



  • lord Of The Rings
  • lord Of The Rings.

f

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

f

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.

f

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

f

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.




f

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

f

Fomorian Shields from Hobbycraft

yesterday I popped to Hobbycraft to see if they had any pieces I could use for the new Midgard rules. I couldn't find any goblets but I did track down the beads needed for heroic deeds.

While I was travelling through the bead tray, I saw one that I thought would make a great Celtic shield.

It has a nice wrap around curve to it but more importantly, sports a nice Celtic swirly pattern.

Ancient Irish warriors fight with the sea devils.
It has a good level of detail to make it look well crafted and ornate. Once I sculpt a boss for the centre I think it will look just the ticket. Nice deep grooves that will catch ink and weathering very well. The swirl too is a very Celtic looking design.

 This tub was £4 which is unbelievable for an armies worth of shields. I might end up using these beads as heroic deeds markers too as they won't roll unlike the rounder ones.

You can get a next size up in container to fill with your beads for £8, again for hundreds of shields this isn't bad. The small tub should be enough though.




f

Fomorian Coastal Scenery

Here are some rock pools to add to a coastal game mat. I've added some kelp seaweed to make them a bit more North sea.







There are also a couple of other prints that I plan to use as Fomorian shrines. Places where they honour their dead and stack weapons and armour in tribute.
To save on the greenstuff, I switched to Milliput as it's cheaper for bigger pieces. 

I'll add some more skulls and weapons etc to these bases.
Update: So the Fomorians crawled out of the icy North sea in the night and adorned the pools with their burial goods.
Fomorian skulls and weapons now litter the swallow pools, soon to be covered by the rising tide.


Barnacles were just grit stuck with PVA glue. After the glue was dry I gave the grit another wash of the glue. This should make it stand up to a good dry brushing.
One of the new swirly shields gets placed in the pool. I gave the water a coat of PVA glue just to get rid of the 3D printing texture.
I will add a little more seaweed now the skulls are in.

Update: These models were sprayed black then dry brushed with grey and white. Contrast colours were then painted over the highlighted pools.



To make them look a bit more aquatic and fantasy, I included colours like blue and purple into the palette.



Here we can see a highlighted model before the Contrast paint washes.








Update: started to pick out the barnacles and shields etc.









f

Halfords Mat Lacquer

I thought I'd give this a go as I had heard good things about it. I thought the scenic rock pools would be the perfect test subject, as if they dried glossy it wouldn't matter.
The results were good. I sprayed the models after I had added some flock, I thought the lacquer would help seal it.
After the scenic pieces I thought I'd try it on the new Irish figures. I consider this a tough, hard coat. Another layer of super mat varnish will be added with a brush when this first coat is dry.
The rock pools got a layer of flock really nail the realistic look.

 The flock and paint work is all sprayed with the new lacquer, seems to be a winner.




f

Finished Rock pools


The last stage was to gloss varnish the water in the pools. This really brought them to life and gave them a sparkle.

The kelp and some of the green rocks were also given a lick with the gloss brush too. Not too much, just enough to give them a damp look.






The bottom of the pools were painted with loads of people's of different colours. This was then given a green wash, then a brown one to add depth to the pool.

 




f

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

f

Scott L. Burson: Comparison: FSet vs. Sycamore

[BULLETIN: Quicklisp now has the latest version of FSet.]

Sycamore, primarily by Neil Dantam, is a functional collections library that is built around the same weight-balanced binary tree data structure (with leaf vectors) that FSet uses.  While the README on that page comments briefly on the differences between Sycamore and FSet, I don't feel that it does FSet justice.  Here is my analysis.

Dantam claims that his library is 30% to 50% faster than FSet on common operations.  While I haven't done comprehensive micro-benchmarking, a couple of quick tests indicates that this claim is plausible.  A look through the internals of the implementation confirms that it is clean and tight, and I must commend him.  There may be some techniques in here that I could usefully borrow.

Most of the performance difference is necessitated by two design choices that were made differently in the two libraries.  One of these Dantam mentions in his comparison: FSet's use of a single, global ordering relation implemented as a CLOS generic function, vs. Sycamore's more standard choice of requiring a comparison function to be supplied when a collection is created.  The other one he doesn't mention: the fact that FSet supports a notion of equivalent-but-unequal values, which are values that are incomparable — there's no way, or at least no obvious way, to say which is less than the other, and yet we want to treat them as unequal.  The simplest example is the integer 1 and the single-float 1.0, which have equal numerical values (and cl:= returns true on them), but which are nonetheless not eql.  (I have a previous blog post that goes into a lot more detail about equality and comparison.)  Since Sycamore expects the user-supplied comparison function to return an integer that is negative, zero, or positive to indicate the ordering of its arguments, there's no encoding for the equivalent-but-unequal case, nor is there any of the code that would be required to handle that case.

Both of these decisions were driven by my goal for the FSet project.  I didn't just want to provide a functional collections library that could be called occasionally when one had a specific need for such a data structure.  My ambition was much grander: to make functional collections into a reasonable default choice for the vast majority of programming situations.  I wanted FSet users (including, of course, myself) to be able to use functional collections freely, with very little extra effort or thought.  While Lisp by itself reaches a little bit in this direction — lists can certainly be used functionally — lists used as functional collections run into severe time complexity problems as those collections get large.  I wanted the FSet collections to be as convenient and well-supported as lists, but without the time complexity issues.

— Or rather, I wanted them to be even more convenient than lists.  Before writing FSet, I had spent years working in a little-known proprietary language called Refine, which happened to be implemented on top of Common Lisp, so it was not unusual to switch between the two languages.  And I had noticed something.  In contrast to CL, with its several different predefined equality predicates and with its functions that take :test arguments to specify which one to use, Refine has a single notiion of equality.  The value space is cleanly divided between immutable types, which are compared by value — along with numbers, these include strings, sets, maps, and seqs — and mutable objects, which are always compared by identity.  And it worked!  I found I did not miss the ability to specify an equality predicate when performing an operation such as "union".  It was just never needed.  Get equality right at the language level, and the problem goes away.

Although FSet's compare generic function isn't just for equality — it also defines an ordering that is used by the binary trees — I thought it would probably turn out to be the case that a single global ordering, implemented as a generic function and therefore extensible, would be fine the vast majority of the time.  I think experience has borne this out.  And just as you can mix types in Lisp lists — say, numbers and symbols — without further thought, so you can have any combination of types in an FSet set, effortlessly.  (A project I'm currently working on actually takes considerable advantage of this capability.)

As for supporting equivalent-but-unequal values, this desideratum flows directly from the principle of least astonishment.  While it might not be too surprising for a set or map implementation to fail distinguish the integer 1 from the float 1.0, it certainly would be very surprising, and almost certainly a source of bugs in a compiler that used it, for it to fail to distinguish two uninterned symbols with the same name.  (I saw a macro expansion recently that contained two distinct symbols that both printed as #:NEW.  It happens.)  A compiler using Sycamore for a map on symbols would have to supply a comparison function that accounted for this; it couldn't just compare the package name and symbol name.  (You'd have to do something like keep a weak hash table mapping symbols to integers, assigned in the order in which the comparison function encountered them.  It's doable, but FSet protects you from this madness.)

Along with those deep semantic design choices, I've spent a lot of time on developing a wide and featureful API for FSet (an effort that's ongoing).  FSet has many features that Sycamore lacks, including:

  • seqs, a binary-tree sequence implementation that holds arbitrary Lisp objects (Sycamore ropes hold only characters, which is certainly an important special case, but why restrict ourselves?)
  • default values for maps and seqs (the value to return when the key is outside the domain is associated with the collection, not supplied at the call site; this turns out to be a significant convenience)
  • generic functions that operate on both lists and FSet collections, to shadow the CL builtins
  • the powerful map-union and map-intersection operations (I'll blog about these in the future)
  • more ways to iterate over the collections (the FSet tutorial has a good summary, about 3/4 of the way down)
  • speaking of the tutorial, FSet has lots more documentation

Let me digress slightly to give an example of how FSet makes programming more elegant and convenient.  Joe Marshall just put up a blog post comparing Go(lang) with Common Lisp, which is worth a read on its own; I'm just going to grab a code snippet from there to show a little bit of what programming with FSet is like.  Here's Joe's code:

 (defun collate (items &key (key #'identity) (test #'eql) (merger (merge-adjoin #'eql)) (default nil))
   (let ((table (make-hash-table :test test)))
     (dolist (item items table)
       (let ((k (funcall key item)))
         (setf (gethash k table) (funcall merger (gethash k table default) item))))))

 (defun merge-adjoin (test)
   (lambda (collection item)
     (adjoin item collection :test test)))

And here's what I would write using FSet:

 (defun collate (items &key (key #'identity))
   (let ((result (map :default (set))))
     (dolist (item items result)
       (includef (@ result (funcall key item)) item))))

(Well, I would probably move result outside the dolist form to make it clearer what the return value is, but let's go with Joe's stylistic choice here.)

For those who haven't used FSet: the form (map :default (set)) creates a map whose default is the empty set, meaning that lookups on that map will return the empty set if the key is not in the map.  This saves the includef form from having to handle that possibility.

My version makes assumptions, it's true, about how you want to collect the items with a given key; it doesn't give you other choices.  It could, but what would be the point?  It's already using a general set with better time complexity than lists, and saving you from having to write anything like merge-adjoin.  The extensible global equivalence relation means you're not going to need to supply a :test either.

I think the FSet-enhanced code is cleaner, more elegant, and therefore clearer than the plain-CL version.  Don't you agree?  Maybe you wouldn't say it's a huge improvement, okay, but it's a small example; in a larger codebase, I would argue, these small improvements add up.

* * * * *

To summarize: if you just want a library you can call in a few places for specific purposes, Sycamore might work better for you (but think hard if you're writing a comparator for symbols).  FSet can certainly be used that way, but it can be much more.  If you want to see one way in which Common Lisp can be made into a better language, without giving up anything that we love about it, I urge you to give FSet a try.

FSet has changed the way I write Lisp programs.  — an FSet user

(UPDATE: the magnitude of the performance difference between FSet and Sycamore surprised me, and inspired me to do some profiling of FSet.  It turned out that I could get a 20% speedup on one micro-benchmark simply by adding some inline declarations.  Mea culpa, mea culpa, mea maxima culpa; I should have done this years ago.   With that change, the generic function overhead appears to be the only significant cause of the remaining ~20% performance difference.  I tried creating a Sycamore set using a thin wrapper around fset:compare, and the resulting performance was very similar to that of FSet with its new inlines.)




f

TurtleWare: Dynamic Vars - Return of the Jedi

Table of Contents

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

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

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

The protocol

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

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

(defclass dynamic-variable () ())

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

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

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

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

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

Control operators

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

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

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

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

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

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

Synchronized hash tables with weakness

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

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

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

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

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

First-class dynamic variables

STANDARD-DYNAMIC-VARIABLE

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

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

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

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

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

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

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

SURROGATE-DYNAMIC-VARIABLE

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

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

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

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

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

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

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

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

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

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


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

Thread-local variables

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

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

The protocol

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

The implementation

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

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

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

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

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

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

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

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

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

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

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

Thread-local slots

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Finally the implementation of SLOT-DLET does not change:

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

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

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

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

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

#<process "Anonymous thread" 0x7f76424c0240>

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

What can we use it for?

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

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

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

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

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

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

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

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