Next
Wednesday, November 17, 2021

Async/Await

Resources

For me, a glaring omission in the above is how a non-GUI thread is supposed to work with async calls provided by another library. And how to avoid deadlock. My first use of async was with websockets, and right away I had deadlock. It seems that lots of people on stackoverflow have this same problem. It seems that ConfigureAwait is the solution. But it's not clear to me how it can be safely used. Also it's not clear how to terminate an async stack when you can't make the top-level call async. But a solution for that is given in stackoverflow as well.

Summary

Working with the breakfast analogy:

private static async Task<Egg> FryEggsAsync(int howMany)
{
  Console.WriteLine("Warming the egg pan...");
  await Task.Delay(3000);
  Console.WriteLine($"cracking {howMany} eggs");
  Console.WriteLine("cooking the eggs ...");
  await Task.Delay(3000);
  Console.WriteLine("Put eggs on plate");            
  return new Egg();
}
  • The async keyword in the signature just means that the body can use await. It doesn't cause any non-synchronous behaviour to occur.
  • Task<Egg> in the signature means that the body must return an Egg. In practice, this function will be partially executed, later resumed, and finally completed, returning an Egg. However when it is partially executed, and comes to a long-running part, it really does return, and it returns the Task which can communicate progress through many resumptions and final completion of the function.
  • Similarly, Task in the signature would mean that the body must return nothing, so you may not have any explicit return even though your signature says you return a task. This is because async methods really have two return types. There is the Task that communicates progress through resumptions and the final product of the work.
// short form
await Task.Delay(3000);

// long form
Task task = Task.Delay(3000); // start a job
await task; // get the output of a job

// short form
Egg egg = await FryEggsAsync(2);

// long form
Task task = FryEggsAsync(2);
Egg egg = await task;
  • Executing a "function that returns Task" starts some processing.
  • Awaiting a task extracts the final product of the work from the Task which implies that we have waited for the processing to complete, but await doesn't mean block, it means "proceed with other work in the mean time".
  • So in the body of FryEggsAsync, our thread prints "warming", then starts a delay, then goes elsewhere, and some time later comes back and prints "cracking". Where does it go? All the way up the stack.

Async All the Way

class Program
{
  static async Task Main(string[] args)
  {
      Console.WriteLine("starting");
      var eggsTask = FryEggsAsync(2);
      var baconTask = FryBaconAsync(3);

      Console.WriteLine("waiting");
      var eggs = await eggsTask;
      var baconTask = await baconTask;

      Console.WriteLine("done");
  }
}

It's tempting to think that it just goes to the next line after FryEggsAsync, which it does, but then you always hit another await, so we've only deferred the question. For a console Main, there is some compiler magic. For a GUI app, your thread goes back to its dominant task, which is pumping the queue of user initiated events to which it must respond. That works great for John's example:

Old Way:

public class MyService
{
  public void StartCostlyWork()
  {
    // Start something in another thread and return.
    // When that thing is done, that thread will trigger the ProcessCompleted event.
    // Starting the work must not throw exception.
  }

  public event EventHandler ProcessCompleted;
}

public class MyViewModel
{
  private MyService service;

  public MyViewModel()
  {
    service = new MyService();
    service.ProcessCompleted += OnMyServiceProcessCompleted;
  }

  public void RunOnGuiThreadFromButtonClick()
  {
    myService.StartCostlyWork();
  }

  // This is triggered regardless of success/failure. Status is reported in the outcome.
  public void OnMyServiceProcessCompleted(object sender, EventArgs outcome)
  {
    // In order to interact with the UI we have to get back to the UI thread.
    Dispatcher.BeginInvoke(() =>
    {
      // Do some UI stuff based on the outcome.
    });
  }
}

New Way:

public class MyService
{
  public async Task<Outcome> DoCostlyWorkAsync()
  {
    // Do something costly.
    // Due to internal await calls, the thread can partially complete and resume as needed.
    // Everything that happened before on the other thread and that was passed to
    // ProcessCompleted now happens here.
  }
}

public class MyViewModel
{
  private MyService service;

  public MyViewModel()
  {
    service = new MyService();
  }

  public async Task RunOnGuiThreadFromButtonClick()
  {
    // It's now possible to let the processing throw errors instead of packaging
    // that into the outcome, and we no longer need the Dispatcher because
    // by default await keeps us on the same thread.
    try
    {
      Outcome outcome = await service.DoCostlyWorkAsync();
      // Do some UI stuff based on the outcome.
    }
    catch (Exception exception)
    {
      // Do some UI stuff based on the outcome.
    }
  }    
}

