<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
    <channel>
      <title>Ben Hansen</title>
      <link>https://www.benhansen.io</link>
      <description></description>
      <generator>Zola</generator>
      <language>en</language>
      <atom:link href="https://www.benhansen.io/rss.xml" rel="self" type="application/rss+xml"/>
      <lastBuildDate>Thu, 14 Nov 2024 00:00:00 +0000</lastBuildDate>
      <item>
          <title>Sintra Boundless Trailblazers</title>
          <pubDate>Thu, 14 Nov 2024 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.benhansen.io/sintra-boundless-trailblazers/</link>
          <guid>https://www.benhansen.io/sintra-boundless-trailblazers/</guid>
          <description xml:base="https://www.benhansen.io/sintra-boundless-trailblazers/">&lt;html&gt;
Happy Mister Llamas &lt;b&gt;Cupcakes&lt;&#x2F;b&gt; Fly Sky High to the Moon to Read Dinopedia
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.crazygames.com&#x2F;&quot;&gt;Pikachu&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;img width=250px src=&quot;https:&#x2F;&#x2F;encrypted-tbn0.gstatic.com&#x2F;images?q=tbn:ANd9GcQYXcnqYbYSdaiGyiLmSq5WB-n-ediP83YKVg&amp;s&quot;&gt;
&lt;br&gt;
&lt;br&gt;
&lt;hr&gt;
&lt;p style=&quot;font-size: 90%&quot;&gt;This website was part of a mini group project for the Sintra Boundless Trailblazers in November 2024.&lt;&#x2F;p&gt;
&lt;&#x2F;html&gt;
</description>
      </item>
      <item>
          <title>5 Years Making a Toy with an SDK</title>
          <pubDate>Thu, 03 Oct 2024 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.benhansen.io/toy-with-an-sdk/</link>
          <guid>https://www.benhansen.io/toy-with-an-sdk/</guid>
          <description xml:base="https://www.benhansen.io/toy-with-an-sdk/">&lt;p&gt;Have you noticed how much some kids love to push buttons or flip a light switch on and off? My son loved it so much I built him a button box using an Arduino, 10 mechanical keyboard switches with RGB lights, and a speaker. Here is a video of him learning his colors, the first activity I built for him.&lt;&#x2F;p&gt;
&lt;iframe style=&quot;float: left; margin: 10px; max-width: 315px; width: 100%; height: 560px&quot;
src=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;embed&#x2F;MyZ18ibgZGk&quot;
title=&quot;YouTube video player&quot;
frameborder=&quot;0&quot;
allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot;
allowfullscreen&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;A family friend, unaware of the device, commented on how many colors our son knew and how rare that is for a 1-year-old. After taking a break during a move and COVID, I kept building new activities (also called apps) for him and my nephews.&lt;&#x2F;p&gt;
&lt;p&gt;I wrote an activity where it just counts how many buttons have been pressed, says the number and color codes the digits like a resistor’s color bands. Super simple. It took less than 30 minutes to code up with the libraries I developed, but I was surprised how much kids liked it. I then made a variant of the app that displays the number on the buttons in binary digits instead of decimal and then watched as my 7 year old nephew learned binary. I don’t think it would be worth making a dedicated toy for either of those activities, but a toy with an SDK and maybe over a hundred apps targeted to different stages of a child&#x27;s growth? That&#x27;s what&#x27;s kept me so excited about this idea for so long.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s fun as a programmer too. Instead of worrying about UI frameworks for 4k monitors, which dependencies to use and everything else that goes into modern app development, it&#x27;s just 10 lights, 10 buttons and a speaker. And there is actually a lot you can do with just that. This platform is about as simple as a &quot;computer&quot; can be while still being multi-purpose.&lt;&#x2F;p&gt;
&lt;p&gt;I have already built 30 apps ranging from arcade style games (e.g. simon says repeat the pattern, reaction time games, bop-a-mole), to a noise machine with 10 different sounds that can be toggled, to streaming kids&#x27; podcasts over WiFi. What really excites me is the potential for a community where people can build and share their own apps. I am going to launch an SDK so people can make their own. Some initial details are on our &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;10buttons&#x2F;&quot;&gt;Github page&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;It has been 2.5 years working on boppo full-time. During that time, I learned enough electronics to design a PCB for the prototypes with an ESP32. I started from the basics. For example, I returned a lab power supply to Amazon because I thought I was supposed to hook my device to positive and ground (should have been positive and negative). I also spent 2 weeks and bought an oscilliscope to realize that my I2C cables were just too long in a prototype. And don&#x27;t ask me how many different speaker drivers I bought looking for good sound. I have gone through at least 6 iterations.&lt;&#x2F;p&gt;
&lt;p&gt;Another challenge I faced was building an efficient audio playback system for the ESP32 chip in Rust. None of the existing libraries worked well in the constrained environment, so I spent a few solid weeks writing and open-sourcing one: &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;10buttons&#x2F;awedio&quot;&gt;awedio&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Earlier this year, my wife joined me to help. She and a friend branded it &lt;a href=&quot;https:&#x2F;&#x2F;boppo.com&#x2F;&quot;&gt;boppo&lt;&#x2F;a&gt; which I love. We have built faceplates that go on top of the buttons, which the tablet detects with NFC. This gives the buttons new meaning and we are building out multiple activities for each faceplate.&lt;&#x2F;p&gt;
&lt;img style=&quot;width: 100%; max-height: 432px; max-width: 500px;&quot; src=&quot;&#x2F;toy-with-an-sdk&#x2F;boppo_plates.webp&quot; &gt;
&lt;p&gt;I have been marketing boppo as a “screenless tablet” because I think that best represents its capabilities (but there have been vocal critics of that wording). A toy with an SDK might be more accurate. Other descriptions or startup analogies that come to mind are:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;A mechanical keyboard with a microcontroller and a speaker&lt;&#x2F;li&gt;
&lt;li&gt;A screenless educational game console&lt;&#x2F;li&gt;
&lt;li&gt;Flipper Zero but for kids and learning not security hacking&lt;&#x2F;li&gt;
&lt;li&gt;Stream Deck without the LCD but with a battery and Wifi&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;A year ago, we started marketing boppo getting ready for a crowdfunding campaign. We collected 10,000 emails and 900 $1 reservations for about $14,000 in ads.&lt;&#x2F;p&gt;
&lt;p&gt;Our project is now live on &lt;a href=&quot;https:&#x2F;&#x2F;www.kickstarter.com&#x2F;projects&#x2F;boppo&#x2F;boppo-the-screenless-tablet?ref=7dg1xx&quot;&gt;Kickstarter&lt;&#x2F;a&gt;. If what I am working on interests you, we would &lt;a href=&quot;https:&#x2F;&#x2F;www.kickstarter.com&#x2F;projects&#x2F;boppo&#x2F;boppo-the-screenless-tablet?ref=7dg1xx&quot;&gt;love your support&lt;&#x2F;a&gt;. If there is anything about this project that you would like to hear more about please &lt;a href=&quot;mailto:ben@boppo.com&quot;&gt;let me know&lt;&#x2F;a&gt;.
&lt;br&gt;
&lt;br&gt;&lt;&#x2F;p&gt;
&lt;a href=&quot;https:&#x2F;&#x2F;www.kickstarter.com&#x2F;projects&#x2F;boppo&#x2F;boppo-the-screenless-tablet?ref=7dg1xx&quot;&gt;
&lt;img style=&quot;border: solid black 1px; width: 100%; max-width: 800px&quot; src=&quot;&#x2F;toy-with-an-sdk&#x2F;Kickstarter_Screenshot.webp&quot;&gt;
&lt;&#x2F;a&gt;
</description>
      </item>
      <item>
          <title>Browser Events Listener</title>
          <pubDate>Fri, 10 Nov 2023 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.benhansen.io/browser-events-listener/</link>
          <guid>https://www.benhansen.io/browser-events-listener/</guid>
          <description xml:base="https://www.benhansen.io/browser-events-listener/">&lt;style&gt;
