Threading challenge: calculate fib(45) while spinning

Ali Çehreli acehreli at yahoo.com
Fri Oct 15 14:01:43 UTC 2021


On 10/14/21 8:54 PM, Ali Çehreli wrote:

 >    writefln!"\rFibonacci(%d) = %d"(n, fibN);

That '\r' bothered me because the actual work needs to know what the 
spinner is doing to clear its remaining character.

 >        receiveTimeout(delay,
 >                       (OwnerTerminated msg) {

And there is a race condition because the spinner can print an extra 
character by the time it receives the OwnerTerminated message. (You can 
observe this by adding e.g. Thread.sleep(300.msecs) after the 
"\rFibonnacci..." line above.)

So, I improved it by removing both of those concerns as well as adding 
the following:

- An optional message when spinning (it can be further improved because 
there is an extra space character if the message is empty)

- A withSpinner() function to work with any delegate

The only requirement is that the delegate should not output to stdout if 
we want a clean output.

import std.stdio : stdout, writef, writefln;
import std.concurrency : receiveTimeout, send, spawn;
import std.traits : ReturnType;
import core.thread : Duration, msecs, Thread;
import std.range : cycle, repeat, walkLength;
import std.format : format;

void main() {
   enum n = 45;

   int fibN; // Left mutable not to complicate the example

   withSpinner({
       fibN = fib(n); // slow
     },
     format!"Calculating fib(%s)"(n));

   writefln!"Fibonacci(%d) = %d"(n, fibN);
}

// The delegate 'dg' should not output to stdout.
void withSpinner(Dg)(Dg dg,
                      string message = null,
                      Duration delay = 100.msecs) {
   shared(bool) spinnerDone = false;
   auto s = spawn(&spinner, message, delay, &spinnerDone);

   // Do work while spinning
   dg();

   // Tell the spinner to stop (the value does not matter)
   s.send(0x0FF);

   // Busy(ish) wait until the spinner is done
   while (!spinnerDone) {
     Thread.yield();
   }
}

void spinner(string message,
              Duration delay,
              shared(bool) * done) {
   foreach (c; `-\|/`.cycle) {
     if (receiveTimeout(delay, (int _) {})) {
       // Stop request received

       // Clear the spinning message
       writef("\r%s  \r", " ".repeat(walkLength(message)));

       // Tell the user we are done
       *done = true;
       return;
     }
     writef!"\r%s %c"(message, c);
     stdout.flush();
   }
}

auto fib(int x) {
   if (x < 2) {
     return x;
   }
   return fib(x-1) + fib(x-2);
}

Ali




More information about the Digitalmars-d-learn mailing list