That's fine, but what if I'm not a view model, and I have to call MyService.DoCostlyWorkAsync? Say I'm a library and I have to call HttpClient.PostAsync and I can't control my entry point?

public interface IWebHelper
{
  void StartCostlyWork();
}

I'm a component in a server. I have to implement IWebHelper and can't change the interface. I don't know who is calling me. It might be another thread. It's probably another application. It might be .NetRemoting, or gRPC. I have to support a synchronous entry point.

public class WebHelper: IWebHelper
{
  public void StartCostlyWork()
  {
    // This starts the task and discards it. This notation isn't specific to async/await.
    // It just tells the compiler to not complain about assigning and not using a variable.
    _ = service.DoCostlyWorkAsync();
  }
}

That's not great because it means that you don't care if the task succeeds. That might be ok if nothing ever threw exceptions, but losing exceptions to the void is a terrible practice. For details, see the "Lost Exception Example" below.

Calling Async From Sync

stackoverflow and microsoft explain some hacks to call Async from Sync. But these are hacks.

The blocking hack ".GetAwaiter().GetResult()" won't help because it requires that ConfigureAwait(false) is used everywhere, even in dependent libraries. HttpClient for example captures the context on some platforms, so in general this won't work.

The thread pool hack seems like the only viable option for a server component that needs to call async helpers from a fixed non-async stack called by some unknown possible via remoting (i.e. IPC).

public sealed class WebDataService : IDataService
{
  public string Get(int id)
  {
    var task = Task.Run(() => AsyncContext.Run(() => GetAsync(id)));
    return task.GetAwaiter().GetResult();
  }
  public async Task<string> GetAsync(int id)
  {
    using (var client = new WebClient())
      return await client.DownloadStringTaskAsync(
      "https://www.example.com/api/values/" + id);
  }
}

In this code we offload the asynchronous work to the thread pool, then block on the resulting task. Task.Run executes the asynchronous method on a thread pool thread. Here it will run without a context, thus avoiding the deadlock. Without a specific context, it can’t access UI elements or the ASP.NET HttpContext.Current, but we don't have those in an opaque server-component environment.

Another issue is that the asynchronous method may resume on any thread pool thread. This is a problem if the method uses per-thread state or if it implicitly depends on the synchronization provided by a UI context. We can't know if dependent libraries will use per-thread state, so this problem is avoided by the use of AsyncContext.Run which forces the asynchronous code to resume on the same thread. But for this you need the Nito.AsyncEx.Conent NuGet, so I'm not using it yet.

I converted the "Lost Exception Example" to "Server Async-From-Sync" to show it now catches exceptions. I couldn't use the ShowMessage hack. That caused deadlock. I'm not sure why, but that's ok because the whole point is for this to be used by non-UI code.

Re-Entrant

But we're not done. Something none of the guides mention is re-entrance. This applies to the normal GUI examples as well as my async-from-sync server scenario. In any case, when the thread "goes elsewhere", that means it could process a request that re-enters the same task. I the breakfast example, if it's a GUI instead of a Console Main (which is the whole point of async) then there's probably a button "StartBreakfast" or "FryEggs" and if that button interacts with a member variable, everything can get confused.

That's just a standard multi-tasking problem, but it needs different solutions in the async world. For example you can't await inside a lock. But this doesn't effect my server example much since the entry points were already of the StartSomeProcess nature and already had to FailIfAlreadyStarted.

Lost Exception Example

In this example, the first click runs the task which via the ShowMessage hack shows how the background task successfully runs to completion despite being discarded. The next time you click, the task throws exception, but your app won't notice. Your debugger will notice. And you could probably configure some app-wide catcher. But for an isolated server component this isn't acceptable. Somewhere we must at least catch and log exceptions.

MainWindow.xaml

<Window
  x:Class="AsyncAwaitTestGUI.MainWindow"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  xmlns:local="clr-namespace:AsyncAwaitTestGUI"
  xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  Width="900"
  Height="600"
  mc:Ignorable="d">
  <DockPanel>
    <Label Name="StatusLabel" Content="Status: Unknown" DockPanel.Dock="Top"/>
    <Button Name="DoWorkButton" Content="DoWork" Click="DoWorkButton_Click" DockPanel.Dock="Top"/>
    <TextBox Name="MessagesTextBox" TextWrapping="Wrap" VerticalScrollBarVisibility="Visible" Height="auto" DockPanel.Dock="Top"/>
  </DockPanel>
