Saturday, June 26, 2021

Know your design tools — The Performing Half-baked object case

Among other important paradigms in software design, object orientation is a common set of design techniques these days. Hierarchies of classes, derivative class instantiations and virtual calls can be often found on software codebases. Hence, the relevance of the Performing Half-baked object case while designing object-oriented software.

The Performing Half-baked object case occurs where a virtual call is executed onto an unborn or uncreated or uninitialized class instance: the actual behavior can be unexpected, of course.

A couple of examples.

Consider the output of this Windows/Visual C++ code:

#include <iostream>

class AA
{
    public:
    AA()
    {
        std::cout << "AA()\n";
        f();
    }
    virtual void f() { std::cout << "AA.f\n"; }
};

class BB : public AA
{
    public:
    BB()
    {
        std::cout << "BB()\n";
        f();
    }
    virtual void f() { std::cout << "BB.f\n";}
};

void main()
{
	auto x = new BB;
}

For another –different– behavior, consider the output of this .NET/Visual C# code:

using static System.Console;

class AA
{
    public AA()
    {
        WriteLine("AA()");
        f();
    }
    public virtual void f() => WriteLine("AA.f");
}

class BB : AA
{
    public BB()
    {
        WriteLine("BB()");
        f();
    }
    public override void f() => WriteLine("BB.f");
}

class Program
{
    public static void Main()
    {
        var x = new BB();
    }
}

The Performing Half-baked object is a case that designers of object-oriented languages and runtimes have typically faced. Any designer using those languages and runtimes should be aware of this and other cases during the process of object creation.

Singleton in .NET

If I need a process-level global write-once/read-many state, a global variable, a Singleton, in .NET Runtime, then I keep in mind what I said at the post: Know your design tools — The Singleton case.

It is also relevant to keep in mind:

  1. the Visual C# language specification about Static constructors,

  2. the section on Static Constructors of the Visual C# Programming Guide,

  3. the description of what BeforeFieldInit does, and

  4. the category (Performance) and the 'When to suppress warnings' section of the CA1810 Rule.

The observations on that old post have been useful for me over the years. Specially while designing stateful software services, where the use of locking, global state, global variables, or Singletons do not hinder throughput. On the other hand, in the context of stateless software services processing a high number of concurrent requests, whose scalability is very important, then –to be clear–, I do not prefer any explicit use of locking, any global state, any global variables or any Singleton.

Here is a demonstrative version of a .NET/Visual C#-based Singleton derived from the sample code of such old post and a couple of MSTest test cases as evidence of its correctness, included post-conditions for same class instance and for same global value:

#region SUT
/// <summary>
///  https://docs.microsoft.com/en-us/archive/blogs/marcod/know-your-design-tools-the-singleton-case
///  https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/classes#static-constructors
///  https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/static-constructors
///  https://docs.microsoft.com/en-us/dotnet/api/system.reflection.typeattributes?view=net-5.0
///  https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1810
/// </summary>
public sealed class TimepointSingleton
{
    #region Nested static class with access to private outer instance constructor.
    public static class Timepoint
    {
        private static readonly TimepointSingleton instance;

        static Timepoint()
        {
            instance = new TimepointSingleton();
        }

        public static TimepointSingleton Instance { get => instance; }
    }
    #endregion

    #region Outer class for instance-level members of Singleton.
    private DateTime point;

    private TimepointSingleton()
    {
        point = DateTime.Now;
        WriteLine("Timepoint acquired");

        //This simulates resource contention to better
        //show up the multithreading problem when no
        //proper thread synchronization is in place.
        Thread.Sleep(4000);
    }

    public DateTime GlobalTimepoint { get => point; }

    public override string ToString() => $"{GlobalTimepoint:s}";
    #endregion
}
#endregion

Test cases (MSTest) as evidence of the expected and actual behavior:

[TestClass]
public class SingletonSpec
{
    [TestMethod]
    public void two_reads()
    {
        //Arrange
        TimepointSingleton a = null;
        TimepointSingleton b = null;

        //Act
        a = TimepointSingleton.Timepoint.Instance;
        b = TimepointSingleton.Timepoint.Instance;

        //Assert
        Assert.IsNotNull(a);
        Assert.IsTrue(AreTheseTheExactSameObject(a, b));
        Assert.AreEqual($"{a}", $"{b}");

        static bool AreTheseTheExactSameObject(object a, object b) => object.ReferenceEquals(a, b);
    }

    [TestMethod]
    public void concurrent_reads()
    {
        //Arrange
        int read_count = 15;
        using var sync = new ManualResetEvent(false);
        var reads = new ConcurrentBag<string>();
        var instances = new ConcurrentBag<TimepointSingleton>();
        var threads = Enumerable.Range(0, read_count).Aggregate(new List<Thread>(), (whole, next) => { whole.Add(new Thread(operation)); return whole; });

        //Act
        threads.ForEach(t => t.Start());
        sync.Set();
        threads.ForEach(t => t.Join());

        //Assert
        Assert.AreEqual(read_count, reads.Count);
        Assert.IsTrue(reads.All(read => string.CompareOrdinal(read, $"{TimepointSingleton.Timepoint.Instance.GlobalTimepoint:s}") == 0));
        Assert.AreEqual(read_count, instances.Count);
        Assert.IsTrue(instances.All(instance => object.ReferenceEquals(instance, TimepointSingleton.Timepoint.Instance)));

        void operation()
        {
            WriteLine($"[{Thread.CurrentThread.ManagedThreadId}] {DateTime.Now:s} Started");
            sync.WaitOne();
            var global_state = TimepointSingleton.Timepoint.Instance;
            reads.Add($"{global_state}");
            instances.Add(global_state);
            WriteLine($"[{Thread.CurrentThread.ManagedThreadId}] {DateTime.Now:s} Instance value: {global_state} hash: {global_state.GetHashCode()}");
        }
    }
}

