Saturday, December 09, 2017

Announcing AltCover

Now I have time to myself, and the season doesn't lend itself to outdoor activities, I can start dusting off my various coding projects and devoting the time and energy to those that I used to expend for pay.

First off the block is AltCover, an alternative code coverage tool for .net and Mono.

This is a project I started back in the dark days in the spring of 2010, when changes in the profiling API of the new CLR 4.0 release meant that the old freeware NCover 1.5.x series no longer functioned. For some considerable while, the only FOSS alternative that worked with the new CLR (by instrumenting the code under test before execution, rather than on-the-fly) seemed to be an initial proof-of-concept on CodeProject.

At that point, I'd been looking for something non-trivial to work on that would provide the opportunity to use F# to build up my fluency in the language; and so the obvious thing to to was to re-implement from scratch and extend to cover such gaps as I found in its functionality when trying to use it as a near drop-in replacement for the now non-functional NCover version.

After some considerable interval and a failed dalliance with PartCover, including my first contribution to a real FOSS tool, but never a resolution to one real sticking problem, where it looked like JITting across assembly boundaries was causing executed lines to not appear in the coverage, I shelved my work in favour of the off-the-shelf OpenCover, where I could intermittently contribute enhancements to cover personal pain points.

Why have I dusted it off again now?

Well, for much the same reasons as before; a non-trivial project that does answer some pain-points (Mono amongst them) that OpenCover's necessarily intimate relationship with the runtime makes difficult. And it provides a reason to play with new toys that have grown up over the last few years, like Fake for builds, and the generous provision of CI tools to FOSS projects so people can take builds rather than having to roll their own.

Tuesday, December 05, 2017

The first day of the rest of my life

Well, that's it. I'm now no longer in employment, so that means I'm sort-of retired, now.

Damn, that's a big backlog of things I've got queued to do!

Sunday, December 03, 2017

November Cycling

Up to 15167.9, 998.5, 217.0 = 187.1 (YTD 2126 miles), with no weather stopping play breaks from the usual two commuting days a week, even despite the cold snap at the end of the month.