</Window>

MainWindow.xaml.cs

namespace AsyncAwaitTestGUI
{
    using System;
    using System.Threading.Tasks;
    using System.Windows;

    public partial class MainWindow : Window
    {
        private MandatorySyncEntry syncEntry = new MandatorySyncEntry();

        public MainWindow()
        {
            this.InitializeComponent();
            this.Title = "AsyncAwaitTestGUI";
            MainWindow.DoShowMessage = this.ShowMessageImpl;
        }

        private void DoWorkButton_Click(object sender, RoutedEventArgs e)
        {
            string status = "Hello";
            if (this.StatusLabel.Content as string == status)
            {
                status = "World";
            }

            this.syncEntry.StartCostlyWork(status);
            this.StatusLabel.Content = status;
        }

        private void ShowMessageImpl(string message)
        {
            this.Dispatcher.Invoke(() =>
            {
                this.MessagesTextBox.Text = message;
            });
        }

        public static Action<string> DoShowMessage = null;

        public static void ShowMessage(string message)
        {
            if (DoShowMessage != null)
            {
                DoShowMessage(message);
            }
        }
    }

    public class MandatoryAsyncService
    {
        public async Task<Outcome> DoCostlyWorkAsync(string input)
        {
            MainWindow.DoShowMessage($"Starting DoCostlyWorkAsync({input})");
            var outcome = new Outcome();
            await Task.Delay(1000);
            if (input == "Hello")
            {
                outcome.Success = true;
            }
            else
            {
                throw new InvalidOperationException("MyFailure");
            }
            MainWindow.DoShowMessage($"DoCostlyWorkAsync({input}) returns {outcome.Success}");
            return outcome;
        }
    }

    public class MandatorySyncEntry 
    {
        private MandatoryAsyncService asyncService = new MandatoryAsyncService();

        public void StartCostlyWork(string input)
        {
            // This starts the task and discards it. This notation isn't specific to async/await.
            // It just tells the compiler to not complain about assigning and not using a variable.
            _ = asyncService.DoCostlyWorkAsync(input);
        }
    }

    public class Outcome
    {
        public bool Success = false;
    }
}

Server Async-From-Sync

In this example, the first click runs the task which via Debug.Write in the IDE console that the background task successfully runs to completion. The next time you click, the task throws exception, and is successfully caught and logged at the point of entry.

I think this solution also makes ConfigureAwait(false) unnecessary, since this technique means it will always run without a context, thus avoiding deadlock.

namespace AsyncAwaitTestGUI
{
    using System;
    using System.Diagnostics;
    using System.Threading.Tasks;
    using System.Windows;

    public partial class MainWindow : Window
    {
        private MandatorySyncEntry syncEntry = new MandatorySyncEntry();

        public MainWindow()
        {
            this.InitializeComponent();
            this.Title = "AsyncAwaitTestGUI";
        }

        private void DoWorkButton_Click(object sender, RoutedEventArgs e)
        {
            string status = "Hello";
            if (this.StatusLabel.Content as string == status)
            {
                status = "World";
            }

            this.syncEntry.StartCostlyWork(status);
            this.StatusLabel.Content = status;
        }
    }

    public class MandatoryAsyncService
    {
        public async Task<Outcome> DoCostlyWorkAsync(string input)
        {
            Debug.Write($"Starting DoCostlyWorkAsync({input})\n");
            var outcome = new Outcome();
            await Task.Delay(1000);
            if (input == "Hello")
            {
                outcome.Success = true;
            }
            else
            {
                throw new InvalidOperationException("MyFailure");
            }
            Debug.Write($"DoCostlyWorkAsync({input}) returns {outcome.Success}\n");
            return outcome;
        }
    }

    public class MandatorySyncEntry 
    {
        private MandatoryAsyncService asyncService = new MandatoryAsyncService();

        public void StartCostlyWork(string input)
        {
            try
            {
                Task.Run(() => asyncService.DoCostlyWorkAsync(input)).GetAwaiter().GetResult();
            }
            catch (Exception exception)
            {
                Debug.Write($"StartCostlyWork({input}) catches {exception}\n");
            }
        }
    }

    public class Outcome
    {
        public bool Success = false;
    }
}
c#
Tuesday, September 21, 2021

Gear

### Mike's Gear ###

Shoes

Ecco 43000451707 EU 46 -- too narrow
Simple 2307 EU 47 -- perfect

