hazardous

thoughts, things, etc. from andrew snow

WSL sleep error - a workaround

overview


Recently, I opened a shell in WSL to start banging away at the website via ssh. Before I opened the connection, I ran sudo apt update as usual, but encountered an error similar to this:

Reading package lists... Done
Building dependency tree
Reading state information... Done
Correcting dependencies... Done
The following additional packages will be installed:
  libc-bin libc-dev-bin libc6-dev libcrypt-dev
Suggested packages:
  glibc-doc
The following NEW packages will be installed:
  libcrypt-dev
The following packages will be upgraded:
  libc-bin libc-dev-bin libc6-dev
3 upgraded, 1 newly installed, 0 to remove and 131 not upgraded.
2 not fully installed or removed.
Need to get 0 B/3331 kB of archives.
After this operation, 44.0 kB disk space will be freed.
Do you want to continue? [Y/n]
Setting up libc6:amd64 (2.31-0ubuntu5) ...
Checking for services that may need to be restarted...
Checking init scripts...
Nothing to restart.
sleep: cannot read realtime clock: Invalid argument
dpkg: error processing package libc6:amd64 (--configure):
 installed libc6:amd64 package post-installation script subprocess returned error exit status 1
Errors were encountered while processing:
 libc6:amd64
E: Sub-process /usr/bin/dpkg returned an error code (1)

the problem


Running apt --fix-broken install did not seem to fix the issue. Reading through the output revealed the problem centered around this:

sleep: cannot read realtime clock: Invalid argument

Damn! I was annoyed: awhile back I decided to try out Ubuntu on WSL, instead of the distro I have been using for a few years, a flavor of Debian specifically tweaked to make the most of the WSL environment called Pengwin.

Unfortunately, when updating the latest Ubuntu LTS - 20.04 Focal Fossa - I kept getting hit with this sleep error, and after some research it appeared to be an issue with WSL - by virtue of it being a virtual OS, it was encountering errors with gcc and a new piece of code added to recent versions dealing with the realtime clock.

And now it seems to have finally become an issue on the Debian package tree as well, at least for anyone using the bullseye rolling releases, that has gcc and the relevant libraries and headers installed.

I can't be too specific because I did not do any rigorous testing - suffice to say, if you're reading this, I'm assuming you are running a configuration that has encountered this issue. Fortunately, it is easily fixed!

the solution

Now, before you go and do the following, here's the process I followed:

  • I applied the fix
  • I did a full system reboot

When I logged back into Windows, I was able to once again run apt update with no problems. The reason I mention this is because it might be worth your time to simply try rebooting your PC to see if this mitigates the issue - as I said before this is not something I spent alot of time trying to examine using rigorous observation. Personally, I adopted the now-common storage scheme utilizing a mix of SSD storage along with traditional disk storage - so rebooting is no longer any sort of inconvenience, and takes maybe 20 seconds from the time I actually click the 'restart' button to when I am right back where I left off.

If that doesn't solve it, the fix is painless.

First, open a file called nanosleep.c and add the following:

#include <time.h>
#include <unistd.h>

// This restores the old behaviour of nanosleep() to use CLOCK_MONOTONIC.
//
// "POSIX.1 specifies that nanosleep() should measure time against the
// CLOCK_REALTIME clock. However, Linux measures the time using the
// CLOCK_MONOTONIC clock.  This probably does not matter [...]"
//
// # gcc -shared -fPIC -o /usr/local/lib/libnanosleep.so nanosleep.c
// # echo /usr/local/lib/libnanosleep.so >> /etc/ld.so.preload
//
 int nanosleep(const struct timespec *req, struct timespec *rem)
 {
     return clock_nanosleep(CLOCK_MONOTONIC, 0, req, rem);
     }

     int usleep(useconds_t usec)
     {
         struct timespec req = {
                 .tv_sec     = (usec / 1000000),
                         .tv_nsec    = (usec % 1000000) * 1000,
                             };
                                 return nanosleep(&req, NULL);
                                 }

When all this has been entered, save the file. If I understand correctly, this is a workaround that undoes a recent change in the way gcc utilizes a particular process by calling the sleep command - a command that, as of now, doesn't play well within WSL. This is noted in the comments included in the above script.

Note: I am using WSL1 here. I do not know if WSL2 has this issue, but I do not believe using this method when running WSL2 would have any negative consequences.

Save the file and exit, and then use the compiler to install the compiled code into your /usr/local/lib directory:

gcc -shared -fPIC -o /usr/local/lib/libnanosleep.so nanosleep.c

Almost there! The final thing to do is, of course, let the operating system know to look for this piece of code, and use it in lieu of the normal process. This is fairly simple - you simply create a file in your /etc directory pointing the OS at your new file.

echo /usr/local/lib/libnanosleep.so >> /etc/ld.so.preload

Note: due to quirks with the way sudo handles the echo commands and piped output, the above command - which requires being executed as root - will not work by simply adding sudo at the front of the command. You will need to run the command as root. The easiest way to do this is by temporarily becoming root, by running sudo su. Enter the command, and then simply type exit to return to your normal user shell.

I'm not sure if this will work right away - as I said earlier, with reboot times as short as they are, I figured I might as well just reboot my PC.

After I rebooted and logged back in, I pulled up a new shell, ran apt update && apt upgrade -y, and voila! Everything started running along as usual.

According to the various discussions on this issue, it's been indicated that a fix is planned to remove this error when using WSL. However, alot of these comments were made as far back as February. Given how simple the above fix is, I might be wrapping the process up into a script and releasing it with the necessary files to automate the process. Not only would it be a bit simpler for less advanced users, but it would also simplify any automated setup or bootstrapping of a new OS.

removing the fix

This should be a no-brainer - if an official fix does ever get released, I do not think this workaround will necessarily break anything. However, for completeness' sake you might still want to undo this process.

To do so, it's fairly easy - simple delete the ld.so.preload file from your /etc directory. You will also still have the libnanosleep.so file in /usr/local/lib, and you can delete it if need be, but unless you desperately need those few kilobytes' worth of storage space it might be handy to keep around. I dunno.

So there ya have it! Some peace of mind to allow the continued enjoyment of the 'best-of-both-worlds' WSL+Windows environment. Gotta say, given the past few decades in which the idea of windows and linux inhabiting the same userspace was an almost unattainable dream - the rapid speed at which WSL has come along has been incredible. Even more so given the fact that in the few years I've been doing this, the above is perhaps the only significant issue that really broke my environment in a way that threatened its usability. Hot damn.