Amusingly, in discussion with colleagues over miles logged this year, the slow-but-steady accumulation of commuter miles through the year, combined with the disappointing summer weather, means that I've ended up ahead of many of the keen recreational cyclists who hadn't yet reached the 2000 mile mark (though that's still way, way behind those who commute almost every day and do recreational cycling on top).

Monday, November 13, 2017

October Cycling

Up to 14984.7, 998.5, 213.1 = 138.5 + 3.5 miles off-meter + 90.3 on the usual cycling holiday = 232.3 (YTD 1939 miles), again, better than I'd been doing most of the summer, with the excuse of a pub crawl around the Stour valley helping. More windy than wet, this month, and continuing mild.

Clearly a busy throughfare

For once, I went through the weird country between Dedham and Stoke-by-Nayland, rather than going around up to Hadleigh, and reminded myself of why I rarely take that route -- crossing the B1068, the road becomes almost disused, and is a long grind of a mile or so on uphill on a decaying surface.

Thursday, October 05, 2017

September Cycling

Up to 14846.2, 998.5, 213.1 = 139.9 + 55.6 + 7.5 miles off-meter == 203 (YTD 1707 miles), better than I'd been doing most of the summer, and in a month where the weather was bad enough that I got severely soaked a couple of times -- but also a month in which I did actually find space for one ride just-for-fun, even if that one ended up dodging showers all the way.

Thursday, August 31, 2017

August Cycling

Up to 14706.3, 942.9, 213.1 = 19.4 + 160.7 + 6.8 == 186.9 (YTD 1504 miles), slightly less than last month, with the weather staying less than summery up to almost the end of the month (and an unusually hot dry Bank Holiday Monday). The basic 4x40 miles/week commute was the backbone, with some side trips on the other bikes, mostly business, only one being a "stretch my legs on a summer afternoon" leisure ride.

Wednesday, August 02, 2017

July Cycling

Up to 14686.9, 782.2, 206.3 = 0 + 143.9 + 43.4 = 187.3 (YTD 1317 miles), in a month where the summer weather ended early, and where rides on holiday being instead of, rather than in addition to, the miles I normally commute in a week.

Peddars Way

As you can tell from the photo, the weather was already on the way downhill in the second week of the month, leading up to our driving home in the rain on St Swithin's Day.

Saturday, July 01, 2017

June Cycling

Up to 14686.9, 638.3, 162.9 = 31 + 177.4 + 20.8 = 229.2 (YTD 1130 miles), in a dry and not too windy month. One long bike-ride, going the pretty way to the shops, but the rest is mostly business (including road-testing maintenance like putting a new chain and new tyres on the winter bike).

Wednesday, June 07, 2017


Elegant little lady

Gone at the age of just 14 months.

She had been out and roaming for much of the spring, home only for meals and bed-time, so I didn't find occasion for cute cat pictures of her, unlike her tree-climbing brother. Only anecdotes, like the time just a week or so ago when the local nuisance muntjac was wandering down the close, and then headed down one of the driveways -- then a little black scrap that had been watching scampered in apparent pursuit. And memories of her leaping up and sprawling on my lap, just a little featherweight who shed handfuls of fur when I combed her.

Sunday, she didn't turn up for supper, but did join the feeding frenzy for the bribe of wet food to come in for the night. Monday, she was not the only one who didn't get out of their basket when I went out early to work, but was looking a bit peaky. When I came home she had found somewhere to hide where I couldn't see her, so I feared for the worse. Again, she emerged late evening, to use the litter tray, but this time showed little interest in food, and was clearly unsteady on her feet.

So, off to the vets, with a fever and dehydration; and it turned out over the last couple of months, she had started to lose weight. Being such a small and fluffy cat compared with the others, and having a healthy appetite but without begging for more, it hadn't been obvious until just a few days ago. The blood tests showed that her white cell count was low, and after she had been relaxed with pain relief, they found a lump in her abdomen. She responded a bit to to rehydration, but her condition deteriorated over night.

Thursday, June 01, 2017

May Cycling

Up to 14655.9, 460.9, 142.1 = 186.9 miles (YTD 901 miles), so a bit up on the previous month, in what ended up rather pleasant weather. No long bike rides for fun, not even the long way around to work, though, as more time got spent in the garden again.

Friday, May 05, 2017

Chunked Enumerable revisited (in F#)

Revisiting an old post on the subject, prompted by a recent question on the MSDN Visual F# forum.

Despite it being a very common thing to want to do, to process a stream of data in blocks, there isn't a library function for it yet; and it's not something that can be put together in a functional form using the pieces we have in the Collections.Seq module. Either, as in my earlier attempts, we have to go into object-land, with intrusions into the implementation details of enumerations (and their disposal), and eager evaluation at best; or we have to go imperative/procedural to get full lazy evaluation as per the code buried within the this C# example.

For idiomatic F#, we don't want an extension method, but a stand-alone function which we can chain into the usual pipeline of |> operations, so we would translate it thus —

where we bury the imperative/mutable kernel, the place where we count the number of steps we take (or stop early when we hit the end of the input), inside an inner function.

Monday, May 01, 2017

April Cycling

Up to 14655.9, 274.0, 142.1 = 173.2 miles, which is about the same as the monthly rate I averaged over Q1 (YTD 714 miles). Way down on last year; I didn't even keep up the 30 Days challenge, as the 4th was wet in the afternoon/early evening. When we have had good weather, I've been more inclined to work in the garden, where it's sheltered from the rather chill winds that characterised most of the month or get things done around the house; and it's less draining to start early, drive, finish early than cycle, work, cycle, work some more, cook. What rain we have had in a generally dry month also managed to skew towards driving and staying dry on days where I might normally have cycled; and these days, the chance to stop in town on the way home has reduced the need or incentive to cycle into town at weekends.

Saturday, April 01, 2017

March Cycling

Up to 14655.9, 148.8, 142.1 = 193.6 miles + ~90 off-meter = 284, which is better than January and February combined (YTD 541 miles), with more commuting and shopping by bike, and in the good weather at the end of the month, another quick CycleBreaks holiday and the first days of cycling with bare arms for the year (even if a chill easterly kept bringing in haze from the North Sea).

Sunday, being the day after the clocks went even an early start was late by the clock, and being Mothering Sunday to boot, finding lunch as well as the usual getting the saddle height just right and getting used to the bike meant I ended up doing a fairly short ride, during which time the cloud and wind picked up, and encouraged me back to base. Monday started grey, but by the time I'd gone most of the way through Ipswich the sun was burning through, so I could take the jacket off for the full circuit of Alton Water and then back again. Tuesday was misty until late morning, but I managed a ride out to Snape and back after it started to clear, with a pit-stop at the Elephant and Castle in Eyke on the way back.

Without an on-board measure this time, I estimated the journeys on Google Maps, and it was quite a surprise to find that the "short" ride to Snape and back was about the same distance as the "long" loop of Alton Water that took me about 50% longer in terms of time in the saddle.

Tuesday, February 28, 2017

February Cycling

Up to 14625.1, 0, 128.1 -- 147.6 miles, which is better than January, since I did manage to average more than one day a week commuting by bike, and the weather has at times felt positively spring-like. No rides just for pure pleasure, as yet, but definitely more inclination to go by bike for business.

Saturday, February 25, 2017

Powershell Transcript cmdlets and secondary runspaces gotcha

So, I had some fun this past week, with a piece of code that, stripped to its essentials, looked like

which yields

Stop-Transcript : An error occurred stopping transcription: The host is not currently transcribing.
At line:1 char:1
+ Stop-Transcript
+ ~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [Stop-Transcript], PSInvalidOperationException
    + FullyQualifiedErrorId : InvalidOperation,Microsoft.PowerShell.Commands.StopTranscriptCommand

with a transcript (again, stripped to its essentials) of

Transcript started, output file is ...
PS>$pool = [System.Management.Automation.Runspaces.RunspaceFactory]::CreateRunspacePool(1,1)
PS># do stuff...
Windows PowerShell transcript end

with nothing after the Close() showing up.

It turns out that, however you create a runspace, even if using an instance of a custom PSHost subclass and explicitly minting one through CreateRunspace(), the runspace will still get attached to an internal PSHost subtype, which couples it to a whole web of other internal and/or sealed types, eventually linking it to the transcription state of the overall PowerShell session. And when a runspace closes, it closes all open transcripts attached to it.


Fortunately, there is one public API available to us that can sever this link, and one that makes a perverse sort of sense, after you've run through all the plumbing:

which finishes with

Transcript stopped, output file is...

and a transcript that looks like

Transcript started, output file is ...

PS>$pool = [System.Management.Automation.Runspaces.RunspaceFactory]::CreateRunspacePool(1,1)
PS>$save = [System.Management.Automation.Runspaces.Runspace]::DefaultRunspace
PS>try {
    [System.Management.Automation.Runspaces.Runspace]::DefaultRunspace = $null
    # do stuff...
    # do more stuff...
finally {
    [System.Management.Automation.Runspaces.Runspace]::DefaultRunspace = $save
Windows PowerShell transcript end

because it turns out that, via a long chain of indirections, it is the -- fortunately thread-static -- global default runspace which is the thing that contaminates our intended-to-be-isolated worker environment.

Now, it would be understandable if runspace construction were to directly use the default runspace as a prototype, but it's nothing so obvious. It actually comes in via the UI object that is tenuously attached to the runspace reaching out to the default runspace. That's not so good, and speaks of excessive internal coupling.

Checking metrics on the assembly System.Management.Automation, we can see that it is indeed highly internally coupled, with a relational cohesion of 7.22, which is a level that is not so much coherent as positively incestuous. So while I didn't spot any other obvious booby-traps waiting to be sprung, I'm sure there are others that will rise up and bite the occasional edge-case.