### Mom and Dad's Gear ###

Laser Printer

Mom's laster printer is a Brother HL-L2360DW
It takes the Brother TN660 High Yield Black Toner
$66.23 (2,600 pages)

Alternatives:
$22.50 (2,600 pages) Remanufactured by Inkfirst
$29.99 (2,600 pages) by Nextpage
$32.99 (2,600 pages) by SaveOn Many

Replacement Guide

- must remove orange plastic from new one
- must slide green switch back and forth two times

Digital Camera

Canon Powershot A520
uses USB-A to USB-B 5-Pin Mini
primecables.com
all-data-cable.com

Printer

Mom has a Lexmark E232 which uses 12A8400 ink. Printers Plus sells this as: LEX-12A8400 Lexmark Optra E232 Standard Toner Cartridge (replaced by 24015SA) $88+tax, but you can easily refil the toner yourself from a kit.

  • contact@123refills.net
  • (SKU) Item: (195-153-01) Dell 1700n / 1710n - IMB 1412 - Lexmark E232 / E330 / E332 Toner Refill Kit by Uni-Kit
  • Price: $39.00
  • Shipping: $14.95 (USPS)
  • Total: $53.95
  • Charge on card appears as "Easy Group / 123Refills"

Helicopter

E-Flite Blade CX2
Easily broken parts:
- rotor blades (definitely buy a couple packs of replacements both upper and lower, they're different)
- upper rotor head (maybe buy one plastic replacement)
- lower rotor head (maybe buy one plastic replacement)
- swashplate (maybe buy one plastic or aluminum replacement)

Saturday, August 7, 2021

iPhone could not be updated

A while back my iPhone failed its update. I was too busy to look into it then.

Just now, I tried again, (updating via iTunes while connected to PC).

Error:
iPhone could not be updated
an unknown error occurred 
(4000)

https://support.apple.com/en-us/HT201210
update windows
 done
  windows 10 home 10.0.19043
update itunes
 12.11.3 
  is the current version
try a different usb cable
 done
reboot both hardware
 done

https://support.apple.com/en-us/HT201413
 security software
  seems unrelated

https://support.apple.com/en-us/HT204770
 follow the steps for your error code
  nothing relevant

Try again (updating via iTunes while connected to PC).
- says "extracting software" (with progress bar) successful
- says "backing up iphone" (with progress bar) successful
- says "preparing iphone for software update" (fails before progress bar starts)

I also removed lint from the usb port.
Tried again.
Same failure.

The existing ios version is 14.6 (18F72)
I'm attempting to install ios 14.7.1

I tried updating from the iphone only (no iTunes) (plugged into power)
- says "about 2 minutes remaining"
- says "preparing update"
- says "downloaded"
- click "install now"
- says "verifying update"
- soon after says "unable to install update, an error occurred installing ios 14.7.1"
- click "retry"
- enter pin (it didn't prompt for that the first time)
- says "verifying update"
- screen went black (that's new)
- shows apple logo with progress bar (looking good)
- progress bar advances but doesn't get to the end
- screen went black again
- shows apple logo with progress bar (again)
- progress bar advances 

Success!
Your iPhone has been updated.

ios
Saturday, May 15, 2021

Softphone

I started using Fongo on iOS in 2015 as a means of paying $20/mo instead of $90/mo for the same service. The idea is that you buy a smart-phone, but you only pay for a data-plan, so you don't have a phone number, can't send/receive voice calls, can't SMS. Then you get a phone app like Fongo (which provides a free local Canadian number for voice calls, but you have to pay for an SMS plan) and voila, you've got a fully featured phone. I'm using Google Fi now for data, which is cheap, international data, charged on a per-use rate.

But Fongo, being free, isn't great. It's reliable for outgoing calls, but sometimes incoming calls go to voicemail before ringing, and the SMS offered by Fongo isn't short code so it doesn't work for most second-factor (confirm your identity) messages (they just don't arrive).

I just surveyed the field again to see if there's a better Fongo alternative. Probably the best thing would be Google Voice, but it only works for personal Google Accounts in the US. They just opened it up on May 6th 2020 to business accounts in Canada, but that's $10/month. Most of the online comparisons are considering VOIP for business, and might not work in Canada. Grasshopper has supported Canadian numbers since 2010 but its pricing starts at $26/month which is a lot more expensive than free. From this comparison we can see that most of the free or cheap softphones are terrible. But Bria Solo is free and looks decent. And the pay-upgrade for individuals is just $3/mo. But it looks more like a number aggregator then a simple phone app, and the free version comes with an advertisement panel. (You can pay a one time fee to turn off the advertisement panel in Fongo). Here's another comparison which includes names line Vonage (which works in Canada), but again it's business centric. A quick search shows that the vonage softphone is $13/mo.

voip.ms (described on reddit as: cheap, reliable, complex to setup) would cost $0.85/month + $0.009/minute when replacing a Fongo number. Apps like Acrobits and Bria (listed here) are just the app. They require a phone number service behind them (such as voip.ms). And as of Aug 2020, voip.ms supports SMS at $0.0075 per message. But they "cannot guarantee that Short Code SMS Messages will work".

So I think Fongo is probably still the best option for a softphone in Canada today.

Friday, May 14, 2021

Windows10 Setup

##############################
## Security

Run Windows Update > Reboot > Check for updates > Repeat

(not for Inspirion 660)
Install and run Dell Command Update > Reboot > Check for updates > Repeat

##############################
## Cleanup

Start Menu > Unpin everything

Task Bar > Unpin everything

Add or remove programs > Uninstall all unwanted apps

##############################
## Configure

Start Menu > Settings > Personalization > Lock Screen > Background > Picture
 > toggle "get fun facts" to off

Right-click Desktop > Personalize > Themes
 Choose wallpaper
 Desktop Icon Settings > [uncheck] Recycel Bin

[right-click] the sound icon in the system tray
> Sounds > File Explorer > Start Navigation > None > Apply

Control Panel > Taskbar and Navigation > Start [in sidebar]
 [off] Show recently added apps
 [off] Show suggestions occassionally in Start
 [off] Show recently opened items in Jump Lists...

[right-click] Clock > Adjust Date/Time > Region > Change date formats >
 Short date: 22-Jun-2020

[right-click] OneDrive in SystemTray > Settings > More > Don't run at startup.
[right-click] OneDrive in SystemTray > Close OneDrive.

[right-click] Taskbar > Taskbar Settings > Colors
 Choose your color: custom
 Choose your default Windows mode: dark
 Choose your default app mode: light

##############################
## File Explorer

Unpin everything from Quick Access

View > Options 
 Open File Explorer to:
  This PC
 Privacy
  [uncheck] Show recently used files in Quick access
  [uncheck] Show frequently used folders in Quick access

View
 [check] File Name Extensions
 [check] Hidden Items

Disk Cleanup > Cleanup System Files > Everything
 This gets rid of most of the contents of windows.old folder.
 Can't delte the rest. It's just 50mb now, so just hide it.

shell:sendto
 Remove stuff you don't like
 Add NotePad2

##############################
## File Explorer Sidebar

http://www.thewindowsclub.com/remove-the-folders-from-this-pc-windows-10
-- in the followind, set: ThisPCPolicy=Hide 

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\FolderDescriptions\{B4BFCC3A-DB2C-424C-B029-7FE99A87C641}\PropertyBag
-- this removes "Desktop"

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\FolderDescriptions\{0ddd015d-b06c-45d5-8c4c-f59713854639}\PropertyBag
-- this removes "Pictures"

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\FolderDescriptions\{f42ee2d3-909f-4907-8871-4c22fc0bf756}\PropertyBag
-- this removes "Documents"

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\FolderDescriptions\{35286a68-3c57-41a1-bbb1-0eae73d76c95}\PropertyBag
-- this removes "Videos"

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\FolderDescriptions\{a0c69a99-21c8-4671-8703-7934162fcf1d}\PropertyBag
-- this removes "Music"

---------------------------

-- to remove "3D Objects"
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\FolderDescriptions\{31C0DD25-9439-4F12-BF41-7FF4EDA38722}
New > Key > PropertyBag
New > String Value > ThisPCPolicy
Hide 
Reboot

---------------------------

https://www.windowscentral.com/how-remove-onedrive-file-explorer-windows-10
-- in the following, set: System.IsPinnedToNameSpaceTree=0 

HKEY_CLASSES_ROOT\CLSID\{018D5C66-4533-4307-9B53-224DE2ED1FE6}
-- this removes "OneDrive"

---------------------------

Hide all the root folders you won't frequently use.

View
 [uncheck] Hidden Items

Create Folder Shortcuts in C: for frequently accessed local and NAS folders.

##############################
## Re-Do

After windows updates, you'll frequently have to redo the following:

HKEY_CLASSES_ROOT\CLSID\{018D5C66-4533-4307-9B53-224DE2ED1FE6}
-- IsPinnedToNameSpaceTree

##############################
## Google

Chrome
 Settings > Sign In > Visit Settings
  > Only Sync: Apps, Extensions, Settings
  > Install and configure Extensions:
   > Home Button At Top Right
   > Adblock Plus
  > Appearance
   > Show home button = disabled
   > Show bookmarks bar = disabled
  > On startup = continue where you left off

Gmail
 Settings > See All Settings
  > Lables > show [Inbox, Starred, All], if unread [scheduled, drafts, spam]
  > Inbox
   > Type = Default
   > Disable reading pane
   > No markers
  > Chat and Meet
   > Chat = Off
   > Chat notification settings = allow, open popups, play sounds
  > Advanced
   > Unread message icon = enabled

Pin these tabs in Chrome
 https://mail.google.com
 https://chat.google.com
 https://holtstrom.com/michael/home.php

Expected Chrome Extensions
 Adblock Plus
 Applicaiton Launcher For Drive
 Google Docs Offline
 Google Mail Checker
 Home Button At Top Right

##############################
## Business

Login to office.com
 Download and install Office 2016
 Sign into OneNote and pin to taskbar
 Sign into Outlook and pin to taskbar
  Right-click Ribbon > Collapse Ribbon
  File > Options > Mail > Outlook Panes > Reading Pane
   Mark items read when viewed 1 second
  https://www.rklesolutions.com/blog/disable-new-buttons/
   File > Options > Quick Access Toolbar > [add to custom] Touch/Mouse Mode > Ok
   Not at top-left you can use "Touch/Mouse Mode" to select mouse which will remove
   the nasty reply icons from the right-sidebar and put them back above your msg
 Sign into Powerpoint and pin to start
 Sign into Word and pin to start
 Sign into Skype For Business and pin to start

##############################
## Outlook DevOps Spam

First select an example of the email you want to delete

Home > Rules > Create Rule
 When I get email
  [checked] From Azure DevOps
  [checked] Subject contains [PR build succeeded]
  [checked] Sent to me only
 Advanced Options
 Next
  [checked] delete it
  [checked] marek it as read
 Next
 Next
  [checked] run this rule now
  [checked] turn on this rule
 Finish

Actually I had to manually re-open and explicitly run the rule to clean the inbox.
   


##############################
## Apps

Install iTunes
 File > Add Folder To Library
 Edit > Preferences > Advanced > (all unchecked regarding iTunes Media folder)
 Control > Shuffle > On, Albums

Install Notepad2

Install Kdiff3

Install AgentRansack

Install Paint.Net

##############################
## Automatic Wake

You may notice that your PC wakes on its own and does something.

You can see why in an admin command prompt:

C:\WINDOWS\system32>powercfg -lastwake
Wake History Count - 1
Wake History [0]
  Wake Source Count - 1
  Wake Source [0]
    Type: Wake Timer
    Owner: [SERVICE] \Device\HarddiskVolume3\Windows\System32\svchost.exe (SystemEventsBroker)
    Owner Supplied Reason: Windows will execute 'NT TASK\Microsoft\Windows\UpdateOrchestrator\Universal Orchestrator Start' scheduled task that requested waking the computer.

You can disable this via:

Control Panel\All Control Panel Items\Security and Maintenance\
 Maintenance
  Automatic Maintenance
   Change Maintenance Settings
    [uncheck] Allow ... to wake ...
    OK

Next
{ "loggedin": false, "owner": false, "avatar": "", "render": "nothing", "trackingID": "UA-36983794-1", "description": "Async\/Await; Gear; iPhone could not be updated; Softphone; Windows10 Setup", "page": { "blogIds": [ 717, 589, 715, 714, 650 ], "nextBtnUrl": "https:\/\/holtstrom.com\/michael\/blog\/show.php?y=5" }, "domain": "holtstrom.com", "base": "\/michael", "url": "https:\/\/holtstrom.com\/michael\/", "frameworkFiles": "https:\/\/holtstrom.com\/michael\/_framework\/_files.4\/", "commonFiles": "https:\/\/holtstrom.com\/michael\/_common\/_files.3\/", "mediaFiles": "https:\/\/holtstrom.com\/michael\/media\/_files.3\/", "tmdbUrl": "http:\/\/www.themoviedb.org\/", "tmdbPoster": "http:\/\/image.tmdb.org\/t\/p\/w342" }