Sunday, September 22, 2019

Freethinking and free inquiry about software creation

I very much enjoy computer programming. I also enjoy reading authors that talk about it from diverse perspectives. Just as much as practicing philosophy and reading whoever author that talks about philosophy —taken as the basic human impulse to go deeper into what causes doubt and wonder.

So far, I would say that some mechanical and tedious tasks related to computer programming are no longer a major challenge because we now have a lot of good tools helping with those tasks. On the other hand, the intellectual challenge of computer programming remains as a major challenge even today. Of course, now many good lessons are at reach, lessons from thought and experience of the last seven decades. But the intellectual challenge of non-trivial software endeavors remains in the category of major intellectual challenges.

Freethinking and free inquiry about the historical development of software creation is still worthwhile: which theses –or which aspects of them–by original authors are still current? How far I am able to articulate now those main theses of the field?

«At the time I was being groomed to become a very good theoretical physicist, but in 1955 that training was aborted when I decided to become a programmer instead. Not suffering from excessive modesty, I had what I considered a very good reason: I had concluded that programming presented a greater challenge than theoretical physics.»

Under the spell of Leibniz's Dream. Prof.dr Edsger W.Dijkstra.

«I felt that my problems as a programmer were for a large portion beyond the scope of what Polya covered. At first I hesitated to say so aloud, because stressing the exceptional nature of the one's field is usually a sure way of making oneself utterly ridiculous. But after careful consideration I concluded that the intellectual challenge presented by the programming task is, indeed, as unprecedented as the high-speed automatic computer itself.»

"Craftsman or Scientist?" prof.dr.Edsger W.Dijkstra.

Saturday, September 14, 2019

Writing good software

Writing good software takes time and a lot of practice, diverse concepts, values –like diligence–, a lot of patience, and other skills. It is kind of similar to master a musical instrument.

It is good to take time everyday to learn and practice: write working code everyday.

Just like music, first you write code everyday to implement usual algorithms (searching, sorting, etc.) by yourself.

Eventually, you gain enough skill to start your own little compositions.

Sunday, September 16, 2018

All great systems are made by great people

About great people as the actual source of great systems, I think we should go deeper on what entails to be called “great”, both, people and systems. The act of design, and its related dexterities, is one factor towards such professional greatness. Much of these questions have been thoroughly researched by many authors. I have quoted one of them in the following old post: Good designs come from good designers, good designers come from....

Furthermore, great people are required for great systems on both closely related realms: technical and management. Great teams grouping individual designers of all sorts of artifacts, e.g., test cases or executable software components, should afford great data processing systems; likewise, management should afford great organizational systems as supportive environments for those teams.

Gerald M. Weinberg —and many others— have already pointed out that the history of software development is paved with failed attempts to realize gains in quality and productivity without first creating a supportive environment. To improve bad situations, many managers spend their money on tooling, methodologies, outsourcing, training, application packages, etc., but they rarely spend enough to remove the management that made those situations in the first place or they hardly spend enough to improve them.

As Gerald M. Weinberg also said, we —software developers— have always been a would-be profession, and we will remain a would-be profession until we outgrow our obsession with quick fixes that don’t involve fixing the managers themselves.

Of course, as the saying goes, «you’ll never clear the water until you get the hogs out of the creek». But, let’s be crystal clear: such hogs are not bad people, but bad ideas. In other words, many underlying theories of software development management are obsolete; including many theories of project management.

This is already known since decades ago. I just recall an old post about it: The Underlying Theory of Project Management is Obsolete.

Saturday, September 15, 2018

Back to basics: Demanding reading

The following article came to my attention: The Satir Change Model.

An article that includes references to the works of Virginia Satir and Gerald M. Weinberg is a promising reading. Of course, the direct reading of the seminal works by those authors –among other luminaries– would be the best reading. Though the closer to the seminal authors, the more demanding the reading would be.

That kind of demanding reading is what I am talking about when I say that we —our profession— need to go back to the basics.

Saturday, August 11, 2018

Gerald M. Weinberg

One of the finest thinkers on computer software development has passed away on August 7, 2018.

«...computer scientist, author and teacher of the psychology and anthropology of computer software development.» Wikipedia

http://www.geraldmweinberg.com

http://secretsofconsulting.blogspot.com

https://www.facebook.com/gerald.m.weinberg

https://twitter.com/JerryWeinberg/

https://www.facebook.com/terra.ziporyn.snider/posts/10156674749391457