&lt;&#x2F;style&gt;
&lt;p&gt;This page shows all events (of event types listed on &lt;a href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;Events&quot;&gt;MDN Event&#x27;s Page&lt;&#x2F;a&gt;) raised on &lt;code&gt;document&lt;&#x2F;code&gt;.
&lt;br&gt;
Press a key, move your mouse and see what events are created.
&lt;br&gt;
&lt;br&gt;
Might be useful to you to debug an issue with a keyboard, mouse, scroll pad or other input device.
&lt;br&gt;
&lt;br&gt;
Open the DevTools Console for more info on events.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;events&quot;&gt;Events&lt;&#x2F;h2&gt;
&lt;div id=&quot;events_log&quot;&gt;
&lt;&#x2F;div&gt;
&lt;script&gt;
&#x2F;&#x2F; Grabbed everything in the table on https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;Events
const EVENTS = [
    &quot;abort&quot;,
    &quot;ended&quot;,
    &quot;addtrack&quot;,
    &quot;change&quot;,
    &quot;removetrack&quot;,
    &quot;messageerror&quot;,
    &quot;message&quot;,
    &quot;messageerror&quot;,
    &quot;message&quot;,
    &quot;animationcancel&quot;,
    &quot;animationend&quot;,
    &quot;animationiteration&quot;,
    &quot;animationstart&quot;,
    &quot;copy&quot;,
    &quot;cut&quot;,
    &quot;DOMContentLoaded&quot;,
    &quot;dragend&quot;,
    &quot;dragenter&quot;,
    &quot;dragleave&quot;,
    &quot;dragover&quot;,
    &quot;dragstart&quot;,
    &quot;drag&quot;,
    &quot;drop&quot;,
    &quot;fullscreenchange&quot;,
    &quot;fullscreenerror&quot;,
    &quot;gotpointercapture&quot;,
    &quot;keydown&quot;,
    &quot;keypress&quot;,
    &quot;keyup&quot;,
    &quot;lostpointercapture&quot;,
    &quot;paste&quot;,
    &quot;pointercancel&quot;,
    &quot;pointerdown&quot;,
    &quot;pointerenter&quot;,
    &quot;pointerleave&quot;,
    &quot;pointerlockchange&quot;,
    &quot;pointerlockerror&quot;,
    &quot;pointermove&quot;,
    &quot;pointerout&quot;,
    &quot;pointerover&quot;,
    &quot;pointerup&quot;,
    &quot;readystatechange&quot;,
    &quot;scroll&quot;,
    &quot;selectionchange&quot;,
    &quot;touchcancel&quot;,
    &quot;touchend&quot;,
    &quot;touchmove&quot;,
    &quot;touchstart&quot;,
    &quot;transitioncancel&quot;,
    &quot;transitionend&quot;,
    &quot;transitionrun&quot;,
    &quot;transitionstart&quot;,
    &quot;visibilitychange&quot;,
    &quot;wheel&quot;,
    &quot;animationcancel&quot;,
    &quot;animationend&quot;,
    &quot;animationiteration&quot;,
    &quot;animationstart&quot;,
    &quot;afterscriptexecute&quot;,
    &quot;auxclick&quot;,
    &quot;beforescriptexecute&quot;,
    &quot;blur&quot;,
    &quot;click&quot;,
    &quot;compositionend&quot;,
    &quot;compositionstart&quot;,
    &quot;compositionupdate&quot;,
    &quot;contextmenu&quot;,
    &quot;copy&quot;,
    &quot;cut&quot;,
    &quot;dblclick&quot;,
    &quot;DOMActivate&quot;,
    &quot;DOMMouseScroll&quot;,
    &quot;error&quot;,
    &quot;focusin&quot;,
    &quot;focusout&quot;,
    &quot;focus&quot;,
    &quot;fullscreenchange&quot;,
    &quot;fullscreenerror&quot;,
    &quot;gesturechange&quot;,
    &quot;gestureend&quot;,
    &quot;gesturestart&quot;,
    &quot;gotpointercapture&quot;,
    &quot;keydown&quot;,
    &quot;keypress&quot;,
    &quot;keyup&quot;,
    &quot;lostpointercapture&quot;,
    &quot;mousedown&quot;,
    &quot;mouseenter&quot;,
    &quot;mouseleave&quot;,
    &quot;mousemove&quot;,
    &quot;mouseout&quot;,
    &quot;mouseover&quot;,
    &quot;mouseup&quot;,
    &quot;mousewheel&quot;,
    &quot;paste&quot;,
    &quot;pointercancel&quot;,
    &quot;pointerdown&quot;,
    &quot;pointerenter&quot;,
    &quot;pointerleave&quot;,
    &quot;pointermove&quot;,
    &quot;pointerout&quot;,
    &quot;pointerover&quot;,
    &quot;pointerup&quot;,
    &quot;scroll&quot;,
    &quot;select&quot;,
    &quot;touchcancel&quot;,
    &quot;touchend&quot;,
    &quot;touchmove&quot;,
    &quot;touchstart&quot;,
    &quot;transitioncancel&quot;,
    &quot;transitionend&quot;,
    &quot;transitionrun&quot;,
    &quot;transitionstart&quot;,
    &quot;webkitmouseforcechanged&quot;,
    &quot;webkitmouseforcedown&quot;,
    &quot;webkitmouseforceup&quot;,
    &quot;webkitmouseforcewillbegin&quot;,
    &quot;wheel&quot;,
    &quot;error&quot;,
    &quot;message&quot;,
    &quot;open&quot;,
    &quot;abort&quot;,
    &quot;error&quot;,
    &quot;loadend&quot;,
    &quot;loadstart&quot;,
    &quot;load&quot;,
    &quot;progress&quot;,
    &quot;webglcontextcreationerror&quot;,
    &quot;webglcontextlost&quot;,
    &quot;webglcontextrestored&quot;,
    &quot;toggle&quot;,
    &quot;cancel&quot;,
    &quot;close&quot;,
    &quot;beforeinput&quot;,
    &quot;change&quot;,
    &quot;input&quot;,
    &quot;formdata&quot;,
    &quot;reset&quot;,
    &quot;submit&quot;,
    &quot;invalid&quot;,
    &quot;search&quot;,
    &quot;abort&quot;,
    &quot;canplaythrough&quot;,
    &quot;canplay&quot;,
    &quot;durationchange&quot;,
    &quot;emptied&quot;,
    &quot;ended&quot;,
    &quot;error&quot;,
    &quot;loadeddata&quot;,
    &quot;loadedmetadata&quot;,
    &quot;loadstart&quot;,
    &quot;pause&quot;,
    &quot;playing&quot;,
    &quot;play&quot;,
    &quot;progress&quot;,
    &quot;ratechange&quot;,
    &quot;seeked&quot;,
    &quot;seeking&quot;,
    &quot;stalled&quot;,
    &quot;suspend&quot;,
    &quot;timeupdate&quot;,
    &quot;volumechange&quot;,
    &quot;waiting&quot;,
    &quot;slotchange&quot;,
    &quot;cuechange&quot;,
    &quot;enterpictureinpicture&quot;,
    &quot;leavepictureinpicture&quot;,
    &quot;abort&quot;,
    &quot;close&quot;,
    &quot;error&quot;,
    &quot;versionchange&quot;,
    &quot;blocked&quot;,
    &quot;upgradeneeded&quot;,
    &quot;error&quot;,
    &quot;success&quot;,
    &quot;abort&quot;,
    &quot;complete&quot;,
    &quot;error&quot;,
    &quot;devicechange&quot;,
    &quot;error&quot;,
    &quot;addtrack&quot;,
    &quot;removetrack&quot;,
    &quot;ended&quot;,
    &quot;mute&quot;,
    &quot;unmute&quot;,
    &quot;change&quot;,
    &quot;messageerror&quot;,
    &quot;message&quot;,
    &quot;complete&quot;,
    &quot;merchantvalidation&quot;,
    &quot;paymentmethodchange&quot;,
    &quot;shippingaddresschange&quot;,
    &quot;shippingoptionchange&quot;,
    &quot;payerdetailchange&quot;,
    &quot;resourcetimingbufferfull&quot;,
    &quot;resize&quot;,
    &quot;bufferedamountlow&quot;,
    &quot;close&quot;,
    &quot;closing&quot;,
    &quot;error&quot;,
    &quot;message&quot;,
    &quot;open&quot;,
    &quot;error&quot;,
    &quot;tonechange&quot;,
    &quot;gatheringstatechange&quot;,
    &quot;selectedcandidatepairchange&quot;,
    &quot;statechange&quot;,
    &quot;addstream&quot;,
    &quot;connectionstatechange&quot;,
    &quot;datachannel&quot;,
    &quot;icecandidateerror&quot;,
    &quot;icecandidate&quot;,
    &quot;iceconnectionstatechange&quot;,
    &quot;icegatheringstatechange&quot;,
    &quot;negotiationneeded&quot;,
    &quot;removestream&quot;,
    &quot;signalingstatechange&quot;,
    &quot;track&quot;,
    &quot;audioprocess&quot;,
    &quot;message&quot;,
    &quot;activate&quot;,
    &quot;contentdelete&quot;,
    &quot;install&quot;,
    &quot;message&quot;,
    &quot;notificationclick&quot;,
    &quot;pushsubscriptionchange&quot;,
    &quot;push&quot;,
    &quot;connect&quot;,
    &quot;audioend&quot;,
    &quot;audiostart&quot;,
    &quot;end&quot;,
    &quot;error&quot;,
    &quot;nomatch&quot;,
    &quot;result&quot;,
    &quot;soundend&quot;,
    &quot;soundstart&quot;,
    &quot;speechend&quot;,
    &quot;speechstart&quot;,
    &quot;start&quot;,
    &quot;voiceschanged&quot;,
    &quot;boundary&quot;,
    &quot;end&quot;,
    &quot;error&quot;,
    &quot;mark&quot;,
    &quot;pause&quot;,
    &quot;resume&quot;,
    &quot;start&quot;,
    &quot;beginEvent&quot;,
    &quot;endEvent&quot;,
    &quot;repeatEvent&quot;,
    &quot;abort&quot;,
    &quot;error&quot;,
    &quot;load&quot;,
    &quot;resize&quot;,
    &quot;scroll&quot;,
    &quot;unload&quot;,
    &quot;copy&quot;,
    &quot;cut&quot;,
    &quot;paste&quot;,
    &quot;cuechange&quot;,
    &quot;addtrack&quot;,
    &quot;change&quot;,
    &quot;removeTrack&quot;,
    &quot;addtrack&quot;,
    &quot;change&quot;,
    &quot;removetrack&quot;,
    &quot;resize&quot;,
    &quot;scroll&quot;,
    &quot;close&quot;,
    &quot;error&quot;,
    &quot;message&quot;,
    &quot;open&quot;,
    &quot;afterprint&quot;,
    &quot;animationcancel&quot;,
    &quot;animationend&quot;,
    &quot;animationiteration&quot;,
    &quot;animationstart&quot;,
    &quot;appinstalled&quot;,
    &quot;beforeprint&quot;,
    &quot;beforeunload&quot;,
    &quot;blur&quot;,
    &quot;copy&quot;,
    &quot;cut&quot;,
    &quot;devicemotion&quot;,
    &quot;deviceorientation&quot;,
    &quot;error&quot;,
    &quot;focus&quot;,
    &quot;gamepadconnected&quot;,
    &quot;gamepaddisconnected&quot;,
    &quot;hashchange&quot;,
    &quot;languagechange&quot;,
    &quot;load&quot;,
    &quot;messageerror&quot;,
    &quot;message&quot;,
    &quot;offline&quot;,
    &quot;online&quot;,
    &quot;orientationchange&quot;,
    &quot;pagehide&quot;,
    &quot;pageshow&quot;,
    &quot;paste&quot;,
    &quot;popstate&quot;,
    &quot;rejectionhandled&quot;,
    &quot;resize&quot;,
    &quot;storage&quot;,
    &quot;transitioncancel&quot;,
    &quot;transitionend&quot;,
    &quot;transitionrun&quot;,
    &quot;transitionstart&quot;,
    &quot;unhandledrejection&quot;,
    &quot;unload&quot;,
    &quot;vrdisplayactivate&quot;,
    &quot;vrdisplayblur&quot;,
    &quot;vrdisplayconnect&quot;,
    &quot;vrdisplaydeactivate&quot;,
    &quot;vrdisplaydisconnect&quot;,
    &quot;vrdisplayfocus&quot;,
    &quot;vrdisplaypointerrestricted&quot;,
    &quot;vrdisplaypointerunrestricted&quot;,
    &quot;vrdisplaypresentchange&quot;,
    &quot;messageerror&quot;,
    &quot;message&quot;,
    &quot;languagechange&quot;,
    &quot;abort&quot;,
    &quot;error&quot;,
    &quot;loadend&quot;,
    &quot;loadstart&quot;,
    &quot;load&quot;,
    &quot;progress&quot;,
    &quot;timeout&quot;,
    &quot;reset&quot;,
    &quot;end&quot;,
    &quot;inputsourceschange&quot;,
    &quot;selectend&quot;,
    &quot;selectstart&quot;,
    &quot;select&quot;,
    &quot;squeezeend&quot;,
    &quot;squeezestart&quot;,
    &quot;squeeze&quot;,
    &quot;visibilitychange&quot;,
    &quot;devicechange&quot;,
];

for (const event of EVENTS) {
  document.addEventListener(event, onEvent);
}

const events_div = document.getElementById(&#x27;events_log&#x27;);

function onEvent(event) {
  console.log(event);
  const ts = event.timeStamp;
  const json = JSON.stringify(event);
  const str = `${ts}: ${event.type} ${json}`;
  events_div.prepend(document.createElement(&quot;br&quot;));
  events_div.prepend(str);
}
&lt;&#x2F;script&gt;
</description>
      </item>
      <item>
          <title>Debugging a Rust MPSC Channel Deadlock</title>
          <pubDate>Tue, 20 Jun 2023 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.benhansen.io/rust-mpsc-deadlock/</link>
          <guid>https://www.benhansen.io/rust-mpsc-deadlock/</guid>
          <description xml:base="https://www.benhansen.io/rust-mpsc-deadlock/">&lt;p&gt;My son is listening to his favorite song on the tablet I have been building for him. It has an activity that allows him to add effects to the song with the various buttons but he told my spouse that you can&#x27;t push too many buttons or it might freeze. Why is he afraid to push buttons?&lt;&#x2F;p&gt;
&lt;p&gt;A deadlock... but originally the only behavior I noticed is the tablet made a terrible sound as it repeated a short segment of audio over and over again and it became completely unresponsive until physically switched off.&lt;&#x2F;p&gt;
&lt;p&gt;It only happened several times a day if at all so I ignored it for several weeks while making other improvements. One time it happened while I had the output monitor streaming on my laptop and saw watchdog timeout errors printed (a watchdog timeout in this case is when a low-priority thread never gets scheduled on a CPU indicating the CPU is under excessive load or stuck in a loop). I was happy to see it because I was wondering if the issue was in the hardware of the tablet and software is easier for me to debug so yea!&lt;&#x2F;p&gt;
&lt;p&gt;The watch dog timer error made it clear that the audio backend task was running non-stop. The other core (the ESP32-S3 is dual core) was running the idle task so it appeared that the only blocked task was the audio backend task. This will turn out to be a false assumption.&lt;&#x2F;p&gt;
&lt;p&gt;The first issue was getting a good stack trace. The tablet is built using an ESP32-S3 which has a built-in USB interface for debugging so that turned out to be pretty easy using OpenOCD.&lt;&#x2F;p&gt;
&lt;p&gt;The stack trace showed the stuck audio task was in a call to &lt;a href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;std&#x2F;sync&#x2F;mpsc&#x2F;struct.Receiver.html#method.try_recv&quot;&gt;try_recv&lt;&#x2F;a&gt; on a std::sync::mpsc::Receiver:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;#0  esp_crosscore_isr (arg=0x3fca1560 &amp;lt;reason&amp;gt;) at &#x2F;home&#x2F;ben&#x2F;esp&#x2F;esp-idf&#x2F;components&#x2F;esp_system&#x2F;crosscore_int.c:91
&lt;&#x2F;span&gt;&lt;span&gt;#1  0x403774b0 in _xt_lowint1 () at &#x2F;home&#x2F;ben&#x2F;esp&#x2F;esp-idf&#x2F;components&#x2F;freertos&#x2F;port&#x2F;xtensa&#x2F;xtensa_vectors.S:1114
&lt;&#x2F;span&gt;&lt;span&gt;#2  0x403819f0 in vTaskDelay (xTicksToDelay=0) at &#x2F;home&#x2F;ben&#x2F;esp&#x2F;esp-idf&#x2F;components&#x2F;hal&#x2F;esp32s3&#x2F;include&#x2F;hal&#x2F;cpu_ll.h:38
&lt;&#x2F;span&gt;&lt;span&gt;#3  0x42003224 in sched_yield () at &#x2F;home&#x2F;ben&#x2F;esp&#x2F;esp-idf&#x2F;components&#x2F;pthread&#x2F;pthread.c:466
&lt;&#x2F;span&gt;&lt;span&gt;#4  0x420bf4eb in std::sync::mpmc::list::Channel&amp;lt;T&amp;gt;::try_recv ()
&lt;&#x2F;span&gt;&lt;span&gt;#5  0x420c149f in &amp;lt;awedio::sounds::wrappers::controllable::Controllable&amp;lt;S&amp;gt; as awedio::sound::Sound&amp;gt;::on_start_of_batch ()
&lt;&#x2F;span&gt;&lt;span&gt;#6  0x42018c68 in &amp;lt;awedio::sounds::wrappers::controllable::Controllable&amp;lt;S&amp;gt; as awedio::sound::Sound&amp;gt;::on_start_of_batch ()
&lt;&#x2F;span&gt;&lt;span&gt;#7  0x42018fbe in awedio_esp32::audio_task::h6eb04a8ebc958b4b ()
&lt;&#x2F;span&gt;&lt;span&gt;#8  0x40382bac in vPortTaskWrapper (pxCode=0x42018f50 &amp;lt;_ZN12awedio_esp3210audio_task17h6eb04a8ebc958b4bE.llvm.17939224925020937006&amp;gt;, pvParameters=0x3fcea8c4) at &#x2F;home&#x2F;ben&#x2F;esp&#x2F;esp-idf&#x2F;components&#x2F;freertos&#x2F;port&#x2F;xtensa&#x2F;port.c:142
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The mpsc channel is used in the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;10buttons&#x2F;awedio&quot;&gt;awedio&lt;&#x2F;a&gt; audio library I developed for the tablet project. It is used to send commands from an application task to the audio rendering task (e.g. pause a sound or change the volume).&lt;&#x2F;p&gt;
&lt;p&gt;Not knowing why the task was stuck in try_recv I tried to make it easier to reproduce the issue. I wasn&#x27;t able to reproduce the issue very reliably without physically pressing a lot of buttons even if I automated what pushing the buttons did in a loop.&lt;&#x2F;p&gt;
&lt;p&gt;After running out of time for the day, the answer came to me that evening while walking around the playground while my kids were playing.&lt;&#x2F;p&gt;
&lt;p&gt;I remembered that the main task which sends tasks to the audio backend is pinned to a single core. I also remembered that I set the audio thread to a higher priority than the main task to avoid audio buffer underruns. So while I originally thought the main task was idle (e.g. waiting for input), when I got back to my computer, a stack trace confirmed that it was actually paused while sending a command across the channel. The audio task was allowed to be on either core. When I push a button an interrupt is generated causing an interrupt service routine to run. Since an ISR runs with very high priority it could interrupt the main thread right at a time when it was sending a command to the audio thread. Apparently the audio thread can then be resumed on its core since it can run on either core.&lt;&#x2F;p&gt;
&lt;p&gt;So it appears try_recv is blocking on the sender. The stack trace shows that try_recv calls sched_yield which I believe is an attempt to let the sender run again to unblock the receiver. That doesn&#x27;t work on FreeRTOS if the tasks have different priorities since the higher priority task will always run if it can.&lt;&#x2F;p&gt;
&lt;p&gt;At first I wasn&#x27;t sure if the issue was with ESP&#x27;s implementation of the Rust std library (maybe it should call a different function to suspend the receiver task) but reading the docs of &lt;code&gt;sched_yield&lt;&#x2F;code&gt; its documented to not yield to tasks of lower priority.&lt;&#x2F;p&gt;
&lt;p&gt;After some thought it seemed possible to be able to reproduce the issue on Linux using a standard Rust toolchain. By pinning two threads to only run on the same core and giving the receiver thread a much higher priority than the sender thread, we reliably see deadlocks after a few seconds in the following code:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;PINNED_CORE&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;usize &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span&gt;(sender, receiver) = channel::&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;usize&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;std::thread::Builder::new()
&lt;&#x2F;span&gt;&lt;span&gt;    .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;sending&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;to_owned&lt;&#x2F;span&gt;&lt;span&gt;())
&lt;&#x2F;span&gt;&lt;span&gt;    .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;spawn&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;move &lt;&#x2F;span&gt;&lt;span&gt;|| {
&lt;&#x2F;span&gt;&lt;span&gt;        thread_priority::set_current_thread_priority(ThreadPriority::Min).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;        core_affinity::set_for_current(core_affinity::CoreId { id: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;PINNED_CORE &lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;loop &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;            sender.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;send&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;42&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;        }
&lt;&#x2F;span&gt;&lt;span&gt;    })
&lt;&#x2F;span&gt;&lt;span&gt;    .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; num_received = Arc::new(std::sync::atomic::AtomicUsize::new(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;));
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;std::thread::Builder::new()
&lt;&#x2F;span&gt;&lt;span&gt;    .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;receiving&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;to_owned&lt;&#x2F;span&gt;&lt;span&gt;())
&lt;&#x2F;span&gt;&lt;span&gt;    .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;spawn&lt;&#x2F;span&gt;&lt;span&gt;({
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; num_received = num_received.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;clone&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;move &lt;&#x2F;span&gt;&lt;span&gt;|| {
&lt;&#x2F;span&gt;&lt;span&gt;            thread_priority::set_current_thread_priority(ThreadPriority::Max).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;            core_affinity::set_for_current(core_affinity::CoreId { id: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;PINNED_CORE &lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;loop &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;                &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; start = Instant::now();
&lt;&#x2F;span&gt;&lt;span&gt;                &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; try_receive_result = receiver.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;try_recv&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;                &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; elapsed = start.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;elapsed&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;                &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span&gt; elapsed &amp;gt; Duration::from_secs(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;                    println!(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;try_recv blocked for &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;{:.2}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt; seconds&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, elapsed.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;as_secs_f32&lt;&#x2F;span&gt;&lt;span&gt;());
&lt;&#x2F;span&gt;&lt;span&gt;                }
&lt;&#x2F;span&gt;&lt;span&gt;                &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;match&lt;&#x2F;span&gt;&lt;span&gt; try_receive_result {
&lt;&#x2F;span&gt;&lt;span&gt;                    Ok(_) =&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;                        num_received.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;fetch_add&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;, std::sync::atomic::Ordering::SeqCst);
&lt;&#x2F;span&gt;&lt;span&gt;                    }
&lt;&#x2F;span&gt;&lt;span&gt;                    Err(TryRecvError::Empty) =&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;                        std::thread::sleep(Duration::from_millis(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;5&lt;&#x2F;span&gt;&lt;span&gt;));
&lt;&#x2F;span&gt;&lt;span&gt;                    }
&lt;&#x2F;span&gt;&lt;span&gt;                    Err(TryRecvError::Disconnected) =&amp;gt; unreachable!(),
&lt;&#x2F;span&gt;&lt;span&gt;                }
&lt;&#x2F;span&gt;&lt;span&gt;            }
&lt;&#x2F;span&gt;&lt;span&gt;        }
&lt;&#x2F;span&gt;&lt;span&gt;    })
&lt;&#x2F;span&gt;&lt;span&gt;    .&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;loop &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    std::thread::sleep(Duration::from_millis(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;500&lt;&#x2F;span&gt;&lt;span&gt;));
&lt;&#x2F;span&gt;&lt;span&gt;    println!(
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Receiving thread has received &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;{}&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;        num_received.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;load&lt;&#x2F;span&gt;&lt;span&gt;(std::sync::atomic::Ordering::SeqCst)
&lt;&#x2F;span&gt;&lt;span&gt;    )
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;    (&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;benhansen-io&#x2F;mpsc_deadlock_reproducer&quot;&gt;full code available&lt;&#x2F;a&gt;)&lt;&#x2F;p&gt;
&lt;p&gt;Example output of code on Linux showing the deadlock:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;...
&lt;&#x2F;span&gt;&lt;span&gt;Receiving thread has received 95667535
&lt;&#x2F;span&gt;&lt;span&gt;Receiving thread has received 95667535
&lt;&#x2F;span&gt;&lt;span&gt;Receiving thread has received 95667535
&lt;&#x2F;span&gt;&lt;span&gt;Receiving thread has received 95667535
&lt;&#x2F;span&gt;&lt;span&gt;try_recv blocked for 34.72 seconds
&lt;&#x2F;span&gt;&lt;span&gt;Receiving thread has received 95738090
&lt;&#x2F;span&gt;&lt;span&gt;Receiving thread has received 100046892
&lt;&#x2F;span&gt;&lt;span&gt;Receiving thread has received 103967861
&lt;&#x2F;span&gt;&lt;span&gt;...
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The better stack traces on Linux showed the code deadlocked at a point between where the sender first writes the value and where it writes a flag to indicate the value has been written. Unfortunately if the receiver attempts to read a value when it is being written it &lt;em&gt;waits&lt;&#x2F;em&gt; for the flag to say that the value has been fully written before continuing even in the try_recv path.&lt;&#x2F;p&gt;
&lt;p&gt;After reviewing the documentation of &lt;a href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;std&#x2F;sync&#x2F;mpsc&#x2F;struct.Receiver.html#method.try_recv&quot;&gt;try_recv&lt;&#x2F;a&gt; which says (emphasis mine):&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Attempts to return a pending value on this receiver &lt;strong&gt;without blocking&lt;&#x2F;strong&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;This method will &lt;strong&gt;never block&lt;&#x2F;strong&gt; the caller in order to wait for data to become available. Instead, this will always return &lt;em&gt;immediately&lt;&#x2F;em&gt; with a possible option of pending data on the channel.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;I was confident that the behavior does not match the documentation.  I filed &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rust&#x2F;issues&#x2F;112723&quot;&gt;a Rust issue&lt;&#x2F;a&gt; on GitHub. It is not normal for the Rust standard library to let me down. The conditions to hit the deadlock are rare and it is being looked at. Thanks to everyone who works on Rust and the standard library. It has been a pleasure to use.&lt;&#x2F;p&gt;
&lt;p&gt;I can prevent the issue for me by pinning the audio backend task to the other core that the main task does not run on. After doing that I haven&#x27;t seen a deadlock 🎉.&lt;&#x2F;p&gt;
&lt;p&gt;Thanks for taking the time to read. If your interested in hearing more about the programmable button tablet I am making for kids in Rust check out &lt;a href=&quot;https:&#x2F;&#x2F;www.10buttons.com&quot;&gt;10buttons.com&lt;&#x2F;a&gt; and follow along.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;some-takeaways&quot;&gt;Some takeaways&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;This is my second blog post entry and I found it very helpful for me personally. I started writing this blog post half-way through the investigation and it helped me organizing my thoughts and dig deeper. I might not have written the Linux reproducer code and filed a bug report without writing the blog post. It also provides a nice reflection allowing me to see where I got stuck and hopefully improve my process in the future.&lt;&#x2F;li&gt;
&lt;li&gt;Having the ability to easily debug an ESP32-S3 using GDB over USB is amazing and helps a lot in debugging non-trivial issues like this.&lt;&#x2F;li&gt;
&lt;li&gt;In this case it would have been nice if the ESP-IDF FreeRTOS implementation would have moved the audio thread to the IDLE core so the main task could resume on its pinned core when the audio thread calls sched_yield.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</description>
      </item>
      <item>
          <title>Linux Directory Permissions</title>
          <pubDate>Tue, 04 Oct 2022 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.benhansen.io/linux-directory-permissions/</link>
          <guid>https://www.benhansen.io/linux-directory-permissions/</guid>
          <description xml:base="https://www.benhansen.io/linux-directory-permissions/">&lt;style&gt;
  table.styled {
    border-collapse: collapse;
  }
  .styled td, .styled th {
    border: thin solid black;
    text-align: center;
    padding: 0.3rem;
  }
  .styled thead tr {
    background-color: #F0F0F0;
    text-align: left;
  }
  .read {
    color: #904000;
  }
  .write {
    color: #8B0000;
  }
  .execute {
    color: #008000;
  }
  .term {
    font-family: Menlo, Consolas, Monaco, Liberation Mono, Lucida Console, monospace;
    border: thin solid black;
    padding: 0.5rem;
    margin: 1rem;
  }
  .term.command {
    background-color: #F0F0F0;
  }
  .term .output {
    white-space: pre;
    display: none;
    overflow: scroll;
  }
&lt;&#x2F;style&gt;
&lt;p&gt;&lt;span class=&quot;read&quot;&gt;Read&lt;&#x2F;span&gt;, &lt;span class=&quot;write&quot;&gt;write&lt;&#x2F;span&gt; and &lt;span class=&quot;execute&quot;&gt;execute&lt;&#x2F;span&gt; permissions are used for directories as well as files on Linux but their meaning for directories is not as straight forward&lt;sup&gt;&lt;a href=&quot;#ref1&quot;&gt;1&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The permissions needed for each action are:&lt;&#x2F;p&gt;
&lt;table class=&quot;styled&quot;&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;
        Action
      &lt;&#x2F;th&gt;
      &lt;th&gt;
        Required Permission(s)
      &lt;&#x2F;th&gt;
    &lt;&#x2F;tr&gt;
  &lt;&#x2F;thead&gt;
  &lt;tr&gt;
    &lt;td&gt;
      Listing the entries of the directory
    &lt;&#x2F;td&gt;
    &lt;td&gt;
      &lt;span class=&quot;read&quot;&gt;Read&lt;&#x2F;span&gt;
    &lt;&#x2F;td&gt;
  &lt;&#x2F;tr&gt;
  &lt;tr&gt;
    &lt;td&gt;
      Adding or removing entries from the directory
    &lt;&#x2F;td&gt;
    &lt;td&gt;
      &lt;span class=&quot;write&quot;&gt;Write&lt;&#x2F;span&gt; and &lt;span class=&quot;execute&quot;&gt;Execute&lt;&#x2F;span&gt; (see also &quot;Restricted Deletion&quot; below)
    &lt;&#x2F;td&gt;
  &lt;&#x2F;tr&gt;
  &lt;tr&gt;
    &lt;td&gt;
      &quot;cd&quot; into the directory
    &lt;&#x2F;td&gt;
    &lt;td&gt;
      &lt;span class=&quot;execute&quot;&gt;Execute&lt;&#x2F;span&gt;
    &lt;&#x2F;td&gt;
  &lt;&#x2F;tr&gt;
  &lt;tr&gt;
    &lt;td&gt;
      Accessing the directory entries&#x27; contents or entry metadata
    &lt;&#x2F;td&gt;
    &lt;td&gt;
      &lt;span class=&quot;execute&quot;&gt;Execute&lt;&#x2F;span&gt;
    &lt;&#x2F;td&gt;
  &lt;&#x2F;tr&gt;
  &lt;tr&gt;
    &lt;td&gt;
      Modifying the directory entries&#x27; contents or entry metadata
    &lt;&#x2F;td&gt;
    &lt;td&gt;
      &lt;span class=&quot;execute&quot;&gt;Execute&lt;&#x2F;span&gt;
    &lt;&#x2F;td&gt;
  &lt;&#x2F;tr&gt;
  &lt;tr&gt;
    &lt;td&gt;
      Remove the directory.
    &lt;&#x2F;td&gt;
    &lt;td&gt;
      As with normal files, the permissions of the parent directory and not the directory itself determine if you can remove it. The directory must be empty.
    &lt;&#x2F;td&gt;
  &lt;&#x2F;tr&gt;
&lt;&#x2F;table&gt;
&lt;p&gt;Or listed inversely each permission allows:&lt;&#x2F;p&gt;
&lt;table class=&quot;styled&quot;&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;
        Permission
      &lt;&#x2F;th&gt;
      &lt;th&gt;
        Symbol
      &lt;&#x2F;th&gt;
      &lt;th&gt;
        &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;File-system_permissions#Numeric_notation&quot;&gt;Value&lt;&#x2F;a&gt;
      &lt;&#x2F;th&gt;
      &lt;th&gt;
        Description for directories
      &lt;&#x2F;th&gt;
    &lt;&#x2F;tr&gt;
  &lt;&#x2F;thead&gt;
  &lt;tr&gt;
    &lt;td&gt;
      &lt;span class=&quot;read&quot;&gt;Read&lt;&#x2F;span&gt;
    &lt;&#x2F;td&gt;
    &lt;td&gt;
      r
    &lt;&#x2F;td&gt;
    &lt;td&gt;
      4
    &lt;&#x2F;td&gt;
    &lt;td&gt;
      Allows listing the names of entries in the directory (e.g. using &quot;ls&quot;) but not entry metadata or contents.
    &lt;&#x2F;td&gt;
  &lt;&#x2F;tr&gt;
  &lt;tr&gt;
    &lt;td&gt;
      &lt;span class=&quot;write&quot;&gt;Write&lt;&#x2F;span&gt;
    &lt;&#x2F;td&gt;
    &lt;td&gt;
      w
    &lt;&#x2F;td&gt;
    &lt;td&gt;
      2
    &lt;&#x2F;td&gt;
    &lt;td&gt;
      Allows adding, removing or moving entries (but not their contents).
    &lt;&#x2F;td&gt;
  &lt;&#x2F;tr&gt;
  &lt;tr&gt;
    &lt;td&gt;
      &lt;span class=&quot;execute&quot;&gt;Execute&lt;&#x2F;span&gt;
    &lt;&#x2F;td&gt;
    &lt;td&gt;
      x
    &lt;&#x2F;td&gt;
    &lt;td&gt;
      1
    &lt;&#x2F;td&gt;
    &lt;td&gt;
      Allows accessing and modifying the entry contents and metadata. Also needed to add, delete or remove entries. Also called &quot;Search&quot; for directories&lt;sup&gt;&lt;a href=&quot;#ref2&quot;&gt;2&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;.
    &lt;&#x2F;td&gt;
  &lt;&#x2F;tr&gt;
&lt;&#x2F;table&gt;
&lt;h2 id=&quot;playground&quot;&gt;Playground&lt;&#x2F;h2&gt;
&lt;p&gt;See how the following commands are handled when executed on a directory named &lt;code&gt;testdir&lt;&#x2F;code&gt; that starts with a single file named &lt;code&gt;A.txt&lt;&#x2F;code&gt; when the executing user has the permissions you choose:&lt;&#x2F;p&gt;
&lt;form id=&quot;permissions_form&quot;&gt;
  &lt;fieldset&gt;
    &lt;legend&gt;Toggle permissions:&lt;&#x2F;legend&gt;
    &lt;span class=&quot;read&quot;&gt;
      &lt;input type=&quot;checkbox&quot; id=&quot;read&quot; name=&quot;read&quot;&gt;
      &lt;label for=&quot;read&quot;&gt;Read&lt;&#x2F;label&gt;
    &lt;&#x2F;span&gt;
    &lt;span class=&quot;write&quot;&gt;
      &lt;input type=&quot;checkbox&quot; id=&quot;write&quot; name=&quot;write&quot;&gt;
      &lt;label for=&quot;write&quot;&gt;Write&lt;&#x2F;label&gt;
    &lt;&#x2F;span&gt;
    &lt;span class=&quot;execute&quot;&gt;
      &lt;input type=&quot;checkbox&quot; id=&quot;execute&quot; name=&quot;execute&quot;&gt;
      &lt;label for=&quot;execute&quot;&gt;Execute&lt;&#x2F;label&gt;
    &lt;&#x2F;span&gt;
  &lt;&#x2F;fieldset&gt;
&lt;&#x2F;form&gt;
&lt;div class=&quot;term command&quot;&gt;$ chmod u=&lt;span id=&quot;mode&quot;&gt;&lt;&#x2F;span&gt; testdir&lt;&#x2F;div&gt;

&lt;div class=&quot;term&quot;&gt;
  &lt;div class=&quot;command&quot;&gt; 
    $ stat testdir

  &lt;&#x2F;div&gt;
  &lt;div class=&#x27;output EMPTY&#x27;&gt;File: testdir
  Size: 4096      	Blocks: 8          IO Block: 4096   directory
Device: 254,0	Inode: 57417470    Links: 2
Access: (0000&#x2F;d---------)  Uid: ( 1000&#x2F;     ben)   Gid: ( 1000&#x2F;     ben)
Access: 2030-01-01 08:30:00.000000000 -0000
Modify: 2030-01-01 08:30:00.000000000 -0000
Change: 2030-01-01 08:30:00.000000000 -0000
 Birth: 2030-01-01 08:30:00.000000000 -0000&lt;&#x2F;div&gt;
  &lt;div class=&#x27;output x&#x27;&gt;File: testdir
  Size: 4096      	Blocks: 8          IO Block: 4096   directory
Device: 254,0	Inode: 57417470    Links: 2
Access: (0100&#x2F;d--x------)  Uid: ( 1000&#x2F;     ben)   Gid: ( 1000&#x2F;     ben)
Access: 2030-01-01 08:30:00.000000000 -0000
Modify: 2030-01-01 08:30:00.000000000 -0000
Change: 2030-01-01 08:30:00.000000000 -0000
 Birth: 2030-01-01 08:30:00.000000000 -0000&lt;&#x2F;div&gt;
  &lt;div class=&#x27;output w&#x27;&gt;File: testdir
  Size: 4096      	Blocks: 8          IO Block: 4096   directory
Device: 254,0	Inode: 57417470    Links: 2
Access: (0200&#x2F;d-w-------)  Uid: ( 1000&#x2F;     ben)   Gid: ( 1000&#x2F;     ben)
Access: 2030-01-01 08:30:00.000000000 -0000
Modify: 2030-01-01 08:30:00.000000000 -0000
Change: 2030-01-01 08:30:00.000000000 -0000
 Birth: 2030-01-01 08:30:00.000000000 -0000&lt;&#x2F;div&gt;
  &lt;div class=&#x27;output wx&#x27;&gt;File: testdir
  Size: 4096      	Blocks: 8          IO Block: 4096   directory
Device: 254,0	Inode: 57417470    Links: 2
Access: (0300&#x2F;d-wx------)  Uid: ( 1000&#x2F;     ben)   Gid: ( 1000&#x2F;     ben)
Access: 2030-01-01 08:30:00.000000000 -0000
Modify: 2030-01-01 08:30:00.000000000 -0000
Change: 2030-01-01 08:30:00.000000000 -0000
 Birth: 2030-01-01 08:30:00.000000000 -0000&lt;&#x2F;div&gt;
  &lt;div class=&#x27;output r&#x27;&gt;File: testdir
  Size: 4096      	Blocks: 8          IO Block: 4096   directory
Device: 254,0	Inode: 57417470    Links: 2
Access: (0400&#x2F;dr--------)  Uid: ( 1000&#x2F;     ben)   Gid: ( 1000&#x2F;     ben)
Access: 2030-01-01 08:30:00.000000000 -0000
Modify: 2030-01-01 08:30:00.000000000 -0000
Change: 2030-01-01 08:30:00.000000000 -0000
 Birth: 2030-01-01 08:30:00.000000000 -0000&lt;&#x2F;div&gt;
  &lt;div class=&#x27;output rx&#x27;&gt;File: testdir
  Size: 4096      	Blocks: 8          IO Block: 4096   directory
Device: 254,0	Inode: 57417470    Links: 2
Access: (0500&#x2F;dr-x------)  Uid: ( 1000&#x2F;     ben)   Gid: ( 1000&#x2F;     ben)
Access: 2030-01-01 08:30:00.000000000 -0000
Modify: 2030-01-01 08:30:00.000000000 -0000
Change: 2030-01-01 08:30:00.000000000 -0000
 Birth: 2030-01-01 08:30:00.000000000 -0000&lt;&#x2F;div&gt;
  &lt;div class=&#x27;output rw&#x27;&gt;File: testdir
  Size: 4096      	Blocks: 8          IO Block: 4096   directory
Device: 254,0	Inode: 57417470    Links: 2
Access: (0600&#x2F;drw-------)  Uid: ( 1000&#x2F;     ben)   Gid: ( 1000&#x2F;     ben)
Access: 2030-01-01 08:30:00.000000000 -0000
Modify: 2030-01-01 08:30:00.000000000 -0000
Change: 2030-01-01 08:30:00.000000000 -0000
 Birth: 2030-01-01 08:30:00.000000000 -0000&lt;&#x2F;div&gt;
  &lt;div class=&#x27;output rwx&#x27;&gt;File: testdir
  Size: 4096      	Blocks: 8          IO Block: 4096   directory
Device: 254,0	Inode: 57417470    Links: 2
Access: (0700&#x2F;drwx------)  Uid: ( 1000&#x2F;     ben)   Gid: ( 1000&#x2F;     ben)
Access: 2030-01-01 08:30:00.000000000 -0000
Modify: 2030-01-01 08:30:00.000000000 -0000
Change: 2030-01-01 08:30:00.000000000 -0000
 Birth: 2030-01-01 08:30:00.000000000 -0000&lt;&#x2F;div&gt;
&lt;&#x2F;div&gt;

&lt;div class=&quot;term&quot;&gt;
  &lt;div class=&quot;command&quot;&gt; 
    $ ls -l testdir

  &lt;&#x2F;div&gt;
  &lt;div class=&#x27;output rw r&#x27;&gt;ls: cannot access &#x27;testdir&#x2F;A.txt&#x27;: Permission denied
total 0
-????????? ? ? ? ?            ? A.txt&lt;&#x2F;div&gt;
  &lt;div class=&#x27;output w EMPTY x wx&#x27;&gt;ls: cannot open directory &#x27;testdir&#x27;: Permission denied&lt;&#x2F;div&gt;
  &lt;div class=&#x27;output rwx rx&#x27;&gt;total 4
-rw-r--r-- 1 ben ben 12 Oct 12  2022 A.txt&lt;&#x2F;div&gt;
&lt;&#x2F;div&gt;

&lt;div class=&quot;term&quot;&gt;
  &lt;div class=&quot;command&quot;&gt; 
    $ cat testdir&#x2F;A.txt

  &lt;&#x2F;div&gt;
  &lt;div class=&#x27;output x rx wx rwx&#x27;&gt;Lorem Ipsum&lt;&#x2F;div&gt;
  &lt;div class=&#x27;output EMPTY rw w r&#x27;&gt;cat: testdir&#x2F;A.txt: Permission denied&lt;&#x2F;div&gt;
&lt;&#x2F;div&gt;

&lt;div class=&quot;term&quot;&gt;
  &lt;div class=&quot;command&quot;&gt; 
    $ echo &quot;New file&quot; &gt; testdir&#x2F;B.txt

  &lt;&#x2F;div&gt;
  &lt;div class=&#x27;output wx rwx&#x27;&gt;&lt;&#x2F;div&gt;
  &lt;div class=&#x27;output x r rw w rx EMPTY&#x27;&gt;bash: line 1: testdir&#x2F;B.txt: Permission denied&lt;&#x2F;div&gt;
&lt;&#x2F;div&gt;

&lt;div class=&quot;term&quot;&gt;
  &lt;div class=&quot;command&quot;&gt; 
    $ cd testdir

  &lt;&#x2F;div&gt;
  &lt;div class=&#x27;output rx wx x rwx&#x27;&gt;&lt;&#x2F;div&gt;
  &lt;div class=&#x27;output EMPTY r w rw&#x27;&gt;bash: line 1: cd: testdir: Permission denied&lt;&#x2F;div&gt;
&lt;&#x2F;div&gt;
&lt;script&gt;
  const r = document.getElementById(&quot;read&quot;);
  const w = document.getElementById(&quot;write&quot;);
  const x = document.getElementById(&quot;execute&quot;);
  const modeLabel = document.getElementById(&quot;mode&quot;);
  const form = document.getElementById(&quot;permissions_form&quot;);
  
  function updateOutputs() {
    let modeString = (r.checked ? &quot;r&quot; : &quot;&quot;)
      + (w.checked ? &quot;w&quot; : &quot;&quot;)
      + (x.checked ? &quot;x&quot; : &quot;&quot;);
    modeLabel.innerHTML = modeString

    if (modeString == &quot;&quot;) {
      modeString = &quot;EMPTY&quot;;
    }
    const outputs = document.querySelectorAll(&quot;.term .output&quot;);
    for (output of outputs) {
      output.style.display = output.classList.contains(modeString) ? &quot;block&quot; : &quot;none&quot;;
    }
  }

  updateOutputs();
  form.addEventListener(&quot;input&quot;, (evt) =&gt; {
    updateOutputs();
  });
&lt;&#x2F;script&gt;
&lt;h2 id=&quot;examples-explained&quot;&gt;Examples explained&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;A directory where your user has &lt;span class=&quot;execute&quot;&gt;execute&lt;&#x2F;span&gt; but not &lt;span class=&quot;read&quot;&gt;read&lt;&#x2F;span&gt; permissions would allow you to edit or modify files that you can not list with &quot;ls&quot; if you already know their path.&lt;&#x2F;li&gt;
&lt;li&gt;A directory where your user has &lt;span class=&quot;write&quot;&gt;write&lt;&#x2F;span&gt; and &lt;span class=&quot;execute&quot;&gt;execute&lt;&#x2F;span&gt; but not &lt;span class=&quot;read&quot;&gt;read&lt;&#x2F;span&gt; would allow you to add new files and modify existing files but not see them.&lt;&#x2F;li&gt;
&lt;li&gt;A directory with only &lt;span class=&quot;write&quot;&gt;write&lt;&#x2F;span&gt; permissions is the same as a directory with no permissions&lt;sup&gt;&lt;a href=&quot;#ref3&quot;&gt;3&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;Most of the time you will always want to set &lt;span class=&quot;execute&quot;&gt;execute&lt;&#x2F;span&gt; if you are also setting either &lt;span class=&quot;read&quot;&gt;read&lt;&#x2F;span&gt; or &lt;span class=&quot;write&quot;&gt;write&lt;&#x2F;span&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;more-advanced-permissions&quot;&gt;More advanced permissions&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;Restricted Deletion (also called Sticky Bit) - For directories only lets the file or directory owner move or delete a file inside the directory.&lt;&#x2F;li&gt;
&lt;li&gt;Set Group Identity (setgid) - For directories causes new files to be created with the same group as the directory rather than the group of the process creating the file.&lt;&#x2F;li&gt;
&lt;li&gt;Set User Identity (setuid) - Ignored by Linux and most Unix systems.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;span id=&quot;ref1&quot;&gt;1&lt;&#x2F;span&gt;. This article is focused on directory permissions. For an intro to Linux permissions in general you can read this &lt;a href=&quot;https:&#x2F;&#x2F;www.linuxfoundation.org&#x2F;blog&#x2F;blog&#x2F;classic-sysadmin-understanding-linux-file-permissions&quot;&gt;Linux Foundation article&lt;&#x2F;a&gt;, the &lt;a href=&quot;https:&#x2F;&#x2F;man7.org&#x2F;linux&#x2F;man-pages&#x2F;man1&#x2F;chmod.1.html&quot;&gt;chmod man page&lt;&#x2F;a&gt; or search for more.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;span id=&quot;ref2&quot;&gt;2&lt;&#x2F;span&gt;. The &lt;a href=&quot;https:&#x2F;&#x2F;man7.org&#x2F;linux&#x2F;man-pages&#x2F;man1&#x2F;chmod.1.html&quot;&gt;chmod man page&lt;&#x2F;a&gt; is the main source of this page&#x27;s information.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;span id=&quot;ref3&quot;&gt;3&lt;&#x2F;span&gt;. See &lt;a href=&quot;https:&#x2F;&#x2F;unix.stackexchange.com&#x2F;a&#x2F;149291&#x2F;45680&quot;&gt;this SO question&lt;&#x2F;a&gt;. I like the OPs analogy of &quot;execute&quot; permitting access to inodes.&lt;&#x2F;p&gt;
</description>
      </item>
    </channel>
</rss>
